4

IDA Pro 7.6

Static disassembly of ARM executable.

This binary has 40K functions or so, and no symbols. So all the functions are sub_49FFA etc.

However, 90% of these functions have a call to a debug logging function, which says what the function is.

e.g.:

sub_49FFA() {
     ....do stuff......
     debug_message(0x9887, "save_config()","Error:  Cannot save config: %s","No disk space!");
     ....do stuff......     
}

So we know sub_49FFA is really save_config()

As this is ARM, this means arg2 is the r1 register pointing to a string. This is shown in the disassembly with LDR r1,=save_config ; "save_config()" immediately before BL debug_message

Is there a way for a script to find all the code refs to debug_message(), and rename the calling function (if not already named) with the string that names the function?

Spent an hour searching, but I'm not skilled in IDA scripting or python so any help would be appreciated.

Up until now, I've been exporting the binary as pseudo code, parsing it to make an .idc script to name the functions, but it's pretty error prone, takes a long time and I often have to load multiple versions of the executable for analysis. Must be a better way.

edit: Thanks to Rolf Rolles for their helpful suggestion of some script functions that might help.

I've been able to cobble something together using the below

ref_addr=0x01D7448

args = idaapi.get_arg_addrs(ref_addr)
func_name = get_func_name(ref_addr)
func_addr = get_name_ea(0, func_name);

print ("func_name for %x is %s (%x)" % (ref_addr,func_name,func_addr))
if args:
   arg_offset = args[2]
   print ("arg_offset: %x   " % arg_offset)     
   debug_func_dcd = idc.get_operand_value(arg_offset, 1) 
   debug_func_addr = idaapi.get_dword(debug_func_dcd)
   print ("debug_func_dcd: %x      debug_func_addr: %x    " % (debug_func_dcd,debug_func_addr))
   str_type = idc.get_str_type(debug_func_addr)
   debug_func = get_strlit_contents(debug_func_addr,-1,str_type).decode("utf-8")
   #print ("value: %x" % debug_func)   
   print ("value: %s" % debug_func)
   set_name(func_addr, str(debug_func), 0)

(I'm sure this isn't best practice and doesn't have error checking etc - just a proof of concept).

However, unlike my original question many of the binaries actually have the debug function string as argument 5. i.e. not held in a register at time of debug call, but rather placed on the stack.

.text:001D7414                 LDR             R4, =aUsbPower_0 ; "usb_power_reset"
.text:001D7434                 STR             R4, [SP,#0x30+function] ; function

.text:001D7448                 BL              debug_log

arg_offset = args[4] gives 001D7434, but idc.get_operand_value(arg_offset, 1) (i.e. [SP,#0x30+function]) gives 0

I don't think I can read register R4 even though we know what the value always is unless we're in a live debug session.

Can I evaluate this in a different way to get the address of R4 being set?

Not sure about the etiquette here - so if this has to be a separate question please let me know.

edit2:

In the end, I decided to simply use DumpPseudoCode and parse it to find the right strings I need to name the function properly.

That means most of the entire executable gets pseudocode generated for it, and probably isn't terribly efficient.

Additionally, because the debug strings contain the source file name e.g. "/src/network/upnp/upnp_server.c" I added logic to create folders for the functions and move all functions to the correct place.

That makes things much better. I just have an excuse to go and make a coffee while the script runs.

2 Answers 2

5

Sounds like you probably want:

  • idautils.CodeRefsTo with flow set to False to get all callers to the logging function
  • ida_typeinf.get_arg_addrs to get the addresses, at some particular call site, of where the function arguments are prepared (pushed or moved into the relevant register / stack slot)
  • idautils.DataRefsFrom to get the address of the string referenced by a particular instruction (the one that prepares the string argument you're interested in)
  • ida_nalt.get_str_type to get the type of the string at a particular address
  • ida_bytes.get_strlit_contents to get the contents of a string by string type and address
  • ida_name.set_name to set the name of the calling function

In response to your edit, that is a bit of a tricky one. IDA isn't wrong by giving you the answer it gave -- that's the address where the value is moved onto the stack in advance of the call. However, you really want the address where the value is moved into R4. IDA won't give this to you directly; you'd have to employ some hand-written data flow analysis. However, Hex-Rays can give you the information you need.

  • Use ida_hexrays.decompile to obtain a cfunc_t object for the calling function
  • Obtain its eamap_t object by calling cfunc_t.get_eamap
  • Use the address where the call to the logging function takes place as a key into the eamap_t
  • Iterate through the vector of cinsn_t objects that you get back looking for an instruction that contains a cot_call object
  • Once you find the call expression x, access its carglist_t object via x.a
  • Extract the string from x.a[N], where N is the index of the argument you want
2
  • Thanks for this - it was helpful. Updated my question with a part I am stuck on if you have any further thoughts.
    – Tis Me
    Feb 13, 2023 at 16:14
  • Many thanks for your updated answer. I didn't realize it would be so simple :P I'm definitely going to look into this, as well as a few (though probably clunky) idea. Thanks again
    – Tis Me
    Feb 14, 2023 at 11:52
3

If you don't want to code yourself such a script, although it looks like it might be the best, you can give a try to the IDA Magic Strings plugin (that I wrote myself). It is often smart enough as to get function names from debugging messages.

1
  • 1
    Thanks for this - will look into it :)
    – Tis Me
    Feb 14, 2023 at 11:53

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.