每日总结-第五十三天-Unicorn

参考链接

https://bbs.pediy.com/thread-224330.htm
http://eternal.red/2018/unicorn-engine-tutorial/#cheatsheet
https://ctf-wiki.github.io/ctf-wiki/reverse/unicorn/introduction-zh/
这里有很多代码示例:
https://github.com/unicorn-engine/unicorn/tree/master/bindings/python

介绍

  • Unocorn引擎是什么?

    简单的来讲,一款模拟器。尽管不太常见,你不能用来模拟整个程序或者系统,同时它也不支持syscall。你只能通过手动的方式来映射内存以及数据写入,然后就可以从某个指定的地址开始执行模拟了。

  • 模拟器在什么时候是有用的?

    • 你可以执行一些恶意软件中你感兴趣的函数而不必创建整个进程
    • CTF比赛中也很常用
    • Fuzzing
    • GDB插件扩充,例如支持长跳转
    • 模拟混淆后的代码

备忘

from unicorn import * - 加载Unicorn库。包含一些函数和基本的常量。
from unicorn.x86_const import* - 加载 X86 和X64架构相关的常量

所有unicorn模块中的常量

UC_API_MAJOR                UC_ERR_VERSION              UC_MEM_READ                 UC_PROT_ALL
UC_API_MINOR                UC_ERR_WRITE_PROT           UC_MEM_READ_AFTER           UC_PROT_EXEC
UC_ARCH_ARM                 UC_ERR_WRITE_UNALIGNED      UC_MEM_READ_PROT            UC_PROT_NONE
UC_ARCH_ARM64               UC_ERR_WRITE_UNMAPPED       UC_MEM_READ_UNMAPPED        UC_PROT_READ
UC_ARCH_M68K                UC_HOOK_BLOCK               UC_MEM_WRITE                UC_PROT_WRITE
UC_ARCH_MAX                 UC_HOOK_CODE                UC_MEM_WRITE_PROT           UC_QUERY_MODE
UC_ARCH_MIPS                UC_HOOK_INSN                UC_MEM_WRITE_UNMAPPED       UC_QUERY_PAGE_SIZE
UC_ARCH_PPC                 UC_HOOK_INTR                UC_MILISECOND_SCALE         UC_SECOND_SCALE
UC_ARCH_SPARC               UC_HOOK_MEM_FETCH           UC_MODE_16                  UC_VERSION_EXTRA
UC_ARCH_X86                 UC_HOOK_MEM_FETCH_INVALID   UC_MODE_32                  UC_VERSION_MAJOR
UC_ERR_ARCH                 UC_HOOK_MEM_FETCH_PROT      UC_MODE_64                  UC_VERSION_MINOR
UC_ERR_ARG                  UC_HOOK_MEM_FETCH_UNMAPPED  UC_MODE_ARM                 Uc
UC_ERR_EXCEPTION            UC_HOOK_MEM_INVALID         UC_MODE_BIG_ENDIAN          UcError
UC_ERR_FETCH_PROT           UC_HOOK_MEM_PROT            UC_MODE_LITTLE_ENDIAN       arm64_const
UC_ERR_FETCH_UNALIGNED      UC_HOOK_MEM_READ            UC_MODE_MCLASS              arm_const
UC_ERR_FETCH_UNMAPPED       UC_HOOK_MEM_READ_AFTER      UC_MODE_MICRO               debug
UC_ERR_HANDLE               UC_HOOK_MEM_READ_INVALID    UC_MODE_MIPS3               m68k_const
UC_ERR_HOOK                 UC_HOOK_MEM_READ_PROT       UC_MODE_MIPS32              mips_const
UC_ERR_HOOK_EXIST           UC_HOOK_MEM_READ_UNMAPPED   UC_MODE_MIPS32R6            sparc_const
UC_ERR_INSN_INVALID         UC_HOOK_MEM_UNMAPPED        UC_MODE_MIPS64              uc_arch_supported
UC_ERR_MAP                  UC_HOOK_MEM_VALID           UC_MODE_PPC32               uc_version
UC_ERR_MODE                 UC_HOOK_MEM_WRITE           UC_MODE_PPC64               unicorn
UC_ERR_NOMEM                UC_HOOK_MEM_WRITE_INVALID   UC_MODE_QPX                 unicorn_const
UC_ERR_OK                   UC_HOOK_MEM_WRITE_PROT      UC_MODE_SPARC32             version_bind
UC_ERR_READ_PROT            UC_HOOK_MEM_WRITE_UNMAPPED  UC_MODE_SPARC64             x86_const
UC_ERR_READ_UNALIGNED       UC_MEM_FETCH                UC_MODE_THUMB              
UC_ERR_READ_UNMAPPED        UC_MEM_FETCH_PROT           UC_MODE_V8                 
UC_ERR_RESOURCE             UC_MEM_FETCH_UNMAPPED       UC_MODE_V9

mu = Uc(arch,mode) - 获取Uc实例。在这里指定目标架构,例如:

  • mu = Uc(UC_ARCH_X86,UC_MODE_64) - 获取X86-64架构的实例。
  • mu = Uc(UC_ARCH_X86,UC_MODE_32) - 获取X86-32架构的实例。

mu.mem_map(ADDRESS,4096) - 映射一片内存区域
mu.mem_write(ADDRESS,DATA) - 向内存中写入数据
tmp = mu.mem_read(ADDRESS,SIZE) - 从内存中读取数据
mu.reg_write(UC_X86_REG_ECX,0X0) - 设置ECX值。
r_esp = mu.reg_read(UC_X86_REG_ESP) - 读取ESP的值。
mu.emu_start(ADDRESS_START,ADDRESS_END) - 开始执行模拟。
命令追踪:

def hook_code(mu, 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(向Unicorn引擎中),我们定义的函数会在执行每一条命令之前被执行。参数含义如下:

  • Uc实例
  • 指令的地址
  • 指令的长度
  • 用户定义数据(通过hook_add()函数传递)

第一个例子: fibonacci

#coding=utf-8
from unicorn import *
from unicorn.x86_const import *

import struct

def read(name):
    with open(name, 'rb') as f:
        return f.read()
      
def u32(data):
    return struct.unpack("I", data)[0]

def p32(num):
    return struct.pack("I", num)

# 为x86-64架构初始化一下Unicorn引擎。
mu = Uc (UC_ARCH_X86, UC_MODE_64)
# Uc函数需要一下参数:
# 第一个参数:架构类型。这些常量以UC_ATCH_为前缀
# 第二个参数:架构细节说明。这些常量以UC_MODE_为前缀

BASE = 0x400000
STACK_ADDR = 0x0
STACK_SIZE = 1024*1024
# mem_map: 映射内存
mu.mem_map(BASE, 1024*1024) # 初始化存储空间
mu.mem_map(STACK_ADDR, STACK_SIZE) # 初始化栈空间


mu.mem_write(BASE, read("./fibonacci")) # 加载程序
# rsp指向栈顶
mu.reg_write(UC_X86_REG_RSP, STACK_ADDR + STACK_SIZE - 1)
# 因为库函数没有加载,所以调用库函数的地方需要跳过
instructions_skip_list = [0x00000000004004EF, 0x00000000004004F6, 0x0000000000400502, 0x000000000040054F]

def hook_code(mu, address, size, user_data):  
    #print('>>> Tracing instruction at 0x%x, instruction size = 0x%x' %(address, size))
    
    if address in instructions_skip_list:
        mu.reg_write(UC_X86_REG_RIP, address+size)
    
    elif address == 0x400560: #that instruction writes a byte of the flag
        c = mu.reg_read(UC_X86_REG_RDI)
        print(chr(c))
        mu.reg_write(UC_X86_REG_RIP, address+size)

mu.hook_add(UC_HOOK_CODE, hook_code)

mu.emu_start(0x00000000004004E0, 0x0000000000400575)

第二个例子: 分析shellcode

from unicorn import *
from unicorn.x86_const import *
 
shellcode = "\xe8\xff\xff\xff\xff\xc0\x5d\x6a\x05\x5b\x29\xdd\x83\xc5\x4e\x89\xe9\x6a\x02\x03\x0c\x24\x5b\x31\xd2\x66\xba\x12\x00\x8b\x39\xc1\xe7\x10\xc1\xef\x10\x81\xe9\xfe\xff\xff\xff\x8b\x45\x00\xc1\xe0\x10\xc1\xe8\x10\x89\xc3\x09\xfb\x21\xf8\xf7\xd0\x21\xd8\x66\x89\x45\x00\x83\xc5\x02\x4a\x85\xd2\x0f\x85\xcf\xff\xff\xff\xec\x37\x75\x5d\x7a\x05\x28\xed\x24\xed\x24\xed\x0b\x88\x7f\xeb\x50\x98\x38\xf9\x5c\x96\x2b\x96\x70\xfe\xc6\xff\xc6\xff\x9f\x32\x1f\x58\x1e\x00\xd3\x80"
 
 
BASE = 0x400000
STACK_ADDR = 0x0
STACK_SIZE = 1024*1024
 
mu = Uc (UC_ARCH_X86, UC_MODE_32)
 
mu.mem_map(BASE, 1024*1024)
mu.mem_map(STACK_ADDR, STACK_SIZE)
 
 
mu.mem_write(BASE, shellcode)
mu.reg_write(UC_X86_REG_ESP, STACK_ADDR + STACK_SIZE/2)
 
def syscall_num_to_name(num):
    syscalls = {1: "sys_exit", 15: "sys_chmod"}
    return syscalls[num]
 
def hook_code(mu, address, size, user_data):
    #print('>>> Tracing instruction at 0x%x, instruction size = 0x%x' %(address, size)) 
 
    machine_code = mu.mem_read(address, size)
    if machine_code == "\xcd\x80": # int 80
 
        r_eax = mu.reg_read(UC_X86_REG_EAX)
        r_ebx = mu.reg_read(UC_X86_REG_EBX)
        r_ecx = mu.reg_read(UC_X86_REG_ECX)
        r_edx = mu.reg_read(UC_X86_REG_EDX)
        syscall_name = syscall_num_to_name(r_eax)
 
        print "--------------"
        print "We intercepted system call: "+syscall_name
 
        if syscall_name == "sys_chmod":
            s = mu.mem_read(r_ebx, 20).split("\x00")[0]
            print "arg0 = 0x%x -> %s" % (r_ebx, s)
            print "arg1 = " + oct(r_ecx)
        elif syscall_name == "sys_exit":
            print "arg0 = " + hex(r_ebx)
            exit()
 
        mu.reg_write(UC_X86_REG_EIP, address + size)
 
mu.hook_add(UC_HOOK_CODE, hook_code)
 
mu.emu_start(BASE, BASE-1) # 根据exit命令退出,直接加载整个shellcode

所以不仅可以直接可执行文件中的一段代码,还可以直接执行shellcode

第三个例子

from unicorn import *
from unicorn.x86_const import *
import struct
 
 
def read(name):
    with open(name) as f:
        return f.read()
 
def u32(data):
    return struct.unpack("I", data)[0]
 
def p32(num):
    return struct.pack("I", num)
 
mu = Uc (UC_ARCH_X86, UC_MODE_32)
 
BASE = 0x08048000
STACK_ADDR = 0x0
STACK_SIZE = 1024*1024
 
mu.mem_map(BASE, 1024*1024)
mu.mem_map(STACK_ADDR, STACK_SIZE)
 
 
mu.mem_write(BASE, read("./function"))
r_esp = STACK_ADDR + (STACK_SIZE/2)     #ESP points to this address at function call
 
STRING_ADDR = 0x0
mu.mem_write(STRING_ADDR, "batman\x00") #write "batman" somewhere. We have choosen an address 0x0 which belongs to the stack.
 
mu.reg_write(UC_X86_REG_ESP, r_esp)     #set ESP
mu.mem_write(r_esp+4, p32(5))           #set the first argument. It is integer 5
mu.mem_write(r_esp+8, p32(STRING_ADDR)) #set the second argument. This is a pointer to the string "batman"
 
 
mu.emu_start(0x8048464, 0x804849A)      #start emulation from the beginning of super_function, end at RET instruction
return_value = mu.reg_read(UC_X86_REG_EAX)
print "The returned value is: %d" % return_value

可以通过修改内存来控制函数的调用参数

第四个例子

from unicorn import *
from unicorn.arm_const import *
 
 
import struct
 
def read(name):
    with open(name) as f:
        return f.read()
 
def u32(data):
    return struct.unpack("I", data)[0]
 
def p32(num):
    return struct.pack("I", num)
 
 
mu = Uc (UC_ARCH_ARM, UC_MODE_LITTLE_ENDIAN)
 
 
BASE = 0x10000
STACK_ADDR = 0x300000
STACK_SIZE = 1024*1024
 
mu.mem_map(BASE, 1024*1024)
mu.mem_map(STACK_ADDR, STACK_SIZE)
 
 
mu.mem_write(BASE, read("./task4"))
mu.reg_write(UC_ARM_REG_SP, STACK_ADDR + STACK_SIZE/2)
 
instructions_skip_list = []
 
CCC_ENTRY = 0x000104D0
CCC_END = 0x00010580
 
stack = []                                          # Stack for storing the arguments
d = {}                                              # Dictionary that holds return values for given function arguments
 
def hook_code(mu, address, size, user_data): 
    #print('>>> Tracing instruction at 0x%x, instruction size = 0x%x' %(address, size))
 
    if address == CCC_ENTRY:                        # Are we at the beginning of ccc function?
        arg0 = mu.reg_read(UC_ARM_REG_R0)           # Read the first argument. it is passed by R0
 
        if arg0 in d:                               # Check whether return value for this function is already saved.
            ret = d[arg0]
            mu.reg_write(UC_ARM_REG_R0, ret)        # Set return value in R0
            mu.reg_write(UC_ARM_REG_PC, 0x105BC)    # Set PC to point at "BX LR" instruction. We want to return from fibonacci function
 
        else:
            stack.append(arg0)                      # If return value is not saved for this argument, add it to stack.
 
    elif address == CCC_END:
        arg0 = stack.pop()                          # We know arguments when exiting the function
 
        ret = mu.reg_read(UC_ARM_REG_R0)            # Read the return value (R0)
        d[arg0] = ret                               # Remember the return value for this argument
 
mu.hook_add(UC_HOOK_CODE, hook_code)
 
mu.emu_start(0x00010584, 0x000105A8)
 
return_value = mu.reg_read(UC_ARM_REG_R1)           # We end the emulation at printf("%d\n", ccc(x)).
print "The return value is %d" % return_value

arm版本

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270