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

news2024/11/23 13:05:55

运行环境: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' * 1024
    STACK_POINT = BASE_ADDR + CODE_LEN + 1024
    UC.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_const

from .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每一步指令,单步trace
    UC.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 + 0x41E

    try:

        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 = 0x400000

    CODE_LEN = 0x3200

    RDATA_LEN = 0x800

    RDATA_OFFSET = 0x6000

    MEM_SIZE = 4 * 1024 * 1024

    MEM = 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' * 1024

    STACK_POINT = ADDRESS + CODE_LEN + 1024

    UC.reg_write(unicorn.x86_const.UC_X86_REG_ESP, STACK_POINT)

    UC.mem_write(STACK_POINT, STACK)

    # 设置hook

    UC.hook_add(UC_HOOK_CODE, trace)

    UC.hook_add(UC_HOOK_MEM_FETCH_UNMAPPED, printf)

    # 模拟执行

    START = ADDRESS + 0x41E

    try:

        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

  

'''

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1588667.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数字图像处理项目——模糊图像边缘检测算法设计及实现(论文/代码)

完整的论文代码见文章末尾 以下为部分内容 摘要 本研究旨在针对大脑核磁图像中的黑色腔体进行有效分割&#xff0c;以提供可靠的腔体定位和分析。为此&#xff0c;采用了三种常用的图像分割方法&#xff1a;8邻域区域生长法、Canny算子边缘检测和8邻域边界跟踪法。 首先&…

机器学习基础入门(一)(机器学习定义及分类)

机器学习定义 给予计算机无需特意带有目的性编程便有学习能力的算法 深度学习算法 主要有监督学习和非监督学习两类 监督学习&#xff08;supervised learning&#xff09; 定义 1、学习由x映射到y的映射关系 2、主动给予机器学习算法正确示例&#xff0c;算法通过示例来学习…

NodeJS解压版环境配置

前往官网下载最新版NodeJS 下载地址&#xff1a;Node.js — Download Node.js 如下图选择“Prebuilt Binaries”(预构建二进制文件)进行下载 解压缩下载的zip压缩包 创建node_global与node_cache文件夹 node_global Node全局目录 node_cache Node缓存目录 设置…

php站长在线工具箱源码优化版

环境要求 PHP > 7.4MySQL > 5.6fileinfo扩展使用Redis缓存需安装Redis扩展 源码下载地址&#xff1a;php站长在线工具箱源码优化版.zip

创新之作:淘宝扭蛋机小程序,让购物与娱乐完美结合

淘宝扭蛋机小程序&#xff0c;是淘宝平台为您精心打造的一款集娱乐与购物于一体的全新体验。在这里&#xff0c;每一次的扭动都蕴含着无限的惊喜与乐趣&#xff0c;让您在轻松愉快的氛围中&#xff0c;发现更多心仪的商品&#xff0c;享受更多购物的乐趣。 我们深知您对新鲜事…

【数据结构】单链表(二)

目录 1.查找数据 2.指定位置插入和删除节点 2.1 指定位置之前插入节点 2.2 指定位置之后插入节点 2.3 删除指定位置节点 2.4 删除指定位置之后的节点 3.销毁链表 我们接着上一篇【数据结构】单链表&#xff08;一&#xff09;-CSDN博客 来继续实现单链表 1.查找数据 在…

【Shell语言学堂】数组练习题

数组练习 1、使用数组和循环实现冒泡排序2、将冒泡排序的代码重构为2个函数&#xff0c;2个关系是a函数调用b函数自定义数组参数&#xff1a; 3、声明一个存储的全整数数组&#xff0c;对其中的每一个值进行10处理4、对硬盘使用空间占比的排序5、对当前目录的文件大小进行排序 …

Vue3 ts环境下的PropType

简介 在Typscript中&#xff0c;我们可以使用PropType进行类型的推断与验证。在日常的开发中我们常常会遇到下面这样的场景&#xff1a; 我们通过request请求从服务端获取了一条数据&#xff0c;数据是个Array的格式&#xff0c;Array中的每个元素又是一个对象&#xff0c;像下…

BFS宽度优先搜索例题(蓝桥杯)——逃跑的牛

问题描述&#xff1a; 农夫John的一头牛逃跑了&#xff0c;他想要将逃跑的牛找回来。现假设农夫John和牛的位置都在一条直线上&#xff0c;农夫John的初始位置为N&#xff08;0≤N≤100,000&#xff09;&#xff0c;牛的初始位置为K&#xff08;0≤K≤100,000&#xff09;。农夫…

MySOL之旅--------MySQL数据库基础( 3 )

本篇碎碎念:要相信啊,胜利就在前方,要是因为一点小事就停滞不前,可能你也不适合获取胜利,成功的路上会伴有泥石,但是走到最后,你会发现身上的泥泞皆是荣耀的勋章! 今日份励志文案: 凡是发生皆有利于我 目录 查询(select) 1.全列查询 2.指定列查询 3.查询字段为表达式 ​编…

数据库被rmallox勒索病毒加密,如何还原?

近年来&#xff0c;网络安全问题日益严峻&#xff0c;勒索病毒作为其中的一种恶意软件&#xff0c;已成为网络安全领域的一大难题。其中&#xff0c;rmallox勒索病毒以其高度的隐蔽性和破坏性&#xff0c;给不少企业和个人带来了严重损失。本文将从rmallox勒索病毒的特点、传播…

【2024年MathorCup数模竞赛】C题赛题与解题思路

2024年MathorCup数模竞赛C题 题目 物流网络分拣中心货量预测及人员排班背景求解问题 解题思路问题一问题二问题三问题四 本次竞赛的C题是对物流网络分拣中心的货量预测及人员排班问题进行规划。整个问题可以分为两个部分&#xff0c;一是对时间序列进行预测&#xff0c;二是对人…

C++初阶:模板进阶

非类型模板参数 模板参数分为类型形参与非类型形参 。 类型形参即&#xff1a;出现在模板参数列表中&#xff0c;跟在 class 或者 typename 之类的参数类型名称 。 非类型形参&#xff0c;就是用一个常量作为类 ( 函数 ) 模板的一个参数&#xff0c;在类 ( 函数 ) 模板中可将…

7、configMap

1、configMap是什么 类似与pod的配置中心&#xff0c;不会因为pod的创建销毁&#xff0c;相关配置发生改变 pod定义硬编码意味着需要有效区分⽣产环境与开发过程中的pod 定义。为了能在多个环境下复⽤pod的定义&#xff0c;需要将配置从pod定义描 述中解耦出来。 2、向容器中…

HarmonyOS实战开发-证书管理、如何实现对签名数据进行校验功能。

介绍 本示例使用了ohos.security.certManager相关接口实现了对签名数据进行校验的功能。 实现场景如下&#xff1a; 1&#xff09;使用正确的原始数据和签名数据进行签名校验场景&#xff1a;模拟服务端对签名数据进行校验&#xff0c;验证客户端身份和原始数据完整性。 2&…

智慧污水井物联网远程监控案例

智慧污水井物联网远程监控案例 在当今数字化转型的浪潮中&#xff0c;智慧水务已成为城市基础设施建设的重要组成部分。其中&#xff0c;基于物联网技术的智慧污水井远程监控系统以其高效、精准、实时的特性&#xff0c;在提升污水处理效能、保障城市水环境安全、实现精细化管…

数据资产可能是个伪命题?

✅作者简介&#xff1a;《数据运营&#xff1a;数据分析模型撬动新零售实战》作者、《数据实践之美》作者、数据科技公司创始人、多次参加国家级大数据行业标准研讨及制定、高端企培合作讲师。 &#x1f338;公众号&#xff1a;风姑娘的数字视角&#xff0c;免费分享数据应用相…

【分享】跨境虾皮Shopee各区域商品详情API返回值(商品,订单,面单等)♥

虾皮(shopee)是一个亚洲区域的电商平台&#xff0c;主要在东南亚地区提供电商服务。虾皮提供了丰富的电商数据&#xff0c;包括商品数据、订单数据、会员数据、评价数据等。 虾皮Shopee♥♥​​​​​​​♥​​​​​​​♥​​​​​​​♥​​​​​​​♥ 1.授权 ​ 接口…

Matlab 将数据写入excel文件

Matlab 将数据写入excel文件 函数&#xff1a;writematrix 功能&#xff1a;将矩阵写入文件 语法 writematrix(A) writematrix(A,filename) writematrix(___,Name,Value) 说明 writematrix(A) 将同构数组 A 写入以逗号分隔的文本文件。文件名为数组的工作区变量名称&…

点动、电子凸轮、电子齿轮

一、常见的运动模式有三种 1、点位运动&#xff1a;进队终点的位置有要求&#xff0c;不在乎运动轨迹。要求定位速度快。可分为JOG电动、MOVE寸动、和VMOVE持续运动三类。 2、连续轨迹运动&#xff1a;也称为插补&#xff0c;系统在高速运动的情况下&#xff0c;既要保证轮廓…