0

I'm trying to automate some of my debugging with a python script that automatically places breakpoints at the first and last instructions of the functions I want to analyze. I'm using the breakpoint's condition to call python code whenever the breakpoint is hit, so I can print a line of text on the console.

These functions have arguments that I want to interact with in the breakpoint's condition code from Python, but I don't know how to access them. Take the following function for example:

.text:00E072A0 ; char __thiscall encryptDataLong(int this, int inData, int outData, size_t bufferSize)
.text:00E072A0 encryptDataLong proc near               ; CODE XREF: encryptData+D↓j
.text:00E072A0                                         ; encryptPw+245↓p
.text:00E072A0
.text:00E072A0 var_14= dword ptr -14h
.text:00E072A0 var_10= dword ptr -10h
.text:00E072A0 var_C= dword ptr -0Ch
.text:00E072A0 var_8= dword ptr -8
.text:00E072A0 var_4= dword ptr -4
.text:00E072A0 inData= dword ptr  4
.text:00E072A0 outData= dword ptr  8
.text:00E072A0 bufferSize= dword ptr  0Ch
.text:00E072A0
.text:00E072A0 sub     esp, 14h ; <-- Breakpoint here

The second and third arguments are pointers to a chunk of memory, and the last argument is the size of this memory. I want to access all three arguments (the two pointers and the size) from python code, but I don't know how to do it. When I hover my mouse over an argument in the pseudocode view, i get something like this on the tooltip:

int inData; // [esp+1Ch] [ebp+4h]

But I don't know how to extract the actual pointers form this in python. I assume the parameters are stored relative to one of the registers (e.g. esp in this case), and the number after the + sign is the offset in memory relative to this register's value, but when manually investigating the running program's memory, I found that this is not the case.

What do those two register offsets mean on the tooltip, and how can I use them in together with get_reg_value and get_bytes to get the actual data in the arguments?


PS: I have found the idaapi.get_arg_addrs(here()) function from the accepted answer on this question, but when running it in the python console while the program is paused on the first instruction of the function, it doesn't return anything. This question also mentions get_arg_addrs, but it seems to be used on the call instruction, and not on the function itself. In my case, I want to get the arguments from within the function, when the program is paused on the first instruction.

1
  • How parameters are passed depends on the architecture and the used calling convention. When using esp relative addresses keep in mind that this is the stack pointer and thus it changes frequently which means also the relative addresses change.
    – Robert
    Mar 6, 2022 at 14:03

1 Answer 1

2

The way parameter pass is according x86 calling convention (32-bit program calling convention) that Robert said in the comment. You can see more detail from this answer: https://reverseengineering.stackexchange.com/a/2965.

The calling convention push into stack arguments value so when the function need to access those arguments it can calculate easily because it know that it was push into the stack. The ebp store the frame pointer which always point to the top of the function stack. So the argument is always before the ebp and the local variable is always after the ebp in 32-bit. That's why to get the function first argument it do ebp+8 because it is always at offset +8 from the ebp, the second argument is ebp+12, third is ebp+16 and so on (it can't be + 4 because that's the address to return after the function is called).

So to get the function argument you first need to get the value of ebp and the plus 8 to that value to get the first argument. The command to get the first argument is as following:

idaapi.get_dword(ida_dbg.get_reg_val("EBP")+8)

The general for any argument is (change argument_position to the position you want, starting from 0):

idaapi.get_dword(ida_dbg.get_reg_val("EBP") + 8+4*<argument_position>)

You can also use idaapi.get_byte, it work the same as idaapi.get_dword but each argument always contains 4 byte (for stack alignment) so i use get_dword.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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