3

I would like to determine if an instruction a) reads from or b) writes to a memory address. I can currently think of two approaches in IDA:

  1. Looking for a list of mnemonics (better: particular opcodes?), e.g. for x86 see here and manually map each and every one of them to read, write.

    This does not seem to be very elegant to me.

  2. Work with the idc.GetOpType(ea, n) function and check for the relevant o_* constants (see here), i.e:

    o_mem      = idaapi.o_mem       # Direct Memory Reference  (DATA)      addr
    o_phrase   = idaapi.o_phrase    # Memory Ref [Base Reg + Index Reg]    phrase
    o_displ    = idaapi.o_displ     # Memory Reg [Base Reg + Index Reg + Displacement] phrase+addr
    

    Yet, these do not tell me if the instruction reads from or writes to memory.

Is there a better way to find out what I want? Ideally a way that works for multiple CPU architectures?

  • Is it required to use IDA? Do you want to do static or dynamic analysis? – Ta Thanh Dinh Sep 15 '16 at 16:47
  • @TaThanhDinh : static analysis. IDA would be nice but if there is another way, that is fine too. – langlauf.io Sep 16 '16 at 7:36
  • 1
    In this case, you may find Xed useful. It gives high-level API to determine whether an instruction may read or write memory. I dont know whether Capstone has similar API. – Ta Thanh Dinh Sep 16 '16 at 8:32
  • Sounds like a good plan. – langlauf.io Sep 16 '16 at 8:35
4

While the API may be cumbersome, most of the information you are looking for is "readily" available from IDA during static analysis. Some of it is somewhat generic, while other pieces require different handling per processor type.

To check if an operand performs read or write access to memory, you can use the following:

import idaapi
import idautils
import idc

OPND_WRITE_FLAGS = {
    0: idaapi.CF_CHG1,
    1: idaapi.CF_CHG2,
    2: idaapi.CF_CHG3,
    3: idaapi.CF_CHG4,
    4: idaapi.CF_CHG5,
    5: idaapi.CF_CHG6,
}

OPND_READ_FLAGS = {
    0: idaapi.CF_USE1,
    1: idaapi.CF_USE2,
    2: idaapi.CF_USE3,
    3: idaapi.CF_USE4,
    4: idaapi.CF_USE5,
    5: idaapi.CF_USE6,
}

def print_insn_mem_interaction(ea):
    insn = idautils.DecodeInstruction(ea)

    # The features are needed for operand flags.
    feature = insn.get_canon_feature()

    for op in insn.Operands:
        # You always get 6 operands. Some of them are set to `o_void` to indicate
        # that they are not used.
        if op.type == idaapi.o_void:
            break

        # There are 3 types of memory references in IDA. We want all 3.
        is_mem = op.type in (idaapi.o_mem, idaapi.o_phrase, idaapi.o_displ)

        # Extract per-operand read/write status from the feature.
        is_write = feature & OPND_WRITE_FLAGS[op.n]
        is_read = feature & OPND_READ_FLAGS[op.n]

        if not is_mem:
            # Operand does not access memory.
            continue

        # Ugly line for the display. Sorry.
        action = 'memory {}'.format('/'.join(filter(bool, ('read' if is_read else None, 'write' if is_write else None))))

        print 'Operand[{}]<{}> : {}'.format(op.n, idc.GetOpnd(ea, op.n), action)

Telling the addresses accessed, though, is a different story.

The parsing of o_displ and o_phrase operands is processor-type specific, and is completely undocumented (actually, the docs say "do as you like"). If you are interested in a working parser for x86 and x64, you can check my code here.

If you need to work with any other architecture, I'd recommend using a more script-friendly disassembler. Capstone is probably a good choice on that matter.

And finally, if you need the actual addresses, you will probably have to turn to dynamic analysis.

0

Other than dynamic analysis (like suggested in the comments) there's no way to get that information from IDA.

IDA does not maintain a complete Intermediate Representation (also called intermediate language) and that sort of information is unavailable to IDA, mostly because it's unneeded for implementation of disassemblers. It is however needed for decompilers, so hex-rays decompiler probably implements a more complete IR but doesn't make it accessible to the user.

Usually using IL each assembly instruction is translated into multiple intermediate language instructions, which together achieve the same goal as the original instruction. This turns instructions into operations that are less complex and are easier to manipulate/process.

There are open source tools that provide or implement an IL such as OpenREIL, which you could use outside of IDA (Specifically it has the STM and LDM instructions).

-1

If you have IDA Pro 6.x, then solution is pretty straightforward: https://www.hex-rays.com/products/ida/support/tutorials/tracing.pdf

  • Sorry, I do not understand what you mean. Could you be a bit more specific ? – langlauf.io Feb 17 '16 at 5:44
  • @stackoverflowwww place the cursor inside the disassembly view, right-click menu or main "Debugger" menu, and you will see "Add read/write trace". Hope that helps – assp1r1n3 Feb 21 '16 at 9:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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