Swf2MP3(国外)保护技术深入剖析
软件类别:国外软件/共享版/音频工具
运行环境:Win9x/Me/NT/2000/XP
开 发 商:http://www.hootech.com/
软件介绍:
flash工具,可以释放出flash电影中(swf 文件)所有的声音元素,同时支持突发声音和流声音,支持2种格式(.mp3和.wav).


前言:My wife要大量地从swf中取Mp3放手机里听(比如新xx的课程),网上正好有这个工具。但无注册码可找,甚感疑惑。况且这个是老外的工具,遂破之。本文主要阐述原理,细节省略。

简单流程:运行后读取注册表中一串60字节的关键数据进行处理,如果符合注册条件就正常使用,不再修改注册表数据否则会将已使用的次数加密后写入该注册表数据的特定地方,同时写入的还有判断是否注册的条件数据,最后弹出注册框显示使用的次数。该软件爆破很简单,改变一下运行的流程,就会在注册表中生成符合注册条件的数据,以后就可以正常使用了。由于卸载时不清除注册表中该处关键数据,导致在该机重装该软件后仍可正常使用。 

一、先来看看如何判断是否注册。关键位置在这:
0040DE6E   .  52            PUSH EDX                                 ;三个外部参数
0040DE6F   .  6A 1E         PUSH 1E                                  ;
0040DE71   .  50            PUSH EAX                                 ;
0040DE72   .  E8 C9EEFFFF   CALL Swf2MP3(.0040CD40  ;//判断注册 并计算使用次数
0040DE77   .  83C4 0C       ADD ESP,0C                               ; 返回eax=1表示注册
0040DE7A   .  85C0          TEST EAX,EAX                            
0040DE7C   .  74 1E         JE SHORT Swf2MP3(.0040DE9C
0040DE7E   .  8B86 8C260000 MOV EAX,DWORD PTR DS:[ESI+268C]         
0040DE84   .  8BCE          MOV ECX,ESI
0040DE86   .  0D 00000096   OR EAX,96000000
0040DE8B   .  8986 8C260000 MOV DWORD PTR DS:[ESI+268C],EAX
 ;  esi+268c 处存放初始值96000000
0040DE91   .  E8 6A270000   CALL Swf2MP3(.00410600
0040DE96   .  8B4424 0C     MOV EAX,DWORD PTR SS:[ESP+C]
0040DE9A   .  EB 13         JMP SHORT Swf2MP3(.0040DEAF
0040DE9C   >  8B4424 0C     MOV EAX,DWORD PTR SS:[ESP+C]   ;  未注册的跳到这
0040DEA0   .  83F8 1E       CMP EAX,1E                                          ;  有没有30次
0040DEA3   .  72 0A         JB SHORT Swf2MP3(.0040DEAF
0040DEA5   .  818E 8C260000>OR DWORD PTR DS:[ESI+268C],40000000      
;  超过试用次数第30位置1
0040DEAF   >  8B8E 8C260000 MOV ECX,DWORD PTR DS:[ESI+268C]
0040DEB5   .  F7C1 00000002 TEST ECX,2000000        判断32位的标志中有无变化
0040DEBB   .  74 18         JE SHORT Swf2MP3(.0040DED5
0040DEBD   .  F7C1 00000004 TEST ECX,4000000
0040DEC3   .  74 10         JE SHORT Swf2MP3(.0040DED5
0040DEC5   .  F7C1 00000010 TEST ECX,10000000
0040DECB   .  74 08         JE SHORT Swf2MP3(.0040DED5
0040DECD   .  F7C1 00000080 TEST ECX,80000000
0040DED3   .  75 37         JNZ SHORT Swf2MP3(.0040DF0C  ;  即注册过的跳走返回1
0040DED5   >  40            INC EAX                                        ;  上次使用的次数inc了一下
0040DED6   .  8D4C24 20     LEA ECX,DWORD PTR SS:[ESP+20]
0040DEDA   .  50            PUSH EAX                                      ;//使用的次数(含本次)
0040DEDB   .  51            PUSH ECX
0040DEDC   .  894424 14     MOV DWORD PTR SS:[ESP+14],EAX
0040DEE0   .  E8 0BEDFFFF   CALL Swf2MP3(.0040CBF0     ;生成注册表60字节数据  0040DEE5   .  83C4 08        ADD ESP,8
0040DEE8   .  8D5424 20     LEA EDX,DWORD PTR SS:[ESP+20]
0040DEEC   .  8BCE          MOV ECX,ESI
0040DEEE   .  52            PUSH EDX
0040DEEF   .  6A 01         PUSH 1
0040DEF1   .  E8 4A010000   CALL Swf2MP3(.0040E040
0040DEF6   .  8B4424 0C     MOV EAX,DWORD PTR SS:[ESP+C]  
;前面调用返回的使用次数
0040DEFA   .  8B4E 1C       MOV ECX,DWORD PTR DS:[ESI+1C]
0040DEFD   .  6A 00         PUSH 0                     ; /lParam = 0
0040DEFF   .  50            PUSH EAX                  //发送使用的次数 显示时用到
0040DF00   .  68 77040000   PUSH 477           ; |Message = MSG(477)
0040DF05   .  51            PUSH ECX                   ; |hWnd
0040DF06   .  FF15 ACD44500 CALL DWORD PTR DS:[<&USER32.PostMessageA 0040DF0C   >  5F            POP EDI
0040DF0D   .  5E            POP ESI
0040DF0E   .  B8 01000000   MOV EAX,1
0040DF13   .  5B            POP EBX
0040DF14   .  83C4 50       ADD ESP,50
0040DF17   .  C3            RETN


0040CD40  /$  8B4C24 04     MOV ECX,DWORD PTR SS:[ESP+4]
.......
0040CE7D  |.  33C0              XOR EAX,EAX
0040CE7F  |.  5B                  POP EBX
0040CE80  \.  C3                  RETN
由于算法比较复杂,用hexrays转换成伪c代码分析。
外部参数a1指向读取的注册表数据的起始处,a2是1Eh即30,a3地址处存放计算得到的已使用的次数(不包括本次)。
signed int __cdecl sub_40CD40(int a1, int a2, int a3) 
{  //内部变量定义省略
   ....
  v4 = a1;
  v3 = *(_DWORD *)(a1 + 12);
  if ( (v3 ^ *(_DWORD *)(a1 + 56) ^ 0xB927102E) != 698926849
    || (v5 = *(_DWORD *)(a1 + 4), v6 = *(_DWORD *)a1, a1 = *(_DWORD *)(a1 + 8), v7 = *(_DWORD *)(v4 + 16), 

(v3 ^ 0xB927102E) + (v7 ^ 0xB927102E) + (a1 ^ 0xB927102E) + (v5 ^ 0xB927102E) + (v6 ^ 0xB927102E) != ~*

(_DWORD *)(v4 + 24))
    || (v8 = *(_DWORD *)(v4 + 36), (v8 ^ 0x28A79C95) * (*(_DWORD *)(v4 + 32) ^ 0x28A79C95) != ~*(_DWORD *)(v4 

+ 48)) )
  {
    *(_DWORD *)a3 = a2 + 1;
    result = 0;
  }
  else
  {
    v9 = v7 ^ *(_DWORD *)(v4 + 20) ^ 0xB927102E;
    if ( v9 != -1875289631    //即0x903959E1 
      || (v6 ^ *(_DWORD *)(v4 + 28) ^ 0xB927102E) != 1686250269
      || (v5 ^ *(_DWORD *)(v4 + 40) ^ 0xB927102E) != -1988021778
      || (v8 ^ *(_DWORD *)(v4 + 44) ^ 0x28A79C95) != -1740411931
      || (*(_DWORD *)(v4 + 32) ^ *(_DWORD *)(v4 + 52) ^ 0x28A79C95) != 374450403 )
    {
      if ( v9 == -1989516033 )
      {   //未注册过的到这 此时a1是*(_DWORD *)(a1 + 8) 注意前面有重新赋值
          //已使用的次数(不含本次)=偏移8处的^偏移40处的异^0xB927102E 
        *(_DWORD *)a3 = a1 ^ *(_DWORD *)(v4 + 40) ^ 0xB927102E; 
        result = 0;
      }
      else
      {
        *(_DWORD *)a3 = a2 + 1;  //31次
        result = 0;
      }
    }
    else
    { //注册过的到这执行
      result = 1;
    }
  }
  return result;
}


//生成60字节注册表数据  a2是已使用的次数(含本次)  a1指向60字节的数据buffer起始地址
signed int __cdecl sub_40CBF0(int a1, int a2) 
{  //内部变量定义省略
   ....
  v3 = time(0);
  v4 = GetTickCount();
  srand(v4 * v3);
  v5 = rand();
  v6 = a1;
  *(_DWORD *)a1 = rand() * v5;
  v7 = rand();
  *(_DWORD *)(v6 + 4) = rand() * v7;
  v8 = rand();
  *(_DWORD *)(v6 + 8) = rand() * v8;                 // 偏移8h处=rand1
  v9 = rand();
  *(_DWORD *)(v6 + 12) = rand() * v9;
  v10 = rand();
  v11 = rand() * v10;
  v12 = *(_DWORD *)v6 + *(_DWORD *)(v6 + 12) + *(_DWORD *)(v6 + 8);
  *(_DWORD *)(v6 + 16) = v11;
  *(_DWORD *)(v6 + 24) = ~(*(_DWORD *)(v6 + 4) + v11 + v12);
  v13 = rand();
  *(_DWORD *)(v6 + 32) = rand() * v13;
  v14 = rand();
  v15 = rand() * v14;
  *(_DWORD *)(v6 + 36) = v15;
  v16 = *(_DWORD *)(v6 + 12) ^ 0x29A8C701;
  v17 = *(_DWORD *)(v6 + 16) ^ 0x896A64FF;
  *(_DWORD *)(v6 + 48) = ~(*(_DWORD *)(v6 + 32) * v15);
  *(_DWORD *)(v6 + 56) = v16;
  *(_DWORD *)(v6 + 20) = v17;
  v18 = rand();
  v19 = a2;                                            //注意v19变成了已使用的次数(含本次)       
  *(_DWORD *)(v6 + 28) = v18;
  *(_DWORD *)(v6 + 40) = v19 ^ *(_DWORD *)(v6 + 8);    // 偏移40处=已使用的次数(含本次)^rand1
  *(_DWORD *)(v6 + 44) = rand();
  v20 = rand();
  v21 = *(_DWORD *)v6;
  v22 = *(_DWORD *)(v6 + 4);
  *(_DWORD *)(v6 + 52) = v20;
  *(_DWORD *)v6 = v21 ^ 0xB927102E;
  v23 = *(_DWORD *)(v6 + 12);
  v24 = *(_DWORD *)(v6 + 8) ^ 0xB927102E;    //v24=rand1^ 0xB927102E
  *(_DWORD *)(v6 + 4) = v22 ^ 0xB927102E;
  v25 = *(_DWORD *)(v6 + 16);
  *(_DWORD *)(v6 + 8) = v24;                //偏移8处的=rand1^ 0xB927102E
  v26 = *(_DWORD *)(v6 + 32);
  *(_DWORD *)(v6 + 12) = v23 ^ 0xB927102E;
  v27 = *(_DWORD *)(v6 + 36);
  *(_DWORD *)(v6 + 16) = v25 ^ 0xB927102E;
  *(_DWORD *)(v6 + 32) = v26 ^ 0x28A79C95;
  *(_DWORD *)(v6 + 36) = v27 ^ 0x28A79C95;
  return 1;
}
到此根据第一个伪代码中的:已使用的次数(不含本次)=偏移8处的^偏移40处的^0xB927102E 
下次程序运行时CALL Swf2MP3(.0040CD40 调用中将计算得到:
运行计数=rand1^ 0xB927102E^已使用的次数^rand1^0xB927102E=已使用的次数  


二、再来看看注册算法。位置在这:

0040CE90  /$  83EC 60       SUB ESP,60
0040CE93  |.  56            PUSH ESI
0040CE94  |.  8B7424 68     MOV ESI,DWORD PTR SS:[ESP+68]
0040CE98  |.  56            PUSH ESI                                 ; /String
0040CE99  |.  FF15 F0D24500 CALL DWORD PTR DS:[<&KERNEL32.lstrlenA>] ; \lstrlenA
0040CE9F  |.  83F8 40       CMP EAX,40
0040CEA2  |.  74 07         JE SHORT 111.0040CEAB
0040CEA4  |.  33C0          XOR EAX,EAX
0040CEA6  |.  5E            POP ESI
0040CEA7  |.  83C4 60       ADD ESP,60
0040CEAA  |.  C3            RETN
0040CEAB  |>  53            PUSH EBX
0040CEAC  |.  55            PUSH EBP
0040CEAD  |.  8B2D A0D24500 MOV EBP,DWORD PTR DS:[<&KERNEL32.lstrcpy>;  kernel32.lstrcpynA
0040CEB3  |.  57            PUSH EDI
0040CEB4  |.  8D7C24 10     LEA EDI,DWORD PTR SS:[ESP+10]
0040CEB8  |.  BB 08000000   MOV EBX,8
0040CEBD  |>  6A 09         /PUSH 9
0040CEBF  |.  8D4424 34     |LEA EAX,DWORD PTR SS:[ESP+34]
0040CEC3  |.  56            |PUSH ESI
0040CEC4  |.  50            |PUSH EAX
0040CEC5  |.  FFD5          |CALL EBP
0040CEC7  |.  57            |PUSH EDI
0040CEC8  |.  8D4C24 34     |LEA ECX,DWORD PTR SS:[ESP+34]
0040CECC  |.  68 08354700   |PUSH 111.00473508                       ;  ASCII "%X"
0040CED1  |.  51            |PUSH ECX
0040CED2  |.  83C6 08       |ADD ESI,8
0040CED5  |.  E8 59DD0200   |CALL 111.0043AC33
0040CEDA  |.  83C4 0C       |ADD ESP,0C
0040CEDD  |.  83C7 04       |ADD EDI,4
0040CEE0  |.  4B            |DEC EBX
0040CEE1  |.^ 75 DA         \JNZ SHORT 111.0040CEBD
0040CEE3  |.  8B4C24 2C     MOV ECX,DWORD PTR SS:[ESP+2C]
0040CEE7  |.  8B5424 14     MOV EDX,DWORD PTR SS:[ESP+14]
0040CEEB  |.  8BC1          MOV EAX,ECX
0040CEED  |.  8B7C24 1C     MOV EDI,DWORD PTR SS:[ESP+1C]
0040CEF1  |.  33C2          XOR EAX,EDX
0040CEF3  |.  8B7424 20     MOV ESI,DWORD PTR SS:[ESP+20]
0040CEF7  |.  25 4AC59854   AND EAX,5498C54A
0040CEFC  |.  8B5C24 10     MOV EBX,DWORD PTR SS:[ESP+10]
0040CF00  |.  8BD0          MOV EDX,EAX
0040CF02  |.  334424 14     XOR EAX,DWORD PTR SS:[ESP+14]
0040CF06  |.  33D1          XOR EDX,ECX
0040CF08  |.  895424 38     MOV DWORD PTR SS:[ESP+38],EDX
0040CF0C  |.  8B5424 28     MOV EDX,DWORD PTR SS:[ESP+28]
0040CF10  |.  8BCA          MOV ECX,EDX
0040CF12  |.  894424 40     MOV DWORD PTR SS:[ESP+40],EAX
0040CF16  |.  33CF          XOR ECX,EDI
0040CF18  |.  8B4424 18     MOV EAX,DWORD PTR SS:[ESP+18]
0040CF1C  |.  81E1 74ECAB79 AND ECX,79ABEC74
0040CF22  |.  8BF9          MOV EDI,ECX
0040CF24  |.  33FA          XOR EDI,EDX
0040CF26  |.  8B5424 18     MOV EDX,DWORD PTR SS:[ESP+18]
0040CF2A  |.  33D6          XOR EDX,ESI
0040CF2C  |.  81F7 651A4398 XOR EDI,98431A65
0040CF32  |.  81E2 B759B15A AND EDX,5AB159B7
0040CF38  |.  8BEA          MOV EBP,EDX
0040CF3A  |.  33D0          XOR EDX,EAX
0040CF3C  |.  8B4424 1C     MOV EAX,DWORD PTR SS:[ESP+1C]
0040CF40  |.  33EE          XOR EBP,ESI
0040CF42  |.  8B7424 24     MOV ESI,DWORD PTR SS:[ESP+24]
0040CF46  |.  895424 4C     MOV DWORD PTR SS:[ESP+4C],EDX
0040CF4A  |.  8B5424 24     MOV EDX,DWORD PTR SS:[ESP+24]
0040CF4E  |.  33F3          XOR ESI,EBX
0040CF50  |.  81E6 3C658A64 AND ESI,648A653C
0040CF56  |.  33C8          XOR ECX,EAX
0040CF58  |.  8B4424 38     MOV EAX,DWORD PTR SS:[ESP+38]
0040CF5C  |.  8BDE          MOV EBX,ESI
0040CF5E  |.  335C24 10     XOR EBX,DWORD PTR SS:[ESP+10]
0040CF62  |.  33F2          XOR ESI,EDX
0040CF64  |.  8BD7          MOV EDX,EDI
0040CF66  |.  81F5 E4651A32 XOR EBP,321A65E4
0040CF6C  |.  33D0          XOR EDX,EAX
0040CF6E  |.  81F3 436584CA XOR EBX,CA846543
0040CF74  |.  81FA 54C64865 CMP EDX,6548C654
0040CF7A  |.  75 6C         JNZ SHORT 111.0040CFE8
0040CF7C  |.  33F5          XOR ESI,EBP
0040CF7E  |.  81FE 6248C5A8 CMP ESI,A8C54862
0040CF84  |.  75 62         JNZ SHORT 111.0040CFE8
0040CF86  |.  33CF          XOR ECX,EDI
0040CF88  |.  81F9 EFA36821 CMP ECX,2168A3EF
0040CF8E  |.  75 58         JNZ SHORT 111.0040CFE8
0040CF90  |.  B8 F1F0F0F0   MOV EAX,F0F0F0F1
0040CF95  |.  F7E3          MUL EBX
0040CF97  |.  C1EA 04       SHR EDX,4
0040CF9A  |.  B8 4FECC44E   MOV EAX,4EC4EC4F
0040CF9F  |.  8BCA          MOV ECX,EDX
0040CFA1  |.  F7E3          MUL EBX
0040CFA3  |.  0FAFCD        IMUL ECX,EBP
0040CFA6  |.  C1EA 02       SHR EDX,2
0040CFA9  |.  0FAFD7        IMUL EDX,EDI
0040CFAC  |.  03CA          ADD ECX,EDX
0040CFAE  |.  8BD5          MOV EDX,EBP
0040CFB0  |.  0FAFD7        IMUL EDX,EDI
0040CFB3  |.  8B4424 40     MOV EAX,DWORD PTR SS:[ESP+40]
0040CFB7  |.  03CA          ADD ECX,EDX
0040CFB9  |.  F7D1          NOT ECX
0040CFBB  |.  3BC8          CMP ECX,EAX
0040CFBD  |.  75 29         JNZ SHORT 111.0040CFE8
0040CFBF  |.  8BCB          MOV ECX,EBX
0040CFC1  |.  B8 ABAAAAAA   MOV EAX,AAAAAAAB
0040CFC6  |.  0FAFCF        IMUL ECX,EDI
0040CFC9  |.  F7E1          MUL ECX
0040CFCB  |.  8B4424 4C     MOV EAX,DWORD PTR SS:[ESP+4C]
0040CFCF  |.  D1EA          SHR EDX,1
0040CFD1  |.  2BDA          SUB EBX,EDX
0040CFD3  |.  03DD          ADD EBX,EBP
0040CFD5  |.  F7D3          NOT EBX
0040CFD7  |.  3BD8          CMP EBX,EAX
0040CFD9  |.  75 0D         JNZ SHORT 111.0040CFE8
0040CFDB  |.  5F            POP EDI
0040CFDC  |.  5D            POP EBP
0040CFDD  |.  5B            POP EBX
0040CFDE  |.  B8 01000000   MOV EAX,1
0040CFE3  |.  5E            POP ESI
0040CFE4  |.  83C4 60       ADD ESP,60
0040CFE7  |.  C3            RETN
0040CFE8  |>  5F            POP EDI
0040CFE9  |.  5D            POP EBP
0040CFEA  |.  5B            POP EBX
0040CFEB  |.  33C0          XOR EAX,EAX
0040CFED  |.  5E            POP ESI
0040CFEE  |.  83C4 60       ADD ESP,60
0040CFF1  \.  C3            RETN

由于算法很变态,没有耐心是看不完的。伪代码如下:
//lpString2是你输入的注册码
bool __cdecl sub_40CE90(LPCSTR lpString2)
{ const CHAR *v1; // esi@1
  bool result; // eax@2
  signed int v3; // ebx@3
  int *v4; // edi@3
  int v5; // ecx@5
  int v6; // ebx@5
  int v7; // ebp@5
  int v8; // edi@5
  int v9; // ecx@5
  int v10; // [sp+4h] [bp-60h]@3
  CHAR Src; // [sp+24h] [bp-40h]@4
  int v12; // [sp+8h] [bp-5Ch]@5
  int v13; // [sp+20h] [bp-44h]@5
  int v14; // [sp+34h] [bp-30h]@5
  int v15; // [sp+10h] [bp-54h]@5
  int v16; // [sp+1Ch] [bp-48h]@5
  int v17; // [sp+Ch] [bp-58h]@5
  int v18; // [sp+14h] [bp-50h]@5
  int v19; // [sp+40h] [bp-24h]@5
  int v20; // [sp+18h] [bp-4Ch]@5
  v1 = lpString2;

  if ( lstrlenA(lpString2) == 64 )//长度必须64位
  {
    v4 = &v10;
    v3 = 8;
    do
    {
      lstrcpynA(&Src, v1, 9);
      v1 += 8;
      sscanf(&Src, "%X", v4);
      ++v4;
      --v3;   //循环8次分成8组
    }
    while ( v3 );

    v14 = v12 ^ (v12 ^ v13) & 0x5498C54A;
    v9 = (v15 ^ v16) & 0x79ABEC74;
    v8 = v16 ^ v9 ^ 0x98431A65;
    v19 = v17 ^ (v18 ^ v17) & 0x5AB159B7;
    v5 = v15 ^ v9;
    v7 = v18 ^ (v18 ^ v17) & 0x5AB159B7 ^ 0x321A65E4;
    v6 = v10 ^ (v10 ^ v20) & 0x648A653C ^ 0xCA846543;
    result = (v13 ^ (v12 ^ v13) & 0x5498C54A ^ v8) == 1699268180
          && (v7 ^ v20 ^ (v10 ^ v20) & 0x648A653C) == -1463465886
          && (v8 ^ v5) == 560505839
          && ~(v8 * v7 + v8 * v6 / 0xDu + v7 * v6 / 0x11u) == v14
          && ~(v7 + v6 - v8 * v6 / 3u) == v19;
  }       //必须5个条件同时为真
   else
  {
    result = 0;
  }
  return result;   //1 表示注册成功
}   
  简单的说将你输入的注册码转换成8个变量,这8个变量互相组合又多出7个变量,然后这15个变量要同时满足5个等式,这样注册才会成功。用伪c代码看成这样,那么直接看汇编你能看懂那你就可以当坛主了。这也是网上找不到注册码的原因。当然你可以直接爆破,这是没有办法的办法。
   

                                                                     天易love
                                                                                                       2009.2.5