1
votes

I'd like to write an assembly program which once loaded into memory, writes new instructions over itself, but I'm not 100% certain how to proceed as I have some doubts about the instruction pointer and other assembly concepts. My hypothesised approach:

_func:

    push rip    ; Not allowed to push RIP, how can I read from RIP?
    jmp stage1

    stage2:

    mov eax, 0
    ret

    stage1:

    pop rbx

    ; How many times should I increment rbx to point to ‘mov eax, 0’?
    ; Assuming this is done:

    ;Move opcodes for ‘mov eax, 1’ into memory where ‘mov eax, 0’ located

    mov [rbx], 0xB8
    mov [rbx+1], 0x01
    mov [rbx+2], 0x00
    mov [rbx+3], 0x00
    mov [rbx+4], 0x00

    jmp stage2

When it jumps to stage 2, instead of 'mov eax, 0', it will encounter the opcodes 'B8 01 00 00 00' and interpret 'mov eax, 1.' Is my general approach correct, and can someone fill in that gap in the code?


Additional Confusion/Issue

Does RBX point to the first byte of the line of instructions, or the 'whole' line? Is the aforementioned approach correct, or should I have written:

mov [rbx], B801000000h

Operating System: Mac OS X 10.9 Assembler: NASM

2
I wouldn't call this "reflection" - that term is for somewhat higher level concept. Self-modifying code is more like it. stackoverflow.com/questions/24676966/…Sten Petrov

2 Answers

1
votes

In 16-bit "real mode" doing this is simple. When I began assembler programming I implemented a loop using reflection.

However under 16- or 32-bit "protected mode" or under "long mode" (64-bit) this is more tricky:

In these modes memory can be protected against overwriting. Since the "Pentium Pro" processor it is also possible to protect memory against execution.

Both Windows and Linux use these features so by default memory that can be modified cannot contain executable code and memory containing executable code cannot be overwritten.

You have to allocate memory that is both writeable and executable (or to change the memory access of existing memory). See "VirtualAlloc" and "VirtualProtect" functions in Windows or "mmap" and "mprotect" in Linux. Then you have to move the code into that piece of memory.

By the way: The following command is wrong:

jmp stage1

It must be "call" instead of "jmp" because "jmp" does not "push" the return address on the stack.

1
votes

In 16-bit "real mode":

mov bx, stage2      ; NASM get offset address

mov cs:[bx], 0xB8
mov cs:[bx+1], 0x01
mov cs:[bx+2], 0x00
mov cs:[bx+3], 0x00
mov cs:[bx+4], 0x00