基础的unicorn模拟简介与库函数调用方案与代码实例

2024-04-12 17:04

本文主要是介绍基础的unicorn模拟简介与库函数调用方案与代码实例,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

运行环境:python
基本的导入:from unicorn import *

简介

1. unicorn对象的初始化:
UC = Uc(unicorn_const.UC_ARCH_X86,unicorn_const.UC_MODE_16)

Uc接收的二值分别指定将模拟的架构和程序位数。后续操作的寄存器(如rax、eax、ax之分别)请严格匹配程序位数。

2. 内存映射
# 基地址
BASE_ADDR = 0x0
# 内存
MEM_SIZE = 16 * 1024
UC.mem_map(BASE_ADDR, MEM_SIZE)

mem_map将为UC标记、分配一段“有效”的空间,超出这个空间的地址操作将可能报Invalid memory operation (UC_ERR_READ_UNMAPPED)错误。

3. 段初始化/内存写
BASE_ADDR = 0x0
MEM_SIZE = 16 * 1024
MEM = b'0' * MEM_SIZE
UC.mem_map(BASE_ADDR, MEM_SIZE)
UC.mem_write(BASE_ADDR, MEM)

这一段是内存分配的继续。unicorn并不会在map后对空间初始化。因此这里对其、对整段分配的内存空间用0覆写,避免可能存在的问题。
段分配前需要获取目标段的物理偏移和虚拟地址,请用PE软件如DIE查看。
代码:

RDATA_OFFSET = 0x6000
RDATA_LEN = 0x800
FILE = open(f".\\test.bin", "rb")
FILE.seek(RDATA_OFFSET)
RDATA = FILE.read(RDATA_LEN)
UC.mem_write(BASE_ADDR + RDATA_OFFSET, RDATA)
4. 堆栈分配

我一般是在代码段后追加一小段,或是将内存空间的末尾作为栈区。这里要注意,分配的堆栈空间可以相对自由,但要分配好sp寄存器的值。

    STACK = b'0' * 1024STACK_POINT = BASE_ADDR + CODE_LEN + 1024UC.reg_write(unicorn.x86_const.UC_X86_REG_SP, STACK_POINT)UC.mem_write(STACK_POINT, STACK)

同样,这里堆栈区进行了一次初始化。

5. 寄存器分配
STACK_POINT = BASE_ADDR + CODE_LEN + 1024
UC.reg_read(unicorn.x86_const.UC_X86_REG_IP)
UC.reg_write(unicorn.x86_const.UC_X86_REG_SP, STACK_POINT)

在引入了unicorn包后,调用寄存器常量可以用unicorn.x86_const.的方式,也可以直接:mips_const.UC_MIPS_REG_15
需要注意的是:
python包可能默认不包含riscv常量,需要自己引入

# Unicorn Python bindings, by Nguyen Anh Quynnh <aquynh@gmail.com>from . import arm_const, arm64_const, mips_const, sparc_const, m68k_const, x86_constfrom .unicorn_const import *from .unicorn import Uc, uc_version, uc_arch_supported, version_bind, debug, UcError, __version__
6. hook与trace
def trace(mu: Uc, address, size, data):md = cs.Cs(cs.CS_ARCH_X86, cs.CS_MODE_16)EIP = mu.reg_read(unicorn.x86_const.UC_X86_REG_EIP)CODE = [i for i in md.disasm(mu.mem_read(address, size), size)][0]print(">>> EIP : %x" % (EIP), CODE.mnemonic, CODE.op_str)

这是一个基本的unicorn trace回调函数模板。注意这里,这个是“trace”的回调函数模板。unicorn对于hook不同的情况,需要定制不同参数的回调函数。如:

    UC.hook_add(UC_HOOK_CODE, trace)  # hook每一步指令,单步traceUC.hook_add(UC_HOOK_MEM_FETCH_UNMAPPED, printf)def trace(mu: Uc, address, size, data):EIP = mu.reg_read(unicorn.x86_const.UC_X86_REG_EIP)CODE = [i for i in md.disasm(mu.mem_read(address, size), size)][0]print(">>> EIP : %x" % (EIP), CODE.mnemonic, CODE.op_str)def printf(mu: Uc, access, address, size, value, user_data):ESP = mu.reg_read(unicorn.x86_const.UC_X86_REG_ESP)# 这里的值是立即数VALUE = bytes(mu.mem_read(ESP + 8, 4))VALUE = int.from_bytes(VALUE, 'little')# 格式是指针,地址归属rdata段FORMAT = mu.mem_read(int.from_bytes(bytes(mu.mem_read(ESP + 4, 4)), 'little'), 4)# 检查是哪种格式if bytes(FORMAT)[0:2] == b'%d':print(">>> call print : %x" % (VALUE))# 从printf的调用处重新执行OLD_IP = mu.mem_read(ESP, 4)  # 获得返回地址OLD_IP = int.from_bytes(bytes(OLD_IP), 'little')mu.emu_stop()try:mu.emu_start(OLD_IP, 2 * 1024 * 1024)except UcError as e:print("ERROR: %s" % e)
7. 启动
    START = ADDRESS + 0x41Etry:UC.emu_start(START, 2 * 1024 * 1024)except UcError as e:print("ERROR ", e)

emu_start的第二个参数可以认为是执行的截止地址,可以匹配最终执行地址,也可以直接写到内存结束等error。

unicorn执行外部库函数

不建议用unicorn模拟调用外部函数的程序。
但如果实在要执行的话,这里有几个思路:
①使用pywin调用库函数,代替原函数。
②使用ctypes调用库函数,代替原函数。
③hook指定地址|trace时做检查。
④hook地址异常。

实例:

#include<stdio.h>int main(){int a,b;a = 1;b = 2;a += b;printf("%d",a);}
from unicorn import *def test():# 一些常量定义,感觉可以挪到全局ADDRESS = 0x400000CODE_LEN = 0x3200RDATA_LEN = 0x800RDATA_OFFSET = 0x6000MEM_SIZE = 4 * 1024 * 1024MEM = b'0' * MEM_SIZE# 读取目标程序FILE = open(f".\\test_1.exe", "rb")# 设置代码段UC = Uc(unicorn_const.UC_ARCH_X86, unicorn_const.UC_MODE_32)UC.mem_map(ADDRESS, MEM_SIZE)FILE.seek(0x400)CODE = FILE.read(CODE_LEN)UC.mem_write(ADDRESS, MEM)  # 对MEM空间进行初始化,空间管理严格的话这里其实也不用初始化UC.mem_write(ADDRESS, CODE)  # 写代码段# 设置RDATA段FILE.seek(0x3800)RDATA = FILE.read(RDATA_LEN)UC.mem_write(ADDRESS + RDATA_OFFSET, RDATA)# 设置栈区STACK = b'0' * 1024STACK_POINT = ADDRESS + CODE_LEN + 1024UC.reg_write(unicorn.x86_const.UC_X86_REG_ESP, STACK_POINT)UC.mem_write(STACK_POINT, STACK)# 设置hookUC.hook_add(UC_HOOK_CODE, trace)UC.hook_add(UC_HOOK_MEM_FETCH_UNMAPPED, printf)# 模拟执行START = ADDRESS + 0x41Etry:UC.emu_start(START, 2 * 1024 * 1024)except UcError as e:print("ERROR ", e)def trace(mu: Uc, address, size, data):EIP = mu.reg_read(unicorn.x86_const.UC_X86_REG_EIP)print(">>> EIP : %x" % (EIP))def printf(mu: Uc, access, address, size, value, user_data):ESP = mu.reg_read(unicorn.x86_const.UC_X86_REG_ESP)# 这里的值是立即数VALUE = bytes(mu.mem_read(ESP + 8, 4))VALUE = int.from_bytes(VALUE, 'little')# 格式是指针,地址归属rdata段FORMAT = mu.mem_read(int.from_bytes(bytes(mu.mem_read(ESP + 4, 4)), 'little'), 4)# 检查是哪种格式if bytes(FORMAT)[0:2] == b'%d':print(">>> call print : %x" % (VALUE))# 从printf的调用处重新执行OLD_IP = mu.mem_read(ESP, 4)  # 获得返回地址OLD_IP = int.from_bytes(bytes(OLD_IP), 'little')mu.emu_stop()try:mu.emu_start(OLD_IP, 2 * 1024 * 1024)except UcError as e:print("ERROR: %s" % e)if __name__ == "__main__":test()# 执行的代码:'''.text:0040141E C7 44 24 1C 01 00 00 00       mov     dword ptr [esp+1Ch], 1.text:00401426 C7 44 24 18 02 00 00 00       mov     dword ptr [esp+18h], 2.text:0040142E 8B 44 24 18                   mov     eax, [esp+18h].text:00401432 01 44 24 1C                   add     [esp+1Ch], eax.text:00401436 8B 44 24 1C                   mov     eax, [esp+1Ch].text:0040143A 89 44 24 04                   mov     [esp+4], eax.text:0040143E C7 04 24 44 60 40 00          mov     dword ptr [esp], offset Format  ; "%d".text:00401445 E8 7A 2A 00 00                call    _printf.text:00401445.text:0040144A B8 00 00 00 00                mov     eax, 0.text:0040144F C9                            leave.text:00401450 C3                            retn'''

在这里插入图片描述

这篇关于基础的unicorn模拟简介与库函数调用方案与代码实例的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/897723

相关文章

无人叉车3d激光slam多房间建图定位异常处理方案-墙体画线地图切分方案

墙体画线地图切分方案 针对问题:墙体两侧特征混淆误匹配,导致建图和定位偏差,表现为过门跳变、外月台走歪等 ·解决思路:预期的根治方案IGICP需要较长时间完成上线,先使用切分地图的工程化方案,即墙体两侧切分为不同地图,在某一侧只使用该侧地图进行定位 方案思路 切分原理:切分地图基于关键帧位置,而非点云。 理论基础:光照是直线的,一帧点云必定只能照射到墙的一侧,无法同时照到两侧实践考虑:关

高效+灵活,万博智云全球发布AWS无代理跨云容灾方案!

摘要 近日,万博智云推出了基于AWS的无代理跨云容灾解决方案,并与拉丁美洲,中东,亚洲的合作伙伴面向全球开展了联合发布。这一方案以AWS应用环境为基础,将HyperBDR平台的高效、灵活和成本效益优势与无代理功能相结合,为全球企业带来实现了更便捷、经济的数据保护。 一、全球联合发布 9月2日,万博智云CEO Michael Wong在线上平台发布AWS无代理跨云容灾解决方案的阐述视频,介绍了

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

usaco 1.2 Transformations(模拟)

我的做法就是一个一个情况枚举出来 注意计算公式: ( 变换后的矩阵记为C) 顺时针旋转90°:C[i] [j]=A[n-j-1] [i] (旋转180°和270° 可以多转几个九十度来推) 对称:C[i] [n-j-1]=A[i] [j] 代码有点长 。。。 /*ID: who jayLANG: C++TASK: transform*/#include<

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

ASIO网络调试助手之一:简介

多年前,写过几篇《Boost.Asio C++网络编程》的学习文章,一直没机会实践。最近项目中用到了Asio,于是抽空写了个网络调试助手。 开发环境: Win10 Qt5.12.6 + Asio(standalone) + spdlog 支持协议: UDP + TCP Client + TCP Server 独立的Asio(http://www.think-async.com)只包含了头文件,不依