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.
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.
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.
How it calls file functions: paths, callgraph.
(Yeah, C++ demangler would be cool.)
How it calls srand/rand functions: paths, callgraph.
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.
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.
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.