首页
社区
课程
招聘
一个代码乱序,膨胀的CrakeMe的破解
发表于: 2016-10-23 02:26 4415

一个代码乱序,膨胀的CrakeMe的破解

2016-10-23 02:26
4415
一个代码乱序,膨胀的CrakeMe的破解

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

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 0
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//