1

It might be too silly of a question, but here you go.

Suppose you have some software that contains a tricky nested assembly sequence (like this one) you try to reverse engineer. brain*[pen&paper] totally rocks, but you would like to consult IDA (or anything similar), but you cannot directly drag&drop into IDA for (whatever reason, maybe a bug).

You have the assembly code for that assembly sequence, and as a work around, you can theoretically compile a program that would contain the following (in C-code),

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

Seems like a fair an easy task. But in practice, how could I compile such a quick and dirty program in practice? Preferably via gcc?

  • what do you have in hand 0x55 or push ebp fir first try capstone for second try key stone – blabb Jan 7 at 15:41
2

I would assemble the code and then analyze it using emulation.

Example assembly taken from the link:

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

Code to be emulated:

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

Output of emulation:

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.

As we can see, RAX holds 1, which is the value computed for 9 / 5. Emulation allows us to easily view the results of every step of the computation in order to understand what is happening.

The program performing the emulation is included below. A colorized gist can be found here.

It consists of 3 major components:

  1. The assembly code to assemble and emulate
  2. Assembly via the Keystone engine, as alluded to by blabb in his comment above
  3. Emulation via Unicorn

A callback function registered with the emulation engine allows us to print information such as register values to STDOUT for each instruction in the instruction stream that is executed.

#!/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()
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.