During malware analysis we can often encounter payloads in form of DLLs. Analyzing them dynamically may not be very handy, because they need some external loaders to run. Different researchers have different tricks to deal with them. In this post I will describe some of my tricks for patching DLLs, so that they can run as independent executables.
Usually we can encounter 2 cases:
- meaningful code starts in one of the exported functions
- meaningful code starts in DllMain
UPDATE: For the case 2, I made a tool that makes automated conversion. You can find it here.
To illustrate those cases with real-life examples I will use 2 malware samples.
First case – represented by Bunitu Trojan (b0a91e1f91078bad48252edc989e868e) and second case: represented by Petya/Mischa dropper (c8e4829dcba8b288bd0ed75717214db6).
Those DLLs have been unpacked from their loaders/crypters – but in order to keep things simple I am not gonna describe here the full process of unpacking.
In both cases we will start from editing the field Characteristics in the File Header and removing the flag indicating that the file is a DLL. I will do it using PE-bear:
I changed the value:
Case #1: When the meaningful code starts in the exported function
In this case, one more modification is required before we save the file.
We have to change the entry point in order to point the appropriate function from export table – the one where the execution of the meaningful code starts. We need to find the Function RVA:
Then, follow it:
On the Disasm tab we can see the code of this function. Now, we should redirect Entry Point to it’s beginning:
And save the file as EXE:
In case of Bunitu Trojan, this function does not take any parameters, so we not need to fill anything more. Now, we can run the saved file like a normal executable (see the patched version at malwr).
Case #2 – meaningful code starts in DllMain
This time we not need to change the Entry Point – just after changing Characteristics in File Header save the file and load it under a debugger (I will use OllyDbg).
Similarly to the main function in typical executables, DLLs have their DllMain function that is executed automatically when they are loaded to the memory (the same function is also executed on few more events – i.e. on DLL unloading).
Let’s recall it’s header:
BOOL WINAPI DllMain( _In_ HINSTANCE hinstDLL, _In_ DWORD fdwReason, _In_ LPVOID lpvReserved );
As we can see, the function takes 3 arguments. The first one (hinstDLL) is the handle to the memory area where the DLL has been loaded. Second stores a value that indicates the reason why the DllMain has been triggered. Read more here.
To make our patched DLL run properly, we must take care that it’s arguments will be filled with proper values – especially important are the first two that I mentioned.
The first argument: hinstDLL – must contain the module handle (ImageBase). Second usually should be filled with 1 – to emulate DLL_PROCESS_ATTACH.
That’s how the Entry Point of the dumped executable looks:
Let’s add a code that will overwrite the arguments with valid values. I will utilize some free cave for this purpose. Fortunately, there is enough space at the end of the code section:
I am gonna do some patching in order to redirect execution to this place. We need 5 bytes for the jump – so, let’s remove/rearrange some code in order to gain the space (if we are lacking in space, some instructions can be also moved to the cave, along with the added code):
Patched – step 1:
Patched – step 2:
I redirected execution to the mentioned cave. We will copy aside the address that is just after the added jump, to go back to this place later.
Now let’s fill the cave with needed code.
To fill the first argument with the valid ImageBase I will copy the value from Process Environment Block (pointed by FS:[30]) . This is example of the code that do this job:
MOV EAX, [FS:0X30] ; copy to EAX handle to PEB MOV EAX, [EAX+0X8] ; copy to EAX the field with ImageBase MOV [EBP+0X8], EAX ; copy the content of EAX into the first argument
Now, let’s fill the second argument with a vale 1, emulating that the DLL is loaded:
MOV DWORD [EBP+0XC], 0X1
The third argument can be filled with NULL:
MOV DWORD [EBP+0X10], 0
Added code:
Now only returning jump is remaining:
And we can save the modified executable:
Now we can run/debug the saved file as a standalone executable.
Ending note
Of course the described techniques are not a silver bullet and they may not cover all the cases you encounter. My goal was just to provide some examples and inspiration for experiments. However, those simple tricks worked for me in many cases making the work much easier.
Appendix
https://en.wikipedia.org/wiki/Win32_Thread_Information_Block
another way to do this is to write a winmain() func in asm and shim it into the bottom of the dll, then have it point to dllmain with the correct arguments (this way you can dynamically point at different “start” functions). Fun fact, this also works in reverse (flip the bi from exe to dll, shim in dllmain, point it at the existing winmain, then point EP at dllmain) and then you have an exe as a dll injection payload via reflection 🙂
interesting and useful tip, thanks 🙂 however it will require adding more code. in more complex cases for sure it is worth – i.e. if we have to run more than one exported function.
Reblogged this on Test.
Would be cool if you could write a post that includes x64dbg (http://x64dbg.com)…
Pingback: How to turn a DLL into a standalone EXE — Test
I have to say, this is a pretty impressive blog. I’ve a pretty thorough search on the Internet and found nothing close to being this informative. -Thank you
I am happy that you enjoyed it, it’s always very motivating to hear some positive feedback! Thanks 🙂
Wow. Superb.
Cave Not found!
Could not convert!
why?
as the message says, there was no space to add the stub necessary for the conversion.
The code cave editing would be easier with multiasm (Multiline Ultimate Assembler):
https://ramensoftware.com/multimate-assembler