How to turn a DLL into a standalone EXE

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:

  1. meaningful code starts in one of the exported functions
  2. 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:

edit_fhdr.png

I changed the value:

changed_fhdr.png

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:

exports.png

Then, follow it:

follow_rva

On the Disasm tab we can see the code of this function. Now, we should redirect Entry Point to it’s beginning:

redirect

And save the file as EXE:

save_as

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:

setup_ep

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:

free_space.png

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):

place_to_patch

Patched – step 1:

place_to_patch2

Patched – step 2:

patched_step2

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:

added_code

Now only returning jump is remaining:

returning_jump

And we can save the modified executable:

copy_mod

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

https://en.wikipedia.org/wiki/Process_Environment_Block

About hasherezade

Programmer and researcher, interested in InfoSec.
This entry was posted in Malware, Techniques, Tutorial. Bookmark the permalink.

11 Responses to How to turn a DLL into a standalone EXE

  1. vyrus001 says:

    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 🙂

    • hasherezade says:

      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.

  2. mrexodia says:

    Would be cool if you could write a post that includes x64dbg (http://x64dbg.com)…

  3. Pingback: How to turn a DLL into a standalone EXE — Test

  4. Stingered says:

    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

  5. avmcomputers says:

    Wow. Superb.

  6. alex234r32r says:

    Cave Not found!
    Could not convert!
    why?

  7. Paul says:

    The code cave editing would be easier with multiasm (Multiline Ultimate Assembler):
    https://ramensoftware.com/multimate-assembler

Leave a comment