这道题有壳,strings搜索是upx3.91。但是不能用upx -d。只好手动脱壳了。
难点在于本题目有反调试,有些函数不能步过。
首先研究壳。
第一句call必须步入。
Sub_44f1c5必须步入。
Sub_44F1C5中,
0x44f214的rep指令把0x400000到0x44f248复制到0x800000位置。
0x44f22c处,rbp是0x84efb5。这句话是start处的第二条指令。这里不必步入。如果步入,
在44EFCB处的call sub_44F020,再步过就会卡死(不知道为什么,奇怪!如果卡死,按ctrl+c可以只是终止而不退出gdb);步入之后可以使用finish命令运行到函数外,即0x44F22E处pop rcx。
在0x44f22f retn之后,到达0x84f248: call 0x84f2ac。
0x84f2ac是一开始没有复制的地址,可以考虑是0x84efb5的代码形成的新代码。此处内存开辟是在系统调用mmap处,刚开辟后此处全为0.
此时可以dump出来0x800000处的内存,见dump2.(我修改了ep,va,filesize)
接着运行到0x84f31c: call 0x84f782,可以步过。
在0x84f339: jmp QWORD PTR [r15],跳转到0x40000c
此时可以dump处0x400000处的内存,见文件bindump.so。这就是脱壳后的文件了,但是不能运行,不清楚原因。可以看到ep是0x400890。
0x40000c: syscall(调用号11,sys_munmap,解除内存映射)
0x40000e: ret
上面两处是elf头部的padding中存储的,感叹做的壳之精准。
ret之后到达:
0x400890: xor ebp,ebp
下一部分,分析主逻辑。
发现Enter the flag出现了两次。
按照程序执行过程只会执行上面一个。
从流程图上可以看到左枝非正常退出,猜测和反调试有关,进入43f380函数,看到调用了ptrace的系统调用,确实是反调试。由于没有脱壳成功,这里没办法修改判断条件,只好用gdb脚本修改eflags寄存器了。
接着分析0x4009AE函数:
这个时候就应该认识到这是错误的分支了,毕竟告诉你是错的flag了。但是我继续分析了,
具体函数还没有写入。根据动态调试,最后比较字符串的函数是42D820.
仔细分析逻辑,需要保证输入地址的64字节和目标地址的64字节相等,而只能输入32字节,根本不能实现。
废了很久时间之后,想到了另一处Enter the flag。
这里并未被ida识别成函数,0x4c8ef4处是我标记的函数,从这里标记可以使函数能使用f5.由于原程序并不会执行到此处,需要gdb脚本中修改pc值。
下面是可以快速进入关键位置的gdb脚本。
#!/bin/bash
file hide
b *0x44f22f
r
si
si
d
b *0x84f339
#jmp QWORD PTR [r15];0x40000c
c
d
si
si
si
b* 0x4009EF
c
set $rip=0x4C8EBC
b *0x4c8f11
c
set $eflags = $eflags |(1<<6)
5
b *0x4C8D09
c
c
d
b *0x4C8DFB
c
下面是本题目的exp
#coding=utf-8
import struct
import string
def u32(data):
return struct.unpack("> 5
edx = v30 ^ v2c
v30 = (v3 + edx) & 0xffffffff
v28 = (v4_4_arr[i+1] >> 11) & 3
edx = u32(CONST_STR[v28 * 4:(v28 + 1) * 4])
v2c = (v4_4_arr[i+1] + edx) & 0xffffffff # xxxx
# print 'v2c',hex(v2c)
print v30 ^ v2c
v4 = (v4+0x100000000-(v30 ^ v2c)) & 0xffffffff
v30 = (v4 << 4) & 0xffffffff
v2c = v4 >> 5
edx = v30 ^ v2c
v30 = (v4 + edx) & 0xffffffff
v28 = v4_4_arr[i] & 3
edx = u32(CONST_STR[v28 * 4:(v28 + 1) * 4])
v2c = (v4_4_arr[i] + edx) & 0xffffffff
v3 = (v3+0x100000000-(v30 ^ v2c)) & 0xffffffff
print 'round',i,v3,v4
byte_arr_8[0:4] = p32(v3)
byte_arr_8[4:8] = p32(v4)
return byte_arr_8
def xor16(byte_arr_16):
for i in range(0,16):
byte_arr_16[i]^=i
def re_all(str16):
byte_arr = bytearray(str16)
xor16(byte_arr)#传入整个bytearray,就是传入地址
byte_arr[0:8] = re_block(byte_arr[0:8])#传入部分bytearray,就是复制之后再传入
byte_arr[8:16] = re_block(byte_arr[8:16])
xor16(byte_arr)
byte_arr[0:8] = re_block(byte_arr[0:8])
byte_arr[8:16] = re_block(byte_arr[8:16])
xor16(byte_arr)
byte_arr[0:8] = re_block(byte_arr[0:8])
byte_arr[8:16] = re_block(byte_arr[8:16])
return str(byte_arr)
def block(str8):
i=0
input1=bytearray(str8)
v3 = u32(input1[8 * i:8 * i + 4])
v4 = u32(input1[8 * i + 4:8 * (i + 1)])
v4_4 = 0 ##0000
for j in range(0, 8):
v30 = (v4 << 4) & 0xffffffff
v2c = v4 >> 5
edx = v30 ^ v2c
v30 = (v4 + edx) & 0xffffffff
v28 = v4_4 & 3
edx = u32(CONST_STR[v28 * 4:(v28 + 1) * 4])
v2c = (v4_4 + edx) & 0xffffffff
v3 = ((v30 ^ v2c) + v3) & 0xffffffff
v4_4 = (v4_4 + CONST) & 0xffffffff
v30 = (v3 << 4) & 0xffffffff
v2c = v3 >> 5
edx = v30 ^ v2c
v30 = (v3 + edx) & 0xffffffff
v28 = (v4_4 >> 11) & 3
edx = u32(CONST_STR[v28 * 4:(v28 + 1) * 4])
v2c = (v4_4 + edx) & 0xffffffff # xxxx
print v30 ^ v2c
v4 = ((v30 ^ v2c) + v4) & 0xffffffff
print 'round', j, v3, v4
input1[8 * i:8 * i + 4] = p32(v3)
input1[8 * i + 4:8 * (i + 1)] = p32(v4)
str8_1=str(input1)
return str8_1
def block2(str8):
input1 = bytearray(str8)
for i in range(0,8):
input1[i]=input1[1]^i
return str(input1)
des = ('52B8137F358CF21B'+'F46386D2734F1E31').decode('hex')
print len(des)
print block('12345678').encode('hex')
des1 = '5b90ef3f91b58fe6'.decode('hex')
print re_all(bytearray(des))