6

Say given the following line in Ida Pro:

mov     [rsp+3F8h+var_3F8], 0

How can I parse and access the items inside the [ ]? What I tried:

  • idc.GetOpnd(addr, n) # returns a string '[rsp+3F8h+var_3F8]'
  • idc.GetOperandValue(addr, n) # returns 4, which is explained in the idc.py file as follows

def GetOperandValue(ea, n): """
Get number used in the operand

This function returns an immediate number used in the operand

@param ea: linear address of instruction @param n: the operand number

@return:

value operand is an immediate value => immediate value

operand has a displacement => displacement

operand is a direct memory ref => memory address

operand is a register => register number

operand is a register phrase => phrase number

otherwise => -1
"""

How can I access the elements of the 'phrase', i.e. the rsp, 3F8h, and var_3F8? I am looking for something like this:

my_op_phrase = idc.ParseOperandPhrase(ea, n)
my_op_phrase[0] #-> 'rsp'
my_op_phrase[0].type #-> idaapi.o_reg

my_op_phrase[1] #-> 0x3F8h
my_op_phrase[1].type #-> idaapi.o_imm

my_op_phrase[2] #-> 'var_3F8'
…

Is this even possible or am I misunderstanding something?

3 Answers 3

4

Note that the original assembly instruction was probably mov [rsp+4], 0(*). This is why idc.GetOperandValue returns 4.

Especially with older compilers, that used push and pop a lot, the value of rsp varies wildly during the execution of a function. What is esp+8 now would be esp+12 after a push; what would be rsp+8 now would be rsp after a pop. So, while reading a piece of (plain) assembly code it's very difficult to keep track of which stack location gets accessed when.

(This has improveed recently; x64 abis use registers to pass parameters so the code doesn't push and pop as much anymore, and compilers like gcc make enough space on the stack and directly put parameters in addresses relative to esp even on 32 bit, so esp/rsp don't change that much anymore. But still, there's a lot of old code to reverse.)

To improve the situation, IDA assigns variable names to stack locations, like your var_3F8. Whenever an instruction does some sp relative addressing, IDA uses the variable name, and emits an additional offset to account for changes to the stack pointer since the function start. So if your original code looks like

mov [rsp+8], rax
sub rsp, 128
mov [rsp+136], rbx
push rcx
mov [rsp+144], rdx

it accesses the same memory address in every case. Ida converts this to

mov [rsp+0+var_8], rax
sub rsp, 128
mov [rsp+128+var_8], rbx
push rcx
mov [rsp+136+var_8], rdx

However, these changes are display only, they do not change your binary! Getting the operands will still return 8, 136 and 144, not the values ida displays to you.

If you want to analyze this automatically, you can either keep track of the stack pointer offset yourself, and adjust the result of GetOperandValue accordingly, or you'll have to use the python string functions on the output of GetOpnd, throw away the middle part, and compare the right part (the variable names).

(*) which seems a bit strange now i think of it, since you're obviously using 64 bit, as your stack pointer is rsp, which would hint at 8 byte alignment.

3
  • 1
    Is this the "current stack delta" what you are referring to? It can be retrieved with GetSpd.
    – Jongware
    Commented Jun 1, 2015 at 15:07
  • @Guntram Blohm: Are you sure about the reason why GetOperandValue() returns 4? The idc.py says: "operand is a register phrase => phrase number". I understand this as: "if there is a register 'phrase', GetOperandValue() returns the phrase number." I don't know though what is meant by this phrase number. What do you think? Commented Jun 1, 2015 at 16:27
  • I don't have access to my office computer at the moment, where IDA is installed, but i'd assume register phrase is almost the same as register name, so each processor register is assigned a number, and ida returns that number if the operand is a register name, or something similar denoting a register on exotic hardware. Commented Jun 1, 2015 at 16:44
3

Assuming addr is the EA of mov [rsp+3F8h+var_3F8], 0:

re.findall('\[(.*)\]', idc.GetDisasm(addr))[0].split('+')

yields the list

['rsp', '3F8h', 'var_3F8']
4
  • This works until you hit a subtraction or multiplication, e.g. mov edx, [eax+ecx*4]. Regex is not really a great option here, but unfortunately it doesn't seem like IDA really gives us much of a choice.
    – Polynomial
    Commented Feb 9, 2016 at 10:20
  • "This works until you hit a subtraction or multiplication" -- Yes, but that's not what the question asked :) Commented Feb 9, 2016 at 13:57
  • @JasonGeffner (a) Easier to do regex GetOpnd(ea, 0) == [rsp+3F8h+var_3F8] (b) How to get the value of var_x (guessable) and arg_x (not su much) (c) In cases such as these (*register + displacement) just GetOperandValue(ea, 1) if GetOpType(ea, 1) == o_displ else None == 48. Commented Jul 26, 2016 at 2:17
  • @Polynomial: that type of operand can be detected by GetOpnd(ea,1) == o_phrase and is probably the only kind of operand you would use regex for, except maybe for o_displ. Although, since you can't resolve it to an actual value, it's probably not likely to ever become an issue. Commented Jul 26, 2016 at 3:02
0

If you want to avoid using a regular expression to extract the base, index, scale from that specific operand type, you can use the instruction.op function from the minsc plugin at https://github.com/arizvisa/ida-minsc. Disclaimer: I maintain the ida-minsc plugin.

Python>db.disasm()
'1c00b2f7f: mov rcx, [rbp+57h+var_20]'

Python>ins.op(1)
SegmentOffsetBaseIndexScale(segment=%ss, offset=0x37, base=%rbp, index=None, scale=1)

# If you need the symbols that compose the expression...
# (although, it's not really that useful compared to a real symbolic language)
Python>[item for item in ins.op(1).symbols]
[<class 'register_t' index=31 dtype=dt_word name='ss' position=0+16>, <class 'register_t' index=5 dtype=dt_qword name='rbp' position=0+64>]

# Translate the address if you need to.
Python>ins.op(idc.here(), 1) + 0x1000
SegmentOffsetBaseIndexScale(segment=%ss, offset=0x1037, base=%rbp, index=None, scale=1)

Your Answer

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

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