下载地址:http://www.crackmes.de/users/indomit/indokgm1
使用工具:OllyDbg 一个用Delphi写的控制台程序,试注册,找提示字符串,然后用OD载入,
根据提示字符串定位到如下代码处: ////////////// 以下是代码 //////////////
00408695 . E8 D6A4FFFF call 00402B70 ; ReadLn
0040869A . A1 70934000 mov eax, [409370]
0040869F . E8 38A5FFFF call 00402BDC
004086A4 . E8 839FFFFF call 0040262C
004086A9 . A1 98A74000 mov eax, [40A798]
004086AE . E8 01B8FFFF call 00403EB4 ; 求长度
004086B3 . 8BD8 mov ebx, eax
004086B5 . 83FB 04 cmp ebx, 4 ; 用户名必须输入4――16个字符
004086B8 . 7C 05 jl short 004086BF
004086BA . 83FB 10 cmp ebx, 10
004086BD . 7E 19 jle short 004086D8
004086BF > A1 04934000 mov eax, [409304]
004086C4 . BA 00894000 mov edx, 00408900 ; ASCII 0A,"<--! Plz, "
004086C9 . E8 12BAFFFF call 004040E0
004086CE . E8 21A7FFFF call 00402DF4
004086D3 . E8 549FFFFF call 0040262C
004086D8 > 83FB 04 cmp ebx, 4
004086DB .^ 7C 95 jl short 00408672
004086DD . 83FB 10 cmp ebx, 10
004086E0 .^ 7F 90 jg short 00408672
004086E2 . A1 04934000 mov eax, [409304]
004086E7 . BA 2C894000 mov edx, 0040892C ; ASCII 0A,"--> Serial"
004086EC . E8 EFB9FFFF call 004040E0
004086F1 . E8 E2A2FFFF call 004029D8
004086F6 . E8 319FFFFF call 0040262C
004086FB . BA 9CA74000 mov edx, 0040A79C
00408700 . A1 70934000 mov eax, [409370]
00408705 . E8 66A4FFFF call 00402B70
0040870A . A1 70934000 mov eax, [409370]
0040870F . E8 C8A4FFFF call 00402BDC
00408714 . E8 139FFFFF call 0040262C
00408719 . A1 9CA74000 mov eax, [40A79C]
0040871E . E8 91B7FFFF call 00403EB4
00408723 . 83F8 10 cmp eax, 10 ; 序列号必须是16个字符
00408726 . 74 0A je short 00408732
00408728 . E8 77FDFFFF call 004084A4
0040872D . E9 39010000 jmp 0040886B
00408732 > B8 01000000 mov eax, 1 ; 计数器
00408737 . BE A4A74000 mov esi, 0040A7A4
0040873C > 8B15 9CA74000 mov edx, [40A79C] ; 序列号首地址
00408742 . 8A5402 FF mov dl, [edx+eax-1] ; 依次取序列号各字符
00408746 . 8BCA mov ecx, edx
00408748 . 80C1 BF add cl, 0BF
0040874B . 80E9 06 sub cl, 6
0040874E . 73 04 jnb short 00408754 ; cl>5则跳(相当于原dl<'A'或dl>'F')
00408750 . 8816 mov [esi], dl
00408752 . EB 18 jmp short 0040876C ; dl>='A'或dl<='F'
00408754 > 8BCA mov ecx, edx
00408756 . 80C1 D0 add cl, 0D0
00408759 . 80E9 0A sub cl, 0A
0040875C . 73 04 jnb short 00408762 ; cl>9则跳(相当于原dl<'0'或dl>'9')
0040875E . 8816 mov [esi], dl
00408760 . EB 0A jmp short 0040876C
00408762 > E8 3DFDFFFF call 004084A4 ; 显示错误信息并终止
00408767 . E9 FF000000 jmp 0040886B
0040876C > 40 inc eax
0040876D . 46 inc esi
0040876E . 83F8 11 cmp eax, 11 ; 这一循环是检测序列号是否仅由16进制字符组成
00408771 .^ 75 C9 jnz short 0040873C
00408773 . 50 push eax
00408774 . 53 push ebx
00408775 . 51 push ecx
00408776 . 52 push edx
00408777 . 31C0 xor eax, eax
00408779 . 31DB xor ebx, ebx
0040877B . 31C9 xor ecx, ecx
0040877D . 31D2 xor edx, edx
0040877F . 8B0D 98A74000 mov ecx, [40A798] ; 用户名指针
00408785 > 0FB619 movzx ebx, byte ptr [ecx]
00408788 . 31D3 xor ebx, edx
0040878A . 01D8 add eax, ebx
0040878C . C1C0 07 rol eax, 7
0040878F . 89DA mov edx, ebx
00408791 . 41 inc ecx
00408792 . 8039 00 cmp byte ptr [ecx], 0 ; 是否已到达串尾
00408795 .^ 75 EE jnz short 00408785
00408797 . 31C9 xor ecx, ecx
00408799 > 31D8 xor eax, ebx
0040879B . C1C3 10 rol ebx, 10
0040879E . 01D3 add ebx, edx
004087A0 . 31D8 xor eax, ebx
004087A2 . C1C0 03 rol eax, 3
004087A5 . 31D0 xor eax, edx
004087A7 . C1C2 08 rol edx, 8
004087AA . 01DA add edx, ebx
004087AC . 31D0 xor eax, edx
004087AE . C1C0 05 rol eax, 5
004087B1 . 41 inc ecx
004087B2 . 83F9 0A cmp ecx, 0A
004087B5 .^ 75 E2 jnz short 00408799
004087B7 . A3 A0A74000 mov [40A7A0], eax ; 这里计算出值A1
004087BC . 5A pop edx
004087BD . 59 pop ecx
004087BE . 5B pop ebx
004087BF . 58 pop eax
004087C0 . B8 01000000 mov eax, 1
004087C5 . BA A4A74000 mov edx, 0040A7A4
004087CA > 33C9 xor ecx, ecx
004087CC . 8A0A mov cl, [edx] ; edx=输入序列号的指针
004087CE . 310D BCA74000 xor [40A7BC], ecx
004087D4 . 83F8 10 cmp eax, 10
004087D7 . 74 07 je short 004087E0
004087D9 . C125 BCA74000>shl dword ptr [40A7BC], 2
004087E0 > 40 inc eax
004087E1 . 42 inc edx
004087E2 . 83F8 11 cmp eax, 11
004087E5 .^ 75 E3 jnz short 004087CA
004087E7 . A1 BCA74000 mov eax, [40A7BC]
004087EC . C1E8 0C shr eax, 0C
004087EF . C1E0 0C shl eax, 0C ; 等价于and eax, 0FFFFF000h
004087F2 . 8B15 A0A74000 mov edx, [40A7A0]
004087F8 . C1EA 14 shr edx, 14
004087FB . 03C2 add eax, edx
004087FD . 8B15 A0A74000 mov edx, [40A7A0]
00408803 . C1E2 14 shl edx, 14
00408806 . C1EA 14 shr edx, 14 ; 等价于and edx, 0FFFh
00408809 . 33C2 xor eax, edx
0040880B . 8B15 A0A74000 mov edx, [40A7A0]
00408811 . C1E2 0C shl edx, 0C
00408814 . C1EA 18 shr edx, 18
00408817 . 33C2 xor eax, edx
00408819 . A3 BCA74000 mov [40A7BC], eax ; 这里计算出A2
0040881E . A1 BCA74000 mov eax, [40A7BC]
00408823 . 3B05 A0A74000 cmp eax, [40A7A0]
00408829 . 75 07 jnz short 00408832
0040882B . E8 B0FCFFFF call 004084E0 ; 检测到爆破?
00408830 . EB 39 jmp short 0040886B
00408832 > E8 CDFBFFFF call 00408404
00408837 . 84C0 test al, al ; al != 0为成功标志
00408839 . 74 07 je short 00408842
0040883B . C605 B4A74000>mov byte ptr [40A7B4], 1
00408842 > 803D B4A74000>cmp byte ptr [40A7B4], 0
00408849 . 74 1B je short 00408866 ; 关键跳转(跳为失败)
0040884B . A1 04934000 mov eax, [409304]
00408850 . BA 44894000 mov edx, 00408944 ; ASCII LF,"<-- You did it! Now write a small tutorial! -->",LF,"<-- And not forget about keygen ;) -->",LF
////////////// 以上是代码 ////////////// 从关键跳转的地方回溯到408832处的call 408404,跟进去一看: ////////////// 以下是代码 //////////////
//这里略去一段代码
00408420 |. E8 53FFFFFF call 00408378
00408425 |. 84C0 test al, al
00408427 |. 74 40 je short 00408469
00408429 |. 8D45 FC lea eax, [ebp-4]
0040842C |. 50 push eax ; /Arg1
0040842D |. A1 A0A74000 mov eax, [40A7A0] ; |
00408432 |. 8945 EC mov [ebp-14], eax ; |
00408435 |. C645 F0 00 mov byte ptr [ebp-10], 0 ; |
00408439 |. A1 BCA74000 mov eax, [40A7BC] ; |
0040843E |. 8945 F4 mov [ebp-C], eax ; |
00408441 |. C645 F8 00 mov byte ptr [ebp-8], 0 ; |
00408445 |. 8D55 EC lea edx, [ebp-14] ; |
00408448 |. B9 01000000 mov ecx, 1 ; |
0040844D |. B8 98844000 mov eax, 00408498 ; |ASCII "%.8x%.8x"
00408452 |. E8 71DCFFFF call 004060C8 ; \indoKGM1.004060C8
00408457 |. 8B45 FC mov eax, [ebp-4]
0040845A |. 8B15 B8A74000 mov edx, [40A7B8]
00408460 |. E8 27BBFFFF call 00403F8C ; 比较字符串,相等标志ZF=0
00408465 |. 74 02 je short 00408469 ; 这里跳表示注册成功
00408467 |. 33DB xor ebx, ebx
////////////// 以上是代码 ////////////// 这个地方有明码比较,既然都出现"%.8x%.8x"之类字样,那肯定是调用象
wsprintf一类的函数了,看一下它的参数及输出,可以知道它是把[40A7A0]和
[40A7BC]处的16进制数值转换成字符串,然后再与输入的序列号比较,相等则
成功,不等则失败。于是得出结论:序列号应该是16字符,并且只能由16进制
字符(大写字母)组成。(其实在主程序中已有相关检测)至于真码的值则存
放在[40A7A0]及[40A7BC]两个双字中。
接下来回到主程序中,看[40A7A0]及[40A7BC]两个双字的值是怎么算出来
的。前者的计算倒没什么问题,从40877F到4087B5这段代码根据用户名计算出
一个数值放到[40A7A0]中。如果把这段代码称为F1函数,这就是说:
dword ptr [40A7A0] = F1(用户名)
但[40A7BC]的计算就大有文章了,先把输入的序列号第一个字符的ASCII码放
到[40A7BC]中,然后重复执行“左移2位后与下一个字符的ASCII码异或”动作
15次,最后再把低12位改成根据已计算好的[40A7A0]算出来的一个数值。如果
把这段代码称为F2函数,那么正确的序列号应该满足:
序列号 = (F1(用户名), F2(序列号))
也就是说正确的序列号必须在某种变换之下能还原到自身,但是只给你这个要
求,不提供任何的具体算法,你能马上构造一个正确的序列号吗?是为这个方
程式的恼人之处。但事已至此,也只能尝试着探索一条从已知的信息计算出未
知序列号的途径: 假设用户名为PEDIY
按前述算法计算出F1("PEDIY")=3973076F
于是,序列号的前8字符应该是这个值,也就是说,可以:
假设序列号为3973076FIJKLMNPQ
这里的IJKLMNPQ都是16进制数字,因而其ASCII码应该在30――39,
以及40――46之间。
注意到“左移两位与下一个ASCII码异或”这个操作的特点:由于
ASCII码至多7位,因而在每一次操作中,在[40A7BC]的高24位中的数
值都只是简单地被左移,而数码本身不会被改变。
通过“左移两位与下一个ASCII码异或”的重复操作,到处理完
"3973076F"这一部分时,[40A7BC]数值为3EC3**,其中3EC3这部分已
不会再改变。后面还有7次左移,共左移14位,"3EC3"的3及E的高2位
被移出,变成"B0**",于是I=B,J=0
序列号现在已经解出到3973076FB0KLMNPQ的程度
这样可以计算到当处理完"3973076FB0"这部分时,[40A7BC]的值
为3EC3F**,最后3以及E的高2位被移出后转化为"B0F**",于是K=F
序列号现在已经解出到3973076FB0FLMNPQ的程度
这样可以计算到当处理完"3973076FB0F"这部分时,[40A7BC]的值
为FB0FC**,最后F被移出转化为"B0FC**",于是L=C
序列号现在已经解出到3973076FB0FCMNPQ的程度
这样可以计算到当处理完"3973076FB0FC"这部分时,[40A7BC]的值
为3EC3F36C,最后3及E的高2位被移出转化为"B0FC**"。注意,这里C后
面的两个二进制位是11,也就是原来的3。因此,C后面紧接的16进制数
字不会小于C,因而它的ASCII码以4开头,用这个4去跟[40A7BC]的16进
“十”位数字6异或得到的是2,这样就可以预计下一个字符处理完的时
候,上述3后面两个二进制位是00,也就是说,下一个数字应该是
1100B,也就是"C",于是M=C
由于最后把后面有把[40A7BC]的低12位改成与[40A7A0]的高12位相
同,再依次与[40A7A0]的低12位及中8位异或,也就是说:
NPQ=([40A7A0]的高12位^中8位^低12位)=397^30^76F=4C8
序列号为3973076FB0FCC4C8 由此可见,由F1(用户名)可以得出序列号的前8字符,由前8字符可
以计算出第9、10以及倒数三个字符,由前10个字符可以计算第11字符,
由前11字符可以计算出第12字符,由前12字符求解第13个字符就有些连猜
带蒙的成分了。这个算法可以具体编程实现,但实在是太拖泥带水了,鄙
人偷懒,不想搞了…… 附F1函数的C实现
long int f1(char *szUserName)
{
long int loc_a = 0,
loc_b = 0,
loc_c = 0;
int loc_i = 0;
while(loc_b = (long int)szUserName[loc_i])
{
loc_b = loc_b ^ loc_c;
loc_a = loc_a + loc_b;
loc_a = (loc_a << 7) | (loc_a >> (32 - 7));
loc_c = loc_b;
loc_i = loc_i + 1;
}
loc_i = 0;
while(loc_i < 10)
{
loc_a = loc_a ^ loc_b;
loc_b = (loc_b << 10) | (loc_b >> (32 - 10));
loc_b = loc_b + loc_c;
loc_a = loc_a ^ loc_b;
loc_a = (loc_a << 3) | (loc_a >> (32 - 3));
loc_a = loc_a ^ loc_c;
loc_c = (loc_c << 8) | (loc_c >> (32 - 8));
loc_c = loc_c + loc_b;
loc_a = loc_a ^ loc_c;
loc_a = (loc_a << 5) | (loc_a >> (32 - 5));
loc_i = loc_i + 1;
}
return loc_a;
}
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)