【文章标题】: Riijj CrackMe9 分析
【文章作者】: hawking
【作者邮箱】: rich_hawking@hotmail.com
【软件名称】: Riijj CrackMe 9
【软件大小】: 320K
【下载地址】: http://bbs1.pediy.com:8081/showthread.php?s=&threadid=36592
【加壳方式】: 未知
【使用工具】: OllyICE PEiD
【操作平台】: win2k
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
本人菜鸟,刚开始学习加密解密的时候就是通过Riijj大侠的几个CrackMe入的门。Riijj大侠针对自写的几个CrackMe进行了深入浅出的分析,令我等初学者受益匪浅。
首先按照惯例用PEiD载入查一下看看有没有加壳,结果是Nothing found [Overla]*,我们再点一下EP Section 后面的 > 按钮,可以看出程序只有四个常用的段,由此推测程序很可能没有加壳。
运行程序看一下,程序有两个文本框,分别用来存放name及key,还有一个Register按钮。随便输入name及key,点按钮后无反应。
OllyICE 载入,提示代码段可能被压缩加密或包含大量嵌入数据(跟一般的没有加壳的程序载入的时候不一样),不分析继续。
00410380 > 55 push ebp ;OEP 停在这里
00410381 E9 357C0000 jmp 00417FBB
00410386 - E9 0FBE4C3C jmp 3C8DC19A
0041038B 64:68 3ED24100 push 0041D23E
00410391 C3 retn
00410392 - E9 8B4D1068 jmp 68515122
00410397 0F6D ??? ; 未知命令
00410399 41 inc ecx
程序到处都是jmp,跳来跳去的。不管,F7单步,去掉无用的push XXXXXXXX retn 指令后整理出程序入口处代码如下:
00410380 > 55 push ebp
00417FBB 8BEC mov ebp, esp
0041134C 6A FF push -1
00419090 68 08014200 push 00420108
00419634 68 4C104100 push 0041104C
004102CD 64:A1 00000000 mov eax, dword ptr fs:[0]
00416626 50 push eax
0041A808 64:8925 0000000>mov dword ptr fs:[0], esp
0041594D 83EC 58 sub esp, 58
0041A520 53 push ebx
00411789 56 push esi
00412DF6 57 push edi
00418801 8965 E8 mov dword ptr [ebp-18], esp
00418B51 FF15 64004200 call dword ptr [<&KERNEL32.GetVersion>] ; KERNEL32.GetVersion
0041BE34 33D2 xor edx, edx
典型的Microsoft Visual C++ 程序入口点代码。^-^
F9跑起来。
在程序界面上的文本框内输入
Registration name: hawking
Registration key: 123456
我们来看看如何定位到它的处理代码段,由于是VC++代码,我们可以通过消息断点来定位。
点工具栏上的W图标打开OD窗口窗口,打开之后可以看到按钮Register 句柄002302FA,ID 为3EA,父窗口句柄0019027C(您看到的和我看到的也许不一样)
右键在ClassProc上设置消息断点 202 WM_LBUTTONUP 这样就能在您单击Register注册按钮的时候将程序断下来了。
点击程序界面上的Register按钮后中断在模块USER32系统领空。
77E00DFC > 55 push ebp ;中断在这儿
77E00DFD 8BEC mov ebp, esp
77E00DFF 8B4D 08 mov ecx, dword ptr [ebp+8]
77E00E02 56 push esi
77E00E03 57 push edi
77E00E04 E8 ED07FFFF call 77DF15F6
我们要返回到自己的程序代码段。打开内存窗口,在riijjcm9 .text区段上F2设置访问中断。 F9跑
00410050 8B4424 08 mov eax, dword ptr [esp+8] ;停在这里
00410054 E9 01890000 jmp 0041895A
00410059 - E9 0F85918B jmp 8BD2856D
0041005E 0000 add byte ptr [eax], al
看看堆栈
0012FB94 77DF1EF0 返回到 USER32.77DF1EF0
0012FB98 0019027C ;眼熟 父窗口句柄
0012FB9C 00000135 ;消息号 不是我们想要的111
0012FBA0 41011245
0012FBA4 002302FA
0012FBA8 002302FA
0012FBAC DCBAABCD
按几下F7之后又回到了USER32系统领空。
77DF1EF0 817C24 04 CDABB>cmp dword ptr [esp+4], DCBAABCD
77DF1EF8 0F85 C4910300 jnz 77E2B0C2
77DF1EFE 83C4 08 add esp, 8
77DF1F01 5D pop ebp
77DF1F02 C2 1400 retn 14
看来没有断下来,继续上面的操作,在我们的代码段下断点,F9运行
00410050 8B4424 08 mov eax, dword ptr [esp+8] ;停在了上次相同的地址
00410054 E9 01890000 jmp 0041895A ;这里应该就是程序用来处理消息的代码段了
00410059 - E9 0F85918B jmp 8BD2856D
0041005E 0000 add byte ptr [eax], al
00410060 68 8B214100 push 0041218B
00410065 C3 retn
再瞅瞅堆栈,这回应该是处理我们刚才引发的按钮消息的地方了。
0012FC60 77DF1EF0 返回到 USER32.77DF1EF0
0012FC64 0019027C ;父窗口句柄
0012FC68 00000111 ;WM_COMMAND
0012FC6C 000003EA ;按钮ID
0012FC70 002302FA
0012FC74 002302FA
0012FC78 DCBAABCD
一路F8
00410050 8B4424 08 mov eax, dword ptr [esp+8]
0041895A 3D 10010000 cmp eax, 110
0041AF11 ^\0F85 ADE4FFFF jnz 004193C4
004193C4 3D 11010000 cmp eax, 111 ;按钮命令
00411462 /0F85 2E5A0000 jnz 00416E96
00417AB0 66:817C24 0C EA>cmp word ptr [esp+C], 3EA ;按钮ID = 3EA
004129A6 /0F85 EA440000 jnz 00416E96
00419323 56 push esi
来到这里
00419323 56 push esi
00413059 8B7424 14 mov esi, dword ptr [esp+14]
00414129 57 push edi
00416CE0 8B3D E0004200 mov edi, dword ptr [<&USER32.EnableWindow>] ; USER32.EnableWindow
0041861D 6A 00 push 0 ;false
0041A518 56 push esi ;按钮句柄
0041212F FFD7 call edi ; USER32.EnableWindow 这里禁用了Register按钮,后面还应该有一个对应的call恢复按钮状态
00416D45 E8 B2220000 call 00418FFC
F7跟进去
00418FFC 8B0D B0264300 mov ecx, dword ptr [4326B0] ;父窗口句柄
004119E9 81EC BC000000 sub esp, 0BC
0041AA9E 8D4424 20 lea eax, dword ptr [esp+20]
0041A49C 53 push ebx
00419A11 56 push esi
004175DF 57 push edi ; USER32.EnableWindow
00410169 8B3D 9C004200 mov edi, dword ptr [<&USER32.GetDlgItemTextA>; USER32.GetDlgItemTextA
0041A823 6A 32 push 32 ;Count
0041B298 50 push eax ;Buffer
004108E0 68 E8030000 push 3E8 ;Control ID
0041CEE7 51 push ecx ;hWnd
0041807F FFD7 call edi ; USER32.GetDlgItemTextA 取name
00418B37 8D9424 94000000 lea edx, dword ptr [esp+94]
004171C2 8BF0 mov esi, eax ;eax中存放的是name长度
0041DA8D A1 B0264300 mov eax, dword ptr [4326B0]
0041831B 6A 32 push 32
004170B4 52 push edx
0041472E 68 E9030000 push 3E9
00415286 50 push eax
00414F64 FFD7 call edi ; USER32.GetDlgItemTextA 取key
0041752F 85F6 test esi, esi ;name长度为0的话则end
00416380 BB 857B0100 mov ebx, 17B85
0041DE2A ^\0F84 044FFFFF je 00412D34
00419AC4 83F8 14 cmp eax, 14 ;key长度小于20的话则end
0041D609 ^\0F82 2557FFFF jb 00412D34
我们输入的key长度不够,重新来过。在0041D609处下断,Ctl+F2重来,输入一个20位的key
0041A09F 33C9 xor ecx, ecx
00410CC9 8BC1 mov eax, ecx
00417B5A 33D2 xor edx, edx
004196CC F7F6 div esi
0041E04F 8BC3 mov eax, ebx
00413AAD BB C9010000 mov ebx, 1C9
00413527 69C0 8F1E0000 imul eax, eax, 1E8F
00410C72 0FBE7C14 2C movsx edi, byte ptr [esp+edx+2C]
004103AE 33D2 xor edx, edx
00413397 F7F3 div ebx
00411697 03FA add edi, edx
00412A77 41 inc ecx
0041AA0D 8BDF mov ebx, edi
004105F3 83F9 32 cmp ecx, 32
004109C2 885C0C 5F mov byte ptr [esp+ecx+5F], bl
00415C87 ^\0F82 3CB0FFFF jb 00410CC9
伪代码:
ebx = 0x17B85 ;
for ( int i = 0 ; i < 0x32 ; i++ )
{
ebx = name[i% strlen(name)] + (ebx * 0x1E8F ) % 0x1C9 ;
temp[i] = ebx & 0xFF;
}
004138DB 33C9 xor ecx, ecx
004156A8 55 push ebp
00413098 85F6 test esi, esi
0041929F BD 01000000 mov ebp, 1
0041C3BE /0F86 63100000 jbe 0041D427
00417707 F6C1 01 test cl, 1
0041CA16 ^\0F85 163BFFFF jnz 00410532
0041C4EC 0FBE540C 30 movsx edx, byte ptr [esp+ecx+30]
004178D7 0FAFEA imul ebp, edx
00413F2E /E9 68840000 jmp 0041C39B
00410532 0FBE7C0C 30 movsx edi, byte ptr [esp+ecx+30]
00419A54 8BC5 mov eax, ebp
00416BC9 33D2 xor edx, edx
00415F0F F7F7 div edi
00410186 8BEA mov ebp, edx
0041C39B 41 inc ecx
00412B6A 3BCE cmp ecx, esi
00412637 /0F82 CA500000 jb 00417707
伪代码:
ebp = 1 ;
for ( int i = 0 ; strlen(name) > 0 && i < strlen(name) ; i++ )
{
if ( i % 2 == 0 )
ebp = name[i] * ebp ;
else
ebp = ebp % name[i] ;
}
0041D427 B9 07000000 mov ecx, 7
004145EE 33C0 xor eax, eax
0041A8FF 8D7C24 10 lea edi, dword ptr [esp+10]
0041818C BE 857B0100 mov esi, 17B85
004106F0 F3:AB rep stos dword ptr es:[edi]
00416E78 66:AB stos word ptr es:[edi]
004110F1 33FF xor edi, edi
伪代码:
key[30] = { 0 } ;
esi = 0x17b85 ;
00410387 0FBE4C3C 64 movsx ecx, byte ptr [esp+edi+64]
0041D23E 8BC1 mov eax, ecx
004174F9 33D2 xor edx, edx
00410258 0FAFC5 imul eax, ebp
00415821 BB 32000000 mov ebx, 32
0041D961 F7F3 div ebx
00413744 33C0 xor eax, eax
0041B514 8A4414 64 mov al, byte ptr [esp+edx+64]
004149EE 85C0 test eax, eax
00411D65 /0F86 C46E0000 jbe 00418C2F
0041AD8A 8BD8 mov ebx, eax
004176D2 8BC6 mov eax, esi
0041029C 33D2 xor edx, edx
0041BA27 69C0 8F1E0000 imul eax, eax, 1E8F
004154B1 BE C9010000 mov esi, 1C9
00419528 F7F6 div esi
004128BF 33D1 xor edx, ecx
00416766 4B dec ebx
00418B2E 8BF2 mov esi, edx
0041A4B1 ^\0F85 1BD2FFFF jnz 004176D2
00418C2F 8BC6 mov eax, esi
004140AE 33D2 xor edx, edx
0041D412 B9 1A000000 mov ecx, 1A
00419602 F7F1 div ecx
00414F86 80C2 41 add dl, 41
004145DB 88543C 10 mov byte ptr [esp+edi+10], dl
0041B977 47 inc edi
00412596 83FF 14 cmp edi, 14
00412939 ^\0F82 48DAFFFF jb 00410387
伪代码:
for ( int i = 0 ; i < 0x14 ; i++ )
{
unsigned long ti = temp[i] < 0x80 ? temp[i] : temp[i] ^ 0xFFFFFF00 ;
int tii = temp[ ( ti * ebp ) % 0x32 ] ;
if ( tii > 0)
{
while ( tii != 0 )
{
esi = ( ( esi * 0x1E8F ) % 0x1C9 ) ^ ti ;
tii-- ;
}
}
key[i] = esi % 0x1A + 0x41 ;
}
00416FB5 33C0 xor eax, eax
0041DACD 5D pop ebp ; 0012FC7C
00412FB3 8A9404 94000000 mov dl, byte ptr [esp+eax+94] ;用户输入key
0041DAB8 8A4C04 0C mov cl, byte ptr [esp+eax+C] ;计算所得key
00416919 3AD1 cmp dl, cl ;比较key与您输入的key 将此处改为 cmp cl,cl即可爆破
00411C3B /0F85 F3100000 jnz 00412D34
00412DE1 40 inc eax
0041DE20 83F8 14 cmp eax, 14
00412000 /0F82 AD0F0000 jb 00412FB3
0041C1BE E8 429CFFFF call 00415E05
F7跟进
00415E05 A1 AC264300 mov eax, dword ptr [4326AC]
0041B51F 6A 00 push 0
0041841A 68 68004300 push 00430068 ; ASCII "Riijj crackme 9"
004139EF 68 4C004300 push 0043004C ; ASCII "Registration successful !"
004106DF 50 push eax
00413953 FF15 A0004200 call dword ptr [<&USER32.MessageBox>; USER32.MessageBoxA 成功!
到这里整个CrackMe就基本分析完了。
--------------------------------------------------------------------------------
【经验总结】
由于Riijj大侠这个CrackMe的变形代码基本都是 push XXXXXXXX retn 这个形式的,加上程序本身代码量不大,所以可以一步一步地跟并整理。但是如果代码量大或者是变形的方式再多一些的话,跟起来就更困难了。
本文没有什么技术含量,只是菜菜写的第一篇脱文,也算是个开始吧。高手莫笑。
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2006年12月19日 21:00:02
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课