当时用angr和unicorn跑脚本都有点问题,就直接ida下断点去追踪了。
用angr模块打印出来所有的真实调用块,然后在ida用idc脚本下断点,ida tracing跟踪,会记录每个断点执行过的地址,如下图:

然后处理下这些地址,生成一个数组,直接用脚本patch。
处理方式就是脚本把地址减去基地址,10414C0D8块 跳到104152DF8块 104152DF8块跳到104150F18块
contentlist = list(content.split('\t'))
#print(contentlist)
for x in range(len(contentlist) - 1) :
if len(contentlist[x+1] ) > 1:
beforeaddr = int(contentlist[x],16) - 0x130000
nextaddr = int(contentlist[x+1],16) - 0x130000
if flow[beforeaddr] != None:
if nextaddr not in flow[beforeaddr]:
flow[beforeaddr].append(nextaddr)
else:
flow[beforeaddr].append(nextaddr)
# print('flow:%s' % flow)
#print('%s' % flow)
print('=======flow:%d=====' % len(flow))
for k, v in flow.items():
print('%#x: ' % k, [hex(child) for child in v])

遍历地址,保存cset的跳转条件
flow = symbol_debug()
print('flow:%d' % len(flow))
print('************************flow******************************')
for k, v in flow.items():
print('%#x: ' % k, [hex(child) for child in v])
# state_list = {}
for parent, childs in flow.items():
if len(childs) > 1:
print('parent11:%s' % parent)
print('childs11:%s, %d' % (childs, len(childs)))
relevant = None
for relevant_node in relevants_without_retn:
if relevant_node.addr == parent:
relevant = relevant_node
block = project.factory.block(parent, size=relevant.size)
for ins in block.capstone.insns:
if ins.insn.mnemonic.startswith('csel') or ins.insn.mnemonic.startswith('cset'):
if relevant not in patch_instrs:
patch_instrs[relevant] = ins
print('patch_instrs[relevant]=11=:%s' % relevant)
#cset在block里面没有的话,就在next block里找
if relevant not in patch_instrs:
print('patch_instrs[relevant]==none:%x' % relevant.addr)
if len(list(supergraph.successors(relevant))) == 1:
successorsNode = list(supergraph.successors(relevant))[0]
block = project.factory.block(successorsNode.addr, size=successorsNode.size)
for ins in block.capstone.insns:
if ins.insn.mnemonic.startswith('csel') or ins.insn.mnemonic.startswith('cset'):
if relevant not in patch_instrs:
print('patch_instrs[relevant]=22=:%s' % relevant)
patch_instrs[relevant] = ins
if relevant not in patch_instrs:
print('has_branches but not csel:%x' % relevant.addr)
print('patch_instrs:%s' % patch_instrs)
然后就直接修改跳转地址
print('************************patch*****************************')
with open(filename, 'rb') as origin:
# Attention: can't transform to str by calling decode() directly. so use bytearray instead.
origin_data = bytearray(origin.read())
origin_data_len = len(origin_data)
recovery_file = filename + '_recovered'
if nopCode == 1:
recovery_file = filename + '_recovered_nop'
recovery = open(recovery_file, 'wb')
# patch irrelevant blocks
if nopCode == 1:
print('nop_nodes:%d' % len(nop_nodes))
print('nop_nodes11:%s' % nop_nodes)
for nop_node in nop_nodes:
fill_nop(origin_data, nop_node.addr-base_addr,nop_node.size, project.arch)
# remove unnecessary control flows
for parent, childs in flow.items():
relevant = None
for relevant_node in relevants_without_retn:
if relevant_node.addr == parent:
relevant = relevant_node
if len(childs) == 1:
print('parent22:%s' % hex(int(parent)))
print('childs22:%s, %d' % (hex(int(childs[0])), len(childs)))
parent_block = project.factory.block(relevant.addr, size=relevant.size)
last_instr = parent_block.capstone.insns[-1]
file_offset = last_instr.address - base_addr
# patch the last instruction to jmp
if project.arch.name in ARCH_ARM64:
# FIXME: For aarch64/arm64, the last instruction of prologue seems useful in some cases, so patch the next instruction instead.
if relevant.addr == start:
# file_offset += 4
patch_value = ins_b_jmp_hex_arm64(last_instr.address, childs[0], 'b')
patch_value = asm_no_branch(last_instr.address, childs[0])
else:
patch_value = ins_b_jmp_hex_arm64(last_instr.address, childs[0], 'b')
patch_value = asm_no_branch(last_instr.address, childs[0])
if project.arch.memory_endness == "Iend_BE":
patch_value = patch_value[::-1]
patch_instruction(origin_data, file_offset, patch_value)
else:
# print('patch_instrs222:%s' % patch_instrs)
print('parent33:%s' % hex(int(parent)))
print('childs33:%s,%s %d' % (hex(int(childs[0])), hex(int(childs[1])), len(childs)))
if relevant in patch_instrs:
instr = patch_instrs[relevant]
# print('patch_instrs111222:%s' % instr)
parent_block = project.factory.block(relevant.addr, size=relevant.size)
last_instr = parent_block.capstone.insns[-2]
file_offset = last_instr.address - base_addr
# patch instructions starting from `cmovx` to the end of block
fill_nop(origin_data, file_offset, relevant.addr +
relevant.size - base_addr - file_offset, project.arch)
if project.arch.name in ARCH_ARM64:
# patch the cset.xx instruction to bx instruction
bx_cond = instr.op_str.split(',')[-1].strip()
patch_value = ins_b_jmp_hex_arm64(last_instr.address, childs[0], bx_cond.lower())
# patch_value = asm_has_branch(instr.address, childs[0], bx_cond.lower())
if project.arch.memory_endness == 'Iend_BE':
patch_value = patch_value[::-1]
patch_instruction(origin_data, file_offset, patch_value)
file_offset += 4
# patch the next instruction to b instruction
# print('childs[1]:%x' % childs[1])
patch_value = ins_b_jmp_hex_arm64(last_instr.address+4, childs[1], 'b')
# patch_value = asm_no_branch(instr.address+4, childs[1])
print('patch_value11:%s'%patch_value)
if project.arch.memory_endness == 'Iend_BE':
patch_value = patch_value[::-1]
patch_instruction(origin_data, file_offset, patch_value)
assert len(origin_data) == origin_data_len, "Error: size of data changed!!!"
recovery.write(origin_data)
recovery.close()
print('Successful! The recovered file: %s' % recovery_file)
还原后,直接f5


还原前:

ps:
以上还原还要修复条件跳转的,可以用符号执行判断,后面没时间我就没弄了
因为没有具体还原出算法,也只是一种还原思路而已,可能这个还原还是有问题的,望赐教。
[公告] 2021 KCTF 春季赛 防守方征题火热进行中!
最后于 2021-2-14 00:18
被花少年编辑
,原因: