MindshaRE is our periodic look at some simple reverse engineering tips and tricks. The goal is to keep things small and discuss every day aspects of reversing. You can view previous entries by going through our blog history or querying a search engine for dvlabs mindshare.
If there's one thing I've noticed about working with as many reverse engineers as I have, it is that we all use our tools differently. Many of the best reversers I've met barely touch a debugger and prefer to do all their analysis statically. Others, like myself, prefer to have static and dynamic tools providing each other with complementary data. Well, for those of you who prefer the static method, here's a quick way to make IDA a bit more useful when dealing with dynamic code flow.
Quite often (especially with C++) you'll run across dynamic calls such as:
...and so forth. Recently, I came across a piece of software that had a 40+ case switch statement. In each case, they would load a unique code pointer into a register. All the cases then lead to a basic block that called that pointer. IDA did not provide me the cross references to all of the code locations. In fact, here is a graph of all paths from the switch statement to a sprintf call:
With a bit of IDAPython, we can quickly add all the proper cross references (edges) and get a more useful idea of the possible paths to sprintf.
To do this, we can utilize the idaapi.add_cref function. The function definition from idaapi.py is:
def add_cref(*args): """add_cref(ea_t frm, ea_t to, cref_t type) -> bool""" return _idaapi.add_cref(*args)
Pretty simple, it takes an address source, an address destination, and a code flow type. For our example, we are going to want to give it a flow type of 'call near' which is defined by the idaapi.fl_CN constant. For reference, the cross reference types defined in idc.py are:
# Flow types (combine with XREF_USER!): fl_CF = 16 # Call Far fl_CN = 17 # Call Near fl_JF = 18 # Jump Far fl_JN = 19 # Jump Near fl_F = 21 # Ordinary flow
So, basically what I did with the application I was looking at, was call idaapi.add_cref(address_of_call_reg32, address_of_func, idaapi.fl_CN) for each function address loaded in each switch case. After doing so, the path from the switch function to sprintf looked a bit more accurate:
These graphs were created using the GraphViewer class (which you can read more about on the Hex-Rays blog here) If you create a dictionary that maps node IDs to function addresses when AddNode is called, you can make the nodes clickable with code inside the OnDblClick handler:
def OnDblClick(self, node_id): f_addy = self.id_to_f[node_id] idc.Jump(f_addy)
This all becomes a lot more useful when you combine the ability to add edges with code for parsing RTTI and scraping vftables.