unicorn的hook——hxp CTF 2017 Fibonacci
hxp CTF 2017 Fibonacci

先逆向分析一下这个代码,我们发现这个实际上就是通过一些计算来一个字符一个字符地输出一个字符串,很明显这个字符串就是我们的flag,然后我们运行一下

我们发现这个运行了半天才出来了五个字符,然后根据主函数的循环是线性的,每次循环都会计算出来一个字符,我们就可以确定时间复杂度多就多在fibonacci这个函数里,我们进去查看一下

一大串的代码属实令人头大,但是我们分析的是时间复杂度,所以我们不需要知道那些变量的计算,我们只需要注意递归的地方就可以了,然后我们发现这个实际上就是斐波那切数列的递归(根据第10行到第18行),所以时间需要才会越来越大,但是我们是可以优化的,我们发现这个函数是有两个参数的,也就是说两个参数是可以确定一个返回值的,也就是我们把计算过的全都记录下来,然后当我们遇到后就可以不用重复计算了,所以我们接下来就是把这个程序重新打一遍,然后加一个map记录一下就行了,很明显这个工程量还是比较大的,这时候我们就可以用到unicorn了,在之前关于unicorn的学习,我们知道unicorn可以模拟cpu运算,而且可以修改cpu寄存器的值,所以我们就完全可以用unicorn直接来边运行边修改
from unicorn import*
from pwn import*
from unicorn.x86_const import*
#上面这些是头文件
print_list=[0x400560,0x400575]
def hook_code(mu,address,size,user_data):
#这个有四个参数,第一个就是Uc类,第二个是这个指令的地址,第三个是指令的长度,第四个是用户参数,这个和我们下面的hook_add有关
print('>>> Tracing instruction at 0x%x, instruction size = 0x%x' %(address, size))
if address in print_list:
c=mu.reg_read(UC_X86_REG_RDI)
print chr(c),
mu.reg_write(UC_X86_REG_RIP,address+size)
mu=Uc(UC_ARCH_X86,UC_MODE_64)
ADDRESS=0x400000
#这个程序地址的加载位置个人建议和程序的地址一样,否则的话一些寄存器的内容会有变化,就是ida的Imagebase下面会有图详细说
STACKRESS=0x100000
#这个栈地址就随便找个地方了,我找的是0-0x100000左闭右开
mu.mem_map(ADDRESS,1024*1024)
mu.mem_map(0,STACKRESS)
#我们从0的基地址开始加载长度为0x100000,所以才是左闭右开的区间
mu.mem_write(ADDRESS,open('./fibonacci').read())
mu.reg_write(UC_X86_REG_RSP,STACKRESS-1)
#因为左闭右开所以我们应该把rsp设为0x100000-1
mu.hook_add(UC_HOOK_CODE,hook_code)
#这个表示的是添加hook_code,这种类型的hook在执行每条指令前都会先执行hook_code
#第一个是常数定义,什么类型的hook,这个下面说,第二个参数就是对应hook的函数名,第三个参数是上面提到的用户数据,这个可有可无,如果不写那么上面的用户数据就是空
mu.emu_start(0x4004e0,0x400583)

无论是从Imagebase还是左边的程序地址我们都可以发现我们对应的程序地址应该是0x400000
这样子我们就可以跑起来了,至于有话的话就直接放代码了,只要把一些对应的寄存器内容搞好来就行了
from unicorn import*
from pwn import*
from unicorn.x86_const import*
key_list={}
stack=[]
skip_list=[0x4004ef,0x4004f6,0x400502,0x40054f]
print_list=[0x400560,0x400575]
def hook_code(mu,address,size,user_data):
#print('>>> Tracing instruction at 0x%x, instruction size = 0x%x' %(address, size))
if address in skip_list:
#这个是要跳过一些指令,比如说调用了bss段内存的指令还有用libc库函数的指令,因为我们只有模拟cpu,这些东西都是没有模拟的,所以用到这些的都需要跳过
mu.reg_write(UC_X86_REG_RIP,address+size)
#把rip设置为下个指令就可以了
if address in print_list:
c=mu.reg_read(UC_X86_REG_RDI)
print chr(c),
mu.reg_write(UC_X86_REG_RIP,address+size)
if address==0x400670:
arg_0=mu.reg_read(UC_X86_REG_RDI)
esi_r=mu.reg_read(UC_X86_REG_RSI)
arg_1=u32(mu.mem_read(esi_r,4))
if(arg_0,arg_1) in key_list:
(ret_rax,ret_bool)=key_list[(arg_0,arg_1)]
mu.reg_write(UC_X86_REG_RAX,ret_rax)
mu.mem_write(esi_r,p32(ret_bool))
mu.reg_write(UC_X86_REG_RIP,0x400582)
else:
stack.append((arg_0,arg_1,esi_r))
if address in [0x4006f1,0x400709]:
ret_rax=mu.reg_read(UC_X86_REG_RAX)
(arg0,arg1,r_rsi)=stack.pop()
ret_bool=u32(mu.mem_read(r_rsi,4))
key_list[(arg0,arg1)]=(ret_rax,ret_bool)
mu=Uc(UC_ARCH_X86,UC_MODE_64)
ADDRESS=0x400000
STACKRESS=0x100000
mu.mem_map(ADDRESS,1024*1024)
mu.mem_map(0,STACKRESS)
mu.mem_write(ADDRESS,open('./fibonacci').read())
mu.reg_write(UC_X86_REG_RSP,STACKRESS-1)
mu.hook_add(UC_HOOK_CODE,hook_code)
mu.emu_start(0x4004e0,0x400583)
几种hook
hook_code
当起始地址<终止地址,在起始地址到终止地址的每一条指令执行之前都会调用一遍hook_code函数,当起始地址>终止地址,整个程序的所有指令执行之前都会调用一遍hook_code函数
def hook_code(um,address,size,user_data):
print('>>> Tracing instruction at 0x%x, instruction size = 0x%x' %(address, size))
mu,hook_add(UC_HOOK_CODE,hook_code)
hook_block
首先我们要了解一个概念:基本块
基本块:未发生跳转的所有指令组成的集合
基本块地址:基本块第一条指令的地址
当起始地址<终止地址,基本块地址在起始地址到终止地址的每个基本块执行之前都会调用一遍hook_code函数,当起始地址>终止地址,整个程序的所有基本块执行之前都会调用一遍hook_code函数
def hook_block(mu,address,size,user_data):
print('>>> Tracing instruction at 0x%x, instruction size = 0x%x' %(address, size))
mu.hook_add(UC_HOOK_BLOCK,hook_block)
#和上面的hook_code相比,就是hook_add的第一个参数要改一下
hook类型表
// All type of hooks for uc_hook_add() API.
typedef enum uc_hook_type {
// Hook all interrupt/syscall events
UC_HOOK_INTR = 1 << 0,
// Hook a particular instruction - only a very small subset of instructions supported here
UC_HOOK_INSN = 1 << 1,
// Hook a range of code
UC_HOOK_CODE = 1 << 2,
// Hook basic blocks
UC_HOOK_BLOCK = 1 << 3,
// Hook for memory read on unmapped memory
UC_HOOK_MEM_READ_UNMAPPED = 1 << 4,
// Hook for invalid memory write events
UC_HOOK_MEM_WRITE_UNMAPPED = 1 << 5,
// Hook for invalid memory fetch for execution events
UC_HOOK_MEM_FETCH_UNMAPPED = 1 << 6,
// Hook for memory read on read-protected memory
UC_HOOK_MEM_READ_PROT = 1 << 7,
// Hook for memory write on write-protected memory
UC_HOOK_MEM_WRITE_PROT = 1 << 8,
// Hook for memory fetch on non-executable memory
UC_HOOK_MEM_FETCH_PROT = 1 << 9,
// Hook memory read events.
UC_HOOK_MEM_READ = 1 << 10,
// Hook memory write events.
UC_HOOK_MEM_WRITE = 1 << 11,
// Hook memory fetch for execution events
UC_HOOK_MEM_FETCH = 1 << 12,
// Hook memory read events, but only successful access.
// The callback will be triggered after successful read.
UC_HOOK_MEM_READ_AFTER = 1 << 13,
} uc_hook_type;

浙公网安备 33010602011771号