type
status
date
slug
summary
tags
category
icon
password
本篇博客借鉴于无名侠大佬的unicorn笔记,非常感谢大佬对unicorn的讲解

自己的见解:

 
 
 

Unicorn的快速入门:

Unicorn是基于qemu模拟器的模拟执行框架,支持Arm, Arm64 (Armv8), M68K, Mips, Sparc, & X86 (include X86_64)等指令集以及可以被多种语言进行使用

虚拟内存:

unicorn相当于一块Cpu,但是他的运行是与真实CPU分开的,但是我们使用的时候要去按照操控一个CPU一样去操控他,比如运行文件,我们需要将文件写入内存,还需要开辟空间这样,而不是直接让unicorn去调用这个我们要调试的文件,下面是一些常用的API
  • uc_mem_map
  • uc_men_read
  • uc_mem_write
上面的这三个对应着我们说的这些操作,分别是开辟空间,map就是用来映射内存的,但是映射内存时,他的参数的address和size都需要与0x1000对齐,否则会报错(UC_ERR_ARG),read就是读入文件,write时写入

Hook机制:

  • 指令执行类:
  • 内存访问类:
 
  • 异常处理类:
Unicorn的Hook是链式的,不是覆盖的,我们可以写多个hook代码来进行hook参数
下面说一下怎么添加hook代码,这里我们就要使用nicorn在python的API代码函数
hook_add函数
 

Hook callback

不同类型的hook对应着不同的callback
  • UC_HOOK_CODE & UC_HOOK_BLOCK 的callback定义
 
  • READ, WRITE & FETCH 的 callback 定义
  • invalid memory access events (UNMAPPED and PROT events) 的 callback 定义
 

Unicorn的简单使用:

1、首先就是导入Unicorn库:

 

2、导入处理器相关常量:

Unicorn 支持多种不同的CPU指令集,每一种指令集都有自己独立的寄存器, Unicorn使用统一API管理多种不同的CPU指令集,并将寄存器名字映射成数字常量。
引用这样就是细化我们接下来寄存器的使用

3、写模拟执行的代码:

这里直接将代码写成硬编码的形式进行赋值

4、创建UC对象并且创建异常处理:

Uc 是unicorn的主类,Uc对象则代表了一个独立的虚拟机实例,它有独立的寄存器和内存等资源,不同Uc对象之间的数据是独立的。Uc的构造函数有两个参数 archmode,用来指定模拟执行的指令集和对应的位数或模式。
arch常量参数一般以 UC_ARCH 开头,MODE常量以UC_MODE 开头。
同一种指令集可以有多种模式,比如x86可以同时运行32位和16位的汇编,arm也有arm模式和Thumb模式,它们是向下兼容的,并可以通过特殊指令来切换CPU运行模式。 调用构造函数时的模式(mode)以第一条执行指令的模式为准。
 

5、映射内存:

对于Unicorn我们不能直接传入字节流,二十应该将执行的代码写入Unicorn的虚拟内存中,先使用uc_mem_map映射一段内存
这段代码在内存地址0x10000处映射了一段大小为2M的内存。还是和前面一样,必须要与0x1000对齐

6、写入代码:

我们将要执行的代码写入到虚拟机的内存中,就是uc_mem_write

7、给寄存器赋值

 
 

8、添加指令集HOOK

 
在begin...end范围内的每一条指令被执行前都会调用callback。
这段代码仅打印指令执行的地址和长度信息。 实际应用中可配合capstone反汇编引擎玩一些更骚的操作。
UC_HOOK_CODE的callback中可以修改PC或EIP等寄存器力来改变程序运行流程。实际上,unicorn调试器的单步调试就是以这个为基础实现的。

9、开机:

emu_start 可以通过timeout参数设置最长执行时长,防止线程死在虚拟机里面
emu_start 执行完成后,可以通过读取内存或寄存器的方式来获取执行结果。

10、获取结果:

 
 

Unicorn调试器的编写:

功能实现:

  • 硬件断点
  • 读写 寄存器信息
  • 反汇编
  • dump内存
  • 单步调试

具体实现:

Unicorn的有强大的指令级HOOK(UC_HOOK_CODE),使得每条指令前都可以进行处理
UC_HOOK_CODE的具体实现在前面已经提到,现在具体说明一下hook_add和callback以及UC_HOOK_CODE之间的关系
hook_add其中有一个type函数,他其中有一个就是UC_HOOK_CODE,它表示这调试的级别,其中的callback是hook代码的具体实现,使用UC_HOOK_CODE可以对每一条指令都进行输出
 
首先编写一个UnicornDebugger类,调试器的各种功能这个类中实现,调用这个类的构造函数就可以附加在Uc虚拟机对象上
这个代码就是在0x1112233添加一个断点,当不如到这个的时候就会等待输入
 

反汇编:

这里的反汇编引擎使用Capstone,可以简单使用这个,给个例子
 
notion image
在我们进行输出的时候,一定要明确是什么版本的,就是32位和64位也要明确指明,而且对于ARM架构,他都是四个字节为一组,所以使用<I
 

实现反汇编:

 
 

进行dump内存:

 

寄存器显示:

将寄存器与文本进行映射
 

调试功能的实现

 

完整代码:

 
 
 

Unicorn调用so之加载模块

Unicorn依旧不能直接调用so文件,所以就像我们之前解析PE文件一样,将ELF文件映射到内存,再python中使用elftools库进行解析ELF文件
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Unidbg使用WMCTF-re方向WP
Loading...