Search code examples
c++assemblyx86-64

Transition from 8-byte to 11-byte instruction lengths for local variables


Notice these C++ local variable instruction lengths are 8-bytes within the red box

48 c7 45 f8 00 00 00 00
...

However, the instruction lengths are 11-bytes within the green box

48 c7 85 78 ff ff ff 00 00 00 00 
...

https://godbolt.org/z/bTsxx63rT

opcodes

Background

I was working on some shellcode that required patching API addresses but noticed errors because the local variable instructions lengths jumped from 8 to 11 bytes. Anyhow, thru trial and error, to enforce all the instructions to be 11 bytes, I added a local array

char enforce_mov_long_form[128];

to avoid accessing locals in the range [rbp-0x8] to [rbp-0x80]

DWORD WINAPI shellcode_start(void)
{

    char enforce_mov_long_form[128];

    // ------------------------------------------------------------
    QWORD           start_shellcode          = 0xAAAAAAAAAAAAAAAA;
    DWORD           sizeofshellcode          = 0xAAAAAAAA;
    QWORD           start_shellcode_injector = 0xAAAAAAAAAAAAAAAA;
    DWORD           pid_injector             = 0xAAAAAAAA;

    QWORD           WinExec                  = 0xAAAAAAAAAAAAAAAA;
    QWORD           Sleep                    = 0xAAAAAAAAAAAAAAAA;
    QWORD           CreateToolhelp32Snapshot = 0xAAAAAAAAAAAAAAAA;
    QWORD           Process32First           = 0xAAAAAAAAAAAAAAAA;
    QWORD           Process32Next            = 0xAAAAAAAAAAAAAAAA;
    QWORD           CloseHandle              = 0xAAAAAAAAAAAAAAAA;
    QWORD           OpenProcess              = 0xAAAAAAAAAAAAAAAA;
    QWORD           VirtualAllocEx           = 0xAAAAAAAAAAAAAAAA;
    QWORD           VirtualFreeEx            = 0xAAAAAAAAAAAAAAAA;
    QWORD           WriteProcessMemory       = 0xAAAAAAAAAAAAAAAA;
    QWORD           CreateRemoteThread       = 0xAAAAAAAAAAAAAAAA;
    QWORD           CompareStringA           = 0xAAAAAAAAAAAAAAAA;
    QWORD           GetCurrentProcessId      = 0xAAAAAAAAAAAAAAAA;
    // ------------------------------------------------------------

Questions

Why is there a sudden transition from 8-byte to 11-byte instruction lengths for local variables?

Are there any micro optimizations gained by confining local variables within [rbp-0x8] to [rbp-0x80]?


Solution

  • Promoting comments to answer:

    x86-64 addressing modes encode the displacement as either a signed 8-bit (1 byte) or signed 32-bit (4 bytes) value, which in either case is sign-extended to 64 bits. Since the range of a signed 8-bit integer is [-128..127] or [-0x80..0x7f], displacements outside this range must use the 32-bit form instead, making the instruction three bytes longer.

    For more on how addressing modes are encoded, see https://wiki.osdev.org/X86-64_Instruction_Encoding and Referencing the contents of a memory location. (x86 addressing modes).