本帖最后由 zbnysjwsnd8 于 2017-7-29 23:04 编辑
CM地址:http://www.52pojie.cn/thread-629598-1-1.html
这篇文章不好写 而且我还是小菜 哪里有错了请大牛们指出。调试前先预测代码的实现,这是个好习惯。若预测有误也没关系,从头开始调试即可。但若是有幸预测正确,则可以节省大量调试时间。
-------《逆向工程·核心原理》
0x0 程序截图
0x1 注册码的存放方式
OD载入 输入假码 然后给GetWindowTextA下断点。
然后执行到4050C1处。观察一下eax寄存器:
eax存放我刚刚输入的假码的地址。
然后 程序将我刚刚输入的假码的地址放在了[LOCAL.2]中 [Asm] 纯文本查看 复制代码 004050C1 |. 8945 F8 MOV [LOCAL.2], EAX ;将注册码保存到[LOCAL.2]中 程序又取出注册码 然后复制到剪辑板中. [Asm] 纯文本查看 复制代码 004050D4 |> \68 04000080 PUSH 0x80000004
004050D9 |. 6A 00 PUSH 0x0
004050DB |. 8B45 F8 MOV EAX, [LOCAL.2] ; 取出注册码
004050DE |. 85C0 TEST EAX, EAX
004050E0 |. 75 05 JNZ SHORT 004050E7
004050E2 |. B8 5FE14200 MOV EAX, 0042E15F
004050E7 |> 50 PUSH EAX
004050E8 |. 68 01000000 PUSH 0x1
004050ED |. BB C8020000 MOV EBX, 0x2C8
004050F2 |. E8 19520100 CALL 0041A310 ; 拷贝到剪辑板
0041A310 /$ 55 PUSH EBP ; 置剪辑板文本
0041A311 |. 56 PUSH ESI ; CrackMe.<ModuleEntryPoint>
0041A312 |. 8B7424 10 MOV ESI, DWORD PTR SS:[ESP+0x10]
0041A316 |. 33ED XOR EBP, EBP
0041A318 |. 85F6 TEST ESI, ESI ; CrackMe.<ModuleEntryPoint>
0041A31A |. 74 77 JE SHORT 0041A393
0041A31C |. 55 PUSH EBP ; /hWnd = 0019FF94
0041A31D |. FF15 44C24100 CALL DWORD PTR DS:[<&USER32.OpenClipboard>] ; \OpenClipboard
0041A323 |. 85C0 TEST EAX, EAX
0041A325 |. 74 6C JE SHORT 0041A393
0041A327 |. 53 PUSH EBX
0041A328 |. 57 PUSH EDI ; CrackMe.<ModuleEntryPoint>
0041A329 |. FF15 4CC24100 CALL DWORD PTR DS:[<&USER32.EmptyClipboard>] ; [EmptyClipboard
0041A32F |. 8BFE MOV EDI, ESI ; CrackMe.<ModuleEntryPoint>
0041A331 |. 83C9 FF OR ECX, 0xFFFFFFFF
0041A334 |. 33C0 XOR EAX, EAX
0041A336 |. F2:AE REPNE SCAS BYTE PTR ES:[EDI]
0041A338 |. F7D1 NOT ECX ; CrackMe.<ModuleEntryPoint>
0041A33A |. 51 PUSH ECX ; /MemSize = 401000 (4198400.)
0041A33B |. 6A 42 PUSH 0x42 ; |Flags = GHND
0041A33D |. FF15 B8C04100 CALL DWORD PTR DS:[<&KERNEL32.GlobalAlloc>] ; \GlobalAlloc
0041A343 |. 8BD8 MOV EBX, EAX
0041A345 |. 85DB TEST EBX, EBX
0041A347 |. 74 3D JE SHORT 0041A386
0041A349 |. 53 PUSH EBX ; /hMem = 002D4000
0041A34A |. FF15 B4C04100 CALL DWORD PTR DS:[<&KERNEL32.GlobalLock>] ; \GlobalLock
0041A350 |. 8BD0 MOV EDX, EAX
0041A352 |. 8BFE MOV EDI, ESI ; CrackMe.<ModuleEntryPoint>
0041A354 |. 83C9 FF OR ECX, 0xFFFFFFFF
0041A357 |. 33C0 XOR EAX, EAX
0041A359 |. F2:AE REPNE SCAS BYTE PTR ES:[EDI]
0041A35B |. F7D1 NOT ECX ; CrackMe.<ModuleEntryPoint>
0041A35D |. 2BF9 SUB EDI, ECX ; CrackMe.<ModuleEntryPoint>
0041A35F |. 53 PUSH EBX ; /hMem = 002D4000
0041A360 |. 8BC1 MOV EAX, ECX ; |CrackMe.<ModuleEntryPoint>
0041A362 |. 8BF7 MOV ESI, EDI ; |CrackMe.<ModuleEntryPoint>
0041A364 |. 8BFA MOV EDI, EDX ; |CrackMe.<ModuleEntryPoint>
0041A366 |. C1E9 02 SHR ECX, 0x2 ; |
0041A369 |. F3:A5 REP MOVS DWORD PTR ES:[EDI], DWORD PTR DS:[ESI] ; |
0041A36B |. 8BC8 MOV ECX, EAX ; |
0041A36D |. 83E1 03 AND ECX, 0x3 ; |
0041A370 |. F3:A4 REP MOVS BYTE PTR ES:[EDI], BYTE PTR DS:[ESI] ; |
0041A372 |. FF15 A8C04100 CALL DWORD PTR DS:[<&KERNEL32.GlobalUnlock>] ; \GlobalUnlock
0041A378 |. 53 PUSH EBX ; /hData = 002D4000
0041A379 |. 6A 01 PUSH 0x1 ; |Format = CF_TEXT
0041A37B |. FF15 48C24100 CALL DWORD PTR DS:[<&USER32.SetClipboardData>] ; \SetClipboardData
0041A381 |. BD 01000000 MOV EBP, 0x1
0041A386 |> FF15 3CC24100 CALL DWORD PTR DS:[<&USER32.CloseClipboard>] ; [CloseClipboard
0041A38C |. 5F POP EDI ; kernel32.774938F4
0041A38D |. 5B POP EBX ; kernel32.774938F4
0041A38E |. 8BC5 MOV EAX, EBP
0041A390 |. 5E POP ESI ; kernel32.774938F4
0041A391 |. 5D POP EBP ; kernel32.774938F4
0041A392 |. C3 RETN
0041A393 |> 8BC5 MOV EAX, EBP
0041A395 |. 5E POP ESI ; kernel32.774938F4
0041A396 |. 5D POP EBP ; kernel32.774938F4
0041A397 \. C3 RETN
然后程序又休眠了200毫秒
[Asm] 纯文本查看 复制代码 0040510A |> \68 01030080 PUSH 0x80000301
0040510F |. 6A 00 PUSH 0x0
00405111 |. 68 C8000000 PUSH 0xC8
00405116 |. 68 01000000 PUSH 0x1
0040511B |. BB 7C060000 MOV EBX, 0x67C
00405120 |. E8 1B460100 CALL 00419740
00419740 /$ 8B4424 08 MOV EAX, DWORD PTR SS:[ESP+0x8]
00419744 |. 50 PUSH EAX ; /Timeout = 0. ms
00419745 |. FF15 58C04100 CALL DWORD PTR DS:[<&KERNEL32.Sleep>] ; \Sleep
0041974B \. C3 RETN
这有可能就是为了多线程做准备了。。
0x2 多线程
给OpenClipboard下断点 然后OD查看一下线程
圈起来的这两个线程需要特别注意一下(程序启动时就已经被创建了。) OD看看这两个线程的代码
[Asm] 纯文本查看 复制代码 00405131 . 56 PUSH ESI ; CrackMe.<ModuleEntryPoint>
00405132 . 57 PUSH EDI ; CrackMe.<ModuleEntryPoint>
00405133 . 53 PUSH EBX
00405134 . E8 53FFFFFF CALL 0040508C
00405139 . 5B POP EBX ; kernel32.774938F4
0040513A . 5F POP EDI ; kernel32.774938F4
0040513B . 5E POP ESI ; kernel32.774938F4
0040513C . C3 RETN
[Asm] 纯文本查看 复制代码 00402E9A . 56 PUSH ESI
00402E9B . 57 PUSH EDI
00402E9C . 53 PUSH EBX
00402E9D . E8 54E4FFFF CALL 004012F6
00402EA2 . 5B POP EBX ; CrackMe.0040512D
00402EA3 . 5F POP EDI ; CrackMe.0040512D
00402EA4 . 5E POP ESI ; CrackMe.0040512D
00402EA5 . C3 RETN
可以看到 这两个线程分别调用了 0x0040508C 和 0x004012F6 这两个函数 那么删掉之前设置的断点 给这两个函数下断点.F9运行 程序会在0x40508C处断下 0x3 寻找点击按钮后执行的代码的所在地址
0x40508C处有段代码需要注意一下:
[Asm] 纯文本查看 复制代码 00405095 |. 833D C6F64200>CMP DWORD PTR DS:[0x42F6C6], 0x1
0040509C |. 0F85 68000000 JNZ 0040510A
我这里执行的时候 0x0042F6C6 处的值不是1
给0x42F6C6下硬件写入断点(大小是1) 并禁用那两个线程的断点 F9运行。 程序会在0x0040518B处断下 [Asm] 纯文本查看 复制代码 00405178 /$ 55 PUSH EBP ; 从剪辑板中取出注册码
00405179 |. 8BEC MOV EBP, ESP
0040517B |. 81EC 04000000 SUB ESP, 0x4
00405181 |. C705 C6F64200>MOV DWORD PTR DS:[0x42F6C6], 0x1 ;注意这里 改写了0x42F6C6处的值为1
0040518B |. 68 00000000 PUSH 0x0 ;在这里断下
00405190 |. BB C4020000 MOV EBX, 0x2C4
00405195 |. E8 76440100 CALL 00419610 ; 取剪辑板文本
0040519A |. 83C4 04 ADD ESP, 0x4
0040519D |. 8945 FC MOV [LOCAL.1], EAX ; 保存到[LOCAL.1]中
00419610 /$ 57 PUSH EDI ; 取剪辑板文本
00419611 |. 33FF XOR EDI, EDI ; CrackMe.<ModuleEntryPoint>
00419613 |. 57 PUSH EDI ; /hWnd = 00401000
00419614 |. FF15 44C24100 CALL DWORD PTR DS:[<&USER32.OpenClipboard>] ; \OpenClipboard
0041961A |. 85C0 TEST EAX, EAX
0041961C |. 74 42 JE SHORT 00419660
0041961E |. 56 PUSH ESI ; CrackMe.<ModuleEntryPoint>
0041961F |. 6A 01 PUSH 0x1 ; /Format = CF_TEXT
00419621 |. FF15 40C24100 CALL DWORD PTR DS:[<&USER32.GetClipboardData>] ; \GetClipboardData
00419627 |. 8BF0 MOV ESI, EAX
00419629 |. 85F6 TEST ESI, ESI ; CrackMe.<ModuleEntryPoint>
0041962B |. 74 28 JE SHORT 00419655
0041962D |. 56 PUSH ESI ; /hMem = 00401000
0041962E |. FF15 B4C04100 CALL DWORD PTR DS:[<&KERNEL32.GlobalLock>] ; \GlobalLock
00419634 |. 8BD0 MOV EDX, EAX
00419636 |. 83C9 FF OR ECX, 0xFFFFFFFF
00419639 |. 8BFA MOV EDI, EDX ; CrackMe.<ModuleEntryPoint>
0041963B |. 33C0 XOR EAX, EAX
0041963D |. F2:AE REPNE SCAS BYTE PTR ES:[EDI]
0041963F |. F7D1 NOT ECX ; CrackMe.<ModuleEntryPoint>
00419641 |. 49 DEC ECX ; CrackMe.<ModuleEntryPoint>
00419642 |. 51 PUSH ECX ; CrackMe.<ModuleEntryPoint>
00419643 |. 52 PUSH EDX ; CrackMe.<ModuleEntryPoint>
00419644 |. E8 D7130000 CALL 0041AA20
00419649 |. 83C4 08 ADD ESP, 0x8
0041964C |. 8BF8 MOV EDI, EAX
0041964E |. 56 PUSH ESI ; /hMem = 00401000
0041964F |. FF15 A8C04100 CALL DWORD PTR DS:[<&KERNEL32.GlobalUnlock>] ; \GlobalUnlock
00419655 |> FF15 3CC24100 CALL DWORD PTR DS:[<&USER32.CloseClipboard>] ; [CloseClipboard
0041965B |. 8BC7 MOV EAX, EDI ; CrackMe.<ModuleEntryPoint>
0041965D |. 5E POP ESI ; kernel32.774938F4
0041965E |. 5F POP EDI ; kernel32.774938F4
0041965F |. C3 RETN
00419660 |> 8BC7 MOV EAX, EDI ; CrackMe.<ModuleEntryPoint>
00419662 |. 5F POP EDI ; kernel32.774938F4
00419663 \. C3 RETN
执行完这段代码后 观察一下eax寄存器
这个1234567890是我输入的假码
因此这段代码就是将之前复制到剪辑板中的注册码读取出来。
有意思的是:
在第一次执行这个函数(0x00405178)的时候 0x42F6C6 处的值是0 并且在执行这个函数之前 0x42F6C6 的值并没有改变。(在这个函数的函数头 0x00405178 处下断点 然后禁用之前设置的所有断点 删掉之前设置的硬件断点(重新在0x42F6C6处下硬件写入断点) 重新载入程序然后F9运行可得知) 执行以后 0x42F6C6 处的值才被改变为1 而在这里 [Asm] 纯文本查看 复制代码 00405095 |. 833D C6F64200>CMP DWORD PTR DS:[0x42F6C6], 0x1
0040509C |. 0F85 68000000 JNZ 0040510A
这段代码判断0x42F6C6处的值是不是1 如果不是的话就转移 到0x0040510A处继续执行。 [Asm] 纯文本查看 复制代码 0040510A |> \68 01030080 PUSH 0x80000301
0040510F |. 6A 00 PUSH 0x0
00405111 |. 68 C8000000 PUSH 0xC8
00405116 |. 68 01000000 PUSH 0x1
0040511B |. BB 7C060000 MOV EBX, 0x67C
00405120 |. E8 1B460100 CALL 00419740 ;休眠200毫秒
00405125 |. 83C4 10 ADD ESP, 0x10
00405128 |. E8 5FFFFFFF CALL 0040508C
0040512D |. 8BE5 MOV ESP, EBP
0040512F |. 5D POP EBP ; 0080D250
00405130 \. C3 RETN
因此这个函数(0x00405178)在第一次调用的时候 并不是由 0x40508C 处调用的。 好了回到正题。 我们要寻找的是 点击按钮以后 程序会执行哪些代码。 一个线程一个线程看。 先看0x40508C处 我怀疑如果点击了按钮以后 JNZ就不会实现转移。 不过这个应该是不可能的。因为在第一次执行0x00405178这个函数的时候 0x42F6C6 处的值是0 执行以后 0x42F6C6 处的值是1 如果0x42F6C6 处的值是1 那么这个JNZ就不会实现转移 而在第一次执行的时候 我们并没有点击[注册]按钮 因此0x40508C处可以排除掉 那还剩下一个线程 0x004012F6 删掉之前设置的所有断点 给0x004012F6 处下断点。 F9运行 程序直接在这里断下 F8一直单步到这里 [Asm] 纯文本查看 复制代码 00401395 |> \837D F0 00 CMP [LOCAL.4], 0x0
00401399 |. 0F84 93000000 JE 00401432
这个跳转还是蛮长的 猜测一下:如果点击了按钮以后 JE是不是就不会转移了呢? 因为转移的话 就会执行下面的代码: [Asm] 纯文本查看 复制代码 00401432 |> \68 01030080 PUSH 0x80000301
00401437 |. 6A 00 PUSH 0x0
00401439 |. 68 C8000000 PUSH 0xC8
0040143E |. 68 01000000 PUSH 0x1
00401443 |. BB 7C060000 MOV EBX, 0x67C
00401448 |. E8 F3820100 CALL 00419740 ; 休眠200毫秒
0040144D |. 83C4 10 ADD ESP, 0x10
00401450 |. E8 A1FEFFFF CALL 004012F6
00401455 |. 8BE5 MOV ESP, EBP
00401457 |. 5D POP EBP ; CrackMe.00401455
00401458 \. C3 RETN
休眠200毫秒以后又重新调用这个函数了。 所以我猜想 点击了按钮以后 JE就不会转移 那么我们验证一下 在这个指令(JE 00401432)的下一条指令(0040139F PUSH 0x80000004)下断点 然后删掉0x004012F6 处的断点 F9运行 点击一下[注册按钮] 可以看到程序直接断了下来 因此 这个地址(0040139F)就是我们要找的地址 0x4 验证的地方(爆破 + 追码) 在0x1中 我们知道了 剪辑板中存放注册码 那么我们给00419610 (取剪辑板文本)下断看看 一直F8 看看能不能在这里断下来。 断下来了 然后执行到返回 到达0x401972处。 查看一下eax寄存器 可以看到 这个又是取出了我输入的假码 F8继续往下走 [Asm] 纯文本查看 复制代码 00401990 |. 68 010100A0 PUSH 0xA0000101
00401995 |. 6A 00 PUSH 0x0
00401997 |. 68 8AE14200 PUSH 0042E18A
0040199C |. 68 01000000 PUSH 0x1
004019A1 |. BB 68010000 MOV EBX, 0x168
004019A6 |. E8 95790100 CALL 00419340
004019AB |. 83C4 10 ADD ESP, 0x10
执行完这个call以后 eax寄存器是这样的 不知道这是干什么的 不要管他继续往下 [Asm] 纯文本查看 复制代码 004019C9 |. 6A 01 PUSH 0x1
004019CB |. B8 A7E14200 MOV EAX, 0042E1A7
004019D0 |. 8945 F4 MOV [LOCAL.3], EAX
004019D3 |. 8D45 F4 LEA EAX, [LOCAL.3]
004019D6 |. 50 PUSH EAX
004019D7 |. 8D45 F8 LEA EAX, [LOCAL.2]
004019DA |. 50 PUSH EAX
004019DB |. E8 2D010000 CALL 00401B0D ; '52pojiE'
004019E0 |. 8945 F0 MOV [LOCAL.4], EAX ; 保存 执行完这个call以后 eax寄存器中的地址指向"52pojiE"这个字符串 继续往下 [Asm] 纯文本查看 复制代码 004019F6 |. 50 PUSH EAX
004019F7 |. 8B5D F8 MOV EBX, [LOCAL.2]
004019FA |. 85DB TEST EBX, EBX
004019FC |. 74 09 JE SHORT 00401A07
004019FE |. 53 PUSH EBX
004019FF |. E8 A2750100 CALL 00418FA6
00401A04 |. 83C4 04 ADD ESP, 0x4
00401A07 |> 58 POP EAX ; 007B0238
00401A08 |. 8945 F8 MOV [LOCAL.2], EAX ; 保存到[LOCAL.2]中
00401A0B |. 8B45 F8 MOV EAX, [LOCAL.2]
00401A0E |. 50 PUSH EAX
00401A0F |. FF75 FC PUSH [LOCAL.1] ; 输入的假码
00401A12 |. E8 98FEFFFF CALL 004018AF ; 比较文本
00401A17 |. 83C4 08 ADD ESP, 0x8
比较文本函数: [Asm] 纯文本查看 复制代码 004018AF /$ 8B5424 04 MOV EDX, DWORD PTR SS:[ESP+0x4]
004018B3 |. 8B4C24 08 MOV ECX, DWORD PTR SS:[ESP+0x8]
004018B7 |. 85D2 TEST EDX, EDX
004018B9 |. 75 0D JNZ SHORT 004018C8
004018BB |. 33C0 XOR EAX, EAX
004018BD |. 85C9 TEST ECX, ECX
004018BF |. 74 06 JE SHORT 004018C7
004018C1 |. 8039 00 CMP BYTE PTR DS:[ECX], 0x0
004018C4 |. 74 01 JE SHORT 004018C7
004018C6 |. 48 DEC EAX
004018C7 |> C3 RETN
004018C8 |> 85C9 TEST ECX, ECX
004018CA |. 75 09 JNZ SHORT 004018D5
004018CC |. 33C0 XOR EAX, EAX
004018CE |. 803A 00 CMP BYTE PTR DS:[EDX], 0x0
004018D1 |. 74 01 JE SHORT 004018D4
004018D3 |. 40 INC EAX
004018D4 |> C3 RETN
004018D5 |> E8 53160000 CALL 00402F2D
004018DA |. 90 NOP
004018DB |. 75 37 JNZ SHORT 00401914
004018DD |> 8B02 /MOV EAX, DWORD PTR DS:[EDX]
004018DF |. 3A01 |CMP AL, BYTE PTR DS:[ECX]
004018E1 |. 75 2B |JNZ SHORT 0040190E
004018E3 |. 0AC0 |OR AL, AL
004018E5 |. 74 24 |JE SHORT 0040190B
004018E7 |. 3A61 01 |CMP AH, BYTE PTR DS:[ECX+0x1]
004018EA |. 75 22 |JNZ SHORT 0040190E
004018EC |. 0AE4 |OR AH, AH
004018EE |. 74 1B |JE SHORT 0040190B
004018F0 |. C1E8 10 |SHR EAX, 0x10
004018F3 |. 3A41 02 |CMP AL, BYTE PTR DS:[ECX+0x2]
004018F6 |. 75 16 |JNZ SHORT 0040190E
004018F8 |. 0AC0 |OR AL, AL
004018FA |. 74 0F |JE SHORT 0040190B
004018FC |. 3A61 03 |CMP AH, BYTE PTR DS:[ECX+0x3]
004018FF |. 75 0D |JNZ SHORT 0040190E
00401901 |. 83C1 04 |ADD ECX, 0x4
00401904 |. 83C2 04 |ADD EDX, 0x4
00401907 |. 0AE4 |OR AH, AH
00401909 |.^ 75 D2 \JNZ SHORT 004018DD
0040190B |> 33C0 XOR EAX, EAX
0040190D |. C3 RETN
0040190E |> 1BC0 SBB EAX, EAX
00401910 |. D1E0 SHL EAX, 1
00401912 |. 40 INC EAX
00401913 |. C3 RETN
00401914 |> F7C2 01000000 TEST EDX, 0x1
0040191A |. 74 14 JE SHORT 00401930
0040191C |. 8A02 MOV AL, BYTE PTR DS:[EDX]
0040191E |. 42 INC EDX
0040191F |. 3A01 CMP AL, BYTE PTR DS:[ECX]
00401921 |.^ 75 EB JNZ SHORT 0040190E
00401923 |. 41 INC ECX
00401924 |. 0AC0 OR AL, AL
00401926 |.^ 74 E3 JE SHORT 0040190B
00401928 |. F7C2 02000000 TEST EDX, 0x2
0040192E |.^ 74 AD JE SHORT 004018DD
00401930 |> 66:8B02 MOV AX, WORD PTR DS:[EDX]
00401933 |. 83C2 02 ADD EDX, 0x2
00401936 |. 3A01 CMP AL, BYTE PTR DS:[ECX]
00401938 |.^ 75 D4 JNZ SHORT 0040190E
0040193A |. 0AC0 OR AL, AL
0040193C |.^ 74 CD JE SHORT 0040190B
0040193E |. 3A61 01 CMP AH, BYTE PTR DS:[ECX+0x1]
00401941 |.^ 75 CB JNZ SHORT 0040190E
00401943 |. 0AE4 OR AH, AH
00401945 |.^ 74 C4 JE SHORT 0040190B
00401947 |. 83C1 02 ADD ECX, 0x2
0040194A \.^ EB 91 JMP SHORT 004018DD
这段代码 用我们输入的假码和"52pojiE"进行比较 因为这两个字符串不一样 所以返回-1(eax = 0xFFFFFFFF) 然后紧接着 有个比较返回值的 [Asm] 纯文本查看 复制代码 00401A1A |. 83F8 00 CMP EAX, 0x0
00401A1D |. 0F85 4C000000 JNZ 00401A6F
这个JNZ实现的话(eax != 0) 函数就直接返回。 那么把这个JNZ用NOP填充 然后F9运行一下试试 [Asm] 纯文本查看 复制代码 00401A1D 90 NOP
00401A1E 90 NOP
00401A1F 90 NOP
00401A20 90 NOP
00401A21 90 NOP
00401A22 90 NOP
所以这个JNZ就是关键跳转 那么注册码应该很明显了 就是52pojiE 附一个成功的截图: |