2

I have large PE file (C++), which has lots of indirect function calls using vtable and jmp dword.

How can I generate a call tree for such functions ? (like with IDA User xrefs chart menu).

Here is an example of disassembled code:

UPX1:2401135C sub_2401135C proc near                  ; CODE XREF: sub_2401263C+Cp
UPX1:2401135C                                         ; StartAddress+21p ...
UPX1:2401135C test    eax, eax
UPX1:2401135E jz      short locret_2401136A
UPX1:24011360 call    ds:off_24057054
UPX1:24011366 or      eax, eax
UPX1:24011368 jnz     short loc_2401136B
UPX1:2401136A
UPX1:2401136A locret_2401136A:                        ; CODE XREF: sub_2401135C+2j
UPX1:2401136A retn
UPX1:2401136B ; ---------------------------------------------------------------------------
UPX1:2401136B
UPX1:2401136B loc_2401136B:                           ; CODE XREF: sub_2401135C+Cj
UPX1:2401136B mov     al, 2
UPX1:2401136D jmp     sub_2401141C
UPX1:2401136D sub_2401135C endp

User xref chart can show only call ds:off_24057054 branch if I will include data xrefs. What can I do to see the jmp sub_2401141C branch ?

1 Answer 1

4

In these cases the target of the calls is generated at runtime and can not be determined statically (with very few exceptions).

You need to record this information while running the binary and incorporate the extra info into your IDB by using the AddCodeXref API or alike.

If you don't want to write your own thing, this plugin can assist you in doing that and more: funcap

EDIT

In the case of jmp subxxx the target of the jump is well defined. You could programatically go throught the whole binary, extract the target of the jump and add the cross reference.

I hacked something together, give it a try.

def is_external_jmp(ins_ea):
    """
    True for JMPs between functions.
    NN_JMP (86): jmp sub_xxx (0xE9 + offset) or jmp loc_xxx (0xE9 + offset)
    """    
    decode_insn(ins_ea)

    if cmd.itype == NN_jmp:
        # HACK: GetOperandValue returns the target
        # address, not the offset (as I would expect)
        target = GetOperandValue(ins_ea, 0)
        (s, e) = current_function_boundaries(ins_ea)

        if target < s or target > e:
            # Not within the current function
            return True

    return False


def current_function_boundaries(ea=None):
    """
    Convenience function
    @returns: boundaries or None
    """
    if not ea:
        ea = ScreenEA()

    f = get_func(ea)

    if not f:
        # Probably called outside a function
        return (None, None)

    else:
        return (f.startEA, f.endEA)


def main():

    for f_ea in Functions():
        for ins_ea in FuncItems(f_ea):
            if is_external_jmp(ins):
                # One of these "jmp sub_xxx"
                target = GetOperandValue(ins_ea, 0)
                AddCodeXref(ins_ea, target, fl_CN)



if __name__ == '__main__':
    main()
3
  • In this example the call target is not generated at runtime. It uses a jump instruction with hardcoded value UPX1:2401136D jmp sub_2401141C
    – Victor
    Apr 27, 2015 at 17:14
  • I just got your question correctly and updated my answer (was reading from a phone). Apr 27, 2015 at 17:52
  • I knew funcap, but you script is another treasure in my toolset now! Thank you. May 4, 2015 at 9:46

Your Answer

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

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