4

I am using IDA pro 6.95 (paid version) to RE some PowerPC code. The PowerPC normally uses r1 as the stack pointer, so IDA builds the stack frame and local variables from r1. Parameters are passed in r3, r4, r5, etc. The problem I am having that I see this:

.text: foo:
.text:.set back_chain, -0x30                     // Stack frame
.text: .set var_4, -4
.text: .set sender_lr,  4
<<<function preamble snipped>>>
.text:               mr        r31, r1        // Copy stack pointer to r31
.text:               stw       r3, 8(r31)     // All local variables are relative
.text:               stw       r4, 0xC(r31)   //  to r31, instead of r1, so IDA
                                              //  doesn't recognize them.

And I want to see this:

.text: foo:
.text:.set back_chain, -0x30                     // Stack frame
.text: .set var_C, -C
.text: .set var_8, -8
.text: .set var_4, -4
.text: .set sender_lr,  4
<<<function preamble snipped>>>
.text:                  mr        r31, r1        // Copy stack pointer to r31
.text:                  stw       r3, var_8(r31) // All local variables are
.text:                  stw       r4, var_C(r31) //  recognized and nameable.

What I want is to convert all, e.g. 8(r31) local variable references in the function to normal var_8(r31) syntax. I do not want to have to change each reference manually. Is there a way to:

  • Convince IDA that r31 is the stack pointer?
  • Change all 8(31) to var_8(r31) in a single command?
  • Otherwise get the effect I am looking for in a way I haven't thought of?
2
  • The old PPC ELF spec (refspecs.linuxbase.org/elf/elfspec_ppc.pdf) defines r31 as 'Used for local variables or "environment pointers"', so maybe this is what you are seeing in this code. So it might help to specify which platform the code you are analyzing is supposed to run on.
    – user22967
    Commented Jan 27, 2018 at 18:53
  • @MichaelEngel Not OP, but I'm also seeing this in the main() function of an executable that was compiled using CodeWarrior. It does the same thing as OP's function: the SP is copied to r31, and r31 is used as the SP in the rest of the function.
    – Léo Lam
    Commented Jan 27, 2018 at 19:24

2 Answers 2

1

Disclaimer: I'm not going to create a working code example here, and I did not test this on PowerPC for your specific purpose. YMMV

I could think of two ways of doing that. First will be the manual approach by using some IDAPython magicry to manually force all offsets based on r31 to the stack frame structure. Second (which is the one likely used) is using a designated API just for that.

You'll need to:

  1. Get the hidden stack frame structure ID for the function you're running on
    This can easily be done by calling the ida_frame.get_frame function with any address within the desired function. This function returns an ID used to identify this structure in the IDB.

  2. go over instruction by instruction and identify all dereferences of r31
    There are many approaches here. From getting the disassembly string and parsing actual text to operand specific APIs and more. So i'll leave this as the first exercise for the reader.

  3. Set those dereferences to dereferencing the stack structure
    This, again, has several APIs to pick from, OffOpEx is an example. If you need to create new stack variables as you go, there are multiple functions for this as well.

The second approach would be using one of two APIs designed for the purpose. the two APIs are called idc.define_local_var and ida_frame.add_regvar.

define_local_var is defined as follows:

def define_local_var(start, end, location, name):
    """
    Create a local variable
    @param start: start of address range for the local variable
    @param end: end of address range for the local variable
    @param location: the variable location in the "[bp+xx]" form where xx is
                     a number. The location can also be specified as a
                     register name.
    @param name: name of the local variable
    @return: 1-ok, 0-failure
    @note: For the stack variables the end address is ignored.
           If there is no function at 'start' then this function.
           will fail.
    """

And will do all the work of locating the stack structure and defining new members if needed. This is probably the first thing you want to play with, however you'll still need to iterate over all the instructions and identify r31 offsets manually.

The add_regvar function is called by define_local_var and is responsible to applying the variable created by define_local_var to instructions. You can read more about it here

1

I was faced with similar issue and found the following workaround:

  1. Create specific structure with field offset equal to your r31 offset
  2. Use "Structure offset (T)" for each interesting instruction.

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.