re | buuctf逆向刷题之crackMe

发布时间 2023-06-26 16:46:35作者: z5onk0

写在前头

本题来自安洵杯2019 crackMe,涉及到的知识点较多,Mark一下

分析

从main开始

反编译main函数,9行这里触发了一个内存写异常,有点奇怪哈

发现SEH

查看汇编,哦这里注册了一个SEH函数,sub4100F

跟进去看一下,很简单的算法,input划分为4个4字节的值,赋给v5,v5数组按照往后递推的方式,由前4个单元生成后1个单元的值,最后逐个字节取v5[32]到v5[35]的值赋给out

sub_411700长这样,第一个v5单元的值和sub_41170函数异或,函数的参数为v5后三个单元和key的异或,最后计算的值再赋给第5个v5单元

都是异或运算,这个算法要逆过来也很简单,知道最后4个单元的值,往前一个个推就能知道v5[0]到v5[3]的值了,递推规则如下,sub_411760都不用逆,照着IDA抄一下就行

v5[i] = v5[i+4]^sub_411760(v5[i+1]^v5[i+2]^v5[i+3]^(key+4*i))

后面才知道sub_4119f0这个算法叫做sm4,有现成的求逆API可以直接调用

input是用scanf读进来的,如果知道key和out,就可以恢复input

sm4生成box

看一下对key的引用,定位到了Handler_0,一个字符串拷贝到v2,经过运算生成key,和输入没关系是吧,那key可以直接动调获取

其实sub_411172就是用来生成sm4 box的函数,第19行那个异或的数是sm4的一个特征值

发现UEH

继续看一下Handler_0第9行,这里注册了一个UEH函数,点进去分析一下,这里的Str2是固定的一串字符串,顺便还看到了sm4生成的output,output经过sub_41126c计算得到Str1

进入sub_41126c看一下,一眼base64,28行这里=换成!,30行这里对编码表凯撒右移了24位

字符串比较

UEH这个函数返回时eip被修改为sub_411136,点进去看一下,发现是最终的字符串比较,Str2是固定的,Str1由input经过sm4加密和变种base64加密生成

发现VEH

还有一点没搞清,Handler0是如何被调用的,查找交叉引用,定位到sub_412AB0,该函数对base64编码表大小写进行了变换,然后注册了VEH函数来调用Handler

IAT hook

继续向上找引用,这里传入了User32.dll和MessageBoxW,是要搞什么鬼

对里面调用的函数进行符号还原,前面就是找到 user32.dll 对应的 IMAGE_IMPORT_DESCRIPTOR 结构体地址,然后找到 MessageBoxW 对应的 IMAGE_THUNK_DATA 结构体地址,用VirtualProtect修改页属性为可写,用WriteProcessMemory将IMAGE_THUNK_DATA字段覆写为sub_411023函数地址

总结一下,典型的IAT hook,将MessageBoxW的IAT地址替换为了sub_411023的函数地址,该函数完成了VEH的注册

mainCRTStartup对构造函数的初始化

还没完,继续分析这个IAT hook函数是被谁调用的,引用回溯到了rdata这里,往上翻翻

这个地方被tmainCRTStartup调用了

看看tmainCRTStartUp函数,32行这里initterm_e调用了rdata区域里保存的函数,对全局/静态C++类的构造函数进行了初始化

总结

上面的分析是根据结果查找原因,倒着推回去的比较乱,下面再梳理总结下

异常处理的注册

  • 程序初始化时,调用链为start->tmainCRTStartUp->initterm_e->IAT hook,修改MessageBoxW函数的IAT表,在主函数中调用MessageBoxW,实际调用的是注册VEH的函数,并对base64编码表进行了变换
  • 在main函数中对SEH进行了注册
  • 在VEH handler中对UEH进行了注册

异常处理的回调

  • 需要知道一个知识点,Windows 用户态异常发生先找调试器,没有再找 VEH,VEH 处理不了再找 SEH, SEH 还处理不了找 UEF

  • 本程序各级异常处理的返回状态都是未完成处理,会继续往下级调异常处理函数,所以本程序的调用顺序为VEH->SEH->UEH

  • VEH中对sm4的box进行了初始化

  • SEH中对input进行sm4加密,获得output

  • UEH中对output进行变种的base64加密,获得Str1,并且和变换过的固定字符串Str2进行比较

总之,题目的算法分析和逆向很简单,麻烦的是这些算法没有集中在main中,而是分散到了各个异常处理函数里面,跳来跳去的对分析造成了干扰,不过只要足够有耐心,不断向上查找引用,还是能分析完的

题解

import base64
import sm4

ori = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
now = ''

# sub_412AB0
for c in ori:
	tmp = ord(c)
	if 97 <= tmp <= 122:
		now += chr(tmp - 32)
	elif 65 <= tmp <= 90:
		now += chr(tmp + 32)
	else:
		now += c

ori += '='
now = now[24:] + now[:24] + '!'		# sub_4110FF -> sub_412760
# ori = ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=
# now = yzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/abcdefghijklmnopqrstuvwx!

# sub_412C30
str2 = "1UTAOIkpyOSWGv/mOYFY4R!!"
str2_swap = ''
for i in range(0, len(str2), 2):
	str2_swap += str2[i+1] + str2[i]

# sub_41126C -> sub_413090
b64string = ''
for s in str2_swap:
	b64string += ori[now.find(s)]
sm4_encoded = base64.b64decode(b64string)

# sub_412EA0
key = sm4.SM4Key(b"where_are_u_now?")
print key.decrypt(sm4_encoded)