小弟最近初学破解,这次我第一次破解成功。尽管是在riijj大神的神文的指导下完成(神文出处),但还是很开心。
特发此文。
Ps:可能因为排版问题,中间的分析看上去有点乱,大家可以重点看最后面的分析哦。或者把它复制到txt文本中会看上去会舒服点。。
=============================================================
这是我们要破解的目标:
ncrackme.exe
1.先用peid看了下,没有壳,用VC6.0编写的 2.用插件-超级字符串找到了关键代码位置 3.发现了一个很重要的EAX,Test EAX,EAX 如果EAX是0,则跳转 00401050 . 817C24 08 110>CMP DWORD PTR SS:[ESP+8],111 00401058 . 75 74 JNZ SHORT 2.004010CE 0040105A . 8B4424 0C MOV EAX,DWORD PTR SS:[ESP+C] 0040105E . 66:3D EA03 CMP AX,3EA 00401062 . 75 42 JNZ SHORT 2.004010A6 00401064 . E8 C7010000 CALL 2.00401230 ; 注意这里哦!它返回值EAX很关键哦~~ 00401069 . 85C0 TEST EAX,EAX ; 如果返回值EAX是0,那么Test EAX,EAX ZF肯定是1,否则为0 0040106B . 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL 0040106D . 68 80504000 PUSH 2.00405080 ; |Title = "ncrackme" 00401072 . 75 1B JNZ SHORT 2.0040108F ; | 如果ZF是1那么就不会跳走了,就成功了~~ 00401074 . A1 B8564000 MOV EAX,DWORD PTR DS:[4056B8] ; | 如果ZF是0那么就跳走了,就失败了~~ 00401079 . 68 64504000 PUSH 2.00405064 ; |Text = "Registration successful." 0040107E . 50 PUSH EAX ; |hOwner => 000906E4 ('Newbie smallsize crackme - v1',class='myWindowClass') 0040107F . FF15 C0404000 CALL DWORD PTR DS:[<&USER32.MessageBoxA>>; \MessageBoxA 00401085 . E8 A6020000 CALL 2.00401330 0040108A . 33C0 XOR EAX,EAX 0040108C . C2 1000 RETN 10 0040108F > 8B0D B8564000 MOV ECX,DWORD PTR DS:[4056B8] ; | 00401095 . 68 50504000 PUSH 2.00405050 ; |Text = "Registration fail." 0040109A . 51 PUSH ECX ; |hOwner => 000906E4 ('Newbie smallsize crackme - v1',class='myWindowClass') 0040109B . FF15 C0404000 CALL DWORD PTR DS:[<&USER32.MessageBoxA>>; \MessageBoxA 004010A1 . 33C0 XOR EAX,EAX 004010A3 . C2 1000 RETN 10 004010A6 > 66:3D EB03 CMP AX,3EB 004010AA . 75 22 JNZ SHORT 2.004010CE 004010AC . A1 C0564000 MOV EAX,DWORD PTR DS:[4056C0] 004010B1 . 85C0 TEST EAX,EAX 004010B3 . 74 19 JE SHORT 2.004010CE 004010B5 . 8B15 B8564000 MOV EDX,DWORD PTR DS:[4056B8] 004010BB . 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL 004010BD . 68 80504000 PUSH 2.00405080 ; |Title = "ncrackme" 004010C2 . 68 30504000 PUSH 2.00405030 ; |Text = "good function, i was cracked" 004010C7 . 52 PUSH EDX ; |hOwner => 000906E4 ('Newbie smallsize crackme - v1',class='myWindowClass') 004010C8 . FF15 C0404000 CALL DWORD PTR DS:[<&USER32.MessageBoxA>>; \MessageBoxA 004010CE > 33C0 XOR EAX,EAX 004010D0 . C2 1000 RETN 10 4.而EAX是它前面一个call的返回值,顺着这个返回值,我来到了call,要怎么样才能让返回值不会0呢 00401230 /$ 8B0D BC564000 MOV ECX,DWORD PTR DS:[4056BC] 00401236 |. 83EC 30 SUB ESP,30 00401239 |. 8D4424 00 LEA EAX,DWORD PTR SS:[ESP] 0040123D |. 53 PUSH EBX 0040123E |. 56 PUSH ESI 0040123F |. 8B35 94404000 MOV ESI,DWORD PTR DS:[<&USER32.GetDlgIte>; USER32.GetDlgItemTextA 00401245 |. 6A 10 PUSH 10 ; /Count = 10 (16.) 00401247 |. 50 PUSH EAX ; |Buffer 存放用户名字的地方 00401248 |. 68 E8030000 PUSH 3E8 ; |ControlID = 3E8 (1000.) 0040124D |. 51 PUSH ECX ; |hWnd => 00160674 (class='#32770',parent=001B065C) 0040124E |. 33DB XOR EBX,EBX ; | 00401250 |. FFD6 CALL ESI ; \GetDlgItemTextA 这个函数会把返回值放到EAX吧。。返回值是字符串长度 00401252 |. 83F8 03 CMP EAX,3 ; 这个地方是比较EAX(值为6)与3的大小,是这个用户名的长度 00401255 |. 73 0B JNB SHORT ncrackme.00401262 00401257 |. 5E POP ESI 00401258 |. B8 01000000 MOV EAX,1 0040125D |. 5B POP EBX 0040125E |. 83C4 30 ADD ESP,30 00401261 |. C3 RETN 00401262 |> A1 BC564000 MOV EAX,DWORD PTR DS:[4056BC] ; 从 JNB 来了这里 00401267 |. 8D5424 28 LEA EDX,DWORD PTR SS:[ESP+28] 0040126B |. 6A 10 PUSH 10 ; 把10压入栈,10是参数1 0040126D |. 52 PUSH EDX ; 把这个地址0012FAF8压入栈了,这个应该是字符串的地址了,参数2 0040126E |. 68 E9030000 PUSH 3E9 ; 3e9转过来就是1001。。参数3 00401273 |. 50 PUSH EAX ; 00401274 |. FFD6 CALL ESI 00401276 |. 0FBE4424 08 MOVSX EAX,BYTE PTR SS:[ESP+8] ; name[0],这里ESP是0012FAD0,用户名所在地址是0012FAD8,ESP+8 后就是“ouyang”的第一个字符,也就是把第一个字符放到EAX 0040127B |. 0FBE4C24 09 MOVSX ECX,BYTE PTR SS:[ESP+9] ; 第二个字符放到ECX,name[1] 00401280 |. 99 CDQ ; 把EDX拓展成EAX的高位。。成64位。。也就是********位 00401281 |. F7F9 IDIV ECX ; 把 EDX:EAX 除以 ECX,余数放在 EDX 00401283 |. 8BCA MOV ECX,EDX ; 00401285 |. 83C8 FF OR EAX,FFFFFFFF ; 把EAX 置为 FFFFFFFF 00401288 |. 0FBE5424 0A MOVSX EDX,BYTE PTR SS:[ESP+A] ; 把name[2]放到EDX 0040128D |. 0FAFCA IMUL ECX,EDX ; ECX和EDX相乘,余数和name[2]相乘 00401290 |. 41 INC ECX ; ECX 自增1,结果增1 00401291 |. 33D2 XOR EDX,EDX ; EDX 清0 00401293 |. F7F1 DIV ECX ; EAX (ffffffff)除以ECX。。 00401295 |. 50 PUSH EAX ; 计算完毕。。0xFFFFFFFF / (1+(name[0] % name[1] * name[2]) 00401296 |. E8 A5000000 CALL ncrackme.00401340 ; 进去看看 进去后的代码: 00401340 /$ 8B4424 04 MOV EAX,DWORD PTR SS:[ESP+4] ;EAX 赋值给了[4050AC]这个东东啊 00401344 |. A3 AC504000 MOV DWORD PTR DS:[4050AC],EAX ;虽然不太明白,但是这个4050AC在后面出现很多哦~ 00401349 \. C3 RETN 出来外面的代码继续: 0040129B |. 83C4 04 ADD ESP,4 ; 清理stack 0040129E |. 33F6 XOR ESI,ESI ; ESI清零 004012A0 |> E8 A5000000 /CALL ncrackme.0040134A ; 进去后 进去后的代码: 0040134A /$ A1 AC504000 MOV EAX,DWORD PTR DS:[4050AC] ;刚刚把4050AC的内容也就是刚刚计算完的结果赋值给EAX 0040134F |. 69C0 FD430300 IMUL EAX,EAX,343FD ; EAX * EAX * 343FD(十进制 214013) 00401355 |. 05 C39E2600 ADD EAX,269EC3 ; 上一条指令运算结果后加上269EC3 0040135A |. A3 AC504000 MOV DWORD PTR DS:[4050AC],EAX ; 然后再把这个最终结果放到4050AC的地方去 0040135F |. C1F8 10 SAR EAX,10 ; EAX 右移 10位 00401362 |. 25 FF7F0000 AND EAX,7FFF ; EAX 加上 7FFF 00401367 \. C3 RETN 回到外面的代码继续: 004012A5 |. 99 |CDQ ; 将EDX 由双字拓展到8字节 成 EDX;EAX 004012A6 |. B9 1A000000 |MOV ECX,1A ; 把1A 赋值给 ECX 004012AB |. F7F9 |IDIV ECX ; EDX;EAX 除以ECX,然后余数放到EDX 004012AD |. 80C2 41 |ADD DL,41 ; EDX 的低16位 加上 41 004012B0 |. 885434 18 |MOV BYTE PTR SS:[ESP+ESI+18],DL ; 把DL赋值给这里面的地址[ESP+ESI+18] 004012B4 |. 46 |INC ESI ; ESI 自增1 004012B5 |. 83FE 0F |CMP ESI,0F ; 004012B8 |.^ 72 E6 \JB SHORT ncrackme.004012A0 ; 结合上一句指令,这应该是要循环16次吧,直到ESI从0变成15 004012BA |. 57 PUSH EDI ; 循环后的EDI压栈,给用户名腾出了位置啊。。 004012BB |. 8D7C24 0C LEA EDI,DWORD PTR SS:[ESP+C] ; 把用户名"ouyang"放到EDI中 004012BF |. 83C9 FF OR ECX,FFFFFFFF ; ECX 置为 FFFFFFFF 004012C2 |. 33C0 XOR EAX,EAX ; EAX 清零 004012C4 |. 33F6 XOR ESI,ESI ; ESI 清零 004012C6 |. F2:AE REPNE SCAS BYTE PTR ES:[EDI] ; 扫描es:edi指向的一系列字节数据,扫描长度由ecx指定,当遇到与al中的数据相等时停止扫描。 004012C8 |. F7D1 NOT ECX ; 取反 004012CA |. 49 DEC ECX ; 自减1,到这里才明白,原来是要求 "ouyang"的长度啊。。 004012CB |. 74 59 JE SHORT ncrackme.00401326 ; 这个应该是如果字符串长度为0 就 。。完蛋,幸好我们的用户名不是0 004012CD |> 8A4434 0C /MOV AL,BYTE PTR SS:[ESP+ESI+C] ; 把 [ESP+ESI+18]的内容还给AL,在往前14条指令中这个[ESP+ESI+18]应该是EDX当时做的运算吧。。 004012D1 |. C0F8 05 |SAR AL,5 ; AL 右移 5 位 004012D4 |. 0FBEC0 |MOVSX EAX,AL ; 004012D7 |. 8D1480 |LEA EDX,DWORD PTR DS:[EAX+EAX*4] ; 这以下三段运算。。不知道想表达什么意思 004012DA |. 8D04D0 |LEA EAX,DWORD PTR DS:[EAX+EDX*8] 004012DD |. 8D0440 |LEA EAX,DWORD PTR DS:[EAX+EAX*2] 004012E0 |. 85C0 |TEST EAX,EAX 004012E2 |. 7E 0A |JLE SHORT ncrackme.004012EE ; EAX 为0 就跳 004012E4 |. 8BF8 |MOV EDI,EAX ; 把运算结果赋值给 EDI ,结果是170.。后面要循环170次。。好可怜 004012E6 |> E8 5F000000 |/CALL ncrackme.0040134A ; 进去看看 进去后的代码: 0040134A /$ A1 AC504000 MOV EAX,DWORD PTR DS:[4050AC] ; 这是把[4050AC]的那个最终结果赋值给EAX 0040134F |. 69C0 FD430300 IMUL EAX,EAX,343FD ; EAX * EAX * 343FD 00401355 |. 05 C39E2600 ADD EAX,269EC3 ; EAX + 269EC3 0040135A |. A3 AC504000 MOV DWORD PTR DS:[4050AC],EAX ; 再次把运算结果还给[4050AC] 0040135F |. C1F8 10 SAR EAX,10 ; 右移16位 00401362 |. 25 FF7F0000 AND EAX,7FFF ; 加上7FFF 00401367 \. C3 RETN 出来外面的代码继续; 004012EB |. 4F ||DEC EDI ; EDI自减1,循环直到为0 004012EC |.^ 75 F8 |\JNZ SHORT ncrackme.004012E6 ; 循环 004012EE |> E8 57000000 |CALL ncrackme.0040134A ; 进去看看 进去后的代码(这一段懒得解释了。。和前面的一样): 0040134A /$ A1 AC504000 MOV EAX,DWORD PTR DS:[4050AC] 0040134F |. 69C0 FD430300 IMUL EAX,EAX,343FD 00401355 |. 05 C39E2600 ADD EAX,269EC3 0040135A |. A3 AC504000 MOV DWORD PTR DS:[4050AC],EAX 0040135F |. C1F8 10 SAR EAX,10 00401362 |. 25 FF7F0000 AND EAX,7FFF 00401367 \. C3 RETN 出来后的继续代码: 004012F3 |. 99 |CDQ ; 又是 EDX;EAX 004012F4 |. B9 1A000000 |MOV ECX,1A ; 004012F9 |. 8D7C24 0C |LEA EDI,DWORD PTR SS:[ESP+C] ; 用户名”ouyang“ 004012FD |. F7F9 |IDIV ECX ; 余数还放在EDX哦。。 004012FF |. 0FBE4C34 2C |MOVSX ECX,BYTE PTR SS:[ESP+ESI+2C] ; 00401304 |. 80C2 41 |ADD DL,41 00401307 |. 0FBEC2 |MOVSX EAX,DL 0040130A |. 2BC1 |SUB EAX,ECX 0040130C |. 885434 1C |MOV BYTE PTR SS:[ESP+ESI+1C],DL 00401310 |. 99 |CDQ 00401311 |. 33C2 |XOR EAX,EDX 00401313 |. 83C9 FF |OR ECX,FFFFFFFF 00401316 |. 2BC2 |SUB EAX,EDX 00401318 |. 03D8 |ADD EBX,EAX 0040131A |. 33C0 |XOR EAX,EAX 0040131C |. 46 |INC ESI 0040131D |. F2:AE |REPNE SCAS BYTE PTR ES:[EDI] 0040131F |. F7D1 |NOT ECX 00401321 |. 49 |DEC ECX 00401322 |. 3BF1 |CMP ESI,ECX 00401324 |.^ 72 A7 \JB SHORT ncrackme.004012CD 00401326 |> 5F POP EDI 00401327 |. 8BC3 MOV EAX,EBX ; 这里把EBX赋值给EAX了。。这个酱油EBX 00401329 |. 5E POP ESI 0040132A |. 5B POP EBX 0040132B |. 83C4 30 ADD ESP,30 0040132E \. C3 RETN 0040132F 90 NOP 00401330 /$ C705 C0564000>MOV DWORD PTR DS:[4056C0],1 0040133A \. C3 RETN ; 前面的指令差不多就不分析了,出来后的结果是EAX为B7 ...要怎么样才能为0呢? 没错。。我们的注册机就是要把EAX(也就是那个酱油EBX)变成0。。 我们把这么一大段中和EBX有关系的指令全部揪出来。。 共有: 0040123D |. 53 PUSH EBX 0040124E |. 33DB XOR EBX,EBX ; | 0040125D |. 5B POP EBX 00401318 |. 03D8 |ADD EBX,EAX 00401327 |. 8BC3 MOV EAX,EBX ; 这里把EBX赋值给EAX了。。这个酱油EBX 其中有用的就是这一句: 00401318 |. 03D8 |ADD EBX,EAX 而这条指令的前一句: 00401316 |. 2BC2 |SUB EAX,EDX 告诉我们。。只要EAX能够和EDX相等,那么EAX就是0,那么EBX也就是0。。 那么要怎么样才能让EAX和EDX相等呢? 找到和EAX以及EDX有关系的指令,啊,往上可以看到这么两句: 00401310 |. 99 |CDQ 00401311 |. 33C2 |XOR EAX,EDX 这两句的意思是,把EDX拓展成EAX的高位,如EAX是FFFFFFF9(-6),那么拓展后EDX;EAX = FFFFFFFF;FFFFFFF9(还是-6) 没错,EDX的每一位都变成了EAX的最高位 然后XOR EAX ,EDX = FFFFFFFF XOR FFFFFFF9 导致了 变成了0000,0006(正6) 很明显,如果本来是+6,那么结果还是正。没错。。这两句是取EAX的绝对值的操作 那么我们应该让EAX为0,导致它的绝对值为0即可,EAX怎么为0呢? 往前看了看: 00401304 |. 80C2 41 |ADD DL,41 00401307 |. 0FBEC2 |MOVSX EAX,DL 0040130A |. 2BC1 |SUB EAX,ECX 这个是取出DL+‘A’的内容赋值给EAX,然后EAX减去ECX。。 那么这个ECX是什么呢? MOVSX ECX,BYTE PTR SS:[ESP+ESI+2C] 而这个 SS:[ESP+ESI+2C] 发现了是我们输入的序列号的每一位字符。。。 呵呵,很明显了,知道每一位的DL+'A'(A就是41啊,傻孩子),然后合起来连成一串str就是我们要的序列号了。。 在0040130A 下断点,然后每次记录这个DL+'A' 合起来就是KTKOLB 哈!注册成功!
==================================================================
小弟也是新手一只,有哪里错了,欢迎大神们提出来,一定虚心改正。