Developing PE file packer step-by-step. Step 8. DLL’s and exports

Previous step is here.

Our packer can do almost everything already, except one thing - packing binaries with exports. This, in particular, is an absolute majority of DLL files and OCX components. Some EXE files also have exports. Our packer should rebuild export table and place it to available space, to make it accessible for the loader.

We can relax a bit for now - we have to add just a small amount of code to the packer (generally, the same for the unpacker, but it will be in assembler).

Let's begin with the packer (simple_pe_packer project). If a file has exports, we have to count them, so right after lines

write:

PE library makes our life much easier here, so we don't have to go into details of how export structures are organized. Further, we replace lines

to

Since either exports, or relocations, or both, will follow the unpacker and TLS, it is necessary that they do not overwrite TLS or the unpacker. Besides that, we have to move lines

up, because we have to change data size precisely by the number of bytes in the unpacker directly after writing it in case a file has TLS or relocations or exports. Place this piece after lines

By the way, this should have been done at the previous step, when we made the packer process relocations. Now we change lines

to

by the same reason - to prevent exports from overwriting relocations.

It's time to process exports, rebuild their directory and place it to the second added section ("coderpub"):

And again PE library makes our life easier. Now we just remove the line, which was added before:

Now we turn to the unpacker. What can we change? We rebuilt export directory, what else do we need? There is one issue. Unlike EXE file, entry point of DLL can be called by the loader more than once. For example, when creating a new thread, or when a process finishes. And we have the unpacker body at entry point address. The unpacker has completed its work already. If we run it the second time, it will just crash. Therefore we have to make the unpacker to check, if a file was unpacked already, and if it was, then jump to original entry point of the unpacked file. We will allocate space in unpacker code for a variable with 4 byte size, filled with null bytes. We will write original entry point address there after unpacking. Before unpacking we will check if this variable is zero, and if not - this means that the file was unpacked already and we will just jump to the address contained in this variable. Firstly, we create the variable itself and compare it with zero:

Let me explain what this code does. To create a variable inside the code, we could use dd or db directive or other similar one in MASM32. Such directives are not allowed in inline MSVC++ assembler. But we need to create variable of 4 byte size, containing zero somehow! I made it this way: assembler command "add byte ptr [eax], al" takes two bytes and has opcode 00 00. Thus, we get 4 null bytes in a row, if we write two such commands one after another - this is what we need. We have to somehow get first instruction address, taking into account that it can be placed at any virtual address - we have the base independent code. This is performed using "call next2" instruction, which jumps over our fake commands and also puts return address to stack, which equals to address of the command after call. Our instructions follow "call". Now we have their address. Further we check, what is stored at this address (mov eax, [eax]), originally there will be zeros, and unpacker body will begin to execute, because jz next3 instruction will perform jump to the label. If the unpacker has been executed already, we write the original entry point address to the variable at was_unpacked address (which points to our fake commands), and jz next3 check fails. Unpacker body will be finished and jump to original entry point of file will be performed. We have to write the entry point address at was_unpacked address:

That's all, we can build and test our packer. I made a new solution with several projects for testing: two DLL files and one EXE. EXE file is linked to one library statically, and loads other one dynamically, and after this calls several functions from these libraries. Statically loaded library and DLL file itself contain static TLS. (Dynamically loaded libraries should not have static TLS, because it will not be initialized). I added this solution to the archive at the end of the article. I packed both DLLs and EXE file (after that I gave both packed DLL files their original names), and made myself sure, that everything works properly, like original files.

Full solution for this step: Own PE Packer Step 8

Leave a Reply

Your email address will not be published. Required fields are marked *