一个娱乐CM的爆破 + 追码
本帖最后由 zbnysjwsnd8 于 2017-7-29 23:04 编辑CM地址:http://www.52pojie.cn/thread-629598-1-1.html
这篇文章不好写 而且我还是小菜 哪里有错了请大牛们指出。调试前先预测代码的实现,这是个好习惯。若预测有误也没关系,从头开始调试即可。但若是有幸预测正确,则可以节省大量调试时间。
-------《逆向工程·核心原理》
0x0 程序截图
0x1 注册码的存放方式
OD载入 输入假码 然后给GetWindowTextA下断点。
然后执行到4050C1处。观察一下eax寄存器:
eax存放我刚刚输入的假码的地址。然后 程序将我刚刚输入的假码的地址放在了中004050C1|.8945 F8 MOV , EAX;将注册码保存到中程序又取出注册码 然后复制到剪辑板中.004050D4|> \68 04000080 PUSH 0x80000004
004050D9|.6A 00 PUSH 0x0
004050DB|.8B45 F8 MOV EAX, ;取出注册码
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:
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:
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:
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:, DWORD PTR DS: ; |
0041A36B|.8BC8 MOV ECX, EAX ; |
0041A36D|.83E1 03 AND ECX, 0x3 ; |
0041A370|.F3:A4 REP MOVS BYTE PTR ES:, BYTE PTR DS: ; |
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毫秒
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:
00419744|.50 PUSH EAX ; /Timeout = 0. ms
00419745|.FF15 58C04100 CALL DWORD PTR DS:[<&KERNEL32.Sleep>] ; \Sleep
0041974B\.C3 RETN
这有可能就是为了多线程做准备了。。
0x2 多线程
给OpenClipboard下断点 然后OD查看一下线程
圈起来的这两个线程需要特别注意一下(程序启动时就已经被创建了。)OD看看这两个线程的代码
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
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处有段代码需要注意一下:00405095|.833D C6F64200>CMP DWORD PTR DS:, 0x1
0040509C|.0F85 68000000 JNZ 0040510A
我这里执行的时候 0x0042F6C6 处的值不是1给0x42F6C6下硬件写入断点(大小是1) 并禁用那两个线程的断点 F9运行。程序会在0x0040518B处断下00405178/$55 PUSH EBP ;从剪辑板中取出注册码
00405179|.8BEC MOV EBP, ESP
0040517B|.81EC 04000000 SUB ESP, 0x4
00405181|.C705 C6F64200>MOV DWORD PTR DS:, 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 , EAX ;保存到中
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:
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而在这里00405095 |. 833D C6F64200>CMP DWORD PTR DS:, 0x1
0040509C |. 0F85 68000000 JNZ 0040510A
这段代码判断0x42F6C6处的值是不是1 如果不是的话就转移 到0x0040510A处继续执行。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一直单步到这里00401395|> \837D F0 00 CMP , 0x0
00401399|.0F84 93000000 JE 00401432
这个跳转还是蛮长的 猜测一下:如果点击了按钮以后 JE是不是就不会转移了呢?因为转移的话 就会执行下面的代码: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继续往下走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寄存器是这样的不知道这是干什么的 不要管他继续往下004019C9|.6A 01 PUSH 0x1
004019CB|.B8 A7E14200 MOV EAX, 0042E1A7
004019D0|.8945 F4 MOV , EAX
004019D3|.8D45 F4 LEA EAX,
004019D6|.50 PUSH EAX
004019D7|.8D45 F8 LEA EAX,
004019DA|.50 PUSH EAX
004019DB|.E8 2D010000 CALL 00401B0D ;'52pojiE'
004019E0|.8945 F0 MOV , EAX ;保存执行完这个call以后 eax寄存器中的地址指向"52pojiE"这个字符串继续往下004019F6|.50 PUSH EAX
004019F7|.8B5D F8 MOV EBX,
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 , EAX ;保存到中
00401A0B|.8B45 F8 MOV EAX,
00401A0E|.50 PUSH EAX
00401A0F|.FF75 FC PUSH ;输入的假码
00401A12|.E8 98FEFFFF CALL 004018AF ;比较文本
00401A17|.83C4 08 ADD ESP, 0x8
比较文本函数:004018AF/$8B5424 04 MOV EDX, DWORD PTR SS:
004018B3|.8B4C24 08 MOV ECX, DWORD PTR SS:
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:, 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:, 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:
004018DF|.3A01 |CMP AL, BYTE PTR DS:
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:
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:
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:
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:
0040191E|.42 INC EDX
0040191F|.3A01 CMP AL, BYTE PTR DS:
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:
00401933|.83C2 02 ADD EDX, 0x2
00401936|.3A01 CMP AL, BYTE PTR DS:
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:
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)然后紧接着 有个比较返回值的00401A1A|.83F8 00 CMP EAX, 0x0
00401A1D|.0F85 4C000000 JNZ 00401A6F
这个JNZ实现的话(eax != 0)函数就直接返回。那么把这个JNZ用NOP填充 然后F9运行一下试试00401A1D 90 NOP
00401A1E 90 NOP
00401A1F 90 NOP
00401A20 90 NOP
00401A21 90 NOP
00401A22 90 NOP
突然提示破解成功所以这个JNZ就是关键跳转那么注册码应该很明显了 就是52pojiE附一个成功的截图: xiao_ya 发表于 2017-7-31 10:10
执行到4050C1处,是怎么执行的?求大神为小白解惑
一路F8就能到{:1_902:} 讲的比较详细,支持下,多多分析,值得学习 这个壁纸不错啊求壁纸~ 谢谢分享思路{:301_993:} 感谢这么好的教材 学习一下。 谢谢老师分享!收藏,学习。 学习了 感谢 支持一下。爆破 学习了,看起来还是有些不懂,慢慢研究