Concept
In x86/x86_32, arguments are usually pushed onto the stack right-to-left before a call. The last value pushed becomes the first parameter inside the function (at [ebp+8]). On __thiscall member functions, ecx holds the this pointer and is not pushed.
Example from encrypt_func
.text:003E37C8 push 1 ; enable_xor (last pushed -> highest arg)
.text:003E37CD lea eax, [ebp+var_28]
.text:003E37CD push eax ; arg_10
.text:003E37CE push [ebp+var_34] ; buffer_2_pointer
.text:003E37D1 lea ecx, [esi+0C40h] ; ecx = this (not pushed)
.text:003E37D7 push [ebp+var_38] ; buffer_1_pointer
.text:003E37DA push 0A8h ; plaintext_buffer_length
.text:003E37DF push ebx ; plaintext_buffer_pointer (first stack arg)
.text:003E37E0 call encrypt_func
Inside encrypt_func, the declaration is:
plaintext_buffer_pointer= dword ptr 8
plaintext_buffer_length= dword ptr 0Ch
buffer_1_pointer= dword ptr 10h
buffer_2_pointer= dword ptr 14h
arg_10= dword ptr 18h
enable_xor= byte ptr 1Ch
Because ebx was pushed last, it sits at [ebp+8] as plaintext_buffer_pointer. 0A8h is at [ebp+0Ch], etc.
Takeaway
- Work backwards from the
callinstruction. - The final
pushbeforecallis parameter 1 at[ebp+8]. - Watch for
lea ecx, ...before the call — that usually signals__thiscall.