| 关键词: nbsp 00494 00493 dword eax mov ptr ebp 字符串 push |
要想在破解的道路上走得远点,单靠爆破是不行的。爆破熟练到一定程度后,应该学会算法分析,然后学着做出注册机。只有这样,才能在各式各样的软件算法面前游刃有余。但是,这样的进阶顺序却有一个突变,即从爆破到算法分析这样一个质的飞跃。要应付好这个飞跃,对于算法新手来说,是比较困难的。也就是说,新手朋友往往面对一个软件的海量代码不知所措,不能有效地选择好关键代码进行分析,从而导致算法分析或者注册机编写的失败。 下面,我们就一起研究一款软件,我将力求用通俗易懂的语言来讲解算法分析和注册机编写,相信大家,特别是对于想要从爆破转到算法分析上的新手朋友来说,将会有不错的收获。 认识软件 “知己知彼,百战不殆”。我们在进行软件破解的时候,自然要对我们的破解对象有个宏观的认识。 “4U WMA MP3 Converter”是一款能将WMA格式转换为MP3格式的软件,我们研究的版本“6.0.2”。软件有没有加壳呢?用PEiD侦测可以知道,并没加壳,而且是用Borland Delphi编写的。 现在我们总结一下。要破解软件,应该先收集软件信息。那么,我们需要什么信息呢?我个人认为,应该包括这些方面:软件用法,有没加壳,用何种语言编译等。只有这些信息收集完后,我们才能方便地进行下一步的工作。 算法分析 上面我们已经收集到足够的信息了,现在进入下一步工作。用OD载入,下断点“bp MessageBoxA”可以定位到以下位置。 0049494D |. E8 3E02F7FF call 00404B90 00494952 |. 50 push eax 00494953 |. 68 CC494900 push 004949CC ; |Text = "Invalid Registration Code! ",CR,LF,"Please enter an available Registration Code." 00494958 |. A1 ECEF4B00 mov eax, dword ptr [4BEFEC] 0049495D |. 8B00 mov eax, dword ptr [eax] 再往上看,我们可以知道,上面有个关键跳转。由此我们可以大胆断定,这里便是关键代码处。在上面靠前的地方下个断点,我选择在0049488C处下断点。F9运行,会跳出一个注册窗口要求注册。不用管它,我们随便输入注册名“hokkien”,注册码为“12345abcde”,点注册按扭后被断,然后F8单步跟踪,可以发现以下的关键位置。 004948E4 call 00494A18 我们F7步入00494A18,来到下面的关键处。 00494A3F |. 55 push ebp 00494A40 |. 68 734B4900 push 00494B73 00494A45 |. 64:FF30 push dword ptr fs:[eax] 00494A48 |. 64:8920 mov dword ptr fs:[eax], esp 00494A4B |. 8D45 FC lea eax, dword ptr [ebp-4] 00494A4E |. BA 8C4B4900 mov edx, 00494B8C ;ASCII "Lb)a6Fcw9K9" 004947747.net3 |. E8 48FFF6FF call 004049A0 ;用固定字符串和注册名连接,返回固定字符串的长度 004947747.net8 |. 8B45 FC mov eax, dword ptr [ebp-4] 004947747.netB |. E8 38FFF6FF call 00404998 ;连接后的字符串的长度 00494A60 |. 8BF0 mov esi, eax 00494A62 |. D1FE sar esi, 1 ;ESI是连接注册名和固定字符串形成的新字符串的长度,然后进行运算,即长度值除2,结果记为N 00494A64 |. 79 03 jns short 00494A69 00494A66 |. 83D6 00 adc esi, 0 00494A69 |> 8D45 F0 lea eax, dword ptr [ebp-10] 00494A6C |. 50 push eax 00494A6D |. 8BCE mov ecx, esi 00494A6F |. BA 01000000 mov edx, 1 00494A74 |. 8B45 FC mov eax, dword ptr [ebp-4] 00494A77 |. E8 7401F7FF call 00404BF0 ;返回指定的前N个字符,所得字符串记为str1 00494A7C |. 8B45 F0 mov eax, dword ptr [ebp-10] 00494A7F |. 50 push eax 00494A80 |. 8D45 EC lea eax, dword ptr [ebp-14] 00494A83 |. 50 push eax 00494A84 |. 8B45 FC mov eax, dword ptr [ebp-4] 00494A87 |. E8 0CFFF6FF call 00404998 00494A8C |. 8BC8 mov ecx, eax 00494A8E |. 8D56 01 lea edx, dword ptr [esi+1] ;edx=N+1 00494A91 |. 8B45 FC mov eax, dword ptr [ebp-4] 00494A94 |. E8 5701F7FF call 00404BF0 ;返回从N+1个位置开始的后面所有字符串,所得字符串记为str2 00494A99 |. 8B55 EC mov edx, dword ptr [ebp-14] 00494A9C |. 8D45 FC lea eax, dword ptr [ebp-4] 00494A9F |. 59 pop ecx 00494AA0 |. E8 3FFFF6FF call 004049E4 ;合并字符串,即 str3=str2+str1,注意顺序! 00494A7747.net |. 8D45 F8 lea eax, dword ptr [ebp-8] 00494AA8 |. 50 push eax 00494AA9 |. B9 0A000000 mov ecx, 0A 00494AAE |. BA 01000000 mov edx, 1 00494AB3 |. 8B45 FC mov eax, dword ptr [ebp-4] 00494AB6 |. E8 3501F7FF call 00404BF0 ;取str3的前10个字符,结果记为str4 00494ABB |. 8D45 F4 lea eax, dword ptr [ebp-C] 00494ABE |. 50 push eax 00494ABF |. 8B45 FC mov eax, dword ptr [ebp-4] 00494AC2 |. E8 D1FEF6FF call 00404998 00494AC7 |. 8BC8 mov ecx, eax 00494AC9 |. BA 06000000 mov edx, 6 00494ACE |. 8B45 FC mov eax, dword ptr [ebp-4] 00494AD1 |. E8 1A01F7FF call 00404BF0 ;从第6位置取str3后的所有字符串,结果记为str5 00494AD6 |. 837D F4 00 cmp dword ptr [ebp-C], 0 00494ADA |. 75 10 jnz short 00494AEC 00494ADC |. 8D45 F4 lea eax, dword ptr [ebp-C] 00494ADF |. BA 8C4B4900 mov edx, 00494B8C ;ASCII "Lb)a6Fcw9K9" 00494AE4 |. 8B4D F8 mov ecx, dword ptr [ebp-8] 00494AE7 |. E8 F8FEF6FF call 004049E4 00494AEC |> 53 push ebx 00494AED |. 8B4D F4 mov ecx, dword ptr [ebp-C] 00494AF0 |. 8B55 F8 mov edx, dword ptr [ebp-8] 00494AF3 |. 8BC7 mov eax, edi 00494AF5 |. E8 92F0FFFF call 00493B8C ;关键,等下将用到这个函数 上面这些代码是对注册名进行一定处理,我们以注册名“hokkien”进行具体的说明。首先,“hokkien”和“Lb)a6Fcw9K9”合并为“hokkien Lb)a6Fcw9K9”,然后取N个字符(本注册名是N=9),即“hokkienLb”,和剩下的字符倒过来又组成新的字符串,即“)a6Fcw9K9 hokkienLb”,然后再取前10位字符,即“)a6Fcw9K9h”,接着,从6个字符开始取后面的所有字符,有“w9K9hokkienLb”。其中,字符串“)a6Fcw9K9h”和“w9K9hokkienLb”是关键,后面将用于注册码的生成。 那么,上面对注册名进行这样复杂的处理有什么用呢?我们继续下去会发现“00494AF5 |. E8 92F0FFFF call 00493B8C”是关键,F7步入00493B8C,来到下面的代码处。 00493B8C /$ 55 push ebp 00493B8D |. 8BEC mov ebp, esp 00493B8F |. 83C4 E0 add esp, -20 00493B92 |. 53 push ebx 00493B93 |. 56 push esi 00493B94 |. 57 push edi 00493B95 |. 33DB xor ebx, ebx 00493B97 |. 895D E0 mov dword ptr [ebp-20], ebx 00493B9A |. 895D F0 mov dword ptr [ebp-10], ebx 00493B9D |. 894D F8 mov dword ptr [ebp-8], ecx 00493BA0 |. 8955 FC mov dword ptr [ebp-4], edx 00493BA3 |. 8B45 FC mov eax, dword ptr [ebp-4] 00493BA6 |. E8 D50FF7FF call 00404B80 00493BAB |. 8B45 F8 mov eax, dword ptr [ebp-8] 00493BAE |. E8 CD0FF7FF call 00404B80 00493BB3 |. 33C0 xor eax, eax 00493BB5 |. 55 push ebp 00493BB6 |. 68 A83C4900 push 00493CA8 00493BBB |. 64:FF30 push dword ptr fs:[eax] 00493BBE |. 64:8920 mov dword ptr fs:[eax], esp 00493BC1 |. 8B45 F8 mov eax, dword ptr [ebp-8] 00493BC4 |. E8 CF0DF7FF call 00404998 ;长度 00493BC9 |. 8945 F4 mov dword ptr [ebp-C], eax 00493BCC |. 837D F4 00 cmp dword ptr [ebp-C], 0 00493BD0 |. 75 0D jnz short 00493BDF 00493BD2 |. 8D45 F8 lea eax, dword ptr [ebp-8] 00493BD5 |. BA C03C4900 mov edx, 00493CC0 ;ASCII "Think Space" 00493BDA |. E8 990BF7FF call 00404778 00493BDF |> 33F6 xor esi, esi 00493BE1 |. BB 00010000 mov ebx, 100 00493BE6 |. 8D45 F0 lea eax, dword ptr [ebp-10] 00493BE9 |. 50 push eax ; /Arg1 00493BEA |. C745 E4 00010>mov dword ptr [ebp-1C], 100;| 00493BF1 |. C645 E8 00 mov byte ptr [ebp-18], 0;| 00493BF5 |. 8D55 E4 lea edx, dword ptr [ebp-1C];| 00493BF8 |. 33C9 xor ecx, ecx;| 00493BFA |. B8 D43C4900 mov eax, 00493CD4 ;|ASCII "%1.2x" 00493BFF |. E8 0461F7FF call 00409D08 ; \WMAMP3Co.00409D08 看了这些代码后,似乎没有什么重要信息。但仔细跟踪后发现,数字0X100是很关键的。如何关键我们等下进行说明,现在我们F7继续下去。 00493C19 |> /8B45 FC /mov eax, dword ptr [ebp-4] ;“)a6Fcw9K9h” 00493C1C |. |8B55 EC |mov edx, dword ptr [ebp-14] ;计数器 00493C1F |. |0FB64410 FF |movzx eax, byte ptr [eax+edx-1] ;取“)a6Fcw9K9h”相应位字符的ASCII 00493C24 |. |03C3 |add eax, ebx ;与上次运算结果相加 00493C26 |. |B9 FF000000 |mov ecx, 0FF 00493C2B |. |99 |cdq 00493C2C |. |F7F9 |idiv ecx 00493C2E |. |8BDA |mov ebx, edx ;%模 00493C30 |. |3B75 F4 |cmp esi, dword ptr [ebp-C] 00493C33 |. |7D 03 |jge short 00493C38 00493C35 |. |46 |inc esi ;另一个计数器 00493C36 |. |EB 05 |jmp short 00493C3D 00493C38 |> |BE 01000000 |mov esi, 1 00493C3D |> |8B45 F8 |mov eax, dword ptr [ebp-8] ;字符串“w9K9hokkienLb” 00493C40 |. |0FB64430 FF |movzx eax, byte ptr [eax+esi-1] 00493C45 |. |33D8 |xor ebx, eax 00493C47 |. |8D45 E0 |lea eax, dword ptr [ebp-20] 00493C4A |. |50 |push eax ; /Arg1 00493C4B |. |895D E4 |mov dword ptr [ebp-1C], ebx;| 00493C4E |. |C645 E8 00 |mov byte ptr [ebp-18], 0;| 00493C52 |. |8D55 E4 |lea edx, dword ptr [ebp-1C];| 00493C55 |. |33C9 |xor ecx, ecx;| 00493C57 |. |B8 D43C4900 |mov eax, 00493CD4 ; |ASCII "%1.2x" 00493C5C |. |E8 A760F7FF |call 00409D08 00493C61 |. |8B55 E0 |mov edx, dword ptr [ebp-20] 00493C64 |. |8D45 F0 |lea eax, dword ptr [ebp-10] 00493C67 |. |E8 340DF7FF |call 004049A0 00493C6C |. |FF45 EC |inc dword ptr [ebp-14] 00493C6F |. |4F |dec edi ;减1 00493C70 |.^\75 A7 \jnz short 00493C19 上面的代码是注册码生成的关键!我们现在进行详细分析。首先取出“)a6Fcw9K9h”的相应位的ASCII,记为A1。然后(A1+上次运算的结果)%0XFF(特别的,第一次运算时,第一次结果设为0X100),所得结果为A2;接着,取出“w9K9hokkienLb”的相应位的ASCII,记为B1;最后,B1异或A2,所得的结果为本次运算的新结果。这样说估计比较难理解,那我们现在就用前几位运算来说明一下。 第一次运算:“)a6Fcw9K9h”的“)”的ASCII为0X29,0X29+0X100==0X129,然后,0X129%0XFF=0X2A。“w9K9hokkienLb”的w的ASCII为0X77,然后,0X77 XOR 0X2A=0X5D,则0X5D就是本次的运算结果。 第二次运算:“)a6Fcw9K9h”的“a”的ASCII为0X61,0X61+0X5D==0XBE,然后,0XBE%0XFF=0XBE。“w9K9hokkienLb”的9的ASCII为0X39,然后,0XBE XOR 0X39=0X87,则0X87就是本次的运算结果。 其他位的运算类似,我就不再细说了。最后我们得到由“)a6Fcw9K9h”和“w9K9hokkienLb”运算出来的16进制数字“5D87F64FE948F85BA6”,然后跟“100”合并为“1005D87F64FE948F85BA6”。那么这个字符串有什么作用呢?我们继续。 00494AFD |. 50 push eax 00494AFE |. 8B03 mov eax, dword ptr [ebx] 00494B00 |. B9 05000000 mov ecx, 5 00494B05 |. BA 01000000 mov edx, 1 00494B0A |. E8 E100F7FF call 00404BF0 00494B0F |. FF75 E8 push dword ptr [ebp-18] 00494B12 |. 68 A04B4900 push 00494BA0 00494B17 |. 8D45 E4 lea eax, dword ptr [ebp-1C] 00494B1A |. 50 push eax 00494B1B |. 8B03 mov eax, dword ptr [ebx] 00494B1D |. B9 05000000 mov ecx, 5 00494B22 |. BA 06000000 mov edx, 6 00494B27 |. E8 C400F7FF call 00404BF0 00494B2C |. FF75 E4 push dword ptr [ebp-1C] 00494B2F |. 68 A04B4900 push 00494BA0 00494B34 |. 8D45 E0 lea eax, dword ptr [ebp-20] 00494B37 |. 50 push eax 00494B38 |. 8B03 mov eax, dword ptr [ebx] 00494B3A |. B9 05000000 mov ecx, 5 00494B3F |. BA 0B000000 mov edx, 0B 00494B44 |. E8 A700F7FF call 00404BF0 00494B49 |. FF75 E0 push dword ptr [ebp-20] 00494B4C |. 8BC3 mov eax, ebx 00494B4E |. BA 05000000 mov edx, 5 00494B53 |. E8 00FFF6FF call 004047747.net8 00494B58 |. 33C0 xor eax, eax 00494B5A |. 5A pop edx 00494B5B |. 59 pop ecx 00494B5C |. 59 pop ecx 00494B5D |. 64:8910 mov dword ptr fs:[eax], edx 00494B60 |. 68 7A4B4900 push 00494B7A 00494B65 |> 8D45 E0 lea eax, dword ptr [ebp-20] 00494B68 |. BA 08000000 mov edx, 8 00494B6D |. E8 92FBF6FF call 00404704 00494B72 \. C3 retn 看到上面这些代码,我们就明白了,这里还要进行进一步处理,即“1005D87F64FE948F85BA6”五个一组进行分组,分隔符为“-”,且注册码的形式为“XXXXX-XXXXX-XXXXX”,所以,我们可以得出注册名“hokkien”的注册码为“1005D-87F64-FE948”。赶紧注册下,呵呵,当然成功了。 注册机代码的编写 下面我们用C语言编写一个注册机。首先,应该进行运算准备,即获取类似“)a6Fcw9K9h”和“w9K9hokkienLb”的字符串。 char regcode[100]={0}; char str1[50]; char str2[50]; char str3[50]; char str4[50]; char str5[50]; char len; char retlen; char newstr[50]; charregname[50]; char* strconst="Lb)a6Fcw9K9"; char *codecon="100"; printf("请输入你需要注册的用户名:"); scanf("%s",regname); strcpy(newstr,regname); strcat(newstr,strconst); len=strlen(newstr); retlen=len>>1; strncpy(str1,newstr,retlen);//取回前retlen个字符 str1[retlen]='\0'; get_n_str(retlen,str2,newstr,len); strcpy(str3,str2); strcat(str3,str1); strncpy(str4,str3,0x0a); str4[0x0a]='\0'; get_n_str(6-1,str5,str3,len); 其中的函数get_n_str的原型是“void get_n_str(char n,char str[],char* constr,char strlen);”,功能是从字符串constr的第n位置开始把后面的字符全部复制给str。其实现代码如下: void get_n_str(char n,char str[],char* constr,char strlen)// strlen是constr,的长度 { for(int i=n;i<=strlen;i++) str[i-n]=constr[i]; } 经过上面的代码,我们可以得到str4和str5,这是注册码生成的关键。下面的代码是对str4和str5进行运算从而生成注册码。 str4len=strlen(str4); str5len=strlen(str5); if(str4len<=str5len) _strlen=str4len; else _strlen=str5len; strcpy(regcode,codecon); charstring[12]; unsigned char regtemp=((str4[0]+0x100)%0xFF)^(str5[0]); char ctemp=0; ultoa(regtemp,string,16); strcat(regcode,string); for(int i=1;i<_strlen;i++) { regtemp= ((regtemp+str4[i])%0xff)^(str5[i]); ultoa(regtemp,string,16); strcat(regcode,string); } printf(" ^_^恭喜你,你的注册码为:"); for(int n=0;n<15;n++) { printf("%C",(toupper(regcode[n]))); if(((n+1)%5==0)&(n+1)<15) printf("-"); } 经过上面这段代码后,我们就可以得到注册码了。编译连接后运行,输入hokkien,得到的注册码正好是“1005D-87F64-FE948”,如图1所示。 图1 总结 新手朋友如果要顺利的进行算法分析,除了耐心外,最好掌握一门编程语言,并且善于应用,只有这样,算法分析和注册机的编写才能游刃有余。好了,就到这里,祝大家学习愉快 |
|
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系
[邮箱地址] 删除
|