ida模拟

看大佬的博客又学到了一点东西,加密后的字符串在ida里无法被读出,那么应该怎么办呢?可以使用unicorn模拟执行脚本。

参考博客:https://xia0ji233.pro/2025/04/14/tencent-race-2025-final/

https://zhuanlan.zhihu.com/p/22633261031

侵删。

unicorn

安装

pip install unicorn

用处

用来模拟代码执行。

脚本

举个例子:

import idaapi
import idc
from unicorn import *
from unicorn.x86_const import *
import ida_name
import mmap
import sys
import idautils
import struct
base_addr = idaapi.get_imagebase()
function_start=0x1234567
function_end=0x12345678

mu = Uc(UC_ARCH_X86, UC_MODE_64)
mu.emu_start(function_start,function_end)

其中需要修改function_startfunction_end,这是个开合空间。然后它会执行中间的代码,并且记录一些东西

它还可以读写寄存器:

mu.reg_write(UC_X86_REG_RIP, function_start)
mu.reg_write(UC_X86_REG_R13, 0xFF)
rax=uc.reg_read(UC_X86_REG_RAX)
rcx=uc.reg_read(UC_X86_REG_RCX)

还可以读写内存:

uc.mem_write(0x7f1234567,data)
uc.mem_read(rsp+offset,2)

还可以开辟内存:

mu.mem_map(ADDRESS, 20 * 0x100000)

还可以进行hook,其中每一条hook指令是这样的:

Hook类型说明用例
UC_HOOK_CODE拦截每一条指令监控,反调试
UC_HOOK_BLOCK拦截每个基本块统计基本块执行次数
UC_HOOK_INTR拦截中断指令监控系统调用
UC_HOOK_MEM_READ读取内存前触发监控变量读取
UC_HOOK_MEM_WRITE写入内存前触发监控变量写入
UC_HOOK_MEM_FETCH取指令前触发捕获未映射的代码执行
UC_HOOK_MEM_READ_UNMAPPED读取未映射内存捕获非法读取
UC_HOOK_MEM_WRITE_UNMAPPED写入未映射内存捕获非法写入
UC_HOOK_MEM_FETCH_UNMAPPED取址未映射内存捕获非法指令执行
UC_HOOK_INSN拦截特定指令监控syscall等

使用是这样的:

# Hook 回调函数
def hook_code(mu, address, size, user_data):
    print(f"Executing instruction at 0x{address:X}, size={size}")
mu.hook_add(UC_HOOK_CODE, hook_code)

启动

当然,这个玩意其实只是一个模拟器,所以需要塞入代码,可以这么写:

from unicorn import *
from unicorn.x86_const import *
from elftools.elf.elffile import ELFFile

def load_elf(filename):
    with open(filename, 'rb') as f:
        elffile = ELFFile(f)
        # 找到代码段
        for segment in elffile.iter_segments():
            if segment['p_type'] == 'PT_LOAD':
                # 映射内存并写入代码
                address = segment['p_vaddr']
                size = segment['p_memsz']
                f.seek(segment['p_offset'])
                code = f.read(segment['p_filesz'])
                # 返回地址和代码
                return address, code
    return None, None

# 创建 Unicorn 实例
uc = Uc(UC_ARCH_X86, UC_MODE_32)

# 加载 ELF 文件
base_address, code = load_elf('your_program.elf')
if code:
    uc.mem_map(base_address, 2 * 1024 * 1024)  # 映射内存
    uc.mem_write(base_address, code)  # 写入代码

    # 设置程序计数器
    uc.reg_write(UC_X86_REG_EIP, base_address)

    # 启动模拟
    uc.emu_start(base_address, base_address + len(code))

或者在ida里面可以:

uc.mem_map(base_address, 2 * 1024 * 1024)
data=idaapi.get_bytes(aligned_addr,PAGE_SIZE)
uc.mem_write(aligned_addr,data)

然后执行就可以了。

大佬的脚本里面有一个很神奇的写入方式:

def hook_mem_unmapped(uc, access, address, size, value, user_data):
    aligned_addr = address&0xFFFFFFFFFFFFF000 
    try:
        uc.mem_map(aligned_addr, PAGE_SIZE)
        data=idaapi.get_bytes(aligned_addr,PAGE_SIZE)
        uc.mem_write(aligned_addr,data)
        return True  # 表示错误已处理,继续执行
    except Exception as e:
        print(f"[-] 动态映射内存页失败: {e}")
        return False  
    
mu.hook_add(UC_HOOK_MEM_FETCH_UNMAPPED, hook_mem_unmapped)

就是没映射这个位置,就跑过去映射它,这样就可以模拟执行了。这样的话,只需要把它原来的rip寄存器改成想要执行的函数地址,然后因为模拟执行,会没有映射,所以就会当场开始贴代码,进入的函数代码也会帖进去,直到执行到结束位置。