如何在 IDAPython 中提取具有位移的操作数的结构?

逆向工程 艾达 x86 蟒蛇
2021-07-02 02:26:33

考虑以下指令: 8D 8C 4E B0 2F FF FF LEA ECX, [ESI+ECX*2-0xD050]

使用 IDAPython,如何提取第二个操作数的结构?我想知道这样的事情:

  • ESI 是基址寄存器
  • ECX 是索引寄存器
  • 2 是指数常数
  • -0xD050 是位移常数

如果我必须一起进行一堆 IDAPython API 调用,那也没关系。到目前为止,我不得不求助于字符串解析,我真的很想摆脱它。


我发现的最相关的 API 函数是idautils.DecodeInstruction(),但它似乎并没有完全覆盖第二个操作数的结构。我的探索见下文:

i = idautils.DecodeInstruction(<ea from above>)

# operand type
assert i.Op2.type == idc.o_disp

# operand value type
assert i.Op2.dtyp == idc.dt_dword

# operand flags
assert i.Op2.flags == idc.OF_SHOW

# structure of o_displ operand is like:
#
#    Memory Reg [Base Reg + Index Reg + Displacement].

def get_reg_const(reg):
    '''
    fetch register number from string name.
    '''
    ri = idaapi.reg_info_t()
    idaapi.parse_reg_name(reg, ri)
    return ri.reg

# we probably expect to find these constants in the operand structure
assert get_reg_const('ecx') == 1
assert get_reg_const('esi') == 6

# the operand structure
assert unsigned2signed(i.Op2.addr)  ==  0xD050  # displacement
assert i.Op2.n          == 1   # operand number
assert i.Op2.phrase     == 4   # "number of register phrase", don't know what this means
assert i.Op2.reg        == 4   # "number of register", don't see how this applies
assert i.Op2.specflag1  == 1   # unknown interpretation, could be "ecx"!?!
assert i.Op2.specflag2  == 78  # 0x4E, unknown interpretation
assert i.Op2.specflag3  == 0   # probably empty
assert i.Op2.specflag4  == 0   # probably empty
assert i.Op2.specval    == 0x200000  # unknown interpretation
assert i.Op2.value      == 0   # "outer displacement" (none here)
1个回答

似乎没有一个优雅的解决方案。似乎是如果你会使用C编写插件,你就可以打电话sib_basesib_indexsib_scale以获取信息。

下面介绍了如何在 Python 中执行此操作。

from idautils import DecodeInstruction
from idaapi import get_reg_name

ea = 0x20AC5 # Assuming this ea is a lea
i = DecodeInstruction(ea)

hasSIB = i.Op2.specflag1
sib = i.Op2.specflag2

if hasSIB:
    base = sib  & 7
    index = (sib >> 3) & 7
    scale = (sib >> 6) & 3
    size = 4 if i.Op2.dtyp == idaapi.dt_dword else 8
    print '[{} + {}{} + {:x}]'.format(
        get_reg_name(base, size),
        get_reg_name(index, size),
        '*{}'.format(2**scale) if scale else '',
        i.Op2.addr
    )

示例输出: [ebx + eax*4 + 8c]