0

I'm trying to write a script that would help display the memory contents of the arguments passed within a function. For example, in the function below, the first argument starts at the EAX register. Printing the contents of EAX gives us the value of the first argument. How can we print all the following arguments?

BOOLAPI FtpCommandA(

HINTERNET hConnect,

BOOL fExpectResponse,

DWORD dwFlags,

LPCSTR lpszCommand,

DWORD_PTR dwContext,

HINTERNET *phFtpCommand

);

Thanks!

  • let's say its x64. – Chuck Nurit Jul 25 '19 at 9:44
  • Get off your high horse buddy. Go ahead delete your comments. – Chuck Nurit Jul 26 '19 at 5:17
  • Dont think you know the answer anyway. – Chuck Nurit Jul 26 '19 at 5:18
2

According to Microsoft Docs, the ABI for x64 Windows looks like this:

The x64 Application Binary Interface (ABI) uses a four-register fast-call calling convention by default. Space is allocated on the call stack as a shadow store for callees to save those registers. There's a strict one-to-one correspondence between the arguments to a function call and the registers used for those arguments. Any argument that doesn’t fit in 8 bytes, or isn't 1, 2, 4, or 8 bytes, must be passed by reference. A single argument is never spread across multiple registers.

The first four integer arguments are passed in registers. Integer values are passed in left-to-right order in RCX, RDX, R8, and R9, respectively. Arguments five and higher are passed on the stack. All arguments are right-justified in registers, so the callee can ignore the upper bits of the register and access only the portion of the register necessary.

Any floating-point and double-precision arguments in the first four parameters are passed in XMM0 - XMM3, depending on position. The integer registers RCX, RDX, R8, and R9 that would normally be used for those positions are ignored, except in the case of varargs arguments.

__m128 types, arrays, and strings are never passed by immediate value. Instead, a pointer is passed to memory allocated by the caller. Structs and unions of size 8, 16, 32, or 64 bits, and __m64 types, are passed as if they were integers of the same size. Structs or unions of other sizes are passed as a pointer to memory allocated by the caller.

You will also find more comprehensive description and examples at that website.

To see how it works in practice, we can write simple program using the FtpCommandA function and calling it like this:

FtpCommandA(NULL, false, 1, "command", 2, NULL);

After disassembling this simple program, you will see: x64CallingConvention

You can notice that indeed, first four arguments are passed using rcx, rdx, r8 and r9 registers while the last two are passed using the stack.

| improve this answer | |
  • Thanks for explaining everything so clearly! – Chuck Nurit Jul 30 '19 at 21:43
1

Another way this could be achieved is by using the Stacktrace function and then printing the arguments' locations - something like this:

dprintf( "%08p %08p %08p %08p %08p %s",
             stk[i].FramePointer,
             stk[i].ReturnAddress,
             stk[i].Args[0],
             stk[i].Args[1],
             stk[i].Args[2],
             Buffer
             );
| improve this answer | |
  • Ok - here's another way I came up with it which can be used to print the actual arguments: use the 'kb' command to output the frames and then use a .foreach to pic the ones that you're after. This one works well as you can craft a command that can then be used in a breakpoint with the 'go' option at the end, if you wanted to automate the process. – Chuck Nurit Aug 15 '19 at 5:21

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.