[IDAPython] Generating nice callgraphs.

As a reverse engineer, you stumble too often in such a problem: a program open/read/close file. And you can easily find functions like CreateFileA(), ReadFile(), etc. But they are buried deep somewhere at the lowest level.

You can render a whole callgraph, but it's usually huge and bulky.

Wouldn't it be cool to see what paths can lead to CreateFileA(), ReadFile(), etc, but only for these couple of functions? Can we limit (or cut) a callgraph?

This is what I did, I wrote an IDAPython plugin that finds all paths for a given functions and renders a (partial) callgraph, which contains only ancestor nodes for a given low-level calls.

N.B.: all files used here are from Windows 7.

Notepad

Let's take Notepad. What paths can lead to file functions?

NPWndProc -> doDrop -> KERNEL32.dll!CreateFileW
NPWndProc -> doDrop -> LoadFile -> New -> CheckSave -> SaveFile -> KERNEL32.dll!CreateFileW
NPWndProc -> doDrop -> CheckSave -> SaveFile -> KERNEL32.dll!CreateFileW
NPWndProc -> CheckSave -> SaveFile -> KERNEL32.dll!CreateFileW
NPWndProc -> NPCommand -> KERNEL32.dll!CreateFileW
NPWndProc -> NPCommand -> New -> CheckSave -> SaveFile -> KERNEL32.dll!CreateFileW
NPWndProc -> NPCommand -> SaveFile -> KERNEL32.dll!CreateFileW
NPWndProc -> NPCommand -> LoadFile -> New -> CheckSave -> SaveFile -> KERNEL32.dll!CreateFileW
NPWndProc -> NPCommand -> CheckSave -> SaveFile -> KERNEL32.dll!CreateFileW
NpOpenDialogHookProc -> KERNEL32.dll!CreateFileW
WinMainCRTStartup -> sub_100003AE4 -> WinMain -> NPInit -> KERNEL32.dll!CreateFileW
WinMainCRTStartup -> sub_100003AE4 -> WinMain -> NPInit -> ProcessSetupOption -> KERNEL32.dll!CreateFileW
WinMainCRTStartup -> sub_100003AE4 -> WinMain -> NPInit -> ProcessSetupOption -> LoadFile -> New -> CheckSave -> SaveFile -> KERNEL32.dll!CreateFileW
WinMainCRTStartup -> sub_100003AE4 -> WinMain -> NPInit -> LoadFile -> New -> CheckSave -> SaveFile -> KERNEL32.dll!CreateFileW
WinMainCRTStartup -> sub_100003AE4 -> WinMain -> NPInit -> ProcessShellOptions -> KERNEL32.dll!CreateFileW
WinMainCRTStartup -> sub_100003AE4 -> WinMain -> NPInit -> ProcessShellOptions -> LoadFile -> New -> CheckSave -> SaveFile -> KERNEL32.dll!CreateFileW
CFileDialogEvents_OnSelectionChange -> UpdateEncoding -> KERNEL32.dll!CreateFileW
NPWndProc -> doDrop -> LoadFile -> New -> CheckSave -> SaveFile -> AnsiWriteFile -> KERNEL32.dll!WriteFile
NPWndProc -> doDrop -> LoadFile -> New -> CheckSave -> SaveFile -> KERNEL32.dll!WriteFile
NPWndProc -> doDrop -> CheckSave -> SaveFile -> AnsiWriteFile -> KERNEL32.dll!WriteFile
...

Full list: here.

Isn't it cool? Of course, you can grep the list of all paths. Want to find a path through func1 and func2? Do: grep func1 | grep func2.

And now let's render this (partial or sliced) callgraph using GraphViz: click here.

dhcpcore.dll

It can send and receive from network using WS2_32.DLL, but can we see top-level functions?

RpcSrvMadcapRenewAddress -> RenewAddress -> RenewMadcapAddress -> GetSpecifiedMadcapMessage -> WS2_32.dll!__imp_recvfrom
RpcSrvCheckServerAvailability -> CheckV4ServerAvailability -> WaitForOfferForDiagnostics -> WS2_32.dll!__imp_recvfrom
RpcSrvMadcapEnumerateScopes -> EnumerateScopes -> ObtainMScopeList -> MadcapDoInform -> GetSpecifiedMadcapMessage -> WS2_32.dll!__imp_recvfrom
RpcSrvMadcapRequestAddress -> RequestAddress -> ObtainMadcapAddress -> GetSpecifiedMadcapMessage -> WS2_32.dll!__imp_recvfrom
RpcSrvMadcapRequestAddress -> RequestAddress -> ObtainMadcapAddress -> RenewMadcapAddress -> GetSpecifiedMadcapMessage -> WS2_32.dll!__imp_recvfrom
RpcSrvRenewIpAddressLeaseEx -> RenewIpAddressLeaseEx -> RenewLease -> GetSpecifiedDhcpMessage -> WS2_32.dll!__imp_recvfrom
RpcSrvRenewIpAddressLeaseEx -> RenewIpAddressLeaseEx -> RenewLease -> GetSpecifiedDhcpMessage -> GetSpecifiedDhcpMessageEx -> ProcessRecvFromSocket -> TryReceive -> WS2_32.dll!__imp_recvfrom
RpcSrvMadcapReleaseAddress -> ReleaseAddress -> ReleaseMadcapAddress -> GetSpecifiedMadcapMessage -> WS2_32.dll!__imp_recvfrom
AcquireParametersByBroadcast -> ReRenewParameters -> DhcpInitRebootState -> RenewLease -> GetSpecifiedDhcpMessage -> WS2_32.dll!__imp_recvfrom
AcquireParametersByBroadcast -> ReRenewParameters -> DhcpInitRebootState -> RenewLease -> GetSpecifiedDhcpMessage -> GetSpecifiedDhcpMessageEx -> ProcessRecvFromSocket -> TryReceive -> WS2_32.dll!__imp_recvfrom
AcquireParametersByBroadcast -> ReRenewParameters -> DhcpRenewState -> RenewLease -> GetSpecifiedDhcpMessage -> WS2_32.dll!__imp_recvfrom
AcquireParametersByBroadcast -> ReRenewParameters -> DhcpRenewState -> RenewLease -> GetSpecifiedDhcpMessage -> GetSpecifiedDhcpMessageEx -> ProcessRecvFromSocket -> TryReceive -> WS2_32.dll!__imp_recvfrom
...

Full list: here.

And now the callgraph: click here.

Wonderfully, dhcpcore.dll can call srand() and rand() functions. Let's see why: paths, callgraph.

Wordpad

How it calls file functions: paths, callgraph.

(Yeah, C++ demangler would be cool.)

FreeCell

How it calls srand/rand functions: paths, callgraph.

And now the script

Here

The networkx library is to be installed: pip install networkx

Modify couple of variables at its beginning. All the info is dumped into IDA output window. Render a PNG file from GV using GraphViz: dot -Tpng 1.gv -o 1.png

My script is a bit raw. Feel free to soup it up.

Further work

Run GraphViz and show the result from IDA window. Pick low-level functions in IDA window as well.

Perhaps, the networkx.algorithms.dag.ancestors function can be used.


List of my other blog posts.

Yes, I know about these lousy Disqus ads. Please use adblocker. I would consider to subscribe to 'pro' version of Disqus if the signal/noise ratio in comments would be good enough.