将文件拖到IDA中,首先查看函数列表
准备分析
- 只有start等几个命名函数,其他都是匿名函数。说明这是一个静态链接文件,并且经过strip。
- 我们需要找到关键的几个函数进行分析,忽略其他libc函数。
- 有.init_array段
简单分析
- 从start函数,我们可以快速定位
main
函数,因为main
函数地址作为参数传入_libc_start_main
函数
该文件的start函数:
其他非静态链接的start函数:
通过对比,我们确认 sub_400D45
是main函数
- 分析main函数
main函数逻辑比较简单,首先读取flag,经过两个函数判断,均返回1则成功。 - 查看400BAC函数
这边有很奇怪的跳转,猜测是junk code。
因为两个跳转都是跳转到400BC8+1,因此400BC8处将不会被执行(但是反汇编器还是将其视为代码,因此产生了一个奇怪的call指令)。将400BC8地址字节填充NOP,函数汇编代码正常,重新创建函数。
反编译之后:
该函数对地址400AA6
进行写操作,400AA6
处在main函数中被调用,因此400AA6
是self-modified code(SMC)。
SMC 分析和代码复原。
- 查看400AA6函数
查看400AA6
地址的引用,除了刚刚的400BAC
,还有400C48
函数。
- 分析
400C48
函数
查看函数汇编代码,发现junk code:
使用上述同样手段修复,即400CD9
处填充NOP,令IDA重新分析函数代码(即先undefine->再code)。
代码对400AA6
处进行了异或和写的操作。那么该函数什么时候执行的呢? 我们查看对该函数的引用:
可以看到该函数在 .init_array段,在该段的函数,于main函数执行之前就会被调用执行。
复原
总结来讲,400AA6
处代码被两个函数进行了改写,分别是0x400C48 和 0x400BAC。其中0x400C48函数操作是确定的,即使用400AA6开头的数据和41E1B0开头数据进行异或操作。随后的改写操作由函数0x400BAC完成。但是在0x400BAC中,使用了用户输入中的7个字节进行异或操作。这7个字节是未知量,待恢复的函数内容也是未知量,如何解决这个问题呢?
考虑到函数的开头(prologue)都是一样的,即
push rbp
mov rbp, rsp
sub rsp, xxh
这几条指令由固定的7个字节组成:55 48 89 E5 48 83 EC
。因此,可以通过函数的prologue倒推用户输入。
有了思路之后,通过IDA Python脚本来进行400AA6的函数恢复。
- 首先推测用户输入的7个字节:
import idc
func_addr = 0x400aa6
# 首先执行函数400c48的异或操作
i = 0
while idc.get_db_byte(func_addr+i) != 0xc3:
b1 = idc.get_db_byte(func_addr+i)
b2 = idc.get_db_byte(0x41E1B0 + i)
idc.patch_byte(func_addr+i, b1^b2)
i+=1
# 使用 prologue 倒推用户输入
prolog = [0x55,0x48,0x89, 0xE5,0x48,0x83,0xEC]
input_bytes = []
for i in range(7):
b1 = idc.get_db_byte(func_addr + i)
input_bytes.append(b1^prolog[i])
print([chr(x) for x in input_bytes])
print(input_bytes)
输出:
['F', '1', '@', 'g', 'C', 'h', 'e']
[70, 49, 64, 103, 67, 104, 101]
- 使用推测出的7个字节继续恢复函数0x400aa6
import idc
func_addr = 0x400aa6
inp = [70, 49, 64, 103, 67, 104, 101]
i = 0
while idc.get_db_byte(func_addr + i) != 0xc3:
idc.patch_byte(func_addr + i, idc.get_db_byte(func_addr + i) ^ inp[i%7])
i += 1
恢复之后的函数:
输入求解
x = [102, 10, 7, 11, 29, 8, 81, 56, 31, 92, 20, 56, 48, 10, 26, 40, 57, 89, 12, 36, 36, 34, 1, 31, 30, 115, 29, 58, 8, 5, 21, 0x0a]
def foo(s, n):
print(n, s)
if (n==32):
return
for i in range(n):
s[n+i] ^= s[i]
foo(s, n<<1)
foo(x, 1)
print("".join([chr(i) for i in x]))
参考文章
https://www.secpulse.com/archives/75049.html