CodeBreakers Magazine – Vol. 1, No. 2, 2006
gev.tls_original.StartAddressOfRawData, size );
memset ( (void*) gev.tls_original.EndAddressOfRawData,
0,
gev.tls_original.SizeOfZeroFill );
}
safe_to_callback_tls = true;
if ( delayed_tls_callback ) {
TLSCallbackThunk ( TLS_dll_handle TLS_reason
TLS_reserved );
}
}
Once you have done that, it is finally safe to call over to
the original program. You should have a published
external global that will be set up by the packing
application that specifies the original program's entry
point. I will call it
DWORD orig_entry;
which will be a member of GlobalExternVars. It will be
initialized to an RVA and we will fix it up to a VA by
adding the load_address. This done only once on the first
pass, of course.
For EXEs, the entry point will never return. For DLLs it
will. Moreover for DLLs there are the original
parameters which must be pushed. This brings us to the
final topic, the last bit needed for DLL support.
18 Last Bit for DLL Support
EXEs go into their entry point only once, and with no
parameters (remember, this is not main(), but well before
that). DLLs, on the other hand enter at least twice and
perhaps once per thread. Obviously, the stuff we did
before (the decompression, relocs, imports, TLS) only
needs to be done once. Easy enough, add a global boolean
that indicates that stuff was done and set it to true after
the first pass.
The slightly more tedious thing is producing a stub that
works for DLLs and exes, since you will want to return
the value.
What I like to do is make use of the declspec ( naked )
attibute I applied to the StubEntryPoint. This causes the
compiler to emit no prolog and epilog code. Consequently,
if we don't mess with the stack, we can do and assembly
jmp to the original entry point, and the right behaviour
will happen if we are an EXE or a DLL. Thusly:
asm jmp gevt.orig_entry;
And all should be running.
19 Afterthoughts on Stubs
Looking at other packers, I have seen some slightly
different stub techniques. I think the most interesting is
UPX, where the packer actually acts somewhat like a
linker, building the stub code dynamically and including
only what is necessary at pack time.
You can implement the stub in the fashion of your
choosing, and you can omit features you don't think will
be necessary in your particular application.
20 What's Next
OK, this was a good bit longer than I expected. Still, I
wanted to communicate as much as possible the details
so that others won't have had to spend as much time in
the debugger as I had. Debugging a compressed exe is a
major pain because the debugging info is all useless so
you have to do it in assembly.
Next installment will cover the packer application, which
will be much more straightforward from the standpoint
of configuration, but will have much more work to do
than the stub.
21 Continuo
This series is about creating exe packers for Windows 32
bit PE files.
In the previous installment I described how to create a
decompression stub that would be bound to an existing
executable. In this (final?) installment I'm going to
describe the actual packer application, which binds the
stub to an arbitrary executable and sets up parameters
the stub will need at runtime. Additionally, it will
perform some duties normally done by the OS loader.
The packer application will wind up being the biggest
hunk of code for the project. Fortunately, it will be fairly
straightforward.
22 First Things
There are some basic things to setup or consider before
we get moving with the actual packer.
22.1 Project Configuration
As mentioned in the previous installment, configuration
is a function of your particular design. For the sake of
discussion in this article we are assuming a design where
the decompression stub is produced as a dll. The binary
of that dll will be incorporated into the packer
application as a binary resource. None of this is strictly
necessary. The stub 'dll' will never exist in the real world
© CodeBreakers Journal,
http://www.CodeBreakersJournal.com