写点什么

强强联手:2021 强网杯 LongTimeAgo 复盘分析

发布于: 1 小时前

花指令

查看main函数能看出有异常跳转。


查看汇编,将此处 c 为代码。



之后再依次去除花指令,再反汇编得到 main 函数伪代码。


主函数不短,先根据参数大概还原函数名,可以看出是常规的对输入加密再 check。


接下来结合动态调试逐步分析。


初步 check 与自定义结构体初始化



这一段出现的变量 v3,v4,v5 最终只在最后一个 if 语句中使用,可以直接动调查看比较的左侧的值即可得知这段代码的作用。



在比较处下断点,可知值为我们输入的长度,因此这段代码实质上是用来计算输入长度。即输入长度为 64。


之后进行数据初始化后进入一个循环。



可以看出循环每次将输入字符串的地址加 8,直到到达字符串结束地址。因此该循环应该是对输入的每 8 个字符进行一次处理。循环 8 次。


每次循环都首先调用sub_401DB0函数,参数为本次循环处理的 8 个字节的起始地址和常数 8。进行分析。



显然前面一段还是用来得到字符串的长度,这里字符串为大写的 16 进制的 16 个字符,则长度为 16,分析可知这里就是将 8 个字符按十六进制转换为 32 位数据并返回,且输入必须在 0123456789ABCDEF 中,否则返回 0。


类似


binascii.a2b_hex(v)再看之前的循环,其中有两段类似的操作,一段处理的是根据输入转换而来的 8 个 dword 数据,一段处理的是 data 段固定的 8 个 dword 数据。


将其分别存储在 buf_char_72 与 buf_char_360 中的每 9 个 dword 中的第 2 个 dword 中。


其中的 while 循环根据数据计算得到一个值,存储在每 9 个 dword 中的第 1 个 dword 中。若输入为 12345678,则该值为 4。若输入为 00345678,则该值为 3。若输入全为 0 或非法(不在 0123456789ABCDEF 中),则该值为 0。实质上保存的是该值的有效字节数。



则这里的 buf_char_72 与 buf_char_360 可能是结构体,应该是用来分别存储输入数据和最后的加密结果。


则可创建如下结构体。



再重新 y 设置 buf_char_72 与 buf_char_360 的数据类型,显然为 struc_data 数组,长度都应该为 8,再重命名变量。



之后该循环如下。



xtea 与 tea 的魔改与异或加密该循环之后,分别调用 4 次 sub_403460 函数、2 次 sub_4029E0 函数和 2 次 sub_402030 函数。



根据参数,显然后面四次函数调用,使用了输入转换来的数据,每两个为一组进行加密处理,密钥即为前面 sub_403460 函数的参数 v25-v28,由于 v25-v28 前面均未使用,则这里 sub_403460 函数应该是对其赋值,进行密钥的初始化。(由于是每两个一组加密,且密钥数据为 4 个,可以猜测是 tea 加密,没有用 Findcrypt 找到说明可能常数被改了)


查看 sub_403460 函数。



显然根据传入参数的不同,每次调用该函数会使得 sub_401EF0 函数生成一个值,类似前面对输入和加密结果的处理,又根据该值生成有效字节数,猜测这里是一个类似前面的结构体。


同样设置结构体。



则 4 次调用可以生成 4 个 key 值,且数据固定。因此可以动调得到 4 个 key 值。第二个参数的数值越大,运行时间越长,不想去分析sub_401EF0函数则等待即可。


相关值如下。


key1 = 0x0FFFD  valid_flag = 2key2 = 0x1FFFD  valid_flag = 3key3 = 0x3FFFD  valid_flag = 3key4 = 0x7FFFD  valid_flag = 3
复制代码


回去分析加密函数sub_4029E0。代码非常长,先去除花指令。对部分变量重命名,再慢慢看。


根据变量引用能看出下面为一个整体部分。



数据有效字节长度范围是 0-4,则 if 语句只可能走向最后两部种情况。若默认数据的有效长度为 4,则能够看出其实就是将第一个数据存储到 v53[0]处。


同理经过观察后面还有几部分类似的代码,根据偏移计算可知,实质上就是将两个输入数据和 4 个 key 值分别进行转储(按照自定义的数据结构体)。



据此并结合内存地址,设置如上结构体。重新设置数据类型与变量名。



输入与密钥的转储之后的一个循环应该是关键的加密。根据值 sum 每次循环都减去 0x70C88617 以及最后循环结束时值为 0xE6EF3D20,得循环次数为 32。显然为 tea 加密族。根据明显可以看到的 >> 11) & 3 可知为 xtea 加密。


v = 0i = 0while v!=0xE6EF3D20:    i+=1    v -= 0x70C88617    v &= 0xffff_ffffprint(i)  # 32
复制代码


则根据 xtea 加密以及相关参数,可推出各个函数的作用。猜测这些函数是针对这种 valid_flag+data 的结构体专门实现的各种运算。




即这里对数据进行了被魔改的 xtea 加密。继续往后看。



之后根据前面生成 key 时所调用过的函数create_data_based_const又生成一个值。再计算其有效字节数形成自定义数据结构体,与加密后的 data1 进行异或。



之后又是类似最前面的代码,将异或后的结果又转储回原输入的地址。


同理,后面使用不同的参数调用函数 create_data_based_const 又生成一个值,与 data2 异或后转储回去。


该异或值可以通过动调,在前面生成密钥的地方修改参数后运行来获得。create_data_based_const(3,5) = 0xFDcreate_data_based_const(3,6) = 0x1FD


至此 sub_4029E0 加密函数分析完毕,即对输入生成的前 4 个 dword 数据分别进行魔改的 xtea 加密后再与固定数据异或。


接下来分析 sub_402030 函数。类似 sub_4029E0 函数,是 tea 加密,其中 delta 由 0x1E3F4AEF^0x230A6353 得到。最后再分别与 create_data_based_const(3,7) = 0x3FD 和 create_data_based_const(3,8) = 0x7FD 异或。即对输入生成的后 4 个 dword 数据分别进行魔改的 tea 加密后再与固定数据异或。


主函数加密后就是 check 部分。



根据已知的 check 数据,可以知道加密后的 valid_flag 值都为 4。则这里首先判断加密结果和 check 数据的有效字节数是否相同。



根据内存地址计算可知 v30[-0xD8]与 v30[-0xD8]实质上就是 input_data 与 res_data 的地址。


print(hex(0x48+0xd84)) # 0x3A8print(hex(0x48+0x484)) # 0x168 则之后就是逐字节比较加密结果和 check 数据是否相同。


总结与解密则加密与 check 流程大致如下。


len(input) == 64every_char in '0123456789ABCDEF'data = binascii.a2b_hex(input)xtea_encrypt_xor(data[:4])tea_encrypt_xor(data[4:8])结合一些混淆,以及实现自定义结构体的相关代码(计算有效字节数,结构体对应的相关运算),使得代码量比较大,增大难度。


最后写出逆向解密脚本。


def xtea_xor_decipher(value, key):    v0, v1 = value[0], value[1]    v0 ^= 0xFD    v1 ^= 0x1FD    delta = 0x70C88617    su = 0xE6EF3D20    for i in range(32):        v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (su + key[(su >> 11) & 3])        v1 &= 0xffff_ffff        su = (su + delta) & 0xffff_ffff        v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (su + key[su & 3])        v0 &= 0xffff_ffff    value[0] = v0    value[1] = v1
def tea_xor_decipher(value, key): v0, v1 = value[0], value[1] k0, k1, k2, k3 = key[0], key[1], key[2], key[3] v0 ^= 0x3FD v1 ^= 0x7FD delta = 0x1E3F4AEF^0x230A6353 su = (32*delta)&0xffff_ffff for i in range(32): v1 -= ((v0<<4) + k2) ^ ((v0>>5) + k3) ^ (v0 + su) v1 &= 0xffff_ffff v0 -= ((v1<<4) + k0) ^ ((v1>>5) + k1) ^ (v1 + su) v0 &= 0xffff_ffff su = su - delta value[0] = v0 value[1] = v1
res = [0x1F306772, 0xB75B0C29, 0x4A7CDBE3, 0x2877BDDF, 0x1354C485 ,0x357C3C3A, 0x738AF06C, 0x89B7F537]key = [0x0FFFD,0x1FFFD,0x3FFFD,0x7FFFD]flag = ''
for i in range(0, len(res)//2, 2): tmp = [res[i], res[i + 1]] xtea_xor_decipher(tmp, key) res[i], res[i + 1] = tmp[0], tmp[1] flag += hex(res[i])[2:] + hex(res[i+1])[2:]
for i in range(len(res)//2, len(res), 2): tmp = [res[i], res[i + 1]] tea_xor_decipher(tmp, key) res[i], res[i + 1] = tmp[0], tmp[1] flag += hex(res[i])[2:] + hex(res[i+1])[2:]
print(flag.upper())# CD402B6A139283822F0DEA49E65794356F44EA9B3F56652F2DA39881EC491878
复制代码


运行验证 flag 正确。



需要网络安全学习视频/工具包/渗透测试/应急响应/漏洞等架构资料的点我【领取资料】

用户头像

我是一名网络安全渗透师 2021.06.18 加入

关注我,后续将会带来更多精选作品,需要资料+wx:mengmengji08

评论

发布
暂无评论
强强联手:2021强网杯LongTimeAgo复盘分析