标题:我的ASM CrackMe分析(PEDIY200帖留念) [分析+源码](详解!新手看)
链接:http://www.unpack.cn/viewthread.php?tid=19965
贴者:NONAME剑人
日期:2007-12-7 16:43
呵呵,好久没写破文了,最近也颓废了一下,终于决心把我自己做的CrackMe的流程给说说了……
(别砸我,我写破文的时候没看源码)
这个CrackMe难度好象不太高,难道是没人品吗:)HEHE
(我还是别开玩笑了,发现越是大大的大牛,越没幽默感,到时候太过于幽默了没法混了就不好了:) )
……,我将在破文的过程中用反汇编,最后给大家汇编代码,可以参考一下,再最后给大家VB的注册机。。。
(我这性格就是古怪,各位看管别介意:) )
好,废话少说,PEiD加载,ACProtect V1.4X -> risco *。HEHE,那是因为我伪装了文件头,用了一个花
00401000 > $ 60 PUSHAD
00401001 . E8 01000000 CALL CrackMe1.00401007 //跳到4010007
00401006 7C DB 7C ; CHAR '|'
00401007 . 830424 06 ADD DWORD PTR SS:[ESP],6 //改变堆栈,使返回时到下一条指令
0040100B . C3 RETN
如果你喜欢F8的话,中招了……
(不过这个花指令会“吃”掉一条堆栈中的数据……)
由于是汇编,比较好看,往下拉就能看到领空了。
0040111D . 68 00020000 PUSH 200 ; /Count = 200 (512.)
00401122 . 68 B4304000 PUSH CrackMe1.004030B4 ; |Buffer = CrackMe1.004030B4
00401127 . 68 B80B0000 PUSH 0BB8 ; |ControlID = BB8 (3000.)
0040112C . FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hWnd
0040112F . E8 24050000 CALL ; \GetDlgItemTextA
00401134 . 50 PUSH EAX //用户名堆2次
00401135 . 50 PUSH EAX
00401136 . 68 00020000 PUSH 200 ; /Count = 200 (512.)
0040113B . 68 B4324000 PUSH CrackMe1.004032B4 ; |Buffer = CrackMe1.004032B4
00401140 . 6A 03 PUSH 3 ; |ControlID = 3
00401142 . FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hWnd
00401145 . E8 0E050000 CALL ; \GetDlgItemTextA
0040114A . 50 PUSH EAX //密码堆两次
0040114B . 50 PUSH EAX
好象没什么太大的动作,继续!
0040114C . 6A 00 PUSH 0 ; /pModule = NULL
0040114E . E8 2F050000 CALL ; \GetModuleHandleA
怎么到这用GETMODULE?下看
00401153 . 05 00100000 ADD EAX,1000 //原来是得到OEP(401000)
00401158 . 50 PUSH EAX //PUSH OEP*2
00401159 . 50 PUSH EAX
0040115A . 54 PUSH ESP ; /pOldProtect
0040115B . 6A 40 PUSH 40 ; |NewProtect = PAGE_EXECUTE_READWRITE
0040115D . 68 00100000 PUSH 1000 ; |Size = 1000 (4096.)
00401162 . 50 PUSH EAX ; |Address
00401163 . E8 26050000 CALL ; \VirtualProtect
利用虚拟内存API改变内存页属性,使该段代码可读
至此,你的堆栈应该是这样了
===========================
0013FB90 00000020 VirtualProtect私M一个……
0013FB94 00401000 CrackMe1.<模块入口点>
0013FB98 00000003 密码长
0013FB9C 00000003 密码长
0013FBA0 00000003 用户名长
0013FBA4 00000003 用户名产
===========================
00401168 . EB 5B JMP SHORT CrackMe1.004011C5
走!
004011C5 > \B8 6A114000 MOV EAX,CrackMe1.0040116A
004011CA . B9 C4114000 MOV ECX,CrackMe1.004011C4
004011CF . 2BC8 SUB ECX,EAX
004011D1 . 8BD1 MOV EDX,ECX
004011D3 > B8 6A114000 MOV EAX,CrackMe1.0040116A
004011D8 . 03C2 ADD EAX,EDX
004011DA . 2BC1 SUB EAX,ECX
004011DC . 8BF0 MOV ESI,EAX
004011DE . 8B06 MOV EAX,DWORD PTR DS:[ESI]
004011E0 . 40 INC EAX
004011E1 . 8906 MOV DWORD PTR DS:[ESI],EAX
004011E3 .^ E2 EE LOOPD SHORT CrackMe1.004011D3
看到这段,你想到了什么?
我们先来看看40116a是什么......
0040116A . AF SCAS DWORD PTR ES:[EDI]
0040116B . C3 RETN
0040116C . BD B6B6D3C0 MOV EBP,C0D3B6B6
00401171 . BA AE9FB5C1 MOV EDX,C1B59FAE
00401176 . BC D3BDB6C0 MOV ESP,C0B6BDD3
0040117B . F600 05 TEST BYTE PTR DS:[EAX],5
0040117E . AF SCAS DWORD PTR ES:[EDI]
0040117F . C3 RETN
00401180 . C3 RETN
00401181 B6 DB B6
00401182 C1 DB C1
再看看4011c4呢……
004011BD . A5 MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ES>
004011BE . AC LODS BYTE PTR DS:[ESI]
004011BF . A5 MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ES>
004011C0 . C2 C4B0 RETN 0B0C4
004011C3 AE DB AE
004011C4 00 DB 00
毫无规律,继续往下看
004011C5 > \B8 6A114000 MOV EAX,CrackMe1.0040116A
004011CA . B9 C4114000 MOV ECX,CrackMe1.004011C4
004011CF . 2BC8 SUB ECX,EAX
这是什么?毫无疑问,应该是40116A-->(4011C4-1)之间的个数
004011D1 . 8BD1 MOV EDX,ECX
我们暂时不知道EDX以后干什么,目前只知道他是个数-1
该这个循环了
004011D3 > /B8 6A114000 MOV EAX,CrackMe1.0040116A
004011D8 . |03C2 ADD EAX,EDX
004011DA . |2BC1 SUB EAX,ECX
004011DC . |8BF0 MOV ESI,EAX
004011DE . |8B06 MOV EAX,DWORD PTR DS:[ESI]
004011E0 . |40 INC EAX
004011E1 . |8906 MOV DWORD PTR DS:[ESI],EAX
004011E3 .^\E2 EE LOOPD SHORT CrackMe1.004011D3
咱们一点一点看
004011D3 > /B8 6A114000 MOV EAX,CrackMe1.0040116A
为什么把40116A递到EAX里呢?
他也不能执行……
004011D8 . 03C2 ADD EAX,EDX
EAX=EAX+位数-1
这个位数-1不大好理解,其实就是40116a到4011c3的个数
(因为EDX=ECX=4011C4-1,简称个数-1,这里的“个数”是指跳过的字节数)
004011DA . 2BC1 SUB EAX,ECX
EAX=EAX-循环指针
看到这里你应该能猜到了——这是个SMC
为什么呢?
因为到目前为止,EAX=基地址(40116A)+总个数-1(EDX)-循环指针(ECX)
怎么说呢,理解理解吧:)
下面就好办了
004011DC . 8BF0 MOV ESI,EAX
因为EAX没法直接框起来,要不编译器会报错,所以就存到ESI了
004011DE . 8B06 MOV EAX,DWORD PTR DS:[ESI]
把ESI(即EAX)里的地址取出来,存入EAX
004011E0 . 40 INC EAX
EAX=EAX+1
这里就是SMC的一个解密
004011E1 . 8906 MOV DWORD PTR DS:[ESI],EAX
不用说了吧,存回去
004011E3 .^\E2 EE LOOPD SHORT CrackMe1.004011D3
注意这里:ECX是默认-1的!
到此,一个SMC就已经解完了
注意!解完了以后OD有个BUG,就是鼠标不听使唤,这是CTRL+A就可以了
很好,但是上面的命令还是很古怪,有两种可能
第1种是 多层SMC
第2种是 数据
第1种暂时无法判断,看第2种吧:)
下命令 D 40116A
0040116A B0 C4 BE B7 B7 D4 C1 BB AF A0 B6 C2 BD D4 BE B7 澳痉吩粱癄堵皆痉
0040117A C1 F7 01 06 B0 C4 C4 B7 C2 D2 D8 B9 AF A0 B6 C2 流澳姆乱毓癄堵
0040118A BD D4 DC F7 C6 C8 01 06 B1 FD C4 D9 DA DA DA DA 皆荀迫饼馁谮谮
0040119A AF A0 C7 CA C7 A2 C2 D2 B6 D7 01 06 B1 FD C3 AA 癄鞘洽乱蹲饼锚
004011AA CF FB C3 AA AF A0 B2 F1 DE DD C8 BC CE BB 01 06 消锚癄柴掭燃位
004011BA 2C 2C AD A6 AD A6 C3 C5 B1 AF 00 ,,门悲.竕@.
什么玩意?八成是2层的smc……
不管,咱们继续
004011E5 . B8 6A114000 MOV EAX,CrackMe1.0040116A
004011EA . B9 C4114000 MOV ECX,CrackMe1.004011C4
004011EF . 2BC8 SUB ECX,EAX
004011F1 . 8BD1 MOV EDX,ECX
004011F3 > B8 6A114000 MOV EAX,CrackMe1.0040116A
004011F8 . 03C2 ADD EAX,EDX
004011FA . 2BC1 SUB EAX,ECX
004011FC . 8BF0 MOV ESI,EAX
004011FE . 8B06 MOV EAX,DWORD PTR DS:[ESI]
00401200 . 83F0 0C XOR EAX,0C //***************
00401203 . 8906 MOV DWORD PTR DS:[ESI],EAX
00401205 .^ E2 EC LOOPD SHORT CrackMe1.004011F3
果然说曹操,曹操到。感觉到很熟悉没?这里我就不多说了,唯一要注意的是这个SMC的算法是XOR EAX,0C
而刚才的是INC EAX
不再多说,F4到401207,下断点D 40116A
0040116A BC C8 B2 BB BB D8 CD B7 A3 AC BA CE B1 D8 B2 BB 既不回头,何必不
0040117A CD FB 0D 0A BC C8 C8 BB CE DE D4 B5 A3 AC BA CE 望..既然无缘,何
0040118A B1 D8 D0 FB CA C4 0D 0A BD F1 C8 D5 D6 D6 D6 D6 必宣誓..今日种种
0040119A A3 AC CB C6 CB AE CE DE BA DB 0D 0A BD F1 CF A6 ,似水无痕..今夕
004011AA C3 F7 CF A6 A3 AC BE FD D2 D1 C4 B0 C2 B7 0D 0A 明夕,君已陌路..
004011BA 20 20 A1 AA A1 AA CF C9 BD A3 00 ——仙剑.竕@.
很好,很标准的字符串
但是不知道大家发现没有,为什么之前要位数-1呢?
你可以重新回过头来看,2次SMC中始终不会对最后一位4011C4的00进行操作
这是因为LOOPD的特性,相当于高级语言中的FOR,只不过像UNTIL一样放在了尾段,所以当ECX=0时
就不再执行了
另外你应该明白前面为什么要VIRTUALPROTECT了吧:)
向下看
00401207 . 68 9C304000 PUSH CrackMe1.0040309C ; /pLocaltime = CrackMe1.0040309C
0040120C . E8 6B040000 CALL ; \GetLocalTime
40309C指向的应该是一个时间结构,不管。但为什么注册程序中会出现时间的东西呢?
时间有随机性,你总不能让用户把什么时间注册告诉你吧……
呵呵,所以只可能是ANTI了
我们不管他,以后会碰上,咱们继续……
查查M$的API大全就知道,这是读秒……
0040121A . 50 PUSH EAX
0040121B . 8BF8 MOV EDI,EAX ; CrackMe1.004001E4
0040121D . 58 POP EAX
混淆视线,在逆向中一定要有毅力:)
不过第2句很蹊跷,咱先记住EDI是第一次的秒数……
0040121E . 58 POP EAX
0040121F . 5E POP ESI
清除GetLocalTime产生的堆栈垃圾:)
来看看这个
0040122A > /51 PUSH ECX
0040122B . |68 9C304000 PUSH CrackMe1.0040309C ; /pLocaltime = CrackMe1.0040309C
00401230 . |E8 47040000 CALL ; \GetLocalTime
00401235 . |8BD1 MOV EDX,ECX
00401237 . |59 POP ECX
00401238 . |BE 9C304000 MOV ESI,CrackMe1.0040309C
0040123D . |66:8B46 0E MOV AX,WORD PTR DS:[ESI+E]
00401241 . |50 PUSH EAX
00401242 . |58 POP EAX
00401243 . |BE 04104000 MOV ESI,CrackMe1.00401004
00401248 . |890E MOV DWORD PTR DS:[ESI],ECX
0040124A .^\E2 DE LOOPD SHORT CrackMe1.0040122A
呵呵,实际上只有最后一层的循环管用。第2次读取时间……
如果你很仔细的话,应该能发现……
0040123D . 66:8B46 0E MOV AX,WORD PTR DS:[ESI+E]
最后一次秒数根本没动,一直保存在AX中
0040124C . 3BF8 CMP EDI,EAX ; CrackMe1.0040035B
比较两次的时间……正常肯定是1秒,非正常那就没准了
0040124E . /74 05 JE SHORT CrackMe1.00401255
00401250 . |BF 01000000 MOV EDI,1
00401255 > \3BF8 CMP EDI,EAX
这里如果EDI=1你就=死吧,咱们走这条路,看看怎么死的
00401257 . /75 05 JNZ SHORT CrackMe1.0040125E
00401259 . |BF 02000000 MOV EDI,2
0040125E > \B8 18104000 MOV EAX,CrackMe1.00401018
很显然,如果两次的秒数是一样的,EDI=2,你可以继续!
这里有个防止爆破的地方,就是如果你只改第一次的MOV EDI,1的JE的话,
你的EDI会既不是1也不是2,这样你还是得死:)
00401263 . B9 0E104000 MOV ECX,CrackMe1.0040100E
00401268 . 83E9 01 SUB ECX,1
0040126B . 33DB XOR EBX,EBX
0040126D > 41 INC ECX
0040126E . 8BF1 MOV ESI,ECX
00401270 . 8B06 MOV EAX,DWORD PTR DS:[ESI]
00401272 . 3BC8 CMP ECX,EAX
00401274 .^ 72 F7 JB SHORT CrackMe1.0040126D
呵呵,这里怎么那么像上面的SMC啊?
别着急,咱们仔细看
00401263 . B9 0E104000 MOV ECX,CrackMe1.0040100E
怎么SMC到执行语句上了?
咱么CTRL+G看看40100E
0040100C . /EB 0A JMP SHORT CrackMe1.00401018
0040100E |90 NOP
0040100F |90 NOP
00401010 |90 NOP
00401011 |90 NOP
00401012 |90 NOP
00401013 |90 NOP
00401014 |90 NOP
00401015 |90 NOP
00401016 |90 NOP
00401017 |90 NOP
00401018 > \6A 00 PUSH 0 ; /pModule = NULL
0040101A . E8 63060000 CALL ; \GetModuleHandleA
0040101F . A3 AC304000 MOV DWORD PTR DS:[4030AC],EAX
00401024 . 6A 00 PUSH 0 ; /lParam = NULL
00401026 . 68 43104000 PUSH CrackMe1.00401043 ; |DlgProc = CrackMe1.00401043
0040102B . 6A 00 PUSH 0 ; |hOwner = NULL
0040102D . 68 00304000 PUSH CrackMe1.00403000 ; |pTemplate = "IsDebugPresent"
00401032 . FF35 AC304000 PUSH DWORD PTR DS:[4030AC] ; |hInst = 00400000
00401038 . E8 09060000 CALL ; \DialogBoxParamA
0040103D . 50 PUSH EAX ; /ExitCode
0040103E . E8 33060000 CALL ; \ExitProcess
恩……
00401268 . 83E9 01 SUB ECX,1
ECX=ECX-1,就是40100E的前一项
XOR EBX,EBX 混淆视线……
看看这个循环
0040126D > /41 INC ECX
0040126E . |8BF1 MOV ESI,ECX
00401270 . |8B06 MOV EAX,DWORD PTR DS:[ESI]
00401272 . |3BC8 CMP ECX,EAX
00401274 .^\72 F7 JB SHORT CrackMe1.0040126D
呵呵,第一次运行的时候ECX又回来了。因为这次没用LOOPD,所以手工得INC ECX
看这个
00401272 . |3BC8 CMP ECX,EAX
当EAX小于ECX的时候停止?这个结束条件貌似很古怪,为什么呢?
我们知道,EAX读取的时候因为有一串NOP,而又是DWORD的读取方式,所以EAX总是等于90909090
而当ECX一出过909090区域,就自己报警了……
00401276 . 83FF 02 CMP EDI,2
00401279 . 75 2D JNZ SHORT CrackMe1.004012A8
这里没跳是很好的,咱们分析分析他发现DEBUG后要干什么……
004012A8 > \B8 24104000 MOV EAX,CrackMe1.00401024
004012AD . B9 3D104000 MOV ECX,CrackMe1.0040103D
004012B2 . 83E9 03 SUB ECX,3
004012B5 . 2BC8 SUB ECX,EAX
004012B7 . 8BD1 MOV EDX,ECX
004012B9 . BB 90909090 MOV EBX,90909090
004012BE > 8BF0 MOV ESI,EAX
004012C0 . 03F2 ADD ESI,EDX
004012C2 . 2BF1 SUB ESI,ECX
004012C4 . 891E MOV DWORD PTR DS:[ESI],EBX
004012C6 .^ E2 F6 LOOPD SHORT CrackMe1.004012BE
熟悉吗?太熟悉了!又是SMC……不过是SMC的另一种形式,不断用NOP(90)覆盖401024-40103D的区域,咱们看看是什么
00401024 . 6A 00 PUSH 0 ; /lParam = NULL
00401026 . 68 43104000 PUSH CrackMe1.00401043 ; |DlgProc = CrackMe1.00401043
0040102B . 6A 00 PUSH 0 ; |hOwner = NULL
0040102D . 68 00304000 PUSH CrackMe1.00403000 ; |pTemplate = "IsDebugPresent"
00401032 . FF35 AC304000 PUSH DWORD PTR DS:[4030AC] ; |hInst = 00400000
00401038 . E8 09060000 CALL ; \DialogBoxParamA
0040103D . 50 PUSH EAX ; /ExitCode
呵呵,这段一覆盖成NOP...
这里F4
004012C8 . 68 00004000 PUSH CrackMe1.00400000
004012C8 . 68 00004000 PUSH CrackMe1.00400000
004012CD . 810424 001000>ADD DWORD PTR SS:[ESP],1000
很阴险,先PUSH一个400000,再改变堆栈,最终是401000,RETN,看看:)
00401000 >/$ 60 PUSHAD
00401001 |. E8 01000100 CALL 00411007
00401006 |. 0000 ADD BYTE PTR DS:[EAX],AL
00401008 |. 002406 ADD BYTE PTR DS:[ESI+EAX],AH
0040100B \. C3 RETN
还是别忘了跳(F7)花……
到401000
00401000 >/$ 60 PUSHAD
00401001 |. E8 01000100 CALL 00411007
00401006 |. 0000 ADD BYTE PTR DS:[EAX],AL
00401008 |. 002406 ADD BYTE PTR DS:[ESI+EAX],AH
0040100B \. C3 RETN
这里很抱歉大家,我可能哪个地方出错了,没做得很完美,这里会因为一个异常退出,而我
原来的意思是让他继续执行下去,一直到ExitProcess……不过也没关系,目的达到了……
好,咱们这条路走完了,走正确的那条路
直接F4到咱们刚才分析的
0040127B . BF 24104000 MOV EDI,CrackMe1.00401024
0040127B . BF 24104000 MOV EDI,CrackMe1.00401024 //edi=401024
00401280 . B9 C3000000 MOV ECX,0C3 //ecx=c3
00401285 . 890F MOV DWORD PTR DS:[EDI],ECX // [401024]=c3
00401287 . 83EF 02 SUB EDI,2 //edi=401022
0040128A . 83C7 03 ADD EDI,3 //edi=401025 (混淆视线)
0040128D . B9 12000000 MOV ECX,12
00401292 . 890F MOV DWORD PTR DS:[EDI],ECX //[401025]=12
00401294 . 83C7 01 ADD EDI,1 //edi=401026
00401297 . B9 CC000000 MOV ECX,0CC
0040129C . 890F MOV DWORD PTR DS:[EDI],ECX //[401026]=cc
0040129E . 47 INC EDI //edi=401027
0040129F . B9 12000000 MOV ECX,12
004012A4 . 890F MOV DWORD PTR DS:[EDI],ECX //[401027]=12
这段,仔细分析一下,就不单独解释了,主要看注释,大致的意思是把401024-401027的段给重写一下……
变成了
00401024 . C3 RETN
00401025 12 DB 12
00401026 CC INT3
00401027 12 DB 12
继续走……
004012D5 > BE 10134000 MOV ESI,CrackMe1.00401310
004012DA . |B9 E8909090 MOV ECX,909090E8
004012DF . |890E MOV DWORD PTR DS:[ESI],ECX
004012E1 . |46 INC ESI
004012E2 . |B9 F9909090 MOV ECX,909090F9
004012E7 . |890E MOV DWORD PTR DS:[ESI],ECX
004012E9 . |B9 FC909090 MOV ECX,909090FC
004012EE . |46 INC ESI
004012EF . |890E MOV DWORD PTR DS:[ESI],ECX
004012F1 . |B9 FF909090 MOV ECX,909090FF
004012F6 . |46 INC ESI
004012F7 . |890E MOV DWORD PTR DS:[ESI],ECX
004012F9 . |B9 FF909090 MOV ECX,909090FF
004012FE . |46 INC ESI
004012FF . |890E MOV DWORD PTR DS:[ESI],ECX
00401301 . |B9 90909090 MOV ECX,90909090
00401306 . |46 INC ESI
00401307 . |890E MOV DWORD PTR DS:[ESI],ECX
00401309 . |3D 34120000 CMP EAX,1234
0040130E .^\74 C5 JE SHORT CrackMe1.004012D5
呵呵,401310不就在底下吗?扯到眼皮子底下了:)
不分析了吧,这段太简单了。实在不幸就F8观察一下
(最后那个JE是迷惑你的:))
好,这段运行完了,401310出来一个
00401310 . E8 F9FCFFFF CALL CrackMe1.0040100E
跟进看看:)
0040100E 90 NOP
0040100F 90 NOP
00401010 90 NOP
00401011 90 NOP
00401012 90 NOP
00401013 90 NOP
00401014 90 NOP
00401015 90 NOP
00401016 90 NOP
00401017 90 NOP
00401018 > 6A 00 PUSH 0 ; /pModule = NULL
0040101A . E8 63060000 CALL ; \GetModuleHandleA
0040101F . A3 AC304000 MOV DWORD PTR DS:[4030AC],EAX
00401024 . C3 RETN
真无聊,回去吧
00401319 . BE 10134000 MOV ESI,CrackMe1.00401310 //ESI是什么?
0040131E . E8 35000000 CALL CrackMe1.00401358
00401323 . EB 1A JMP SHORT CrackMe1.0040133F //走到这里就完了!各位记住401323是GAMEOVER!
00401325 > 66:3D BA0B CMP AX,0BBA //走到这里也就完了!各位记住401325是GAMEOVER!
00401329 . 75 14 JNZ SHORT CrackMe1.0040133F
我们还不知道401310要干什么,所以先不管他……
跟进CALL...
00401358 /$ 5F POP EDI ; CrackMe1.00401323
00401359 |. 58 POP EAX
0040135A |. 58 POP EAX
0040135B |. 5B POP EBX
0040135C |. 5B POP EBX
0040135D |. 50 PUSH EAX
0040135E |. 53 PUSH EBX
0040135F |. 68 DC134000 PUSH CrackMe1.004013DC
00401364 |. 6A 01 PUSH 1
00401366 |. B8 41164000 MOV EAX,CrackMe1.00401641 ; ASCII "%8 @"
0040136B |. 83C0 FF ADD EAX,-1
0040136E |. 8BF8 MOV EDI,EAX
00401370 |. B9 60184000 MOV ECX,CrackMe1.00401860
00401375 |. 8B38 MOV EDI,DWORD PTR DS:[EAX]
00401377 |. 8939 MOV DWORD PTR DS:[ECX],EDI
00401379 |. BF 40000000 MOV EDI,40
0040137E |. 83C1 04 ADD ECX,4
00401381 |. 8939 MOV DWORD PTR DS:[ECX],EDI
00401383 |. 83E9 04 SUB ECX,4
00401386 |. FFD1 CALL ECX
00401388 \. C3 RETN
什么玩意?这就是算法?别着急,我们慢慢看……
首先是前面关于栈的操作
00401358 /$ 5F POP EDI ; CrackMe1.00401323
00401359 |. 58 POP EAX
0040135A |. 58 POP EAX
0040135B |. 5B POP EBX
0040135C |. 5B POP EBX
0040135D |. 50 PUSH EAX
0040135E |. 53 PUSH EBX
很好理解,去EDI是把CALL当成跳转用
4个POP,2个PUSH是把位数变为一个(还记得以前是PUSH了2次吗?)
0040135F |. 68 DC134000 PUSH CrackMe1.004013DC
00401364 |. 6A 01 PUSH 1
这2个PUSH咱们走着瞧:)
00401366 |. B8 41164000 MOV EAX,CrackMe1.00401641 ; ASCII "%8 @"
0040136B |. 83C0 FF ADD EAX,-1
0040136E |. 8BF8 MOV EDI,EAX
EDI=eax=401640
00401370 |. B9 60184000 MOV ECX,CrackMe1.00401860
ECX=401860
00401375 |. 8B38 MOV EDI,DWORD PTR DS:[EAX]
00401377 |. 8939 MOV DWORD PTR DS:[ECX],EDI
呵呵,看到没?把DWORD PTR DS:[EAX]覆盖到DWORD PTR DS:[ECX]
偷窥一下EAX是什么
00401640 >-\FF25 38204000 JMP DWORD PTR DS:[<&user32.BlockInput>] ; user32.BlockInput
覆盖到ECx又怎么样呢?慢慢看
00401379 |. BF 40000000 MOV EDI,40
0040137E |. 83C1 04 ADD ECX,4
00401381 |. 8939 MOV DWORD PTR DS:[ECX],EDI
呵呵,结合上面的EAX内容FF 25 38 20 40 00
而复制由于是Dword,所以只复制了 FF 25 38 20
你应该能看见,40没复制过来,所以……
到401381结束时,ECX是401864,而
00401383 |. 83E9 04 SUB ECX,4
要他返回来,干吗?
00401386 |. FFD1 CALL ECX
HIHI~CALL啊!
当你运行到401386时,你可以看见
00401386 |. FFD1 CALL ECX ;
怎么样?!这就是我这个CM用的独特技术——让OD混淆你的API,
至于前面这个
00401366 |. B8 41164000 MOV EAX,CrackMe1.00401641 ; ASCII "%8 @"
0040136B |. 83C0 FF ADD EAX,-1
是可以改的,比如改成
mov eax,401642
sub eax,2等等
有的时候,OD还会把你的API认成别的API那我们可乐了(我就碰到过,不过后来又改回来了)
这个时候,你知道前面PUSH 1是什么意思了吧?他是API BlockInput的参数....
不过我们可不想让他运行:)还是NOP掉的好
不过在NOP API前请特别注意!!!一定要把堆栈里API的参数也给POP掉,否则……
这样一来,前面的PUSH 4013DC也说得通了,因为有个
00401388 \. C3 RETN
呵呵,F8走!
004013DC . 57 PUSH EDI
把EDI给PUSH进去,不管,向下看……
004013DD . B9 48000000 MOV ECX,48
004013E2 . 890E MOV DWORD PTR DS:[ESI],ECX
004013E4 . 46 INC ESI
004013E5 . B9 65000000 MOV ECX,65
004013EA . 890E MOV DWORD PTR DS:[ESI],ECX
004013EC . 46 INC ESI
004013ED . B9 6C000000 MOV ECX,6C
004013F2 . 890E MOV DWORD PTR DS:[ESI],ECX
004013F4 . 46 INC ESI
004013F5 . 890E MOV DWORD PTR DS:[ESI],ECX
004013F7 . 46 INC ESI
004013F8 . B9 6F000000 MOV ECX,6F
004013FD . 890E MOV DWORD PTR DS:[ESI],ECX
明显,在往401310到401315写入代码(当运行到4013DD时,ESI为401310)
看看401310
00401310 . 48 DEC EAX
00401311 . 65:6C INS BYTE PTR ES:[EDI],DX
00401313 . 6C INS BYTE PTR ES:[EDI],DX
00401314 . 6F OUTS DX,DWORD PTR ES:[EDI]
00401315 . 0000 ADD BYTE PTR DS:[EAX],AL
00401317 . 0090 BE101340 ADD BYTE PTR DS:[EAX+401310BE],DL
什么东西?看数据窗
00401310 48 65 6C 6C 6F 00 00 00 Hello...
晕死……
004013FF . B8 89134000 MOV EAX,CrackMe1.00401389
00401404 . BB DB134000 MOV EBX,CrackMe1.004013DB
00401409 . 83EB 01 SUB EBX,1
0040140C . 2BD8 SUB EBX,EAX
0040140E . 8BCB MOV ECX,EBX
00401410 > B8 89134000 MOV EAX,CrackMe1.00401389
00401415 . 03C3 ADD EAX,EBX
00401417 . 2BC1 SUB EAX,ECX
00401419 . 8BF0 MOV ESI,EAX
0040141B . 8B16 MOV EDX,DWORD PTR DS:[ESI]
0040141D . 83F2 14 XOR EDX,14
00401420 . 8916 MOV DWORD PTR DS:[ESI],EDX
00401422 .^ E2 EC LOOPD SHORT CrackMe1.00401410
不说了,SMC,操作是XOR EDX,14,直接在
00401424 . 68 3D144000 PUSH CrackMe1.0040143D
下断
忽然发现
晕死……
0040142A . 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
0040142C . 68 89134000 PUSH CrackMe1.00401389 ; |Title = "前面都是ANTI,现在开始..:"
00401431 . 68 89134000 PUSH CrackMe1.00401389 ; |Text = "前面都是ANTI,现在开始..:"
00401436 . 6A 00 PUSH 0 ; |hOwner = NULL
00401438 . E8 21020000 CALL ; \MessageBoxA
00401424 . 68 3D144000 PUSH CrackMe1.0040143D
00401429 . C3 RETN
转到40143d
0040143D . 68 52144000 PUSH CrackMe1.00401452
00401442 . 33FF XOR EDI,EDI
00401444 . C3 RETN
转到401452
00401452 ? BB 40184000 MOV EBX,CrackMe1.00401840
00401457 . 8B03 MOV EAX,DWORD PTR DS:[EBX]
00401459 . 3D D2040000 CMP EAX,4D2
0040145E . 75 08 JNZ SHORT CrackMe1.00401468
00401460 . 68 8C144000 PUSH CrackMe1.0040148C
00401465 . C3 RETN
00401466 . EB 00 JMP SHORT CrackMe1.00401468
00401468 > BE 46144000 MOV ESI,CrackMe1.00401446
0040146D . B9 0C000000 MOV ECX,0C
00401472 > 8B06 MOV EAX,DWORD PTR DS:[ESI]
00401474 . 48 DEC EAX
00401475 . 8906 MOV DWORD PTR DS:[ESI],EAX
00401477 . 46 INC ESI
00401478 .^ E2 F8 LOOPD SHORT CrackMe1.00401472
0040147A . BB 40184000 MOV EBX,CrackMe1.00401840
0040147F . B9 D2040000 MOV ECX,4D2
00401484 . 890B MOV DWORD PTR DS:[EBX],ECX
00401486 . 68 46144000 PUSH CrackMe1.00401446
0040148B . C3 RETN
恩……有点晕了,(看看表:23:16了!明天还上学呢……不管,继续吧)
(唉~没熬出头的学生啊……)
慢慢来,首先咱们第一次运行时401840还没值呢,所以这个也不可能成立了
00401452 ? BB 40184000 MOV EBX,CrackMe1.00401840
00401457 . 8B03 MOV EAX,DWORD PTR DS:[EBX]
00401459 . 3D D2040000 CMP EAX,4D2
0040145E . /75 08 JNZ SHORT CrackMe1.00401468 走
吐血,又是SMC,我被我自己写的SMC给玩死了……(这个CM我是编了几个星期才完成的)
分析:
00401468 > \BE 46144000 MOV ESI,CrackMe1.00401446
基地址:401446
0040146D . B9 0C000000 MOV ECX,0C
ECX:0C 循环13次
00401472 > /8B06 MOV EAX,DWORD PTR DS:[ESI]
00401474 . |48 DEC EAX
00401475 . |8906 MOV DWORD PTR DS:[ESI],EAX //SMC(算法:DEC EAX)
00401477 . |46 INC ESI //指针+1
00401478 .^\E2 F8 LOOPD SHORT CrackMe1.00401472
看看401446是何方神圣现在
00401446 . 60 PUSHAD
00401447 . E8 01000000 CALL CrackMe1.0040144D
0040144C 7C DB 7C ; CHAR '|'
0040144D . 830424 06 ADD DWORD PTR SS:[ESP],6
00401451 . C3 RETN
这……这不是传说中的......AC花指令(就是文件头那个)
唉……继续吧
0040147A . BB 40184000 MOV EBX,CrackMe1.00401840
0040147F . B9 D2040000 MOV ECX,4D2
00401484 . 890B MOV DWORD PTR DS:[EBX],ECX
呵呵,401840终于变成了4D2,还记得刚才提到的吗?
00401486 . 68 46144000 PUSH CrackMe1.00401446
0040148B . C3 RETN
走了
00401446 . 60 PUSHAD
00401447 . E8 01000000 CALL CrackMe1.0040144D
0040144C 7C DB 7C ; CHAR '|'
0040144D . 830424 06 ADD DWORD PTR SS:[ESP],6
00401451 . C3 RETN
老办法,F7……
你会发现,又回到……
00401452 . BB 40184000 MOV EBX,CrackMe1.00401840
00401457 . 8B03 MOV EAX,DWORD PTR DS:[EBX]
00401459 . 3D D2040000 CMP EAX,4D2
0040145E . 75 08 JNZ SHORT CrackMe1.00401468
这回的JNZ我们可不会放过了……
00401460 . 68 8C144000 PUSH CrackMe1.0040148C
00401465 . C3 RETN
跳到40148c
那么不讲面子:)
0040148C . /EB 05 JMP SHORT CrackMe1.00401493
0040148E . |25 73257390 AND EAX,90732573
00401493 > \B8 92144000 MOV EAX,CrackMe1.00401492
00401498 . 8BF0 MOV ESI,EAX
上来就一个JMP
别管,继续
目前ESI=EAX=401492
0040149A . 33FF XOR EDI,EDI
0040149C . BF 00000000 MOV EDI,0
004014A1 . B2 00 MOV DL,0
占用硬盘空间……(-_-)
004014A3 . 8816 MOV BYTE PTR DS:[ESI],DL
这还是比较关键的
改了这句话……
0040148E . 25 73257300 AND EAX,732573
见鬼了,干吗改这句呢?
004014A5 . 68 8E144000 PUSH CrackMe1.0040148E
004014AA . B8 89164000 MOV EAX,CrackMe1.00401689
004014AF . 83C0 FF ADD EAX,-1
004014B2 . 8BF8 MOV EDI,EAX
004014B4 . B9 70184000 MOV ECX,CrackMe1.00401870
004014B9 . 8B38 MOV EDI,DWORD PTR DS:[EAX]
004014BB . 8939 MOV DWORD PTR DS:[ECX],EDI
004014BD . BF 40000000 MOV EDI,40
004014C2 . 83C1 04 ADD ECX,4
004014C5 . 8939 MOV DWORD PTR DS:[ECX],EDI
004014C7 . 83E9 04 SUB ECX,4
004014CA . FFD1 CALL ECX
好熟悉啊啊啊啊啊啊啊,这不是我的原创大法吗(笑~)
那为什么要PUSH 40148E呢?难道这是个字串吗?
CTRL+A一下,原形毕露
0040148E . 25 73 25 73 0>ASCII "%s%s",0
呵呵,%s%s....
直接在CALL ECX下F4,NOP并POP掉
吐发黑色的血(中毒?),又来了
004014CC . B8 B4304000 MOV EAX,CrackMe1.004030B4
004014D1 . 33FF XOR EDI,EDI
004014D3 . BB 00000000 MOV EBX,0
004014D8 > 83C3 01 ADD EBX,1
004014DB . 83C0 01 ADD EAX,1
004014DE . 8BF0 MOV ESI,EAX
004014E0 . 8B0E MOV ECX,DWORD PTR DS:[ESI]
004014E2 . 0BC9 OR ECX,ECX
004014E4 . 75 05 JNZ SHORT CrackMe1.004014EB
004014E6 . BF 01000000 MOV EDI,1
004014EB > 83FF 01 CMP EDI,1
004014EE .^ 75 E8 JNZ SHORT CrackMe1.004014D8
NND,还一点一点来吧
如果你记忆力不错的话,你会猜到这是什么:)用户名
004014CC . B8 B4304000 MOV EAX,CrackMe1.004030B4 ; ASCII "abcdefghijklmn"
我实在不想说了,这段实在太简单了(观众:偷懒!),判断位数……保存到EBX里
有一点我要强调一下
004014E2 . 0BC9 OR ECX,ECX
004014E4 . 75 05 JNZ SHORT CrackMe1.004014EB
004014E6 . BF 01000000 MOV EDI,1
这个永远不会执行,所以你也别期望"快点出去"。。。
004014F5 . 6A 00 PUSH 0
004014F7 . B8 42164000 MOV EAX,CrackMe1.00401642
004014FC . 83C0 FE ADD EAX,-2
004014FF . 8BF8 MOV EDI,EAX
00401501 . B9 70184000 MOV ECX,
00401506 . 8B38 MOV EDI,DWORD PTR DS:[EAX]
00401508 . 8939 MOV DWORD PTR DS:[ECX],EDI
0040150A . BF 40000000 MOV EDI,40
0040150F . 83C1 04 ADD ECX,4
00401512 . 8939 MOV DWORD PTR DS:[ECX],EDI
00401514 . 83E9 04 SUB ECX,4
00401517 . FFD1 CALL ECX
恢复BLOCKINPUT,看见没?OD已经把ECX给搞晕了……
00401519 . 68 23134000 PUSH CrackMe1.00401323 //GAMEOVER
0040151E . C3 RETN
同理可证,
0040151F > \B8 B4324000 MOV EAX,CrackMe1.004032B4 ; ASCII "1234567890123"
00401524 . 33FF XOR EDI,EDI
00401526 . BB 00000000 MOV EBX,0
0040152B > 83C3 01 ADD EBX,1
0040152E . 83C0 01 ADD EAX,1
00401531 . 8BF0 MOV ESI,EAX
00401533 . 8B0E MOV ECX,DWORD PTR DS:[ESI]
00401535 . 0BC9 OR ECX,ECX
00401537 . 75 05 JNZ SHORT CrackMe1.0040153E
00401539 . BF 01000000 MOV EDI,1
0040153E > 83FF 01 CMP EDI,1
00401541 .^ 75 E8 JNZ SHORT CrackMe1.0040152B
00401543 . 83FB 0A CMP EBX,0A
00401546 . 77 2A JA SHORT CrackMe1.00401572
00401548 . 6A 00 PUSH 0
0040154A . B8 42164000 MOV EAX,CrackMe1.00401642
0040154F . 83C0 FE ADD EAX,-2
00401552 . 8BF8 MOV EDI,EAX
00401554 . B9 70184000 MOV ECX,
00401559 . 8B38 MOV EDI,DWORD PTR DS:[EAX]
0040155B . 8939 MOV DWORD PTR DS:[ECX],EDI
0040155D . BF 40000000 MOV EDI,40
00401562 . 83C1 04 ADD ECX,4
00401565 . 8939 MOV DWORD PTR DS:[ECX],EDI
00401567 . 83E9 04 SUB ECX,4
0040156A . FFD1 CALL ECX
0040156C . 68 23134000 PUSH CrackMe1.00401323
00401571 . C3 RETN
是判断密码长度的(已经被搞晕了……我写的CM怎么自己还……已经23:41了,坚持!)
………………………………
00401572 > \33DB XOR EBX,EBX
00401574 . 33D2 XOR EDX,EDX
00401576 . 33C9 XOR ECX,ECX
00401578 . BE B4304000 MOV ESI,CrackMe1.004030B4 ; ASCII "abcdefghijklmn"
0040157D . BF B4324000 MOV EDI,CrackMe1.004032B4 ; ASCII "1234567890123"
终于到了,终于到了.........
00401572 > \33DB XOR EBX,EBX //下面三句是做初始化
00401574 . 33D2 XOR EDX,EDX
00401576 . 33C9 XOR ECX,ECX
00401578 . BE B4304000 MOV ESI,CrackMe1.004030B4 ; ASCII "abcdefghijklmn"
0040157D . BF B4324000 MOV EDI,CrackMe1.004032B4 ; ASCII "1234567890123"
00401582 . 83C6 01 ADD ESI,1 //esi==>用户名第2位
00401585 . 8B1E MOV EBX,DWORD PTR DS:[ESI] //ebx==>逆向用户名第2-5位,结合!
(比如bcde就是65646362)
00401587 . 83C7 01 ADD EDI,1
0040158A . 030F ADD ECX,DWORD PTR DS:[EDI] //ecx==>逆向密码2-5位
0040158C . 83C6 01 ADD ESI,1
0040158F . 031E ADD EBX,DWORD PTR DS:[ESI] //ebx=ebx+逆向用户名3-6位
(打个岔:今天就到这,明天继续,都23:48了,睡觉睡觉……顺便膜拜一下风间仁这个牲口)
(回来了。。。)
00401591 . 83C7 04 ADD EDI,4
00401594 . 030F ADD ECX,DWORD PTR DS:[EDI] //ecx=ecx+逆向密码6-9位
00401596 . 83C6 04 ADD ESI,4
00401599 . 031E ADD EBX,DWORD PTR DS:[ESI] //ebx=ebx+逆向用户名7-10位
0040159B . 83C7 01 ADD EDI,1
0040159E . 030F ADD ECX,DWORD PTR DS:[EDI] //ecx=ecx+逆向密码7-10位
004015A0 . 83EE 03 SUB ESI,3
004015A3 . 031E ADD EBX,DWORD PTR DS:[ESI] //ebx=ebx+逆向用户名4-7位
004015A5 . 83EF 02 SUB EDI,2
004015A8 . 030F ADD ECX,DWORD PTR DS:[EDI] //ecx=ecx+逆向密码5-8位
晕了,继续
004015AA . B8 00104000 MOV EAX,CrackMe1.<模块入口点>
004015AF . 03C8 ADD ECX,EAX
004015B1 . 83C1 01 ADD ECX,1 //ecx=ecx+400001h
004015B4 . B8 46144000 MOV EAX,CrackMe1.00401446
004015B9 . 83C0 01 ADD EAX,1
004015BC . 03D8 ADD EBX,EAX //ebx=ebx+401447h
004015BE . B8 89134000 MOV EAX,CrackMe1.00401389
004015C3 . 83C0 03 ADD EAX,3
004015C6 . 03C8 ADD ECX,EAX //ecx=ecx+40138ch
004015C8 . B8 6A114000 MOV EAX,CrackMe1.0040116A
004015CD . 83C0 30 ADD EAX,30
004015D0 . 03D8 ADD EBX,EAX //ebx=ebx+40119ah
004015D2 . 33C0 XOR EAX,EAX
004015D4 . 0BC0 OR EAX,EAX
004015D6 . 75 39 JNZ SHORT CrackMe1.00401611
永远不会跳转……
004015D8 . 3BD9 CMP EBX,ECX
004015DA . 75 3A JNZ SHORT CrackMe1.00401616
如果OK了不跳,否则去死
去死路线:
00401616 ? 6A 00 PUSH 0
00401618 ? B8 41164000 MOV EAX,CrackMe1.00401641 ; ASCII "%8 @"
0040161D . 83C0 FF ADD EAX,-1
00401620 . 8BF8 MOV EDI,EAX
00401622 . B9 50184000 MOV ECX,CrackMe1.00401850
00401627 . 8B38 MOV EDI,DWORD PTR DS:[EAX]
00401629 . 8939 MOV DWORD PTR DS:[ECX],EDI
0040162B . BF 40000000 MOV EDI,40
00401630 . 83C1 04 ADD ECX,4
00401633 . 8939 MOV DWORD PTR DS:[ECX],EDI
00401635 . 83E9 04 SUB ECX,4
00401638 . FFD1 CALL ECX
又是一个API变形,恢复BlockInput
0040163A . 68 23134000 PUSH CrackMe1.00401323
0040163F . C3 RETN
去死了……
OK路线:
004015DF > \6A 40 PUSH 40
004015E1 . 68 DE154000 PUSH CrackMe1.004015DE
004015E6 . 68 6A114000 PUSH CrackMe1.0040116A
004015EB . 6A 00 PUSH 0
004015ED . B8 5F164000 MOV EAX,CrackMe1.0040165F
004015F2 . 83C0 FF ADD EAX,-1
004015F5 . 8BF8 MOV EDI,EAX
004015F7 . B9 00194000 MOV ECX,
004015FC . 8B38 MOV EDI,DWORD PTR DS:[EAX]
004015FE . 8939 MOV DWORD PTR DS:[ECX],EDI
00401600 . BF 40000000 MOV EDI,40
00401605 . 83C1 04 ADD ECX,4
00401608 . 8939 MOV DWORD PTR DS:[ECX],EDI
0040160A . 83E9 04 SUB ECX,4
0040160D . FFD1 CALL ECX
API变形,MessageboxA
0040160F . /EB 05 JMP SHORT CrackMe1.00401616
见去死路线……
不知大家发现没有,这里有个严重的BUG
就是你必须第一次注册就成功,要不然以后解“既不回头”就会出现双重解码
其他的SMC肯定也有这个问题,但反过头来想一想,有一个指令的SMC,为什么没出错呢?
00401459 . 3D D2040000 CMP EAX,4D2 //关键就在这里了!!!
0040145E . 75 08 JNZ SHORT CrackMe1.00401468
00401460 . 68 8C144000 PUSH CrackMe1.0040148C
00401465 . C3 RETN
00401466 . EB 00 JMP SHORT CrackMe1.00401468
00401468 > BE 46144000 MOV ESI,CrackMe1.00401446
0040146D . B9 0C000000 MOV ECX,0C
00401472 > 8B06 MOV EAX,DWORD PTR DS:[ESI]
00401474 . 48 DEC EAX
00401475 . 8906 MOV DWORD PTR DS:[ESI],EAX
00401477 . 46 INC ESI
00401478 .^ E2 F8 LOOPD SHORT CrackMe1.00401472
0040147A . BB 40184000 MOV EBX,CrackMe1.00401840
0040147F . B9 D2040000 MOV ECX,4D2
00401484 . 890B MOV DWORD PTR DS:[EBX],ECX
00401486 . 68 46144000 PUSH CrackMe1.00401446
0040148B . C3 RETN
为什么说关键在401459呢?因为第一次运行的时候会在内存某处有个标记,标记被SMC过,所以没死……
看来以后用SMC得小心点了:)
注册机就不发了,留给大家:)
--------------------------------------------------------------------------------
【经验总结】
第2部分:反思
反思这个CM,其实并不怎么困难为什么只有一个人出KEY……(也许是我的人品有问题^_^)
呵呵,不是抬高自己的意思。这个CM主要看着眼晕(实际上自己也写晕了……),眼花缭乱:)
不过这个CM给大家一个教训,那就是SMC解码时一定要想着第2次……………………
第3部分:后记
随便吧……写注册机去了
下载链接:http://www.unpack.cn/attachment.php?aid=13571
|