【吾爱破解六周年小玩具-大叔的草莓】题解
本帖最后由 MistHill 于 2014-3-10 18:21 编辑原帖:
吾爱破解六周年小玩具-大叔的草莓CM20140313
http://www.52pojie.cn/thread-240878-1-1.html
(出处: 吾爱破解论坛 - LCG - LSG |软件安全|病毒分析|破解软件|软件论坛|www.52pojie.cn)
好事情哦,有好玩的,又有丰厚的奖品,真是大叔们的节日,希望大家都来参与,你不会失望的!
拿到题目后,首先静态地检查一下,即俗称“查壳”。嗯,UPX,很好。但是“UPX -d”解压缩失败,看来Kido封装时抹掉了解压信息。
不过我有脚本可以轻松脱掉!推销一下:lol ,在这里:“UPX完美脱壳脚本”。
这样分析起来方便一些,可以上IDA去理解那些密码学的东西。
先运行一下,唔“弹窗”出来了,提示“硬件码”;点“确定”,正常进入界面,不错。说明脱壳没有问题,困惑的是这个草莓怎么连最基本的文件校验都没有!?(Peace:走着瞧吧,大叔!{:1_912:} )
但是界面显示的是“注册”按钮,“试用版”没有意义。解出KeyFile.dat文件,再来,这次提示"检测到程序被非法篡改或注册文件为伪造!!!",杯具鸟。
…………
分析的结果是:解密KeyFile.dat文件的内容需要硬盘的物理序列号,而序列号可从“硬件码”解密得到。虽然可以从后续代码对KeyFile.dat解密后数据的处理一步一个地分析,这个过程实在痛苦。
蒜辣,偷个懒。昨天去主帖回复,瞧了瞧Key绑定的硬件码。这样,事情就简单了!
…………未玩,待续!
放个“完美破解版”,大家先玩玩。Patch的方法,以后会详细说。学习Kido的UPX压缩处理,逼迫你去用我的脚本!:lol
10/03/2014 编辑
300CB进帐,一高兴,再写一段。
首先感谢那个谁,whoever,帮我加上了原帖链接,否则搞得那些糊涂的小伙伴一头雾水,你这究竟是个啥玩意儿!?
不过,尊敬的Ms. Peace,按原帖某条某款,我这个至少也值1000CB喔:完美破解成品+通用key(虽说这个KeyFile.dat不是我自己生成的:lol )。
我其实是惦记着秀才娘子家的床(指那2000CB)来着,害怕一不留神让别人抬了去。那也没办法,得承认确实没有孔先生那么学识渊博,“茴香”这两字儿就老是写不对。
屌丝祖辈就是这个Q样,屌得很:说我牛,那是骂我菜鸟(Newbie),说我是大婶,当我神经病迈?总之,精神上先败了一阵。
说到CB,想到另一件事。前段时间,大概是本版有个帖子悬赏1000CB破网络验证,玩了一下,搞定了(就是让服务端返回剩余次数99999那个,看过的人可能有点印象)。明显是求破了,后来被版主删帖。
这位仁兄把大家当傻子,肯定不妥!是的,既不是牛鬼蛇神,就装一回Q。人有时侯傻一点儿好,会过得比较快乐。
该仁兄就PM我了,说要兑现那1000CB,让我帮帮他。回:不用了,玩玩而已。知道那个附件其实是假的、投石问路的东西。回:你这样藏着掖着、不来真家伙我也帮不了。
后来,真东西来了。我一看就明白是啥了,很多年前曾接触过这行,灰色的。主程序在我的虚拟机里界面都出不来,需要Direct3D的支持,折腾了半天终于解决了这个困扰了我好几年的问题。发现我真笨,很简单的,但觉得这比1000CB要值!
然后,分析它用于注入的apphelp.dll,这是个系统DLL,不知是那位老兄弄的,"Bad Idea!"。它由system32里的一个系统DLL载入,程序文件夹里的根本就没用上。阻止操作系统的文件保护后,将其复制到system32文件夹,系统有的功能又不正常。
后来不得不修改主程序,先载入这个DLL。帮他解决问题后,这家伙就杳无音信了。
废话这么多,是想说结果不重要,如果你享受这个过程就好了!
言归正传。本题的破解思路简单明了:想办法正确解密KeyFile.dat文件,然后测试软件的功能是否正常!
有一种手机游戏,当你收集齐那些必须的宝贝后,才能过到下一关。现在,整个过程就象这样!需要找出两个关键的东西,才能通关。
但是这个游戏我们应该从哪里下手呢?
最通用的快速定位办法是在OD里当出现那个"检测到程序被非法篡改或注册文件为伪造!!!"提示时,F12暂停,从"Call stack"里一层一层往回找跳向user32.MessageBoxExA的判断。
这个方法比较啰嗦,还需要一定的经验。针对本题更简单、直接一点的办法是定位对文件KeyFile.dat的读入,即断kernel32.CreateFileA和kernel32.ReadFile。
很基本的操作,为节省篇幅,直接给出关键代码段。
从两个APIs的调用断下后,RET……,直到这里:
00401A1B 68 04000080 PUSH 0x80000004
00401A20 6A 00 PUSH 0x0
00401A22 8B45 FC MOV EAX, DWORD PTR
00401A25 85C0 TEST EAX, EAX
00401A27 75 05 JNZ SHORT 00401A2E
00401A29 B8 C06C4700 MOV EAX, 00476CC0
00401A2E 50 PUSH EAX
00401A2F 68 01000000 PUSH 0x1
00401A34 BB 103C4000 MOV EBX, 00403C10
00401A39 E8 67190000 CALL 004033A5
00401A3E 83C4 10 ADD ESP, 0x10 ; <- 返回这里
这时,"ALT+K"打开"Call stack"窗口。注意最顶一行:"Called from"=0040154E,"Procedure / arguments"=004016E2。
Address Stack Procedure / arguments Called from Frame
0012FD28 00401553 CM201403.004016E2 CM201403.0040154E 0012FD24
好了,我们的游戏就从Sub_004016E2开始:
004016E2 55 PUSH EBP
004016E3 8BEC MOV EBP, ESP
004016E5 81EC 34000000 SUB ESP, 0x34
004016EB C745 FC 00000000 MOV DWORD PTR , 0x0
004016F2 C745 F8 00000000 MOV DWORD PTR , 0x0
...
00402400|> \B8 01000000 MOV EAX, 0x1
00402405|.E9 00000000 JMP 0040240A
0040240A|>8BE5 MOV ESP, EBP
0040240C|.5D POP EBP
0040240D\.C2 0800 RETN 0x8
OD重新载入"小玩具",断下004016E2。
但凡写过点儿代码的人,包括用E(不是不屑,是羡慕嫉妒恨,我根本就不会:()的人,都知道对函数的调用都需要传递参数,无论是值或引用/指针,一是通过栈,二是通过寄存器。
下面只贴关键CALL的代码,其他一律F8过。注意调用之前的ECX值(输入之一)和调用之后的EAX值(主要输出),然后就是栈上的参数个数及其内容。
很快,遇到第一个关键CALL:
00401727|.68 04000080 PUSH 0x80000004
0040172C|.6A 00 PUSH 0x0
0040172E|.A1 18EA4900 MOV EAX, DWORD PTR
00401733|.85C0 TEST EAX, EAX
00401735|.75 05 JNZ SHORT 0040173C
00401737|.B8 C06C4700 MOV EAX, 00476CC0
0040173C|>50 PUSH EAX
0040173D|.68 04000080 PUSH 0x80000004
00401742|.6A 00 PUSH 0x0
00401744|.68 D06C4700 PUSH 00476CD0 ; ASCII "AD04E9F801BF0DB6"
00401749|.68 02000000 PUSH 0x2
0040174E|.B8 01000000 MOV EAX, 0x1
00401753|.BB C0AD4400 MOV EBX, 0044ADC0
00401758|.E8 541C0000 CALL 004033B1
Stack:
0012FCD4 00000002
0012FCD8 00476CD0ASCII "AD04E9F801BF0DB6"
0012FCDC 00000000
0012FCE0 80000004
0012FCE4 00476CC5ASCII "PeaceWorld"
0012FCE8 00000000
0012FCEC 80000004
0040175D|.83C4 1C ADD ESP, 0x1C ; 平衡栈,此值可用于判断参数个数
先说一下E的调用特征。首先,猜测004033B1只是一个接口,EBX的0044ADC0才是函数的入口地址!切记!
其次,调用传递了三个参数:寄存器一个EAX=0x01;栈上有两个,=00000002。
第三,每参数占用3个DWORDs:指向字符串的指针、00000000(含义未知)、数据类型80000004(栈上两个参数的类型为简单的ASCII字符串)。这点和VB类似,虽说E是基于VC6的。
F8,注意EAX返回值,比如00160700。也是一个指向字符串的指针,ASCII "5"(\0x35\0)。后面00401781处会转换其为Hex值,作为计算判断的初始值。意义不详,只有作者自己知道,貌视重要,现在不管它。
提前说一下,显然Sub_0044ADC0是关键的。其功能是后来跟进去才清晰的:一个解密函数。Key在00476CC5,密文在00476CD0,解密后明文在00160700。后面再详细说它,这里我们只管继续F8。
004017A4|.68 00000000 PUSH 0x0
004017A9|.BB 20344000 MOV EBX, 00403420 ; 取执行文件所在文件夹
004017AE|.E8 F21B0000 CALL 004033A5
...
004017E0|.B8 01000000 MOV EAX, 0x1
004017E5|.BB C0AD4400 MOV EBX, 0044ADC0 ; 解密得到"\KeyFile.dat"
004017EA|.E8 C21B0000 CALL 004033B1
Stack:
0012FCD4 00000002
0012FCD8 00476CE1ASCII "C3C67669019F120876D61E59C4CEC2FD"
0012FCDC 00000000
0012FCE0 80000004
0012FCE4 00476CC5ASCII "PeaceWorld"
0012FCE8 00000000
0012FCEC 80000004
...
004017FB|.B9 02000000 MOV ECX, 0x2
00401800|.E8 BDFDFFFF CALL 004015C2 ; strcat,得到"C:\Temp\52CM2013\T240878\KeyFile.dat"
...
00401843|.68 00000000 PUSH 0x0
00401848|.BB 40344000 MOV EBX, 00403440 ; 取执行文件名,"CM20140313.exe"
0040184D|.E8 531B0000 CALL 004033A5
...
00401871|.B8 01000000 MOV EAX, 0x1
00401876|.BB 30AE4400 MOV EBX, 0044AE30 ; 计算文件Checksum/Hash
0040187B|.E8 311B0000 CALL 004033B1 ; 返回 EAX=EE45E211
0012FCE0 00000001
0012FCE4 0015D868ASCII "CM20140313.exe"
0012FCE8 00000000
0012FCEC 80000004
这个文件校验和非常重要,后面会一再使用!注意:这里的File_Checksum值EE45E211是我解压缩后调试用执行文件的,非原执行文件的!
继续:
004018E3|.B8 01000000 MOV EAX, 0x1
004018E8|.BB 30AC4400 MOV EBX, 0044AC30 ; 取硬盘序列号
004018ED|.E8 BF1A0000 CALL 004033B1 ; 返回 EAX=0015D868 ASCII "01000000000000000001"
0012FCE0 00000001
0012FCE4 00000000
0012FCE8 00000000
0012FCEC 80000301
不要奇怪,这是我调试虚拟机里第一个硬盘的序列号!记为:Harddisk_SN。这个虚拟系统的第一个硬盘序列号总是这串值,所以最近遇到的几个目标都限制在虚拟机里运行。继续:
0040199D|.B8 01000000 MOV EAX, 0x1
004019A2|.BB 50AD4400 MOV EBX, 0044AD50 ; 加密序列号
004019A7|.E8 051A0000 CALL 004033B1 ; 返回 EAX=00160740 ASCII "49C6227137E9F5F18614F3A2C9E58E5493FBD238F68B307E"
0012FCD4 00000002
0012FCD8 0015D868ASCII "01000000000000000001"
0012FCDC 00000000
0012FCE0 80000004
0012FCE4 00476CC5ASCII "PeaceWorld"
0012FCE8 00000000
0012FCEC 80000004
重要:这里我调试机器的“硬件码”就出来了,它简单地由硬盘序列号加密得到。继续:
00401A2F|.68 01000000 PUSH 0x1
00401A34|.BB 103C4000 MOV EBX, 00403C10 ; 读入Key
00401A39|.E8 67190000 CALL 004033A5 ; 返回 EAX=0015CF68
0012FCE0 00000001
0012FCE4 00160710ASCII "C:\Temp\52CM2013\T240878\KeyFile.dat"
0012FCE8 00000000
0012FCEC 80000004
看到了吧,到了我们一开始定位到的文件读取调用Sub_00403C10。返回的指针0015CF68是一个结构/对象:=00000001,大概是引用记数。=00000058为读入KeyFile.dat的字节数。...=Key文件内容。
00401A50|.B9 02000000 MOV ECX, 0x2
00401A55|.E8 68FBFFFF CALL 004015C2 ; strcat, 返回 0015FC10 ASCII "EE45E21101000000000000000001"
0012FCE8 0015E0C0|Arg1 = 0015E0C0 ASCII "EE45E211"
0012FCEC 0015D868\Arg2 = 0015D868 ASCII "01000000000000000001"
...
00401A8D|.B8 01000000 MOV EAX, 0x1
00401A92|.BB 70AC4400 MOV EBX, 0044AC70
00401A97|.E8 15190000 CALL 004033B1
0012FCD4 00000002
0012FCD8 0015CF68Key对象
0012FCDC 00000000
0012FCE0 80000005
0012FCE4 0015FC10ASCII "EE45E21101000000000000000001"
0012FCE8 00000000
0012FCEC 80000004
在处理Key的内容了,Sub_0044AC70又是一个关键CALL,要跟进去!
0044AC70 .B8 64100000 MOV EAX, 0x1064
0044AC75 .E8 56B20000 CALL 00455ED0
0044AC7A .55 PUSH EBP
0044AC7B .56 PUSH ESI
...
0044AC98 .53 PUSH EBX
0044AC99 .57 PUSH EDI
0044AC9A .55 PUSH EBP
0044AC9B .E8 B2A80100 CALL 00465552 ; 请求00000058字节的内存,返回地址00A463E0,并复制Key数据。
...
0044ACDA .50 PUSH EAX
0044ACDB .F3:A4 REP MOVS BYTE PTR ES:, BYTE PTR
0044ACDD .8B8C24 84100000MOV ECX, DWORD PTR
0044ACE4 .33C0 XOR EAX, EAX
0044ACE6 .8B51 0C MOV EDX, DWORD PTR
0044ACE9 .83C9 FF OR ECX, 0xFFFFFFFF
0044ACEC .8BFA MOV EDI, EDX
0044ACEE .F2:AE REPNE SCAS BYTE PTR ES:
0044ACF0 .F7D1 NOT ECX
0044ACF2 .49 DEC ECX
0044ACF3 .51 PUSH ECX
0044ACF4 .52 PUSH EDX
0044ACF5 .8D4C24 20 LEA ECX, DWORD PTR
0044ACF9 .E8 38290000 CALL 0044D636
0012EC34 0015FC10|Arg1 = 0015FC10 ASCII "EE45E21101000000000000000001"
0012EC38 0000001C|Arg2 = 0000001C
0012EC3C 0012EC50\Arg3 = 0012EC50
0044ACFE .6A 00 PUSH 0x0
0044AD00 .55 PUSH EBP
0044AD01 .53 PUSH EBX
0044AD02 .53 PUSH EBX
0044AD03 .8D4C24 24 LEA ECX, DWORD PTR
0044AD07 .E8 1F2D0000 CALL 0044DA2B ; ECX=0012EC54, "Key Schedule"
0012EC30 00A463E0|Arg1 = 00A463E0 ; pOutput, 明文
0012EC34 00A463E0|Arg2 = 00A463E0 ; pInput, 密文 - KeyFile.dat的内容
0012EC38 00000058|Arg3 = 00000058 ; Size of Input, 密文长度
0012EC3C 00000000\Arg4 = 00000000 ; 0 - decrypting?
0044AD0C .8B03 MOV EAX, DWORD PTR ; 解密后第一个DWORD的值
0044AD0E .3BC5 CMP EAX, EBP ; 是否 > 密文长度?
0044AD10 .77 16 JA SHORT 0044AD28 ; YES - Game over!
0044AD12 .8D4B 04 LEA ECX, DWORD PTR
0044AD15 .50 PUSH EAX
0044AD16 .51 PUSH ECX
0044AD17 .E8 C465FCFF CALL 004112E0
0044AD1C .8B9424 80100000MOV EDX, DWORD PTR
0044AD23 .83C4 08 ADD ESP, 0x8
0044AD26 .8902 MOV DWORD PTR , EAX
0044AD28 >53 PUSH EBX
0044AD29 .E8 4DA80100 CALL 0046557B
0044AD2E .83C4 04 ADD ESP, 0x4
0044AD31 .8D4C24 14 LEA ECX, DWORD PTR
0044AD35 .E8 4E2E0000 CALL 0044DB88
0044AD3A .5F POP EDI
0044AD3B .5B POP EBX
0044AD3C >5E POP ESI
0044AD3D .5D POP EBP
0044AD3E .81C4 64100000 ADD ESP, 0x1064
0044AD44 .C3 RETN
关键调用Sub_0044D636的作用:用字符串File_Checksum+Harddisk_SN("EE45E21101000000000000000001")作为key去初始化0012EC54处的"Key Schedule"。
关键调用Sub_0044DA2B是一个解密KeyFile.dat文件的函数!
到了这里,再跟下去就没有意义了。首先,我的文件校验值File_Checksum=EE45E211是错的!这不是问题,用未解压缩的原版调试一下就得到了,正确值为:3E9CDB90。
问题在于没有与KeyFile.dat文件对应的Harddisk_SN,即Key文件所绑定机器的序列号!无法得到正确的解密结果!
试想要是弄到那个序列号,哼哼……
这个在实战中是不太可能得到的,只能硬着头皮继续往下走。现在,既然善良的Ms. Peace已经提供了它,也明知快餐不利于健康,但是我决定还是去回帖,偷看那个与Key绑定的硬件码。不知算不算作弊,想知道的,也去回复吧!
有了硬件码,再由它计算序列号就简单了!还记得本文一开始提到00401758处的第一个关键CALL Sub_0044ADC0吗?
0040174E|.B8 01000000 MOV EAX, 0x1
00401753|.BB C0AD4400 MOV EBX, 0044ADC0
00401758|.E8 541C0000 CALL 004033B1
0012FCD4 00000002
0012FCD8 00476CD0ASCII "AD04E9F801BF0DB6"; <- ***
0012FCDC 00000000
0012FCE0 80000004
0012FCE4 00476CC5ASCII "PeaceWorld"
0012FCE8 00000000
0012FCEC 80000004
演示一下怎么利用它来解密序列号。将密文"AD04E9F801BF0DB6",替换为本机的硬件码:在数据段找块空地儿,比如0048EE00,写入“硬件码”,再相应修改的指针为这样:
0012FCD4 00000002
0012FCD8 0048EE00ASCII "49C6227137E9F5F18614F3A2C9E58E5493FBD238F68B307E" ; <- *** 硬件码
0012FCDC 00000000
0012FCE0 80000004
0012FCE4 00476CC5ASCII "PeaceWorld"
0012FCE8 00000000
0012FCEC 80000004
F8,就得到解密的序列号EAX=0015D868 ASCII "01000000000000000001"。
其实作为对称密码算法,加密和解密使用相同的加/解密函数,只是"Key Schedule"的初始化不同!从前面提到的加密序列号"01000000000000000001"为硬件码的地方004019A7跟进去,经过Sub_0044CD1D,直到这里:
0044CE0D > /8B45 DC MOV EAX, DWORD PTR
0044CE10 . |3945 08 CMP DWORD PTR , EAX
0044CE13 . |0F8D AB000000 JGE 0044CEC4
...
0044CE61 .8B45 E8 MOV EAX, DWORD PTR
0044CE64 .8B55 10 MOV EDX, DWORD PTR
0044CE67 .57 PUSH EDI
0044CE68 .8B48 F8 MOV ECX, DWORD PTR
0044CE6B .8B5A F8 MOV EBX, DWORD PTR
0044CE6E .51 PUSH ECX
0044CE6F .50 PUSH EAX
0044CE70 .53 PUSH EBX
0044CE71 .8D85 6CFFFFFF LEA EAX, DWORD PTR
0044CE77 .52 PUSH EDX
0044CE78 .50 PUSH EAX
0044CE79 .E8 0F680000 CALL 0045368D ; 循环加/解密8字节的块
0012FACC 0012FC00|Arg1 = 0012FC00 ; 输出内存地址
0012FAD0 00A46284|Arg2 = 00A46284 ASCII "01000000"/"00000000" ; 输入块内容:明文/密文
0012FAD4 00000008|Arg3 = 00000008 ; 输入块字节数
0012FAD8 00A464D4|Arg4 = 00A464D4 ASCII "PeaceWorld" ; Key
0012FADC 0000000A|Arg5 = 0000000A ; Key字节数
0012FAE0 00000000|Arg6 = 00000000 ; 0 - 加密/1 - 解密
...
0044CEBF .^\E9 49FFFFFF JMP 0044CE0D
0044CEC4 >397D D8 CMP DWORD PTR , EDI
0044CEC7 .0F8E 99000000 JLE 0044CF66
...
0044CF26 .E8 62670000 CALL 0045368D ; 加/解密不足8字节的块
0012FACC 0012FC34|Arg1 = 0012FC34
0012FAD0 00A46284|Arg2 = 00A46284 ASCII "0001"
0012FAD4 00000004|Arg3 = 00000004
0012FAD8 00A464D4|Arg4 = 00A464D4 ASCII "PeaceWorld" ; Key
0012FADC 0000000A|Arg5 = 0000000A
0012FAE0 00000000|Arg6 = 00000000 ; 0 - 加密/1 - 解密
...
整个序列号"01000000000000000001"分三次调用Sub_0045368D进行加密。Sub_0045368D是个啥,跟进去看看:
00453715|> /FF75 1C PUSH DWORD PTR
00453718|. |56 PUSH ESI
00453719|. |FF75 0C PUSH DWORD PTR
0045371C|. |57 PUSH EDI
0045371D|. |E8 97000000 CALL 004537B9
00453722|. |FF75 14 PUSH DWORD PTR
00453725|. |68 64EB4A00 PUSH 004AEB64
0045372A|. |57 PUSH EDI
0045372B|. |57 PUSH EDI
0045372C|. |E8 88000000 CALL 004537B9
00453731|. |FF75 1C PUSH DWORD PTR
00453734|. |56 PUSH ESI
00453735|. |57 PUSH EDI
00453736|. |57 PUSH EDI
00453737|. |E8 7D000000 CALL 004537B9
0045373C|. |8345 0C 08 ADD DWORD PTR , 0x8
00453740|. |83C4 30 ADD ESP, 0x30
00453743|. |83C7 08 ADD EDI, 0x8
00453746|. |4B DEC EBX
00453747|.^\75 CC JNZ SHORT 00453715
Sub_004537B9就是DES加/解密函数,调用时"Key Schedule"不同而已!它同样被调用了三次,所以被称为"Triple DES"(TDEA/Triple DEA)。Key - "PeaceWorld"是这样用的:"PeaceWor"、"ld\0\0\0\0\0\0"、"PeaceWor"。帖出来给那些不了解的人科普一下。
解密KeyFile.dat文件的那个算法还没来得及仔细看,所以没法说。
到这里,我们有了正确的File_Checksum="3E9CDB90",Harddisk_SN="WD-WCAV9T236652",那么File_Checksum+Harddisk_SN="3E9CDB90WD-WCAV9T236652"。原帖的KeyFile.dat文件已经能被正确解密了,读者自己再接着跟下去继续玩吧,文件的编码很有意思——再次解密、比较!
下面说说怎么Patch。就两处:
1. Patching "File Checksum"
00401876|.BB 30AE4400 MOV EBX, 0044AE30
0040187B|.E8 311B0000 CALL 004033B1 ; <- *** ORG: File_checksum = 3E9CDB90
00401880|.83C4 10 ADD ESP, 0x10
本来很简单的事,0040187B处代码5字节,刚好够"MOV EAX, 3E9CDB90",不多不少。这里想分享一下我的心得,弄得复杂了一点。请在OD里载入我Patch的那个版本,会看到我改成了:
0040187B E8 002B0700 CALL 00474380 ; to my patching code
00474380 8B1424 MOV EDX, DWORD PTR ; 00401880, 原返回地址
00474383 8D82 362B0700 LEA EAX, DWORD PTR ; 004743B6, Call 004033B1后的返回地址
00474389 50 PUSH EAX
0047438A 8D82 311B0000 LEA EAX, DWORD PTR ; 004033B1
00474390 50 PUSH EAX
00474391 8B4424 0C MOV EAX, DWORD PTR ; 修正传递给Sub_004033B1的参数
00474395 894424 08 MOV DWORD PTR , EAX
00474399 8B4424 10 MOV EAX, DWORD PTR
0047439D 894424 0C MOV DWORD PTR , EAX
004743A1 8B4424 14 MOV EAX, DWORD PTR
004743A5 894424 10 MOV DWORD PTR , EAX
004743A9 8B4424 18 MOV EAX, DWORD PTR
004743AD 894424 14 MOV DWORD PTR , EAX
004743B1 895424 18 MOV DWORD PTR , EDX ; 保存原返回地址
004743B5 C3 RETN ; Call 004033B1 (Get File_Checksum)
004743B6 8B4424 10 MOV EAX, DWORD PTR ; 取保存的原返回地址
004743BA 890424 MOV DWORD PTR , EAX ; 设置返回地址
004743BD B8 90DB9C3E MOV EAX, 0x3E9CDB90 ; Patching File_Checksum
004743C2 C3 RETN ; 返回原调用
这是想说,1) 在原位置不能容下Patch代码时,怎么调整调用参数。2) 避免重定位,一律使用相对地址。这在新增的代码段需要调用API时,显得特别重要!
2. Patching "Harddisk Serial Number"
004018E3|.B8 01000000 MOV EAX, 0x1
004018E8|.BB 30AC4400 MOV EBX, 0044AC30 ; 取硬盘序列号
004018ED|.E8 BF1A0000 CALL 004033B1 ; 返回 EAX=0015D868 ASCII "01000000000000000001"
跟进Sub_0044AC30,最后会来到:
0044DEAB/$ 55 PUSH EBP
0044DEAC|. 8BEC MOV EBP, ESP
0044DEAE|. 81EC 24050000 SUB ESP, 524
...
0044DEE2|. FF15 60524700 CALL NEAR DWORD PTR [<&KERNEL32.CreateFileA>]
...
0044DF4D|. FF15 64524700 CALL NEAR DWORD PTR [<&KERNEL32.DeviceIoControl>]
0044DF53|. 85C0 TEST EAX, EAX
0044DF55|. 5B POP EBX
0044DF56|. 75 04 JNZ SHORT 0044DF5C ; API 成功
0044DF58|> 33C0 XOR EAX, EAX
0044DF5A|. EB 41 JMP SHORT 0044DF9D ; API 失败
0044DF5C|> 8D4E 10 LEA ECX, DWORD PTR ; ESI - the OutBuffer
0044DF5F|. 8D85 DCFAFFFF LEA EAX, DWORD PTR
Patch点选在0044DF5F处,这时:1) KERNEL32.DeviceIoControl已成功调用,2) ESI指向API返回的结果。改为:
0044DF5F E8 5F640200 CALL 004743C3
0044DF64 90 NOP
004743C3 57 PUSH EDI
004743C4 8D7E 24 LEA EDI, DWORD PTR
004743C7 B8 4457572D MOV EAX, 0x2D575744
004743CC AB STOS DWORD PTR ES:
004743CD B8 41433956 MOV EAX, 0x56394341
004743D2 AB STOS DWORD PTR ES:
004743D3 B8 32543633 MOV EAX, 0x33365432
004743D8 AB STOS DWORD PTR ES:
004743D9 B8 35362032 MOV EAX, 0x32203635
004743DE AB STOS DWORD PTR ES:
004743DF B0 20 MOV AL, 0x20
004743E1 803F 00 CMP BYTE PTR , 0x0
004743E4 74 03 JE SHORT 004743E9
004743E6 AA STOS BYTE PTR ES:
004743E7 ^ EB F8 JMP SHORT 004743E1
004743E9 5F POP EDI
004743EA 8D85 DCFAFFFF LEA EAX, DWORD PTR
004743F0 C3 RETN
将硬盘序列号改为:"DWW-AC9V2T6356 2"。为什么不是按"WD-WCAV9T236652"改?请查MSDN文档!
想起很多年前,在温酒舞/酒吧时代,当时为了自己写代码去读这个序列号,分析过CIH病毒简单到约1024字节的代码,学习到从Ring3跳到Ring0的方法,这样就可以不需要经过vxd直接操作端口了。
如果需要对Patch后的文件压缩/加壳,记得要调整一下代码段的虚拟大小:
NumberName VirtSize RVA PhysSizeOffset Flag
1 .text 00073376 00001000 00074000 00001000 60000020
这是原帖执行文件正确脱壳后的数据。将00073376修改为Patch后的实际大小000733F1:
1 .text 000733F1 00001000 00074000 00001000 60000020
否则加壳后的程序运行时会崩溃。
小结:1) 作者提供了关键数据,使难度降低。2) 因为是CrackMe/KeygenMe,很理解作者的纠结:太难的话,参与性就差,所以作了简化处理。
实践中,就算是你拿到了序列号之类的HWID,也一定是经过很多次变形/变换,那些算法甚至是用VM保护,才使用。另外,没有反跟踪、反调试,代码也没有变形膨胀,读起来很流畅,有利于大家学习。
这部分结束,有时间再试试看能否Keygen。
本帖最后由 MistHill 于 2014-3-8 14:07 编辑
占楼备用!
差点儿忘记了,女生们,节日快乐!{:1_919:}
再补充一句,这个破解版需要原帖的Key文件:KeyFile.dat。
这个能干嘛的 学习经验的小白 不明觉厉中~~~~~~~~~~~~ 本帖最后由 ly269935419 于 2014-3-9 11:39 编辑
感谢大神思路,总算会了,学习了。顺便把我的一些分析拿出来给大神指点下。ida看图比较好说一点,可惜无法上图。我直接说一些关键地方
call 4033B1解字符码(顺便求详细分析,让小菜好好学习解码)jnz loc_40200B这是有KeyFile.dat和无KeyFile.dat的两种结果。不跳为验证,跳为不验证,非注册版。jl short loc_401BB9为非法验证跳(共有2处,ida直接看call sub_4033AB上的跳转,这里必须要ecx赋值正确)。下面的到运行开始可能是正式版功能部分,这个= =,估计要正确文件解出来的赋值(我猜2个call sub_4033B1处,eax赋值0)。剩下的坐等大神发详细解密贴,怒赞大神。 大神又调戏小菜啦{:301_991:}。本来一开始就打算Patch,后来没看到注册用户名(其实我看漏了,用户名就在图片上,额。。。。。),一想就算了,分析下就好。后来又看到大神出招,注意到了图片,于是打算patch注册名,后来发现内存里数据长度不够,额。于是向上找了找发现了类硬盘的数据生成注册名,于是偷个懒看大神的,额。。。。好吧,我喜欢偷懒。最后希望大神能分析下类硬盘数据生成注册名的过程,额。。。。。喜欢偷懒,额。。。。。。。。,私聊贵,额。。。。。。。屌丝伤不起。
页:
[1]