环境:Windows XP
工具:
IDA
OD
EXEINFOPE
0x00 查壳
0x01 分析
拖入OD,字符串查找看一看。
跟进去看就可以知道关键call了
0040196A . 48 dec eax
0040196B . 0F85 C4000000 jnz CrackMe1.00401A35
00401971 . B8 E8030000 mov eax,0x3E8 ; Case 111 (WM_COMMAND) of switch 00401956
00401976 . 66:3945 10 cmp word ptr ss:[ebp+0x10],ax
0040197A . 0F85 B5000000 jnz CrackMe1.00401A35
00401980 . 8B4D 08 mov ecx,dword ptr ss:[ebp+0x8]
00401983 . E8 88F8FFFF call CrackMe1.00401210 ; 返回值要为0x2EF
00401988 . 3D 29090000 cmp eax,0x929 ; Switch (cases 168..1435)
0040198D . 7F 54 jg XCrackMe1.004019E3
0040198F . 74 3A je XCrackMe1.004019CB
00401991 . 3D 68010000 cmp eax,0x168
00401996 . 74 23 je XCrackMe1.004019BB
00401998 . 3D EF020000 cmp eax,0x2EF
0040199D . 0F85 92000000 jnz CrackMe1.00401A35
004019A3 . 6A 00 push 0x0 ; /Style = MB_OK|MB_APPLMODAL; Case 2EF of switch 00401988
004019A5 . 68 0CFD4000 push CrackMe1.0040FD0C ; |Title = "CrackMe"
004019AA . 68 14FD4000 push CrackMe1.0040FD14 ; |Text = "This is Flag!"
004019AF . 6A 00 push 0x0 ; |hOwner = NULL
004019B1 . FF15 08C14000 call dword ptr ds:[<&USER32.MessageBoxA>>; \MessageBoxA
00401983跟进去,翻到后面有个这个:
00401267 . 68 402E4100 push CrackMe1.00412E40 ; /pStartupinfo = CrackMe1.00412E40
0040126C . FF15 28C04000 call dword ptr ds:[<&KERNEL32.GetStartup>; \GetStartupInfoA
00401272 . 833D 502E4100>cmp dword ptr ds:[0x412E50],0x0
00401279 0F85 6D060000 jnz CrackMe1.004018EC
0040127F . 833D 542E4100>cmp dword ptr ds:[0x412E54],0x0
00401286 0F85 60060000 jnz CrackMe1.004018EC
0040128C . 833D 602E4100>cmp dword ptr ds:[0x412E60],0x0
00401293 0F85 53060000 jnz CrackMe1.004018EC
00401299 . 833D 642E4100>cmp dword ptr ds:[0x412E64],0x0
004012A0 . 0F85 46060000 jnz CrackMe1.004018EC
004012A6 . 833D 682E4100>cmp dword ptr ds:[0x412E68],0x0
004012AD . 0F85 39060000 jnz CrackMe1.004018EC
004012B3 . 833D 582E4100>cmp dword ptr ds:[0x412E58],0x0
004012BA . 0F85 2C060000 jnz CrackMe1.004018EC
004012C0 . 833D 5C2E4100>cmp dword ptr ds:[0x412E5C],0x0
004012C7 . 0F85 1F060000 jnz CrackMe1.004018EC
004012CD . F605 6C2E4100>test byte ptr ds:[0x412E6C],0x80
004012D4 . 0F85 12060000 jnz CrackMe1.004018EC
判断STARTUPINFO信息来反调试。
规定输入内容的长度
0040133E . 8D041B lea eax,dword ptr ds:[ebx+ebx]
00401341 . 3BC1 cmp eax,ecx
00401343 .^ 75 B2 jnz XCrackMe1.004012F7
规定输入内容的字符的范围
00401370 > /8A8414 840000>mov al,byte ptr ss:[esp+edx+0x84]
00401377 . |3C 41 cmp al,0x41
00401379 . |7C 04 jl XCrackMe1.0040137F
0040137B . |3C 46 cmp al,0x46
0040137D . |7E 10 jle XCrackMe1.0040138F
0040137F > |3C 30 cmp al,0x30
00401381 .^|0F8C 70FFFFFF jl CrackMe1.004012F7
00401387 . |3C 39 cmp al,0x39
00401389 .^|0F8F 68FFFFFF jg CrackMe1.004012F7
0040138F > |42 inc edx
00401390 . |3BD1 cmp edx,ecx
00401392 .^\7C DC jl XCrackMe1.00401370 ; 规定只能是数字和大写字母
然后是核心算法的内容了,最后面这里就是比较内容决定是否是正确了:
004018A0 > /8B840C AC0000>mov eax,dword ptr ss:[esp+ecx+0xAC]
004018A7 . |3B840C D40000>cmp eax,dword ptr ss:[esp+ecx+0xD4]
004018AE . |75 22 jnz XCrackMe1.004018D2 ; 这里不能跳
004018B0 . |83C1 04 add ecx,0x4
004018B3 . |83F9 28 cmp ecx,0x28
004018B6 .^\7C E8 jl XCrackMe1.004018A0
这是是填入内容的语句
00401767 . 88BC0C AC0000>mov byte ptr ss:[esp+ecx+0xAC],bh
0040187F . 888414 D40000>mov byte ptr ss:[esp+edx+0xD4],al
IDA分析一下就知道,其实两个算法是一样的,只是换了个变量名。
0x02 结果
算法过程感觉很复杂,就没有用代码实现一遍了。
方法一:爆破
ABCDEF1234567890ABABCDEF1234567890AB
0012FA5C 3C 81 64 30 E8 EE 0A 90 20 1B 46 52 C8 20 FE D4 <乨0桀.?FR?
0012FA6C 8C FE 00 00 00 00 00 00 00 00 00 00 00 00 00 00 岨..............
0012FA7C 00 00 00 00 00 00 00 00 B4 0A C0 6D AB 59 97 1F ........?續玒?
0012FA8C B4 B4 0A C0 6D AB 59 97 1F B4 00 00 00 00 00 00 创.續玒??.....
0012FA9C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ACADAEAFBABBBCBDBEBFCACBCDCECFDADBDC
0012FA5C 3C 81 64 30 E8 EE 0A 90 20 1B 46 52 C8 20 FE D4 <乨0桀.?FR?
0012FA6C 8C FE 00 00 00 00 00 00 00 00 00 00 00 00 00 00 岨..............
0012FA7C 00 00 00 00 00 00 00 00 73 EA 21 80 C5 24 E3 5A ........s?€?鉠
0012FA8C D1 F0 F5 D4 0A C1 20 E5 44 03 00 00 00 00 00 00 佯踉.?錎......
0012FA9C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
这样的话基本上就可以看出输入的每两个字符组成16进制,通过一定的映射关系,映射到对应的值,而且每两个字符之间互不影响。这样的话就可以列举0x00-0xFF所有值,找到每个值的映射关系,制成一张表,就可以通过查表来看出
3C 81 64 30 E8 EE 0A 90 20 1B 46 52 C8 20 FE D4 8C FE
对应什么字符串了。
方法二:追码
刚刚也有提到,两个算法是一样的
00401649 . E8 B2F9FFFF call CrackMe1.00401000 ; 下面是开始两个算法
0040164E . 8B4C24 34 mov ecx,dword ptr ss:[esp+0x34]
00401652 . 33D2 xor edx,edx
00401654 . 895424 20 mov dword ptr ss:[esp+0x20],edx
00401658 . 895424 24 mov dword ptr ss:[esp+0x24],edx
0040165C . 894C24 1C mov dword ptr ss:[esp+0x1C],ecx
00401660 > 8A21 mov ah,byte ptr ds:[ecx] ; 这里的al是输入的内容
00401662 . 895424 10 mov dword ptr ss:[esp+0x10],edx ; bh就是上面的算法计算出来的
这样的话就可以在00401649处的call下断点,查看BH的值,就可以得知要输入的字符串是什么了。
得到:
838EFBFFE7D9CDDFCFC4C1C5C7CFC9CBB3C9
在i春秋里提交:
flag{838EFBFFE7D9CDDFCFC4C1C5C7CFC9CBB3C9}