-
-
一个代码乱序,膨胀的CrakeMe的破解
-
发表于: 2016-10-23 02:26 4415
-
一个代码乱序,膨胀的CrakeMe的破解
CreakeMe的情况介绍:压缩壳,IAT加密,关键代码被加密,程序内大量的乱序跳转,代码膨胀。
嗯,介绍完毕,开始干活。
1.到达OEP
压缩壳,直接esp定律,一步到位:
顺利找到IAT的位置,发现被加密了,且api地址的计算过程也有乱序,膨胀,不过也不能慌。。。。
观察IAT的代码,发现是在堆中,那他必然是通过计算出api的真实地址然后动态生成的hook函数。
让我们来想想一下这个过程:
1.从程序某处读取程序需要的api,通过GetProcAddres获取到函数地址;
2.根据api地址动态生成hook函数;
3.把hook函数地址写入IAT中。
既然想(猜)到了大致的过程,也就有了大致的对策。
我们可以找到他获取函数地址的地方,然后保存真实的函数地址,等到他写入IAT的时候,我们把hook地址替换成真正的api地址,就可以完美脱壳了。
2.脱壳
在0x475080处下4字节硬件写入断点,重新运行程序:
第一次断在系统领空:
跟出去发现memcpy,跟了跟代码发现似乎并不是关键点。
再次运行,这次断在:
Edx是iat的位置,eax是hook函数地址,看来颇为理想,接着就是找获取真正api地址的地方了,由于程序中大量的乱序,膨胀代码,所以手动一步一步似乎不是个好活,不过幸亏OD有run跟踪。
我们在00590895处下断点,取消上边的硬件断点,然后打开run跟踪,记录一次从获取API地址到最后写入hook地址的过程。
稍等几分钟,大概跑了27万行代码之后又回到了我们的断点处。
根据我们上边的 猜测,混淆IAT的过程,第一步就是获取真正的API地址,那我们从最后写入hook地址出发就等于回到了这个过程的开始,那run跟踪的开始一段代码很可能就有我们要的结果。
Od的run跟踪,从下往上是执行的顺序,也就是说最下边的是先执行的代码,我们都知道系统api的地址,大多都是在7开头的地址,知道这个就好办了:
直接拉到run跟踪的最下方,然后找那个地方有7开头的地址:
大概20几行就发现一个,在这下个断点,然后运行一下:
断下来之后果然就是api地址:
找到了这两个地址就好办了,我们可以写一个脚本,等他拿到真正的地址之后,我们保存这个地址,然后等他写入的时候替换掉。
接着就是找一个运行脚本的时机,通过栈回溯,我找到一个不错的地址:
观察了几次,这个call就是用来加密IAT的,在这下断点,等运行到了执行脚本,实在是再好不过了。
操作过程如下:
1.重新运行程序,在0047A41D及其下一行下断点。
2.运行脚本:
简单的解释一下:
004c10F7 和 004c0895就是两行关键代码的地址,由于是堆中地址,所以可能有变化,不过也不要紧,等程序到0047A41D看一下程序基址调整一下就可以了。
脚本写的比较挫,最后会跑起来。。。。所以最好手动执行。
不过跑起来也不影响脱壳,直接用od自带的脱壳插件就可以脱下来了。
3.破解
大概在2年前就尝试过破解,最后失败了。后来就有了一组正确的key。
今天也是用一组正确的key来搞定的,并且反推key的过程也是直接参考的破文。
好吧,我只是来见识见识乱序,膨胀代码的。。。。。。。。。
反推key的过程请看这个:
http://bbs.pediy.com/showthread.php?t=101765
有了一组正确的key:
User:lacoucou
Pass:766063730966427304975B6B5C5163
我们就好干活了,直接用IDA加载脱壳后的程序,找到关键函数:
在这两行下断点,输入正确的key等断下来之后,打开ID A的trace,设置如下:
同时还要设置下调试选项:
去掉红框处的线程和dll的事件通知(因为有通知的时候trace会停止。)
然后开始trace:
1.设置为instruction trace
2.运行程序
3.然后你就可以去干别的了。。。。。。。。。。。。。。。。。。。。。。。。。。。。
等了大约20多分钟,终于跑出来,指令大概有11万行,根据之前的失败经验,我大致知道了那些是junkcode,所以就用python脚本把这些过滤掉:
过掉之后,大约还有将近7000行代码,感觉差不多了,再用脚本记录下这些地址:
不重复断点大概5600多个,用OD加载之后就可以慢慢跟了。。。。
其实也可以根据情况,再次过滤吊一些没用的代码。
关键代码就不贴了,只说下生成解密key的算法:
1.有两个表,一个用户表usertable和一个密码表passTable
2.用用户输入的用户名与用户表做运算,得到一个15字节的数组。
3.用用户输入的密码转换成16自己数然后与密码表做运算,也得到一个得到一个15字节的数组。
4.把这两个对数据的对应位相加就是最后的解密key.
程序的后边流程似乎有用得到的尝试解码函数,计算校验和,不相等写重新写回加密数据。。。不过没跟。。。。。。。。。。
哎,直接放上注册机吧。
#python
传个附件,内含一个脱壳后的程序和注册机。
CrakeMe.zip
CrakeMe下载:
http://bbs.pediy.com/showthread.php?t=97892
参考:http://bbs.pediy.com/showthread.php?t=101765
CreakeMe的情况介绍:压缩壳,IAT加密,关键代码被加密,程序内大量的乱序跳转,代码膨胀。
嗯,介绍完毕,开始干活。
1.到达OEP
压缩壳,直接esp定律,一步到位:
0047148B /. 55 PUSH EBP 0047148C |. 8BEC MOV EBP, ESP 0047148E |. 6A FF PUSH -0x1 00471490 |. 68 28514700 PUSH crackme.00475128 00471495 |. 68 58214700 PUSH crackme.00472158 ; SE 处理程序安装 。。。。。。。。。。。 004714B1 |. FF15 80504700 CALL NEAR DWORD PTR DS:[0x475080]
顺利找到IAT的位置,发现被加密了,且api地址的计算过程也有乱序,膨胀,不过也不能慌。。。。
观察IAT的代码,发现是在堆中,那他必然是通过计算出api的真实地址然后动态生成的hook函数。
让我们来想想一下这个过程:
1.从程序某处读取程序需要的api,通过GetProcAddres获取到函数地址;
2.根据api地址动态生成hook函数;
3.把hook函数地址写入IAT中。
既然想(猜)到了大致的过程,也就有了大致的对策。
我们可以找到他获取函数地址的地方,然后保存真实的函数地址,等到他写入IAT的时候,我们把hook地址替换成真正的api地址,就可以完美脱壳了。
2.脱壳
在0x475080处下4字节硬件写入断点,重新运行程序:
第一次断在系统领空:
77773923 F3:A5 REP MOVS DWORD PTR ES:[EDI], DWORD PTR DS:[ESI]
跟出去发现memcpy,跟了跟代码发现似乎并不是关键点。
再次运行,这次断在:
00590895 8902 MOV DWORD PTR DS:[EDX], EAX 00590897 E8 39000000 CALL 005908D5
Edx是iat的位置,eax是hook函数地址,看来颇为理想,接着就是找获取真正api地址的地方了,由于程序中大量的乱序,膨胀代码,所以手动一步一步似乎不是个好活,不过幸亏OD有run跟踪。
我们在00590895处下断点,取消上边的硬件断点,然后打开run跟踪,记录一次从获取API地址到最后写入hook地址的过程。
稍等几分钟,大概跑了27万行代码之后又回到了我们的断点处。
根据我们上边的 猜测,混淆IAT的过程,第一步就是获取真正的API地址,那我们从最后写入hook地址出发就等于回到了这个过程的开始,那run跟踪的开始一段代码很可能就有我们要的结果。
Od的run跟踪,从下往上是执行的顺序,也就是说最下边的是先执行的代码,我们都知道系统api的地址,大多都是在7开头的地址,知道这个就好办了:
直接拉到run跟踪的最下方,然后找那个地方有7开头的地址:
Run trace, 选定行 返回=18. 线程= 地址=005910F6 =POP EDX 修改后的寄存器=EDX=73FFDA90, ESP=0019FEE4
大概20几行就发现一个,在这下个断点,然后运行一下:
断下来之后果然就是api地址:
005910F6 5A POP EDX ; ntdll.RtlAllocateHeap
找到了这两个地址就好办了,我们可以写一个脚本,等他拿到真正的地址之后,我们保存这个地址,然后等他写入的时候替换掉。
接着就是找一个运行脚本的时机,通过栈回溯,我找到一个不错的地址:
0019FF54 |0047A41D 返回到 crackme.0047A41D
观察了几次,这个call就是用来加密IAT的,在这下断点,等运行到了执行脚本,实在是再好不过了。
操作过程如下:
1.重新运行程序,在0047A41D及其下一行下断点。
2.运行脚本:
VAR apiEa BP 004c10F7 BP 004c0895 GO EOB do_Work do_Work: CMP eip,4c10f7 JE label_1 CMP eip,4c0895 JE label_2 label_1: MOV apiEa,edx go label_2: MOV eax,apiEa go
简单的解释一下:
004c10F7 和 004c0895就是两行关键代码的地址,由于是堆中地址,所以可能有变化,不过也不要紧,等程序到0047A41D看一下程序基址调整一下就可以了。
脚本写的比较挫,最后会跑起来。。。。所以最好手动执行。
不过跑起来也不影响脱壳,直接用od自带的脱壳插件就可以脱下来了。
3.破解
大概在2年前就尝试过破解,最后失败了。后来就有了一组正确的key。
今天也是用一组正确的key来搞定的,并且反推key的过程也是直接参考的破文。
好吧,我只是来见识见识乱序,膨胀代码的。。。。。。。。。
反推key的过程请看这个:
http://bbs.pediy.com/showthread.php?t=101765
有了一组正确的key:
User:lacoucou
Pass:766063730966427304975B6B5C5163
我们就好干活了,直接用IDA加载脱壳后的程序,找到关键函数:
.Data:004014EB call sub_46411F .Data:004014F0 add esp, 18h
在这两行下断点,输入正确的key等断下来之后,打开ID A的trace,设置如下:
同时还要设置下调试选项:
去掉红框处的线程和dll的事件通知(因为有通知的时候trace会停止。)
然后开始trace:
1.设置为instruction trace
2.运行程序
3.然后你就可以去干别的了。。。。。。。。。。。。。。。。。。。。。。。。。。。。
等了大约20多分钟,终于跑出来,指令大概有11万行,根据之前的失败经验,我大致知道了那些是junkcode,所以就用python脚本把这些过滤掉:
import re reList=[r"lea *esp, *\[esp\+4\]",r"jmp",r"call",r"(inc|dec) *(eax|ebx|ecx|edx|esi|edi)",r"(add|sub) *(eax|ebx|ecx|edx|esi|edi), *\d{1,}",r"pusha|popa|pushf|popf|stos",r"rol|ror|rcl|shld|rcr|shl|and|nop",r"mov ax, 8321h",r"mov dx, 5678h",r"(push|pop) *(eax|ebx|ecx|edx|esi|edi)",r"dec esp"] def parseLine(f,lineString): for reStr in reList: matchObj = re.search(reStr, lineString,re.I) if matchObj: return f.write(lineString) def main(): outFile=open("out.txt","w") infile = open("CrakeMe.txt","r") while 1: lines = infile.readlines(100000) if not lines: break for line in lines: if line: parseLine(outFile,line) infile.close() outFile.close() main()
过掉之后,大约还有将近7000行代码,感觉差不多了,再用脚本记录下这些地址:
#python addrList=[] f = open("out.txt","r") lines = f.readlines() for line in lines: if line: addr=line[0:6] if addr not in addrList: addrList.append(addr) with open("odBp.txt","w") as f: for x in addrList: bpstr="bp %s\n"%x f.write(bpstr) print len(addrList)
不重复断点大概5600多个,用OD加载之后就可以慢慢跟了。。。。
其实也可以根据情况,再次过滤吊一些没用的代码。
关键代码就不贴了,只说下生成解密key的算法:
1.有两个表,一个用户表usertable和一个密码表passTable
2.用用户输入的用户名与用户表做运算,得到一个15字节的数组。
3.用用户输入的密码转换成16自己数然后与密码表做运算,也得到一个得到一个15字节的数组。
4.把这两个对数据的对应位相加就是最后的解密key.
程序的后边流程似乎有用得到的尝试解码函数,计算校验和,不相等写重新写回加密数据。。。不过没跟。。。。。。。。。。
哎,直接放上注册机吧。
#python
keyList=[0x4F,0x20,0x44,0x74,0x39,0x41,0x57,0x1C,0x4E,0x13,0x4B,0x62,0x94,0x19,0x6F] strUserName=raw_input("Please input your username:") strUserTable="XiHEzOBmalONIeN" strPassTable="MxzYCshwEqdjDbm" userList=[] i=0 nUserNameLen=len(strUserName) for x in range(0,15): i=x%nUserNameLen userList.append((ord(strUserTable[x])+0x10)^ord(strUserName[i])) passList=[] for n in xrange(0,15): passList.append(keyList[n]-userList[n]) passDecodeList=[] for m in xrange(0,15): passDecodeList.append((ord(strPassTable[m])-0x10)^passList[m]) printList=[] for j in xrange(0,15): printList.append("%02X"%(passDecodeList[j]&0xff)) print "".join(printList)
传个附件,内含一个脱壳后的程序和注册机。
CrakeMe.zip
CrakeMe下载:
http://bbs.pediy.com/showthread.php?t=97892
参考:http://bbs.pediy.com/showthread.php?t=101765
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
赞赏
他的文章
看原图
赞赏
雪币:
留言: