Recently, in response to a customer incident we needed to reverse engineer a malware sample of WhiteRabbit ransomware that proved to be tricker than expected. As we’ll see, this sample maps a PE into memory with a stomped header, making it hard to reverse engineer.
The fix-stomped-imports
Binary Ninja plugin introduced below will allow us to reconstruct the Import Address Table so that we can statically see what API calls are being made by the malware.
GitHub: https://github.com/nettitude/binja-fix-stomped-imports
Upon receiving the sample, we started with the basics and after some simple triage looked to see if the sample was packed. We open it up in x64dbg and placed a breakpoint on VirtualAlloc & friends after hitting the PE entry point and sure enough, we saw a Read-Write-Execute (RWX) allocation that gets called into from the source PE.
Dumping the memory region to a file however produces an interesting result, as the region doesn’t initially look like a PE as it has no recognisable headers such as the MZ header or section table. Scrolling through the region however sets the spidey-senses tingling as it smells like a PE. There are areas with bytes that start at page-aligned offsets with null padding between them, as well as a region with some strings that looks like an Import Table.
We can open up and rebase the dumped file in a decompiler like Binary Ninja to start to better understand what the second stage is doing, however as the dump is not a valid PE we have to load it as straight up shellcode.
Obviously, we don’t have any sections like a PE, but the sample doesn’t appear to be simple shellcode either as it makes calls to offsets similar to the way a PE does, but without the headers we have no way of knowing what these calls are.
Looking at the offsets we see something that resembles an Import Address Table (IAT).
If we return to debugging the sample after it calls into the allocated region, we can see that there are indeed calls to API functions, and following the offset we see what looks like the IAT.
We want to apply this table to our dump in Binary Ninja, luckily the Binary Ninja API is great to work with, and it provides easy functions to add segments, sections, update data variables as well re-perform the analysis.
To that end, we created a plugin for Binary Ninja that does exactly that. Fix-stomped-imports will take an IAT dump and then create the relevant sections and fix the imports before updating the analysis.
parse_iat_dump
will take a paste of the IAT from x64dbg (or similar tools) and parse it to extract the imports and their offsets.
create_memory_regions
creates the relevant segments and sections for the imported functions at the correct memory regions by iterating over the values and creating a section that is page aligned with the lowest address in the imports and the appropriate length. We then need to update the analysis so that when we add imports Binary Ninja knows what we are updating in this new section.
fix_imports
then gets the function from the platform type libraries, if it exists, and update the data variable at the import address with the function prototype so that the correct arguments, etc, are shown. The name of the data variable is also updated to that of the function, as in a real IAT.
Once this has completed, it should be much easier to see what is going on in the sample. Trying this out, we copy the lines from the IAT in x64dbg.
We invoke our plugin from the plugins menu and then just paste in the IAT dump from x64dbg:
As expected, this successfully creates the externs
section and the IAT.
The function bodies of the imports are still empty, but that doesn’t matter for our analysis as the function name and arguments are populated in our sample and we can now much more easily reverse engineer exactly what it is doing.
The code for this plugin is available on GitHub and the plugin itself has been submitted to the Binary Ninja team for approval.
GitHub: https://github.com/nettitude/binja-fix-stomped-imports
Many thanks to the Binary Ninja team for their assistance in writing the plugin as well as to @herrcore from OALabs for his assistance with a similar sample. @herrcore was also able to determine that similar samples do in fact use a modified, obfuscated PE header, and has content available to go over this.