I have a function with the first few instructions defined as follows:

sub  rsp, 0x80
lea  rbp, [rsp + 0x20]
mov  qword [rbp + 0x58], rsi
mov  qword [rbp + 0x50], rbx
mov  qword [rbp + 0x70], rcx
mov  dword [rbp + 0x78], edx
mov  eax, dword [rbp + 0x78]
test eax, eax

So, in order we have:

  1. stack frame of 0x80 -- fine
  2. Some kind of structure at RSP+0x20 -- probably
  3. set some member of RBP at offset 0x58 to RSI
  4. same for offset 0x50
  5. Same for offset 0x70 -- wait what? RBP+0x70 is 0x10 bytes beyond our stack frame.

It seems like we have an on-stack structure at RBP yet it's accessing data from the caller's frame. If we were accessing data from the caller I would have expected the compiler to use one base register for the current function (as we saw with the RBP usage) and then another register for the the caller's data offsets, say some offset from RSP. I understand the compiler isn't required to follow that pattern, so my question is: Is there some C or C++ that could generate this assembly sequence in practice or am I looking at weird obfuscated code?

EDIT: This is a windows binary. There are two parameters to this function, RCX and RDX.

  • 1
    Since this is x64 you are looking at _fastcall calling convention i presume you know that in this convention either 6 arguments (gcc ). Or 4 arguments (msvc) are passed via registers and also these are optionally saved by callee function called as shadow space – blabb Mar 30 at 6:25
  • Please add more context, like what kind of function is it, EABI, etc Here's my guess: - I agree with the stack frame size - I think rbp acts as a frame pointer, which is unusual (modern compilers tend to rely only on rsp) - storing rsi/rbx make me thing saving non-volatile registers - storing rcx/edx look like Windows EABI where first param is 64-bit (ptr?) and int - re-reading the stack (edx) looks like un-optimize code (e.g. debug build) and tested against 0 – wisk Mar 30 at 6:56

After reading up on "shadow stack space" as suggested by @blabb it looks like this is an unoptimized build, so the 2 parameters RDX and RCX are being saved in the caller's scratch space, presumably to make debugging easier. I got most of this information from the following post:

What is the 'shadow space' in x64 assembly?

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.