是否有方便的程序集来模拟应用程序转换工具?

逆向工程 艾达 部件 C 二进制 汇编
2021-07-04 14:57:22

这可能是一个太愚蠢的问题,但你去吧。

假设您有一些软件,其中包含您尝试逆向工程的棘手嵌套程序集序列(例如这个)。brain*[pen&paper]完全不可靠,但是您想咨询 IDA(或任何类似的东西),但是您不能直接拖放到 IDA 中(无论出于何种原因,可能是错误)。

您拥有该汇编序列的汇编代码,作为一种变通方法,理论上您可以编译一个包含以下内容的程序(在 C 代码中),

int main(int argc, char *argv[])
{
  // your tricky assembly sequence here, then
  // perhaps puts(eax), puts(ebx) etc
  return(0)
}

似乎是一件容易的事。但是在实践中,我怎么能在实践中编译这样一个快速而肮脏的程序呢?最好通过gcc?

1个回答

我会汇编代码,然后使用仿真进行分析。

从链接中获取的示例程序集:

mov     rax, QWORD PTR [rbp-16]   ; Move i (=9) to RAX
movabs  rdx, -3689348814741910323 ; Move some magic number to RDX (?)
mul     rdx                       ; Multiply 9 by magic number
mov     rax, rdx                  ; Take only the upper 64 bits of the result
shr     rax, 2                    ; Shift these bits 2 places to the right (?)
mov     QWORD PTR [rbp-8], rax    ; Magically, RAX contains 9/5=1 now, 
                                  ; so we can assign it to j

要模拟的代码:

mov     rax, 9                    ;                         
movabs  rdx, -3689348814741910323 ;      
mul     rdx                       ;                            
mov     rax, rdx                  ;                       
shr     rax, 2                    ;

仿真输出:

Initial state:
RAX = 0x0
RDX = 0x0
=================================================
>>> 0x1000000:  mov rax, 9
RAX = 0x9
RDX = 0x0
=================================================
>>> 0x1000007:  movabs  rdx, 0xcccccccccccccccd
RAX = 0x9
RDX = 0xcccccccccccccccd
=================================================
>>> 0x1000011:  mul rdx
RAX = 0x3333333333333335
RDX = 0x7
=================================================
>>> 0x1000014:  mov rax, rdx
RAX = 0x7
RDX = 0x7
=================================================
>>> 0x1000017:  shr rax, 2
RAX = 0x1
RDX = 0x7

>>>Emulation complete.

正如我们所看到的,RAX 持有 1,这是为 9 / 5 计算的值。仿真使我们能够轻松查看计算的每一步的结果,以便了解正在发生的事情。

执行仿真的程序包含在下面。可以在此处找到彩色要点

它由 3 个主要组件组成:

  1. 汇编和模拟的汇编代码
  2. 通过 Keystone 引擎组装,正如 blabb 在上面的评论中所提到的
  3. 通过 Unicorn 进行仿真

注册到仿真引擎的回调函数允许我们将执行的指令流中的每条指令的寄存器值等信息打印到 STDOUT。

#!/usr/bin/python3    

from keystone import *
from capstone import *
from unicorn import *
from unicorn.x86_const import *


# 9 divided by 5
ASM =  "mov rax, 9;                         \
        movabs  rdx, -3689348814741910323;  \
        mul rdx;                            \
        mov rax, rdx;                       \
        shr rax, 2;"


# Use Keystone Engine to assemble
def assemble_snippet():

    try:
        ks = Ks(KS_ARCH_X86, KS_MODE_64) # initialize assembler object
        encoding, count = ks.asm(ASM)    # save results of assembly
    except KsError as e:
        print("ERROR: %s" %e)

    return bytes(encoding)               # return assembled object code




# callback for tracing instructions
# Use Capstone Engine to disassemble
def hook_code(uc, address, size, user_data):

    # print contents of registers of interest
    print("RAX = 0x%x" % uc.reg_read(UC_X86_REG_RAX))
    print("RDX = 0x%x" % uc.reg_read(UC_X86_REG_RDX))
    print("=================================================")

    # print disassembly of intruction stream
    instruction = uc.mem_read(address, size)
    md = user_data
    for i in md.disasm(instruction, address):
        print(">>> 0x%x:\t%s\t%s" %(i.address, i.mnemonic, i.op_str))



# from https://github.com/unicorn-engine/unicorn/blob/8621bca53758532ad6a4ec5d17684fcdb9923cc6/bindings/python/sample_x86.py#L475
def emulate():

    ADDRESS = 0x1000000                      # memory address where emulation starts
    CODE = assemble_snippet()                # object code to emulate

    mu = Uc(UC_ARCH_X86, UC_MODE_64)         # Initialize emulator in X86-64bit mode

    mu.mem_map(ADDRESS, 2 * 1024 * 1024)     # map 2MB memory for this emulation
    mu.mem_write(ADDRESS, CODE)              # map machine code to be emulated to memory
    mu.reg_write(UC_X86_REG_RSP, ADDRESS + 0x200000) # set up stack

    md = Cs(CS_ARCH_X86, CS_MODE_64)         # initialize disassembler

    mu.hook_add(UC_HOOK_CODE, hook_code, md)

    print("Initial state:")

    try:
        # emulate machine code in infinite time
        mu.emu_start(ADDRESS, ADDRESS + len(CODE))
    except UcError as e:
        print("ERROR: %s" % e)

    # final state
    print("RAX = 0x%x" % mu.reg_read(UC_X86_REG_RAX))
    print("RDX = 0x%x" % mu.reg_read(UC_X86_REG_RDX))
    print("\n>>>Emulation complete.")


if __name__ == "__main__":
    emulate()