小弟最近初學破解,這次我第一次破解成功。盡管是在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
哈!注冊成功!
==================================================================
小弟也是新手一隻,有哪裡錯了,歡迎大神們提出來,一定虛心改正。