babytea
这个题目用到了比较简单的TEA加密,但是使用异常处理来打乱了控制流。
分析
首先是main
函数:
1 | .text:00A21200 ; int __cdecl main(int argc, const char **argv, const char **envp) |
并没有什么需要关注的点,就是调用了tea
函数进行加密,接着进行比较并输出结果。
下面是tea
函数:
1 | .text:00A21000 ; int __cdecl tea(unsigned int a1, unsigned int *flag, _DWORD *keys) |
我们可以发现函数最开始有mov eax, large fs:0
指令(见上图),说明函数用到了异常处理,这时直接F5就大概率没有用了,虽然有patch的方法,但是因为题目比较简单我就直接汇编分析了。
接下来的部分是将参数分别放入v0、v1、keys,并初始化sum,并且会触发第一次异常:
这次异常触发后程序会执行loc_A210BE
部分的代码,将v0、v1分别与dword_A3F038
和dword_A3F03C
进行xor。
接下来就是加密部分,我们都知道TEA加密每轮会对sum进行修改,经过分析发现,程序每次修改完sum后会判断sum >> 31
的结果是否为0,如果为0就会触发异常,而在异常处理中会将sum与1234567h
进行xor(见上图)。
对sum进行修改之后就是正常的加密流程,使用sum、keys、移位操作对v0、v1进行加法操作。
在32轮加密结束之后会触发最后一次异常,将v0、v1的结果存入dword_A3F038
和dword_A3F03C
为了下一次加密使用。
解密
因为sum每次不一定只是加了delta,也可能是还进行了异或,所以我们不能直接让解密程序的sum等于delta * 32
,这里我们先模拟一边加密过程,将sum在每轮的值保存到list,供解密使用。
1 | import ctypes |
babysmc
顾名思义,题目用到了SMC,还不了解的同学可以自行Google。
分析
程序随便输入一个字符串之后会卡死一段时间,最后也没有输出对错,猜测应该是输入的字符会在SMC中用到,如果输入的不对那么SMC就无法还原正确的汇编指令,程序自然无法正常执行。
main
函数如下,我们会发现里面调用了sub_351850
和sub_3518C0
,但是这两个函数的汇编看起来不太对,说明这两个函数应该是需要被SMC解密的。
1 | .text:003546C0 ; int __cdecl main(int argc, const char **argv, const char **envp) |
这里smc
是用来SMC解密的,它的参数是inpStr[0]
和inpStr[33]
,而这两个参数用来与内存中的数据分别进行xor操作,这里我们就能够明确SMC是与我们的输入有联系的,再仔细看smc函数的内部,首先smc函数将0x351850
开始的0x100
个字节与inpStr[0]
进行xor,然后是将0x003517C0
开始的0x100
个字节与inpStr[33]
进行xor。
这里就需要我们对汇编语言的机器码有一些了解了,首先我们不难发现程序中的函数一般都是以push ebp
开头的,也就是调整栈指针相关的指令,那么按理来说sub_351850
和sub_3517C0
也不例外,而push ebp
的机器码是0x55
,也就是说函数都是以0x55
为第一个字节开头的。
在SMC解密前,sub_351850
和sub_3517C0
的第一个字节是0x57
和0x74
,所以我们可以根据0x74 ^ ord('!') == 0x55
和(0x57 ^ ord('!')) ^ ord('#') == 0x55
来得知inpStr[0]
就是#
,inpStr[33]
就是!
。(这里要注意sub_351850
是被xor了两次,自己仔细分析一下就能得知)
sub_351850
如下:
1 | int __cdecl sub_351850(int a1, int a2) |
SMC还原得到的sub_3518C0
如下:
1 | bool __cdecl sub_3518C0(unsigned __int8 *a1) |
1 | 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 |
1 | bool __cdecl sub_8418C0(unsigned __int8 *a1) |
解密
因为代码太多了,就不一一列举了,经过分析可以发现首先是用了Sbox进行字节代换,然后是进行32个多项式计算,也就是32个方程组,直接z3即可。
1 | import z3 |