利用簡單的unicron去除ollvm控制流程平坦化

一.腳本如下

#-*- coding=utf-8 -*-
from unicorn import *
from unicorn import arm_const
from unicorn.arm_const import *
from capstone import *
from capstone.arm import *
from keystone import *
def hook_code(uc, address, size, user_data):
	global FindFlag
	global Finding
	global JMP_PC
	ban_ins = ["bl"]
	#print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size))
	
	for ins in md.disasm(bin1[address:address+size],address):
		print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size))
		print(">>> 0x%x:\t%s\t%s" % (ins.address, ins.mnemonic, ins.op_str))
		print("")

		if FindFlag:
			if JMP_PC[Finding] == 0:
				print("jmp is %x"%(ins.address))
				JMP_PC[Finding]=ins.address
				Finding=0
				FindFlag=0
			else:
				input('Something is error')

		flag_pass = False
		for b in ban_ins:
			if ins.mnemonic.find(b) != -1:
				flag_pass = True
				break
		if ins.op_str=='#0x3f4':#this is jmp 0x3f4 0x3f4 is switch case
			print("jmp switch")
			Finding=ins.address
			JMP_PC[Finding]=0
		if ins.address==0x3F0:#first blood
			print("jmp switch")
			Finding=ins.address
			JMP_PC[Finding] = 0
		if Finding and ins.address==0x414:
			print("ready to jmp")
			FindFlag=1

	if flag_pass:
		print("will pass 0x%x:\t%s\t%s" % (ins.address, ins.mnemonic, ins.op_str))
		uc.reg_write(UC_ARM_REG_PC, address + size)
	return
def hook_mem_access(uc,type,address,size,value,userdata):
	pc = uc.reg_read(UC_ARM_REG_PC)
	print('pc:%x type:%d addr:%x size:%x' % (pc,type,address,size))
	#uc.emu_stop()
	return False
def hook_error(uc,type,address,size,value,userdata):
	pc = uc.reg_read(UC_ARM_REG_PC)
	print('pc:%x type:%d addr:%x size:%x' % (pc,type,address,size))
	#uc.emu_stop()
	return False
FindFlag=0
Finding=0
JMP_PC={}
md = Cs(CS_ARCH_ARM,CS_MODE_ARM)
md.detail = True #enable detail analyise
offset = 0x3D4 #function start
end = 0x600	#function end
f=open('C:/Users/wei/Desktop/KanXueCTF/unicorn/test_arm','rb')
bin1 = f.read()
f.close()
mu = Uc(UC_ARCH_ARM, UC_MODE_ARM)
#init stack
mu.mem_map(0x80000000,0x10000 * 8)

mu.mem_map(0, 4 * 1024 * 1024)
mu.mem_write(0,bin1)
mu.reg_write(UC_ARM_REG_SP, 0x80000000 + 0x10000 * 6)

mu.reg_write(UC_ARM_REG_LR, end)#retn addr

mu.hook_add(UC_HOOK_CODE, hook_code)
mu.hook_add(UC_HOOK_MEM_UNMAPPED,hook_mem_access)
#mu.hook_add(UC_ERR_HOOK,hook_error)
mu.emu_start(offset,end)
print(JMP_PC)

ks = Ks(KS_ARCH_ARM, KS_MODE_ARM)

patch_file=open('C:/Users/wei/Desktop/KanXueCTF/unicorn/patch_test_arm','wb')
bin_w=bytearray(bin1)
for key,value in JMP_PC.items():
	jmp_addr=value
	addr=key
	print('{key}:{value}'.format(key=hex(addr), value=hex(jmp_addr)))
	r=ks.asm('b 0x%x'%(jmp_addr),addr)[0]
	print(list(map(hex,r)))
	for i in range(len(r)):
		bin_w[addr+i]=r[i]
patch_file.write(bin_w)
patch_file.close()

下面重點講一下腳本的作用

核心就是,記錄一下進入switch之前的分支和switch之後的分支

像那些bl,blx之類的都跳過

二.代碼註釋

def hook_code(uc, address, size, user_data):
	global FindFlag #是否查找完畢
	global Finding  #是否正在查找
	global JMP_PC  #記錄跳前和跳後的
	ban_ins = ["bl"]
	#print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size))
	
	for ins in md.disasm(bin1[address:address+size],address):
		print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size))
		print(">>> 0x%x:\t%s\t%s" % (ins.address, ins.mnemonic, ins.op_str))
		print("")

		if FindFlag:                    #發現結束的標誌
			if JMP_PC[Finding] == 0:
				print("jmp is %x"%(ins.address))
				JMP_PC[Finding]=ins.address
				Finding=0
				FindFlag=0
			else:
				input('Something is error')

		flag_pass = False                  #這一段是跳過 bl blr
		for b in ban_ins:
			if ins.mnemonic.find(b) != -1:
				flag_pass = True
				break

		if ins.op_str=='#0x3f4':#這一段是 跳入swtich
			print("jmp switch")
			Finding=ins.address #finding 不爲0 開始了
			JMP_PC[Finding]=0

		if ins.address==0x3F0:#這是第一次進入分支但是不是用的 b
			print("jmp switch")
			Finding=ins.address
			JMP_PC[Finding] = 0
		if Finding and ins.address==0x414: #結束查找了
			print("ready to jmp")
			FindFlag=1                 #結束的標誌 下一條指令就是了

	if flag_pass:
		print("will pass 0x%x:\t%s\t%s" % (ins.address, ins.mnemonic, ins.op_str))
		uc.reg_write(UC_ARM_REG_PC, address + size)
	return

這個使用具體實例patch的程序來自於

https://github.com/0x1shyboy1/uncorn-patch-easyollvm/blob/master/test_arm

patch前後代碼對比

patch後

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章