I posted something similar to this on openrce, but I figured I'd shout
out to all of you as well.
As you've probably noticed, gcc compiled binaries allocate the stack
space for function call arguments early on, and then mov instead of
push. For a function
function(arg1,arg2)
It is implemented as:
mov [esp+4], arg2
mov [esp], arg1
call function
Even though this is still cdecl, it differs from the push/call most
people are used to. Apparently this is referred to as SUB/MOV method
(thx Ero!) and is default for gcc. When IDA disassembles binaries
using this method, it looks somewhat weird:
mov [esp+3C218h+var_3C214], arg2
mov [esp+3C218h+var_3C218], arg1
call function
I'm sure you've seen this all over the place, as this is how
objc_msgSend() gets it's arguments. My initial reaction to this was to
write a script that would jump to each function that called msgSend to
rename the [esp] variable to recipient and the [esp+4]. I did this by
taking the top two variables in the frame and renaming them. This was
a bad idea. What ended up happening is that in some functions the
stack offset changed part way through the function, causing the names
to be off about half the time.
The solution? OpAlt(). The Edit->Operand type->Manual.../Alt-F1. This
does not propagate like a standard variable renaming does, it just
changes the one instance of that operand. This will cause a little bit
more work when scripting, but the results are somewhat more accurate.
Renaming the variable in the frame is still ok, but only if you check
that the stack offset for all outgoing calls are the same. This can be
done easily by collecting all the calls to msgSend in the function and
comparing the offsets, then if they differ, a simple backtrace up to
the non-IDA values for the operands.
RegNo = {-1:"R_none",0:"R_ax",1:"R_cx",2:"R_dx", 3:"R_bx", 4:"R_sp",
5:"R_bp", 6:"R_si", 7:"R_di", 8:"R_r8", 9:"R_r9", 10:"R_r10",
11:"R_r11", 12:"R_r12", 13:"R_r13", 14:"R_r14",15:"R_r15"}
# Recipient => [esp]
rec_lambda = lambda cursor, opnum: GetOpType(cursor, opnum) == o_phrase and \
RegNo[get_instruction_operand(cvar.cmd,opnum).reg] == "R_sp"
# Selector => [esp+4]
sel_lambda = lambda cursor, opnum: GetOpType(cursor, opnum) == o_displ and \
RegNo[get_instruction_operand(cvar.cmd,opnum).reg] == "R_sp" and \
get_instruction_operand(cvar.cmd,opnum).addr == 4
note: GetSpd() gives you the stack offset. It's not easy to google for
if you didn't know that offhand.
|