0% found this document useful (0 votes)
2K views20 pages

How To Write Your Own Packer

CodeBreakers Magazine - Vol. 1, No. 2, 2006 how to Write Your Own Packer by BigBoote. Aside from making your executables smaller, packing is a good way to quickly and easily obfuscate your work. Existing well know packers either have an explicit 'unpack' function, or there are readily available procdump scripts for generating an unpacked version.

Uploaded by

api-3708963
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2K views20 pages

How To Write Your Own Packer

CodeBreakers Magazine - Vol. 1, No. 2, 2006 how to Write Your Own Packer by BigBoote. Aside from making your executables smaller, packing is a good way to quickly and easily obfuscate your work. Existing well know packers either have an explicit 'unpack' function, or there are readily available procdump scripts for generating an unpacked version.

Uploaded by

api-3708963
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 20

CodeBreakers Magazine – Vol. 1, No.

 2, 2006

How to Write Your Own Packer
by BigBoote
Vol. 1, No. 2, 2006

Abstract:
Why write your own packer when there are so many existing ones to choose from? Well, aside from making your executables smaller,  
packing is a good way to quickly and easily obfuscate your work.

© CodeBreakers Journal, http://www.CodeBreakers­Journal.com
CodeBreakers Magazine – Vol. 1, No. 2, 2006

How to Write Your Own Packer produced   took   about   1.5   weeks   to   produce   including 
research and  bug fixing.  Subsequent ones  took far less 
By BigBoote since I had already done the hard part, which is figuring 
  out how. Hopefully this document will save you that time 
as well!
You do not have to use assembler for the most part. If you 
Why   write   your   own   packer   when   there   are   so   many  
can   part   with   supporting   some   esoteric   features,   you 
existing  ones to  choose  from?  Well,  aside  from  making  
won't have to use it at all. All of that is relevant for the 
your   executables   smaller,   packing   is   a   good   way   to  
decompression  stub  only  anyway.  The   packer  can  be  in 
quickly   and   easily   obfuscate   your   work.   Existing   well­
Logo or Object­Oriented COBOL if you like.
know packers either have an explicit 'unpack' function,  
or   there   are   readily   available   procdump   scripts   for   OK, enough of the blahblahblah, on to technical stuff....
generating an unpacked version.

3 Big Picture
1 Intro Simple.   Executable   is   analyzed,   transformed,   and   an 
extra piece of code is attached which gets invoked instead 
Why   write   your   own   packer   when   there   are   so   many  of the original program. This piece is called a 'stub' and 
existing   ones   to   choose   from?   Well,   aside   from   making  decompresses the image to its original location. Then it 
your   executables   smaller,   packing   is   a   good   way   to  jumps   to   that   original   location.   But   you   know   this 
quickly   and   easily   obfuscate   your   work.   Existing   well­ already.
know packers either have an explicit 'unpack' function, or 
there   are   readily   available   procdump   scripts   for  Sounds   simple,   but   there   are   pitfalls   that   await   you. 
generating an unpacked version. Some of these include:
Since this document has quickly exploded in length I'm 
• Support for simplified Thread Local Storage, which is 
going to break it up into separate installments. In this 
key in supporting Delphi applications
installment   I   will   cover   the   qualitative   aspects   of 
Support for code relocation fixups in dlls if you care 
producing a packer. I'll discuss what you're getting into 
about packing dlls. Recall ActiveX controls are dlls 
and how the packer is structured in general. I'll briefly 
too, as are other common things you might be 
discuss some pitfalls, and I'll give some links to technical 
interested in packing
information you will need to be familiar with before going 
into the next installments.
• Support for some stuff that must be available even in 
In the next two installments I'll go into details of how to  the compressed form. This includes some of your 
implement   the   components   of   the   packer   and   how   I  resources and export names in dlls
usually go about producing them. Dealing with bound imports

2 What You're Getting Into • Support for stripping out directory entries that will 


confuse the Windows loader since the decompression 
It's not really hard, per se, but it is rather tedious code.  won't have happened and they will point to nothing 
Lots   of   pointer   manipulation   and   translation   to   keep  useful, like the IAT and debug info
track of. Aside from that, if you can write code to add and 
subtract integers and do file IO, you've got all the skill 
• Support for doing relocation fixups manually on your 
needed!   As   mentioned,   it   is   tedious   code   so   you   will 
decompression stub since it will certainly be in a 
probably   do   well   to   not   attempt   this   coding   on   a 
different memory location than where the linker 
hangover; trust me, I know.
thought it would be when it was compiled
FYI, the last packer I produced was fairly full­functioned 
(exes   and   dlls,   several   compression   algorithms   with  • Dealing with differences in interpretation of the PE 
debug   capability   and   advanced   support   such   as   TLS  spec between different vendor's linkers. Borland 
(critical for Delphi apps)) and it weighed in at about 3700  linkers interpret aspects of the spec differently from 
lines   for   the   packer   tool   and   about   1000   lines   for   the  Microsoft's so you need to be ready for that.
decompression   stub   it   embeds   in   the   target.   That's 
somewhere   around   70   printed   pages   of   code.  So,  not   a 
huge   app,   but   not   a   tiny   one   either.   The   first   one   I 

© CodeBreakers Journal, http://www.CodeBreakers­Journal.com
CodeBreakers Magazine – Vol. 1, No. 2, 2006

• Working around bugs in Microsoft code. There is an  • Making the Packer Application
infamous one relating to OLE and the resource  The packer application does all the hard work. This 
section. Many packers do not accommodate this and  makes since when you realize the stub is supposed to 
this is important for ActiveX support. do as little as possible to have a minimum impact on 
runtime.

4 First Step I'll try to keep code examples to a minimum but there 


may   be   some   reference   to   structure   members   when 
OK, enough of the horror stories. The first step is to get  describing what's going on and maybe a snippet or two 
painfully   familiar   with   the   file   format   of   executables.  where code is clearer than human language. Most of the 
This is called the 'Portable Executable' format, or PE for  important structures can be found in WINNT.H for those 
short. I will discuss it briefly here. You will need more  who wish to read ahead.
detail   in   reality.   Rather   than   attempting   to   duplicate 
that, here are some references you will find helpful:
6 Continuo
The Portable Executable File Format from Top to 
Bottom  Last   installment   I   mentioned   some   of   the   big­picture 
aspects of creating an exe packer. In this installment I 
http://mup.anticrack.de/Randy%20Kath%20­ am going to talk about a particular part of the packer, 
%20PE%20Format.html/]http://mup.anticrack.de/Randy the decompression stub. This is the simpler part. In the 
%20Kath%20­%20PE%20Format.html
next installment(s) I'll talk about the packer application 
itself. Again, this isn't going to be source for a packer, but 
a good and readable discussion, but not totally accurate  I   might  do   a   straightforward   one   and   publish  it  as  an 
when   it   comes   to   the   import   section.   Dead   wrong   in  addendum to this series if folks are interested in having 
implying   that  these sections always  exist  ­­   they   easily  some working source as a starting point.
can not exist. Still, a good read.
The decompression stub has several responsibilities:
An In­Depth Look into the Win32 Portable Executable 
• Find the packed data
File Format pts 1 and 2
//h**p://www.msdnaa.net/Resources/Display.aspx?ResID • Restore data contents
=1083]http://www.msdnaa.net/Resources/Display.aspx? • Perform relocation fixups
ResID=1083
//h**p://www.msdnaa.net/Resources/display.aspx?ResID • Resolve all imports since the Windows loader couldn't 
=1323]http://www.msdnaa.net/Resources/display.aspx?R do it
esID=1323
• Perform thread local storage duties since the 
great article, weak on discussion of resource section Windows loader couldn't do it
• Boink over to the original program
Microsoft Portable Executable and Common Object File  • You may also have to handle being reentered if you 
Format Specification are packing a dll
//h**p://www.microsoft.com/whdc/hwdev/hardware/pecof
f.mspx]http://www.microsoft.com/whdc/hwdev/hardware Oh, and it also has to run. So lets start with that...
/pecoff.mspx

horse's mouth. Dry. Accurate.
7 A Stub That Runs
It's useful to remember that your decompression stub is 
5 Next Step actually   a   parasite   onto   a   program   that   was   never 
OK, after you've gotten familiar with those, we can start  expecting for it to be there. As such, you should try to 
to write some code. I'm going to save that for the next  minimize   your   impact   on   the   runtime   environment   in 
installments (probably two). They will detail: your packer. I had mentioned before that you could make 
a   packer  in  Logo   or  Object­Oriented  COBOL,  and  that 
• Making the Unpacker Stub really was only partially true. You can make the packer 
The stub has several responsibilities aside from the  application that way fer sure ­­ and you might even be 
obvious decompression. It also has to perform duties  able to make the unpacker that way sometimes ­­ but you 
normally done by the Windows loader. will really be much happier with C/C++/ASM for the stub 
part. I personally like C++. Anyway, it will be smaller. If 
you don't care about the size, still using stuff like Delphi 
or VB for the stub would be problematic because it hoists 

© CodeBreakers Journal, http://www.CodeBreakers­Journal.com
CodeBreakers Magazine – Vol. 1, No. 2, 2006

in   subtle   stuff   like   TLS   and   runtimes,   and   they   don't  assumptions. In real life you don't have to do it this way, 
have   primitives   needed   to   thunk   over   to   the   original  but let's temporarily  pretend  we  are  and  at  the  end  of 
program.  Plus it  can  hose  COM  stuff  that  the  original  this   series   you'll   know   how   you   might   like   to   do   it 
app isn't expecting. So let's assume the unpacker will be  different.
in the  lower­level  languages I spoke  of and take solace 
that   this   is   pretty   straightforward   code,   and   that   the 
Big picture is that there will be two projects, producing 
packer still can be in whatever. two distinct executables ­­ the packer stub and the packer 
application.   Their   configuration   will   be   significantly 
Since the stub is a parasite, and since it will have to be 
different.
located in a spot at the original application's convenience, 
we will have to be relocating it dynamically in the packer 
We are going to do a bit of ledgerdemain with the stub 
application. To help with this we will make the stub with 
project   which   will   be   explained   later,   but   for   now, 
relocation records. These are usually used for dlls when 
configure a boiler plate project for your stub thusly:
they can't be loaded at their preferred address. We will 
make use of them when binding the stub to the original  • Produce a DLL
application.
• Use static multithreaded runtime libraries

If   you're   an   avid   ASM   coder,   many   things   are   more  • Disable RTTI and exception support


straightforward   since   you   can   take   care   to   produce 
position­independent   code.   This   won't   necessarily   free  If there are options for the boilerplate code it generates, 
you   from   all   relocation   concerns,   however.   The  make   it   simple,   so   that   there   is   just   DllMain   being 
decompression library of choice may well not be position  implemented. We're going to throw all that away anyway. 
independent.   Also,   and   references   to   global   data   will  Go ahead and build it as a sanity check, which should go 
need to be fixed up. through fine.
We're making the packer stub a DLL not because it will 
ultimately be a DLL ­­ it won't. We're doing this because 
8 Choice of Compressor we want the relocation records. You _can_ create it as an 
You   can   pretty   much   use   whatever   compressor   library  exe project and cause it to have relocation records (linker 
you want, so long as you build it in a way that doesn't  option /FIXED:no), but I find the Microsoft's linker will 
spew out stuff via printf or other UI functions. There are  crash randomly in that configuration. Stick with the DLL 
plenty   free   compressors   out   there.   You   might   want   to  config and you'll be OK.
start with something like zlib. It won't give you the best 
Next,   change   the   config   thusly   (this   is   for   Microsoft's 
compression, but I know it works in this scenario. Also, 
tools, you'll have to look up the equivalents for Borland's 
another   is   UCL.   This   compresses   better   and   is   much 
or gcc):
smaller   code­wise.   It   is   GPL,   however,   and   maybe   you 
care about the licensing implications.
Check   the   docs   to   the   compressor   you   want   for  Linker options:
configuration   options   and   related   stuff.   For   example, 
BZip2 requires BZ_NO_STDIO to be defined to have no  add any library paths your compressor lib will be 
printf stuff. needing
/nodefaultlib don't use default libs
Configure the build to be compatible with the stub and  /map(filename) DO generate a mapfile
compression  library.   For   me,  I  disable   RTTI   and   make  remove /debug don't generate debug info
sure   I   am   linking   the   static   runtime   library,  change /incremental:yes to /incremental:no disable 
incremental linking
multithreaded.   I   optimize   for   size.   The   output   should 
produce a static library, of course, rather than a dll, since 
the   goal   is   to   add   no   dependencies   beyond   the   apps 
original ones. Compiler options:

Setting Up Projects ­­ and now for something completely  add any defines or header paths your compressor lib 
different will be needing
/FAcs generate listing files with source, assembly, and 
machine code
OK,   I   am   going   to   take   a   brief   break   from   code   and 
/Fa(filename) specify where these listings go
technological stuff and talk about project configuration.  remove /GZ compiler­generated stack checks
Normally   I   wouldn't   since   that's   a   personal   choice,  remove any debug options, it won't help us where we're 
however this time I will because things I talk about later  going
will   be   dependent   upon   some   of   the   configuration 

© CodeBreakers Journal, http://www.CodeBreakers­Journal.com
CodeBreakers Magazine – Vol. 1, No. 2, 2006

BOOL WINAPI _DllMainCRTStartup ( HANDLE, 
these options are probably available as checkboxes, so 
DWORD, LPVOID )
you won't have to manually add them.
{
//(program will go here)
return TRUE;
}
The gist is that we are not going to have normal debug 
capabilites so we turn off that stuff. Instead, we will be 
relying on the listing of the compiler­generated assembly  This should resolve the linker error and will be our entry 
to see code and the linker­generated mapfile to see actual  point.   The   place   our   program   will   ultimately   go   is 
addresses.   All   this   is   interesting   stuff   in   any   project  indicated by the comment. Ultimately we'll never hit the 
'return   TRUE'   statement;   it's   just   there   to   make   the 
really,   but   it   is   all   we   have   for   debugging   in   this   one.
compiler happy, and the function signature is what it is 
If   you   build   now   you   will   should   get   a   linker   error  to make the linker happy.
complaining   about   an   unresolved   external   symbol 
If you want to be more arty, you can do the following:
DllMainCRTStartup@12.   This   is   good!   If   you   don't   get 
that then the default libs are coming in. The symbol is  #pragma comment ( linker, "/entry:\"StubEntryPoint\"" )
possible different for Borland stuff. Other errors probably  void declspec ( naked ) StubEntryPoint() {
mean something else needs to be fixed; this is the only  //(program will go here)
one you should get for Microsoft's compiler. }

which is syntactically clearer.
9 Runtime dependencies
This   is   cosmetic   so   don't   feel   bad   if   you   find   the 
You   cannot   assume   what   runtime   dependencies   the 
equivalent   pragmas   for   your   compiler/linker.   Also,   this 
original app has. Thus, you cannot make calls to funky 
perverts what the compiler normally thinks about and I 
dlls (vbrunX.dll, etc). You have no idea if they are there. 
have   seen   it   crash   randomly.   I   have   found   when   the 
You will do well to statically link your runtime library. 
compiler   gets   in   a   crashing   mood,   that   putting   in:
You will do much (much) better, however, to not link any 
runtime libraries at all! ASM coders will take delight in 
this   fact   already,   because   they   are   hard­core,   but   this  asm nop
need not dissuade the C/C++ coders who are accustomed 
to malloc() strcmp() new std::vector<> or such. All this is 
in a couple places seems to get it back on track. Ain't that 
doable.   You   will   just   have   to   provide   your   own 
a laugh?! Whatever...
implementation   of   these   functions.   Fortunately,   this   is 
pretty easy since you can call native functions exported  As   code   is   added,   you   should   periodically   build.   The 
by   Kernel32.dll.   /That/   dll   is   certainly   present,   and  linker will add more and more complaints like above and 
certainly one that is already used by the original app you  we will have to implement the underlying methods the 
are packing so feel free to use it when you like. compiler is emitting references to. Here's a tip: when you 
installed your dev tools, you may have had the option to 
install the source to the C Runtime. It will be helpful in 
10 Making a Trivial C some cases since you can cut and paste some parts. In 
Runtime to Link Instead of the particular, a function:
Proper One extern "C" declspec ( naked ) void _chkstk(void)
Replacing   the   C   Runtime   might   sound   scary   but 
remember we only want to implement what is necessary;  is sometimes emitted by the compiler quietly (if you have 
this will turn out to be a small set of things. The linker  a large array on the stack, like for a buffer). Just cut­and­
will help you figure out what these are. Recall that we  paste that one; it's funky.
turned off default library searching with the /nodefault 
switch   (or   equivalent   for   your   linker,   that's   for  FYI, I typically have to implement:
Microsoft's). If you configured as I suggested above, we've 
memcpy
got a linker error already: DllMainCRTStartup@12 We'll  memset
fix that one first. memcmp
malloc
Discard   your   boiler­plate   DllMain.   Replace   it   with:
free
realloc

© CodeBreakers Journal, http://www.CodeBreakers­Journal.com
CodeBreakers Magazine – Vol. 1, No. 2, 2006

calloc My   preferred   technique,   however,   is   to   get   the   packer 


operator new ( unsigned int nSize ) application   to   help   me   out.   That   way   the   same   stub 
operator delete ( void* pv ) works for exes and dlls and in the same way. It involves a 
global variable, and there are going to be several of those, 
so let me discuss that first.
To get you going on what it means to do this sort of roll­
your­own­C­runtime, please see the following article. It's 
good   and   will   save   me   from   repeating   the   infomation  12 Packer Parameter Globals
here. There's sample implementation as well.
There are going to be parameters that are computed by 
Reduce EXE and DLL Size with LIBCTINY.LIB the packing application and that will be embedded in the 
http://msdn.microsoft.com/msdnmag/issues/01/01/hood/d stub so it can do it's work at runtime. These require a bit 
efault.aspx]http://msdn.microsoft.com/msdnmag/issues/ of special handling because the packer application needs 
0...od/default.aspx
to find these items at pack time. You could hard­code in 
OK, we're now setup to do the work! the   addresses   into   the   packer.   You   would   get   these 
addresses from the mapfile generated by the linker. This 
is a  bit tacky  because  you will have  to  double check it 
11 Unpacking Stub each   time   you   alter   the   stub,   which   will   be   quite 
Responsibilities frequently while developing. Instead, I prefer to do a bit 
of legerdemain with structures, sections, and segments. 
I   mentioned   way   back   that   the   stub   has   the   following 
This only needs to be done for the variables published to 
duties:
the packer. Regular globals you might want to have can 
• Find the packed data be done as usual without concern.
• Restore data contents First,   simple   stuff.   I   make   one   structure   with   all   the 
• Perform relocation fixups, if needed important   globals.   Then   one   global   instance   of   that 
structure. Thus there is only one object the packer has to 
• Resolve all imports since the Windows loader couldn't 
locate at pack time. Let's call that structure:
do it
• Perform thread local storage duties since the //in a header, say GlobalExternVars.h
• Windows loader couldn't do it struct GlobalExternVars
{
• Boink over to the original program //stuff goes here
• You may also have to handle being reentered if you  };
are packing a dll
Now we will do some kookiness in our main .cpp file:
It's important that the stub restore the original data to 
it's exact original location. This is because we don't know  #pragma data_seg ( ".A$A" )
about what references are in the original code to things  declspec ( allocate(".A$A") ) extern struct 
like   global   data   structures   and   functions   pointers   in  GlobalExternVars gev =
{
things like vtables.
//initializers go here
Recall   that   the   format   of   the   PE   file   (links   to   good  };
discussions were provided in the previous installment) is  #pragma data_seg ()

organized into sections, which have a location and size. 
This information is stored in the section headers, which  What   the   Hell   is   that?   Well,   it   creates   a   special   data 
describe where the sections go in memory (relative to the  section for our global variables. Dirty little secret about 
load address). the linker is that it sorts the section names lexically, and 
To do this properly, we will be needing to know our load  discards the portion at the '$' and after. By naming the 
address. If we are a stub for an exe we can simply do a  section '.A$A' we will be forcing the global vars structure 
GetModuleHandle(NULL)   and   the   returned   module  to be at the very beginning of the data section, which will 
handle is the base load address. This won't work for a dll  be   easy   for   the   packing   application   to   locate.  Next,   we 
however. The module handle for the dll is on the stack.  will   merge   some   sections   with   the   following   linker 
We can write some code to get it, or we can choose not to  options. You can put these on the link line, or you can be 
do   the   'arty   entry   point'   and   it   is   referenceble   as   a  fancier and place them in the code with a pragma (if your 
parameter (do not attempt to reference those parameters  tools support such). I think putting them in the pragma 
if it is the stub for an exe unless you are fond of crashes). makes it more obvious from the code standpoint that the 
stuff is fragile and should be handled carefully if changes 

© CodeBreakers Journal, http://www.CodeBreakers­Journal.com
CodeBreakers Magazine – Vol. 1, No. 2, 2006

are needed. 14 Decompressing the


#pragma comment(linker, "/MERGE:.rdata=.A") Original Data
#pragma comment(linker, "/MERGE:.data=.A")
#pragma comment(linker, "/MERGE:.bss=.A") The compressed data is stuff attached by the packer. Like 
the stub, it will have stuck it somewhere. It can located 
most anywhere you like. A popular choice is to locate it at 
So the global data (and don't forget your compression lib  the _end_ of where the original data was located. Then, 
might have some too) will all be merged into one section,  decompressing   that   data   from   start   to   finish   to   it's 
with   the   external   variable   structure   at   the   very  original   location   causes   the   data   to   be   ultimately   be 
beginning. Oh, notice that I merged .bss in too. This has  overwritten. Fancy. This will only work of course if the 
a   subtle   simplifying   effect.   .bss   is   used   to   hold  compressed   data   is   smaller   than   the   original,   but   we 
_uninitialized_ globals. These don't normally take up file  generally   hope   that   our   compressor   actually,   uh, 
space (since they are uninitialized) but they do take up  compresses, and makes things smaller.
memory.   The   packer   will   have   to   take   this   in 
consideration when laying out the actual stub it builds.  The compressed data is located somewhere placed by the 
By merging it into the data section, it will take up actual  packing application. Where?  Who knows.  There  will  be 
file space and thus the packer won't have to worry about  needed a published external global specifying where and 
it.   There   will   be   very   little   .bss   at   all   so   don't   be  setup   at   pack   time.   So   add   a
disturbed about it taking up space; we're talking bytes. DWORD   RVA_compressed_data_start;
DWORD compressed_data_size;
to the GlobalExternVars struct. Transforming the RVA to 
13 Computing the Load the VA by adding the load_addresss previously computed 
Address will   tell   you   where   the   compressed   data   is   located   at 
OK, regardless of whether you have used my technique  runtime.
for   publishing   packer   globals   or   rolled   your   own,   let's 
assume that it is done. Now, the original point was that  The specific format of your compressed data is completely 
we would be needing the the base address at runtime in  up to you. Since essentially we will be restoring data to 
the   stub   so   that   we   can   convert   Relative   Virtual  original locations, which are chunks (the sections of the 
Addresses   (RVAs)   to   actual   Virtual   Addresses   (VAs).  original PE file), the simple stream format of:
Recall the VA = RVA + base address.
struct original_directory_information
My technique is to have a published global which is the  dword section_count
RVA of the stub entry point. The packer sets this up. The  section 1 header
{
stub   then   takes   the   address   of   the   actual   entry   point, 
dword RVA_location
subtracts the  RVA  computed  and  stored  by  the   packer,  dword size
and   the   result   is   the   load   location   of   the   packed  }
executable. I store this result in a 'regular' global (which  (section 1 compressed data)
doesn't   need   to   be   part   of   the   GlobalExternVars). ...
I do this first thing in the main stub entry point thusly: The   original_directory_information   is   the   stuff   in   the 
DataDirectory   member   of   the 
//global var
DWORD load_address = 0; //computed actual load 
IMAGE_OPTIONAL_HEADER of the PE headers of the 
address for convenience original app. The packer will have changed these values 
to   be   suitable  for  the   stub,  so  it  will  need  to stick  the 
//in the stub entry point function original in the compressed stream so we can get to those 
load_address = (DWORD) StubEntryPoint­  values at runtime. This will suffice for the stream. Feel 
gev.RVA_stub_entry;
free  to   add   whatever   you  might  like   to   it   as  well.   The 
decompression routine pseudo­code is:
Note, if you did not do my entry point rename trick, you 
would   use   the   name   of   your   funtion   instead,   possibly  struct section_header {
DWORD RVA_location;
_DllMainCRTStartup.   This   technique   always   works 
DWORD size;
regardless of wether the appliaton is a DLL or EXE. };
Once   you   have   the   load   address   you   are   all   set   up   to 
decompress to the proper location.
//'regular' non­published global
IMAGE_DATA_DIRECTORY origdirinfo 
IMAGE_NUMBEROF_DIRECTORY_ENTRIES;

© CodeBreakers Journal, http://www.CodeBreakers­Journal.com
CodeBreakers Magazine – Vol. 1, No. 2, 2006

• whiz through the records getting the DWORD at the 
address they indicate and add the offset
void decompress_original_data() {
void* pvCompData = (void*) 
Pretty   straightforward.   The   format   of   the   relocation 
( gev.RVA_compressed_data_start + load_address );
initialize_compressor ( pvCompData, 
records is a little bit odd and is structured the way it is 
gev.compressed_data_size;); presumably   for   size   considerations.   The   records   are 
organized as a series of chunks of records, one chunk per 
decompress_data ( &origdirinfo, sizeof(origdirinfo) ); page. The records in the chunk reference an offset into 
the   page.   Additionally,   for   padding   consideration   there 
int section_count;
are   records   that   are   essentially   no­ops   and   should   be 
decompress_data ( &section_count, sizeof(section_count) )
;
ignored. Pseudo­code follows:

for ( int i = 0; i < section_count; ++i ) { void perform_relocations () {
section_header hdr; //see if no relocation records
decompress_data ( &hdr, sizeof(hdr) ); if 
void* pvOrigLoc = (void*) ( hdr.RVA_location +  ( origdirinfoIMAGE_DIRECTORY_ENTRY_BASERELO
load_address ); C.VirtualAddress == 0 )
decompress_data ( pvOrigLoc, hdr.size ); return;
}
//compute offset
cleanup_compressor(); IMAGE_DOS_HEADER* dos_header = 
} (IMAGE_DOS_HEADER*) load_address;
IMAGE_NT_HEADERS32* nt_hdr = 
This will be called in the main entry point of the stub  (IMAGE_NT_HEADERS32*)
right after computing the actual load address. &((unsigned char*)load_address)dos_header­>e_lfanew;
DWORD reloc_offset = load_address ­ nt_hdr­
That's it! What could be easier? Well, notice that we're  >OptionalHeader.ImageBase;
using   a   stream   model   for   our   compressor.   Most 
compression libraries come pretty close to implementing  //if we're where we want to be, nothing further to do
that but you have to do ever so slightly more to make it  if ( reloc_offset == 0 )
return;
that simple. I wrap up my compressors in a class so that 
they   all   implement   the   above   interface   to   make  things  //gotta do it, compute the start
simple   like   above.   Swaping   out   compressors   then   just  IMAGE_BASE_RELOCATION* ibr_current = 
means making a new adaptor class. The rest of the stub  (IMAGE_BASE_RELOCATION*)
need   not   be   touched   to   put   in   different  (origdirinfoIMAGE_DIRECTORY_ENTRY_BASERELOC
compressors/encryptors. .VirtualAddress + load_address );

//compute the end
Now that all the original data is decompressed into it's  IMAGE_BASE_RELOCATION* ibr_end = 
original location, we have to do stuff that the Windows  (IMAGE_BASE_RELOCATION*)
loader   normally   does.   This   includes   relocation   fixups,  &((unsigned 
imports lookup, and TLS initialization/thunking. char*)ibr_current)origdirinfo[IMAGE_DIRECTORY_EN
TRY_BASERELOC.Size];

15 Performing Relocation //loop through the chunks


while ( ibr_current < ibr_end && ibr_current­
Fixups >VirtualAddress ) {
DWORD RVA_page = ibr_current­>VirtualAddress;
This is really only necessary for packed DLLs since EXEs  int count_reloc = ( ibr_current­>SizeOfBlock ­ 
are supposed to be always loaded at their preferred base  IMAGE_SIZEOF_BASE_RELOCATION ) / 
address. In fact, relocation records are usually stripped  sizeof(WORD);
from EXEs so there's nothing to process. WORD* awRelType = (WORD*)((unsigned 
char*)ibr_current + 
Details   of   the   relocation   record   format   are   sufficiently  IMAGE_SIZEOF_BASE_RELOCATION);
detailed in the articles reference in the first installment.  for ( int i = 0; i < nCountReloc; ++i ) {
For us to process them we: WORD wType = awRelTypenIdx >> 12;
WORD wValue = awRelTypenIdx & 0x0fff;
• compute an offset of the preferred base address and  if ( wType == IMAGE_REL_BASED_HIGHLOW ) { //do it
the actual load address *((DWORD*)(RVA_page + wValue + load_address)) += 
reloc_offset;
• find the relocation records from the original directory  }
information we just decompressed

© CodeBreakers Journal, http://www.CodeBreakers­Journal.com
CodeBreakers Magazine – Vol. 1, No. 2, 2006

ibr_current = (IMAGE_BASE_RELOCATION*) IMAGE_IMPORT_MODULE_DIRECTORY.dwModuleNa
&((unsigned char*)ibr_current)ibr_current­>SizeOfBlock; meRVA   has   the   name   of   the   dll   you   will   need   to 
} LoadLibrary() on). Once you get the address, you stick it 
} in the parallel location in the ImportAddressTable array. 
You do this for each member.
This is the majority of what is needed to support DLLs.  In the case when the ImportNameTable is not present, 
There is a little bit more discussed later. Given that this  however,   as   with   Borland's   linker,   you   must   get   the 
is so straightforward, I'm a little surprised at the number  address   of   the   function   name   from   the 
of packers out there that do not support DLLs. ImportAddressTable   itself.   Then   you   overwrite   it   with 
The next major thing we have to do is to resolve all the  the function address.
imports.   This   is   only   a   little   more   involved   that   the  It   is   important   to   use   the   ImportNameTable   in 
relocation records. preference to the ImportAddressTable because of a thing 
called 'bound executables'. If you want to test your work 
on   a   bound   executable,   consider   that   notepad.exe   is 
16 Resolving Imports bound.
Resolving   the   imports   consists   of   walking   through   the 
Import   Address   Table   of   the   original   application   and  After processing each DLL you may or may not wish to 
doing GetProcAddress to resolve the imports. This is very  do   a   FreeLibrary.   It's   going   to   depend   on   how   you 
similar   to  the   relocation   record   logic  that   I   won't   do   a  implement your packer application. We'll discuss that in 
pseudo­code   example.   Details   of   these   structures   are  the next installment, and it relates to 'merged imports'. 
given in the links provided in the first installment. The  For   now,   suffice   it   to   say   that   if   you   perform   merged 
structures all start at: imports, you can call FreeLibrary, but if you do not, you 
must not call it. You might want to put the call in and 
origdirinfoIMAGE_DIRECTORY_ENTRY_IMPORT.Virtual comment it out while developing until you have merged 
Address
imports   implemented.   Merged   imports   is   important   for 
properly   supporting   TLS   that   potentially   exist   in 
There are a couple caveats I should mention however: implicitly   loaded   DLLs.   This   leads   into   the   final 
responsibility   for   the   stub,   which   is   handling   TLS 
• The structures are wired together via RVA pointers.  support.
These need to have the load_address added to make a 
real pointer
• The pointers in the structure to strings are real  17 Supporting TLS
pointers. These _do_not_ need the load_address  Thread Local Storage, or TLS, is a handy programming 
added. Relocation processing will have already fixed  mechanism. We don't care mostly, since we're not using 
these up. it,   but   the   original   application   to   be   packed   might   be 
using it indeed. In fact, Delphi always uses it, and so if 
Don't forget about importing by ordinal. You will know  we're   going   to   support   packing   Delphi   apps,   we   better 
this is happening because the pointer to the string will  accomodate it.
have   the   high   bit   set   (   (ptr   &   0x8000000)   !=   0   ).
Borland and Microsoft linkers do different things, so you  TLS fundamentally is done via API calls. In general, you 
have   to   be   prepared   to   get   the   string   from   either   of  allocate an 'index' which you store in a global variable. 
different spots. Basically, there are two parallel arrays,  With this index you can get a DWORD value specific to 
the ImportNameTable which you get from: each   thread.   Normally   you   use   this   value   to   store   a 
pointer   to   a   hunk   of   memory   you   allocate   once   per 
IMAGE_IMPORT_MODULE_DIRECTORY.dwImportNa thread.   Because   people   thought   this   was   tedious,   a 
meListRVA special   mechanism   was   created   to   make   it   easier. 
Consequently, you can write code like this:
and the ImportAddressTable which you get from:
declspec ( thread ) int tls_int_value = 0;
IMAGE_IMPORT_MODULE_DIRECTORY.dwIATPortio and each thread can access it's distinct instance by name 
nRVA like any other variable. I don't know if there is an official 
name for this form of TLS, so I'll call it 'simplified TLS'. 
The ImportNameTable is optional. Borland doesn't use it.  This is done in cooperation of the operating system, and 
If it is present, you should use it to get the name of the  there   are   structures   within   the   PE   file   that   makes   it 
function   and   GetProcAddress()   it's   pointer   (the  happen. Those structures are contained in a chunk that 

© CodeBreakers Journal, http://www.CodeBreakers­Journal.com
CodeBreakers Magazine – Vol. 1, No. 2, 2006

is pointed to by yet another directory entry: to   invoke   the   original   callbacks,   and   to   indicated   that 


there is a deferred call. Initialize these globals thusly:
origdirinfoIMAGE_DIRECTORY_ENTRY_TLS.VirtualA
ddress bool safe_to_callback_tls = false;
bool delayed_tls_callback = false;

The  problem   is  that   the   processing   of   this  information 


happens by the OS on the creation of every thread prior  and provide some auxilliary globals to hold data that is 
to   execution   being   passed   to   the   thread   start   address.  delayed:
This would not normally be a concern for us, except that 
at   least   one   thread   has   been   started   before   we   can  PVOID TLS_dll_handle = NULL;
DWORD TLS_reason = 0;
unpack the data: our thread! What we have to do is set 
PVOID TLS_reserved = NULL;
up a fake TLS management section to capture what the 
OS has done before we started, then manually copy this 
information to the original app as our last step. the thunk implementation proceeds as such:

For this, I add two items to the external global packer  extern "C" void NTAPI TLS_callback ( PVOID DllHandle, 
data structure: DWORD Reason, PVOID Reserved ) {
if ( safe_to_callback_tls ) {
GlobalExternVars PIMAGE_TLS_CALLBACK* ppfn = 
{ g_pkrdat.m_tlsdirOrig.AddressOfCallBacks;
//(other stuff we previously described) if ( ppfn ) {
IMAGE_TLS_DIRECTORY tls_original; while ( *ppfn ) {
IMAGE_TLS_DIRECTORY tls_proxy; (*ppfn) ( DllHandle, Reason, Reserved );
}; ++ppfn;
}
}
The   packer   application   will   copy   the   original   data   to  } else {
tls_original   for   our   use   at   runtime.   tls_proxy   will   be  delayed_tls_callback = true;
almost   an   exact   copy,   except   two   items   will   not   be  TLS_dll_handle = DllHandle;
modified from the stub: TLS_reason = Reason;
TLS_reserved = Reserved;
tls_proxy.AddressOfIndex }
tls_proxy.AddressOfCallBacks }

In the stub we will inialize the AddressOfIndex to point  This will provide a place for the OS to store the slot info, 
to   a   normal   global   DWORD   variable,   and   we   will  which   we   will   later   restore,   and   if   it   does   call   thunks 
initialize   AddressOfCallBacks   to   point   to   an   array   of  then we will capture the parameters for later when we 
function pointers in the stub. The function pointers array  will   invoke   the   original   thunks   after   decompression. 
is a list of things that is called whenever a new thread is  Again, this is all done because the OS will be doing this 
created.   It   is   intended   to   be   used   for   user   defined  stuff   before   we   have   a   chance   to   decompress.   After   we 
initialization of the TLS objects. Alas, no compiler I have  decompress,   we   pass   the   call   straight   to   the   original 
seen has ever used them. Moreover, on the Windows 9x  application.
line, these functions are not even called. Still, we support 
it   in   case   one   day   they   are   used.   We   point   the  We handle this last step like so:
AddressOfCallbacks   to   an   array   of   two   items,   one 
void FinalizeTLSStuff() {
pointing   to   a   function   of   our   implementation,   and   the  if 
second being NULL to indicate the end of the list. ( origdirinfoIMAGE_DIRECTORY_ENTRY_TLS.Virtual
Address != 0 ) {
There will be a global DWORD for the TLS slot:
*gev.tls_original.AddressOfIndex = TLS_slot_index;
void* TLS_data;
DWORD TLS_slot_index;
asm
{
The   TLS   callback   function   must   be   of   the   form: mov ecx, DWORD PTR TLS_slot_index;
mov edx, DWORD PTR fs:02ch
mov ecx, DWORD PTR edx+ecx*4
mov pvTLSData, ecx
extern "C" void NTAPI TLS_callback ( PVOID DllHandle, 
}
DWORD Reason, PVOID Reserved );
int size = gev.tls_original.EndAddressOfRawData ­
gev.tls_original.StartAddressOfRawData;
also you add two global booleans indicating that it is safe  memcpy ( pvTLSData, (void*) 

© CodeBreakers Journal, http://www.CodeBreakers­Journal.com
CodeBreakers Magazine – Vol. 1, No. 2, 2006

gev.tls_original.StartAddressOfRawData, size ); 19 Afterthoughts on Stubs


memset ( (void*) gev.tls_original.EndAddressOfRawData, 
0, Looking   at   other   packers,   I   have   seen   some   slightly 
gev.tls_original.SizeOfZeroFill ); 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 
safe_to_callback_tls = true;
only what is necessary at pack time.
if ( delayed_tls_callback ) {
TLSCallbackThunk ( TLS_dll_handle TLS_reason  You   can   implement   the   stub   in   the   fashion   of   your 
TLS_reserved ); choosing, and you can omit features you don't think will 
}
be necessary in your particular application.
}

Once you have done that, it is finally safe to call over to  20 What's Next


the   original   program.   You   should   have   a   published  OK, this was a good bit longer than I expected. Still, I 
external   global   that   will   be   set   up   by   the   packing  wanted to communicate as much as possible the details 
application   that   specifies   the   original   program's   entry  so that others won't have had to spend as much time in 
point. I will call it the debugger as I had. Debugging a compressed exe is a 
major pain because the debugging info is all useless so 
DWORD orig_entry;
you have to do it in assembly.
Next installment will cover the packer application, which 
which will be a member of GlobalExternVars. It will be 
will be much more straightforward from the standpoint 
initialized to an RVA and we  will fix  it up to  a VA by 
of   configuration,   but   will   have   much   more   work   to   do 
adding the load_address. This done only once on the first 
than the stub.
pass, of course.
For EXEs, the entry point will never return. For DLLs it 
will.   Moreover   for   DLLs   there   are   the   original  21 Continuo
parameters which must be pushed. This brings us to the  This series is about creating exe packers for Windows 32­
final topic, the last bit needed for DLL support. bit PE files.
In the previous installment I described how to create a 
18 Last Bit for DLL Support decompression stub that would be bound to an existing 
executable.   In   this   (final?)   installment   I'm   going   to 
EXEs  go into their  entry  point only  once,  and  with no 
describe the actual packer application, which binds the 
parameters (remember, this is not main(), but well before 
stub to an arbitrary executable and sets up parameters 
that). DLLs, on the other hand enter at least twice and 
the   stub   will   need   at   runtime.   Additionally,   it   will 
perhaps   once   per   thread.   Obviously,   the   stuff   we   did 
perform   some   duties   normally   done   by   the   OS   loader.
before   (the   decompression,   relocs,   imports,   TLS)   only 
needs to be done once. Easy enough, add a global boolean 
The   packer   application   will   wind   up   being   the   biggest 
that indicates that stuff was done and set it to true after 
hunk of code for the project. Fortunately, it will be fairly 
the first pass.
straightforward.
The slightly more tedious thing is producing a stub that 
works for DLLs and exes, since you will want to return 
the value. 22 First Things
What I like to do is make use of the declspec ( naked )  There are some basic things to setup or consider before 
attibute I applied to the StubEntryPoint. This causes the  we get moving with the actual packer.
compiler to emit no prolog and epilog code. Consequently, 
if we don't mess with the stack, we can do and assembly  22.1 Project Configuration
jmp to the original entry point, and the right behaviour 
will happen if we are an EXE or a DLL. Thusly: As mentioned in the previous installment, configuration 
is a function of your particular design. For the sake of 
asm jmp gevt.orig_entry; discussion in this article we are assuming a design where 
And all should be running. 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.CodeBreakers­Journal.com
CodeBreakers Magazine – Vol. 1, No. 2, 2006

as such since we are going to snip out interesting pieces.  memory since it has mapped the sections appropriately.
You   could   just   as   easily   use   a   tool   to   spew   just   the 
interesting pieces to binary resources, or encode them as  So, I would further suggest creating a utility class that 
static data in a C source file. This choice is per taste and  incorporates   the   address   translator   mentioned   earlier 
we   are   going   to   choose   the   resource   approach.   We   are  (along   with   logic   to   initialize   it)   that   can   provide 
also   going   to   be   a   command­line   app.   So... translated   access   from   RVA   to   physical   pointer   for 
regions   within   a   PE   file.   Stick   in   an   RVA,   get   out   a 
Configure   your   project   as   a   command­line   (console)  physical memory pointer. We can use this device for both 
application. Create a RC file and include a resource that  the memory­mapped original, and also for the resource­
is the stub 'dll' produced by your previous project. That's  loaded stub. You don't have to do this but it will make 
really it for configuration. I'm sure that will be a welcome  your life easier. This is a plus because it's already going 
simplification after having set the stub project! to get a little harder as it is wink.gif. You may wish to 
throw in a couple other convenient PE­specific items, like 
pointers   to   the   image   headers.   We'll   be   using   various 
22.2 Utility Code fields in these headers at several points throughout the 
packing process.
There are going to be some things that are simple, but  One other thing that will make you happier in the long 
very tedious, and you will probably like to produce some  run   is   to   produce   some   sort   of   wrapper   for   your 
machinery to tend to these tasks. compression library of choice. In doing so you can both 
One such task relates to translating addresses. We have  simplify use of the library and also be able to swap out a 
to do this in a couple places for different reasons, so you  different compressor should you choose. For example:
might   consider   making   some   sort   of   general   purpose 
class Compressor {
address translator. It will need to handle several distinct 
public:
ranges of addresses being mapped independently to other  Compressor ( HANDLE hFile ); //create; write to given 
ranges. In practical terms, there won't be a huge number  file at current file pos
of range mappings (like about 5), so if you want to just  void InsertData ( const void *pv, int size ); //stick some 
keep a list of range mappings and do a linear search no  uncompressed data in
one will chastise you. void Finish(); //finish any pending writes
DWORD CompressedCount(); //count of output 
Another tedious thing (I find) is reading little bits and  (compressed) data
pieces   from   the   original   executable   file.   This   is  };
particularly   true   when   navigating   a   network   of   objects 
since you have to run along pointer paths. To make this  This sort of interface I have found to be suitable for all 
much more bearable I use a memory­mapped file for the  compression libraries I have considered, though of course 
original   executable.   Read­only   access   is   fine   since   we  I wouldn't use it for things  other than this exe packer.
won't be altering the original (BTW, if for some reason 
you   do   want   to   write   to   the   mapped   image,   but   not  Other   than   that   you   might   like   to   make   a   general­
disrupt your original file, remember you can map it copy­ purpose resource tree walker, but we'll discuss that later 
on­write. I've done this for some protectors.) I don't use  in   the   implementation.   Making   this   part   generic   is 
this approach for the output file, however, because most  mostly   useful   if   you  wish   to   reuse   it   in   other   projects.
of that will be sequential write.
Lastly, I would like to reiterate that the pointers in the  With that being said, we are ready to move onto the...
executable   are   RVA's.   This   means   you   will   need   to   do 
_two_ things to transform them to real pointers. First, if 
you've mapped the image to an address, you will need to 
23 Basic Tasks
add   that   base   address.   The   stub   'dll'   compiled   in   as   a  Here are the fundamental things the packer will need to 
resource will be accessed through a memory address once  do.
we   LockResource()   on   it.   That   address   is   the   base 
• Determine Size of original
address.   Now,   that's   all   you   have   to   do   on   a   running 
module (i.e. one the OS loader mapped in), but that's not  • Setup new section(s); modify originals
all   we   have   to   do.   The   second   thing   we   have   to   do   is  • Create and add stub outside this region
consider the file and section alignment of the executable  • Preserve export info
(do _not_ assume they are they same). The net result of 
• Fixup TLS stuff
this is that there will need to be an adjustment on a per­
section basis to the resultant pointer. Again, this is not  • Relocate the Relocations
necessary   for   a   module   loaded   by   the   OS   loader   into 

© CodeBreakers Journal, http://www.CodeBreakers­Journal.com
CodeBreakers Magazine – Vol. 1, No. 2, 2006

• Compress and stow original data implementation   finds   the   resources   via   section   name 


(.rsrc)   rather   than   looking   up   in   the   directory   (at 
• Process the resource directory
IMAGE_NT_HEADERS::OptionalHeader.DataDirectoryI
• write out the results MAGE_DIRECTORY_ENTRY_RESOURCE.VirtualAddr
ess). The result of this is that you take care in handling 
and a couple minor fixups like changing the entry point  this one. More details on that when we discuss resources.
and some of the directory entries.
There   are   a  variety   of   choices   in   determining   how  you 
24 Details want the new sections to be laid out. Some packers keep 
all the original ones, making them 0 size, and adding the 
Here are the details of each of these tasks. new   sections.   Other   packers   consolidate   the   original 
sections   into   one   uninitialized   section   and   append   the 
24.1 Determine Size of Original new   ones.   This   is   largely   a   matter   of   personal   choice.

This   is   the   easiest   task   as   it   is   indicated   in   the   PE  For example UPX consolidates the sections and splits the 


header of the original. It is located at: consolidation in two. The lower­addressed part is named 
UPX0 and is uninitialized. The higher­addressed part is 
IMAGE_NT_HEADERS::OptionalHeader.SizeOfImage
named UPX1 and contains the compressed data and the 
This is important, because this determines the start of  stub. The reasoning behind this choice is apparently that 
where we will bind our stub. After we bind our stub we  it has less runtime impact since the compressed data will 
will   update   this   value   to   include   the   stub's   additional  ultimately   overwrite   itself.   ASPack   on   the   other   hand 
size. leaves the  original sections  in  place  and  adds  two  new 
ones,   one   for   the   stub   and   one   for   the   stub   data 
(compressed data presumably). Many packers allow you 
24.2 Setup New Section(s);
to   give   arbitrary   names   to   the   sections   as   a   minor 
Modify Originals method   of   hiding   what   packer   was   used.   Amusingly, 
Sections,   which   are  basically   areas  of   the   file   that  the  ASPack allows you to do this for the stub code section (by 
loader allocates and possible memory­maps to regions in  default .aspack)  but the  data section has a fixed name 
the running process' address space, are described in the  (.adata). Go figure.
PE header. They can take up zero disk space, when tells  If   you're   making   a   new   packer   then   for   development 
the loader to allocate the memory, but not to map part of  purposes  you  may  wish  to  simply  keep  all the sections 
the   file   in   (e.g.   this   is   routinely   done   for   sections   like  and   append   your   new   one.   Later   you   can   tweak   the 
uninitialized data.) section handling stuff since its trivial.
Since we are going to pack the application, and since we  In our example, we're going to stick all the stub code and 
will have to initialize it ourselves (i.e. the loader can't do  compressed data into one section which we will append to 
it   for   us)   we   will   need   to   modify   the   existing   section  the end. If you're going to do some resource preservation 
headers.   In   particular   We   will   need   to   modify   the  (like   to   preserve   icons   and   stuff   needed   for 
'characteristics'   of   the   section   to   convert   them   all   to  COM.OLE.ActiveX   like   registration   stuff)   there   will   be 
writeable since we will be writing when decompressing  yet another section added after the stub (because of the 
(IMAGE_SECTION_HEADER::Characteristics). Also, we  Microsoft OLE bug).
need   to   modify   the   size   of   compressed   sections   to   0 
(IMAGE_SECTION_HEADER::SizeOfRawData).   The  I keep a list of the section headers, keeping a reference to 
PointerToRawData   need  not  be   modified,   but  I  usually  the stub section on hand. The original sections I setup 
set it to 0 anyway. once   and   forget   about.   The   stub   section   will   be 
manipulated as we go since we really don't know how big 
It's   worth   noting   that   the   section   names   have   not  it's going to be yet until we compress the data. I setup the 
meaning   whatsoever   (with   one   exception   I   shall   note),  name, characteristics and virtual address now since we 
and   you   can   change   them   at­will.   They   are   purely  know   them.   I   use   the   following   for   characteristics
mnemonic.   The   important   bits   of   data   that   may   be 
broken into sections (or combined with existing sections) 
are   all   located   through   the   'directories'   located   at  IMAGE_SCN_CNT_INITIALIZED_DATA|
IMAGE_NT_HEADERS::OptionalHeader.DataDirectory. IMAGE_SCN_CNT_CODE|
IMAGE_SCN_MEM_EXECUTE|
IMAGE_SCN_MEM_READ|
Now   for   the   exception:   due   to   a   defect   in   the   internal 
IMAGE_SCN_MEM_WRITE
implementation   of   OLE   automation,   one   section,   the 
resource section, must preserve its name. The defective 

© CodeBreakers Journal, http://www.CodeBreakers­Journal.com
CodeBreakers Magazine – Vol. 1, No. 2, 2006

which   pretty   much   turns   on   all   access.   The  reasons.


IMAGE_SECTION_HEADER::VirtualAddress I initialize 
to: The data section should be distinct because we took pains 
to put the public stub data (the stuff we will be fixing­up 
IMAGE_NT_HEADERS::ImageBase +  for   the   stub   to   use   at   runtime)   located   at   the   very 
IMAGE_NT_HEADERS::OptionalHeader.SizeOfImage
beginning. Having it broken out in the source stub 'dll' 
makes   it   easy   to   find   this   important   area.
which sticks it at the end of the original exe's PE­mapped 
address space. (We'll have to fixup SizeOfImage for the  The .idata section usually can be part of the data section, 
result later, when we know how be the stub and data is). but   we   want   it   separate   because   we   are   going   to 
completely regenerate it. Having it in a separate section 
This will leave until later the need to fix up the fields: makes   it   easier   to   throw   out   the   original   (after 
VirtualSize   ­   how   big   in   memory processing)   and   replace   it   with   our   new   one.
PointerToRawData   ­   where   in   file
SizeOfRawData   ­   how   much   in   file   is   mapped   into  The .reloc section contains the relocation data. Similar to 
memory. .idata, we are going to process the original and replace it 
with the new contents.
24.3 Create and Add Stub The .text section is not special in itself. It is just what is 
Outside this Region left   over   from   not   being   in   the   other   sections.

OK, this is the twistiest part, mostly because there is a  If   you   don't   have   three   sections   containing   the   above 


lot of pointers to follow and also a bit of translation of  information   you   may   want   to   revisit   your   stub 
those pointers (in one case we will have to translate the  configurations.   Again,   the   particular   name   is   not 
pointers   twice!).   Hope   you   implemented   some   of   that  important, just the contents.
utility code we mentioned! You can take solace in the fact 
that   the   big   picture   is   quite   simple,   and   so   all   the 
complexity is in managing the indirection. 24.4 Starting to Process the
The big picture of stub creation is appending chunks of 
Original Stub
memory, then whizzing through that memory and fixing  The stub section will be small, so I build it up completely 
up pointers. We have to do the fixups because when we  in memory before transferring it to the final disk image. 
built the stub project the linker calculated where items  You   can   use   whatever   technique   for   managing   the 
were.   We   are   going   to   be   moving   stuff   to   a   place  memory   you   like,   but   you   might   have   to   do   some 
appropriate for the particular exe we are packing, so the  reallocations as the section grows. If you use C++ you can 
original   calculations   will   be   invalid   and   must   be  free   yourself   from   this   minor   chore   by   using   a 
corrected. std::vector<unsigned char> as your buffer. That way you 
can append with push_back() or insert() or resize() as you 
The way we described the decompression stub project in  chose.
installment  2   of   this  series  possibly  made   you  want  to 
wretch because of the funky linker options and various  We   mentioned   earlier   in   Utility   Code   how   you   could 
pragmas. Well, all that was done to make this operation  create   a   class   for   handling   the   details   of   accessing 
more manageable. If you set up the project as described  portions of PE files. Both the original application and the 
your  resulting stub 'dll' (which I shall call 'stub.dll' for  stub 'dll' are PE files and you can use an instance of this 
convenience)   should   have   four   sections.   This   was  utility class for each of these.
achieved via the various /MERGE options for the linker. 
We are going to create the new stub section by appending 
The names are not really important, but we want four to 
the code (.text in the cited example), the data (.A in the 
make it easier to find the important stuff. You can use 
example),   then   imports   (.idata)   and   finally   the 
DUMPBIN.EXE to see what the sections are. I am going 
relocations   (.reloc).   As   mentioned   earlier,   since   these 
to assume that you have four sections named:
sections   will   be   in   a   location   than   what   the   linker 
.text ­ code section thought, we must fixup internal pointers to reference the 
.A ­ specially organized data section in stub new   location.   Happily,   the   linker   provided   what   is 
.idata ­ import section essentially   a   list   of   32­bit   values   that   are   virtual 
.reloc ­ relocation data addresses (_not_ RVA's) of all such pointers. We just add 
a delta to that value that we compute. To compute that 
Ultimately we will merge all these together, but we want  delta   you   will   need   to   know   where   the   section   it 
them   separate   in   the   original   stub   'dll'   for   special  originally pointed to came from, and where it moved to. 

© CodeBreakers Journal, http://www.CodeBreakers­Journal.com
CodeBreakers Magazine – Vol. 1, No. 2, 2006

You then add this to the 32­bit value located at the place  to   support   TLS   in   DLLs.   This   is   an   obscure   feature 


specified   in   the   relocation   record.   Tedious?   Yes. (common   in   EXEs   though)   with   some   subtle   problems. 
The   problems   are   subtle   enough   that   even   Microsoft 
To   simplify   this   relocation   task   I   suggest   using   the  advises against using it, and I have never seen it done in 
Address Translator utility class mentioned earlier. Then  production code.
you   just   stick   your   address   in   and   get   back   what   it 
The problem is that the OS loader allocates TLS at load 
translates to. To use this, however, it must be setup. You 
time and stuffs pointers in appropriate places. However, 
setup the translator as you append your sections. Here is 
this   is   a   one­shot   opportunity   and   it   does   not   perform 
some pseudo­code of how to do it for this example packer:
this   action   if   a   DLL   is   loaded   later,   like   when   the 
application   calls   LoadLibrary()   and   such.   Consequently 
Merging the code and data sections given:
folks are cautioned against using it in a DLL unless you 
• buffer of bytes for destination stub section (empty) absolutely, positively, know the DLL will only be loaded 
implicitly, not explicitly.
•  translator (empty)
• original stub 'dll' w/ mechanism to access sections by  Well, guess what? Unless we take pains to change affairs, 
RVA _all_   the   original   applications   DLLs   will   be   loaded 
explicitly  (by  the stub) and thus  TLS in the DLLs will 
• list of sections in stub 'dll'
fail.   We   can   change   the   affairs   by   manipulating   the 
• RVA of start of stub section in destination exe  imports section to load the application's original DLLs. 
(computed earlier) We do this by adding bogus import structures that makes 
• preferred load address of destination exe it   look   like   the   stub   is   going   to   use   all   the   dlls   the 
original application did.
then do the following:

24.5 Merging Imports Data


• for .text section in stub 'dll' If you're developing a new packer, I advise initially doing 
• append all .text section to buffer the straight­forward append of the imports like you did 
for   the   code   and   data   for   starters.   This   will   work   for 
• add entry in translator translating from (original 
every real application I have ever seen. After your packer 
stub .text RVA start, original stub .text length) to 
is   working,   then   you   might   consider   adding   import 
(RVA dest + buffer.size(),
merging to support the non­existent­but­possible TLS­in­
original stub .text length)
a­DLL clients.
• resize buffer as needed to align on 32­bit boundary
I'm   going   to   hand­wave   through   this   because   it's   so 
• remember current size of buffer; this will be the index 
excruciatingly   boring   and   virtually   never   needs   to   be 
to public data
done. I will, however, tell you what you need to do and 
• for .A (data) section in stub 'dll' you can sift through the headers. If you get the rest of 
append all .A section to buffer your packer working, performing this task will involve no 
• add entry in translator translating from (original  new  technology  ­­  just  more   pointers,   translations,   and 
stub .A RVA start, appends. Briefly, to do this you must:
original stub .A length) to (RVA dest + buffer.size(),
• Go   through   the   stub's   imports;   collect   this 
original stub .A length)
information
• resize buffer as needed to align on 32­bit boundary
• Go   through   the   application's   imports;   merge   this 
information (selectively)
OK, at this point we have merged our code and data. We  • synthesize a new import section
also   have   an   index   that   corresponds   to   where   in   the 
buffer the packer public data is located. Keep that as an  • append it with limited fixups
index rather than pointer because as we grow the buffer,  Going   through   the   stub's   imports   we   only   really   care 
the   pointer   will  become   invalid  whereas  the   index  will  about the module name. This list of names will form a 
not. We also have set up the first two section entries in  'stop   list'   which   will   inhibit   merging   the   original 
the   translator   that   will   allow   us   to   transform   (stub)  application stuff. No need to force an import of a module 
original   pointers   into   (stub)   destination   pointers. that is already coming in, and who wants to fixup all the 
pointers anyway.
You can see the process is pretty much the same for the 
code and data portions of the stub. Really, it can also be  Going   through   the   application's   imports,   we   ignore 
the same for the import section. That is, unless you want  modules that come in through the stub. For all others we 

© CodeBreakers Journal, http://www.CodeBreakers­Journal.com
CodeBreakers Magazine – Vol. 1, No. 2, 2006

arbitrarily   select   an   import   (I   usually   just   choose   the  exists you must process it and copy the result to the 


first) and create a new import descriptor, Import Name  parallel item in the IAT, or you must process the IAT 
Table, Import Address Table, and strings for such. Your  only.
address translator will be of invaluable help in keeping 
track of where all the individual descriptors moved. The  Some of the items are an ordinal, which means you do 
major issue is that you will have to insert data _into the  nothing since it is not a pointer. Don't forget to check for 
middle_ of  the original  stub's  import table. This comes  this.
from the extra import module descriptors for the bogus 
imports.   The   result   is   all   the   pointers   from   the   stub's  After you have appended the Import section (either the 
original   descriptors   become   skewed   by   the   amount  easy way or the hard) and fixed up the pointers, set the:
additional   descriptors.   If   you   stuff   in   two   translation 
records for each half you will be OK. IMAGE_NT_HEADERS::OptionalHeader.DataDirectoryI
MAGE_DIRECTORY_ENTRY_IMPORT.VirtualAddress
Regardless of whether or not you do the import merging  IMAGE_NT_HEADERS::OptionalHeader.DataDirectoryI
or   simple   append,   you   must   still   perform   a   special  MAGE_DIRECTORY_ENTRY_IMPORT.Size
relocation pass on the imports data. The reason is that 
the pointers in the import section do not have relocation  to refer to the newly appended area. Go ahead and align 
records! These pointers are RVAs, and thus relative and  up the size of the buffer to a DWORD boundary for the 
thus   don't   need   to   be   fixed   up   at   load   time.  next   set   of   appends.   Now   we   are   ready   to   move   on   to 
Unfortunately,   the   thing   to   which   they   were   originally  exports.
relative has changed, and so we must fix them up. It's 
pretty straightforward.
24.6 Exports
Fixing up the import's RVAs means whizzing through the 
structures,   using   the   translator   to   get   the   translated  If you're just packing exe's (and not dlls) you won't have 
address, and saving back the result. to   worry   about   this   since   exe   don't   typically   export 
anything. On the other hand, if you _do_ intend to pack 
The   structure   of   the   imports   section   is   adequately  dlls, you will definitely have to deal with it. The exports 
described   in   the   articles   I   referenced   in   installment   1,  section needs to be available even before the stub has a 
and   I   refer   you   there   for   details,   however   there   are   a  chance to decompress the original data.
couple items I would like to point out:
This would be a straightforward append except we have 
to   fixup   RVAs,   so   we   have   to   traverse   the   structures 
I have never found the declaration of the Import Module  anyway. Fortunately, this is much simpler than what we 
Directory structure in the  headers. If anyone finds the  (potentially) did for import merging.
'official' declaration I would like to know its name and  There   is   one   root   structure   ­­ 
location.   Anyway,   it's   a   simple   struct,   and   here   is   the  IMAGE_EXPORT_DIRECTORY ­­ which is indicated in 
hand­crafted version I use: the directory of the original exe at:
struct IMP_MOD_DIR {
IMAGE_NT_HEADERS::OptionalHeader.DataDirectoryI
DWORD dwINTRVA; /*name table; may not exist*/
MAGE_DIRECTORY_ENTRY_EXPORT.VirtualAddress
DWORD dwTimeDateStamp; /*for bound exes, ignore*/
DWORD dwForwarderChainRVA; /*for bound exes, 
ignore*/ After copying that structure over we will need to fixup 
DWORD dwModNameRVA; /*name of dll*/ the three members:
DWORD dwIATRVA; /*import address table, must exist*/
};
AddressOfFunctions
AddressOfNames
AddressOfNameOrdinals
The   import   section   consists   of   an   array   of   these 
terminated by an empty one.
to reflect the RVAs of where they will be copied. Append 
• The INT contains a list of the ordinal, or name and 
them verbatim over from the original, one after the other, 
hint, of an imported symbol. following the IMAGE_EXPORT_DIRECTORY structure. 
• The INT may not exist. Borland shuns the INT  The   contents   are   largely   OK   as­is   except   for 
apparently, whereas Microsoft embraces AddressOfNames   and   some   special   cases   of 
• The IAT, for an unbound exe, contains the same  AddressOfFunctions.
information as the INT for an unbound exe. For 
Borland (which shuns the INT) the IAT must contain  First,   we   will   need   to   travel   across   the   original 
this information. The net effect is that if the INT  application's   AddressOfNames   array,   appending   the 

© CodeBreakers Journal, http://www.CodeBreakers­Journal.com
CodeBreakers Magazine – Vol. 1, No. 2, 2006

name   over   to   the   destination   and   setting   the 


corresponding   entry   in   the   destination's   copy   of  After   having   performed   stub   relocations   we   can   decide 
AddressOfNames   to   refer   to   this   copy.   This   is  whether   we   need   to   make   the   resultant   executable 
straightforward. relocatable. That decision should be made on the basis of 
whether the original is relocatable. There are two ways to 
Second, we will need to do something a bit odd. We will  tell this; the presence of a relocation directory entry is a 
travel across the original AddressOfFunctions array and  good one. There is also a characteristics bit that indicates 
look   for   pointers   (that   are   RVAs)   that   are   within   the  that relocation records have been stripped. Suffice it to 
export   section.   What   is   this   for?   Forwarded   functions!  say that if the original is not relocatable, then we don't 
Wack!   Anyway,   these   are   not   addresses   of   exported  need to make the result relocatable. If it is relocatable we 
objects   (functions,   data)   but   are   strings   that   must   be  need to create a relocation section for the stub. The stub 
copied.   In   this   special   case,   do   like   we   did   for   the  will   handle   doing   the   application   at   runtime.
AddressOfNames array and copy the string and set the 
pointer to point to that copy. To create a relocation section for the stub we first sort 
the   array   of   fixed­up   relocation   addresses   we   created 
while doing stub fixups. The sorting is needed to handle 
Setup:
the   quirky   format   of   the   relocation   section.

IMAGE_NT_HEADERS::OptionalHeader.DataDirectoryI Recall from installment 2 that the relocation records are 
MAGE_DIRECTORY_ENTRY_EXPORT.VirtualAddress stored   in   chunks,   one   chunk   per   page,   and   as   16­bit 
IMAGE_NT_HEADERS::OptionalHeader.DataDirectoryI records that are essentially offsets into the page. I refer 
MAGE_DIRECTORY_ENTRY_EXPORT.Size you to installment 2 for details, and to the references in 
installment 1. Suffice it to say, we travel along our now­
to where we stuck it and how big it became. Finally align  sorted  array,  emitting   chunk   headers  whenever  a   page 
up the buffer to a DWORD boundary for further appends,  change is detected and emitting 16­bit records otherwise. 
and you're done with this part. A page is on a 4096 boundary for 32­bit PE files so you 
can   AND   the   address   with   0xfffff000   to   find   it's   page 
FYI, we are 50% through our to­do list. And we haven't 
value, and you can AND the address with 0x00000fff to 
compressed any data yet! It's all downhill from here...
find   it's  offset   for   the   relocation   record.   Also   take   care 
that   when   you   detect   a   page   change,   you   will   possibly 
24.7 Do Stub Fixups and need   to   pad   to   a   32­bit   boundary   by   adding   a   no­op 
Relocating the Relocations relocation   record   (IMAGE_REL_BASED_ABSOLUTE).

At this point most of the stub's stuff has been built up  After processing all records set the
and we can fixup it's pointers to reflect the fact that we 
have extracted and moved it's original components. This  IMAGE_NT_HEADERS::OptionalHeader.DataDirectoryI
task is very similar to the relocation fixups performed in  MAGE_DIRECTORY_ENTRY_BASERELOC.VirtualAdd
the   stub.   The   difference   is   in   computing   the   delta   to  ress
IMAGE_NT_HEADERS::OptionalHeader.DataDirectoryI
apply.
MAGE_DIRECTORY_ENTRY_BASERELOC.Size

In normal relocation, like what the stub performs, there 
is only one delta. This is because the image as a whole  to   reflect   this   new   chunk   that   we   added.   We   should 
moves, and all items are relocated by the same amount.  already be aligned to a 32­bit boundary.
In our case, different sections have moved differently, and 
thus each item must be treated as having it's own delta. 24.8 Setup for TLS Stuff

The  delta  computation  in   this  case  is computed  as  the  If the original application used TLS we need to set some 
change between the RVA of the original item to be fixed  things  up   so   that   the   stub   can   help   out.   This  is   fairly 
up (RVAFixupOrig) and the RVA of the item after it has  straightforward.   Especially   if   there   is   none!
been moved (RVAFixupDest). The item at RVAFixupDest 
must   then   be   adjusted   by   this   delta. TLS   information   is   communicated   to   the   stub   through 
the public data. Way back, when we were appending the 
Since this translated RVADest is a relocated relocation, I  data section took note of the index that starts the data 
save it into an array of DWORDs for the next step. This  section. Also, since when we build the stub to have that 
saves   me   from   going   through   the   relocation   structure  structure at the beginning, now we can cast the address 
twice. of the buffer offset by the index to a pointer to the public 
structure.   Again,   we   can't   stow   this   pointer   since 

© CodeBreakers Journal, http://www.CodeBreakers­Journal.com
CodeBreakers Magazine – Vol. 1, No. 2, 2006

whenever we append  to  the   buffer  we  risk  reallocating  development   in   order   to   isolate   problems.   Not   useful 
memory,   but   we   can   recompute   the   pointer   as   needed  otherwise.
from the index between appends.
OK,   assuming   you   have   implemented   the   wrapper 
Anyway,   if   there   is   no   TLS,   as   evidenced   by:
interface I suggested in Utility Code, above, we are ready 
to do some compressing! Well almost. The compression of 
IMAGE_NT_HEADERS::OptionalHeader.DataDirectoryI the original data could be large, so I prefer not to do it to 
MAGE_DIRECTORY_ENTRY_TLS.VirtualAddress memory and rather directly compress to the output file 
(ergo   the   HANDLE   constructor   argument   in   the 
Compressor class). So we must compute the file position 
being 0, then we can simply clear out the copy of the tls 
of where this data goes.
directory in the public data (we called it tls_original in 
installment 2). We zeroed the size of the original PE sections, so the first 
real one is our new stub section. We need to compute the 
If   there   _is_   TLS,   then   we   copy   the   original   TLS 
file   offset   to   this   new   section   (PointerToRawData).
directory structure to the tls_original in the public data, 
and   copy   over   a   few   items   to   the   tls_proxy:
You   should   make   a   copy   of   the   original 
IMAGE_NT_HEADERS   if   you   haven't   already.   We   will 
SizeOfZeroFill manipulate   it   to   reflect   our   output.   Let's   call   it 
Characteristics nthdrDest  and  initialize   it   to   the   original  exe's  values.
StartAddressOfRawData Then calculate:
EndAddressOfRawData
nthdrDest .FileHeader.NumberOfSections = (new section 
count)
Note, the addresses do not need to be translated (shock­ int nSectionHeadersPos = 
of­shocks)   because   they   reference   data   in   the   original  IMAGE_DOS_HEADER::e_lfanew +
application,   which   we   have   not   moved.   The   stub   only  sizeof(IMAGE_NT_HEADERS);
accesses   that   data   _after_   it   decompresses   it. int nFirstSectionPos = nSectionHeadersPos +
(new section count) * 
sizeof(IMAGE_SECTION_HEADER);
Setup:

Align   up   the   nFirstSectionPos   according   to 


IMAGE_NT_HEADERS::OptionalHeader.DataDirectoryI IMAGE_DOS_HEADER::OptionalHeader.FileAlignment
MAGE_DIRECTORY_ENTRY_TLS.VirtualAddress
IMAGE_NT_HEADERS::OptionalHeader.DataDirectoryI
MAGE_DIRECTORY_ENTRY_TLS.Size This  is  the   PointerToRawData   for   our  stub  data.   Stick 
that value into the section information that we created 
way back in the beginning (it was the last item in the 
to   refer   to   the   tls_proxy   structure.   You   compute   the  list).
VirtualAddress with something like:
Do a seek to this position + the sizeof the buffer we have 
Stub Section RVA + dwIdxStubPublicData + offsetof 
( GlobalExternVars, tls_original ) been building up. The net effect of this is to cause the 
compressed   data   to   be   appended   to   the   stub   section 
without   having   to   stow   it   in   memory.
Nothing   was   appended   here,   no   need   to   align   up   the 
buffer. Instantiate   the   compressor   on   the   file   handle   (now 
properly positioned).
24.9 Compressing the Original As we mentioned in installment 2, the exact format of the 
Data data   stream   is   a   matter   of   design.   I   had   made   a 
suggestion   of   using:
Finally! We compress data! There are many compression 
libraries to choose from, take your pick so long as you can 
use   in   the   decompression   stub.   Recall   that   means  struct original_directory_information
operating with a minimal C runtime (which we produced  dword section_count
ourselves). The old standby of zlib works just fine for this  section 1 header
purpose,   but   don't   expect   spectacular   compression. {
dword RVA_location
dword size
You may also choose to implement a dummy compressor 
that   does   no   compression   at   all.   This   is   useful   during 

© CodeBreakers Journal, http://www.CodeBreakers­Journal.com
CodeBreakers Magazine – Vol. 1, No. 2, 2006

} version   information   so   that   one's   experience   with 


(section 1 compressed data) Explorer can be gratifying and fulfilling, but also so we 
... can support various OLE features.
if we were to use that, then we would invoke the following  If you don't care about these things simply carry on. If 
using the _original_ exe's NT headers: you do care, then more 'fun' awaits.

InsertData  The 'fun' that awaits is similar to what we did for exports 
( IMAGE_NT_HEADERS::OptionalHeader.DataDirectory earlier in that we walk a structure and optionally copy 
, stuff over, adjusting the pointer when we do and leaving 
sizeof(IMAGE_NT_HEADERS::OptionalHeader.DataDire it pointing to the original data in the compressed section 
ctory) ); otherwise.
DWORD dwSectionCount = 
IMAGE_NT_HEADERS::FileHeader.NumberOfSections;
InsertData ( &dwSectionCount, sizeof(dwSectionCount) ) The   difference   is   that   this   structure   is   more   complex, 
; with more objects and a more complex decision on what 
for each section IMAGE_SECTION_HEADER to keep. First let me briefly tell you what you want to 
InsertData ( &  keep uncompressed because that's the easy part to know 
IMAGE_SECTION_HEADER::VirtualAddress, and tedious part to  figure out experimentally. You will 
sizeof(IMAGE_SECTION_HEADER::VirtualAddress) );
want   to   keep   uncompressed   the   following   resources:
InsertData ( & 
IMAGE_SECTION_HEADER::SizeOfRawData,
sizeof(IMAGE_SECTION_HEADER::SizeOfRawData) );
• first RT_ICON should be kept
InsertData ( (actual pointer to original data),
IMAGE_SECTION_HEADER::SizeOfRawData ); • first RT_GROUP_ICON should be kept
• first RT_VERSION should be kept
In   other   words,   we   are   pushing   the   RVA   of   where   the 
data goes, the physical (uncompressed) size, and then the  • first "TYPELIB" should be kept
physical data. We do this for each section of the original.
• all "REGISTRY" should be kept

When we are done we invoke Finish() on the compressor 
to   flush   any   remaining   data   not   written. OK, that being said, keep in mind that resources are a 
multi­level tree of directories. You need to keep track of 
We   get   the   number   of   actual   compressed   bytes   with  at what level you are to make your comparisons in order 
CompressedCount().   This   we   add   to   the   size   of   the to determine whether to keep a resource or not. Also, as a 
buffer   we   were   building   and   store   it   in   the  perceived convenience, all the fixed sized structures are 
SizeOfRawData field of the section header for the stub. coalesced   at   the   beginning   with   variable   length   ones 
afterwards. This means all the directory structures are 
Finally,   get   a   pointer   to   the   structure   containing   the  at the beginning, with things like string identifiers and 
public   data   (this   is   why   we   didn't   write   out   this   until  resource data afterwards.
now).   Set   the   value   of   the   stub   entry   point   (after  I   do   a   similar   thing   as   with   the   stub   and   build   this 
translating,   of   course),   the   RVA   of   the   start   of   the  section in memory with a managed array of bytes. Once 
compressed data (which is the RVA of the stub + the size  it is constructed I write it out later.
of the stub buffer) and the size of the compressed data 
(which   we   got   from   the   compressor   when   done). You can walk the tree once to find where this boundary 
between fixed and variable sized data lays, then copy the 
Then   seek   back   to   the   position   PointerToRawData   we  fixed data verbatim. It's interesting to not that most of 
just computed and write out the stub buffer. Basically we  the   pointers   in   this   section   are   relative   to   the   section 
just   concatenated   the   two   in   reverse   order. itself, and thus do not require translation. The exception 
to this is the pointers to the actual resource data, which 
Finished with generating and writing out the stub! is an RVA.
Walk the tree a second time and append all the string 
24.10 Processing the Resource identifiers. Adjust the pointers to these strings keeping 
in mind that they are _not_ RVAs, but are rather relative 
Directory offsets into the resource section.
Processing   the   resource   directory   is   a   strictly   optional 
Walk   the   tree  a   third  time   and  copy   over  the  resource 
task.   It  is  a   bit   tedious.   Benefits   of   processing   include 
chunks   for   the   resources   types   of   interest   described 
preserving   the   ever­important   application   icon   and 
above. Keep in mind that these actually _are_ RVAs, so 

© CodeBreakers Journal, http://www.CodeBreakers­Journal.com
CodeBreakers Magazine – Vol. 1, No. 2, 2006

you   will   need   to   add   the   RVA   of   the   beginning   this  • IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT ­­ 
section.   What   is   that?   Well,   it   is   the   RVA   of   the   last  kiss it goodbye
section,   plus   its   size,   aligned   up   to   the 
• IMAGE_DIRECTORY_ENTRY_IAT ­­ expunge it
NT_HEADERS::OptionalHeader.SectionAlignment.   The 
resource   chunks   should   be   aligned   between   appends. • IMAGE_DIRECTORY_ENTRY_DEBUG ­­ (we don't 
really have bugs, anyway)
Setup   the   section   header   for   this   additional   section.   It 
_must_ have the name .rsrc. Setup the VirtualAddress of  Seriously,  though,   the   first  two   are   used  by   the  loader 
this   section   to   the   RVA   we   just   computed.   Setup   the  and   will   cause   crashing   behaviour.   Removing   them 
PointerToRawData in a similar manner, except use the  harms   nothing.   The   last   one   might   be   nice,   but   the 
last   sections   PointerToRawData   +   SizeOfRawData   and  debugger can't get to the data until after the application 
align   the   result   up   by   the   value   of  is running, which is too late.
IMAGE_NT_HEADERS::OptionalHeader.FileAlignment  • Writing out the Remainder
instead.   Set   the   SizeOfRawData   to   the   size   of   the 
• Copy over the original DOS stub.
resulting   chunk,   and   the   VirtualSize   to   the   same.   You 
can   align   these   values   up   if   you   like. • Write out the modified PE header.

Similar   to   what   we   did   with   the   stub,   seek   to   the  Position   to   the   section   header   offset   we   computed 
PointerToRawData and write out the data in the buffer  (nSectionHeadersPos) Loop through the section headers 
we've been building. we have been keeping on­hand and write them out. If you 
have a modified resource section, take care to rename the 
Finally, set:
original and make the new one be named .rsrc to work 
IMAGE_NT_HEADERS::OptionalHeader.DataDirectoryI
around the Microsoft OLE automation bug.
MAGE_DIRECTORY_ENTRY_RESOURCE.VirtualAddr Close your file.
ess
IMAGE_NT_HEADERS::OptionalHeader.DataDirectoryI
MAGE_DIRECTORY_ENTRY_RESOURCE.Size 25 Beyond Packers
I think it's useful to consider from a big picture of what a 
and we are done with that.
packer is, because subsets of the technology can be used 
for   different   applications.   For   instance,   we   bound   new 
24.11 Dotting I's and Crossing code   and   data   to   an   arbitrary   executable   that   was  not 
T's designed   to   host   it,   without   damaging   the   original 
program.   This   is   like   an   exe   binder.   Discard   the 
There   are   some   details   that   will   need   to   be   fixed   up  compression and a lot of the manipulation of directories 
before writing the rest of the stuff out. Mostly this has to  and   you   can   produce   one.   Similarly,   one   could   retain 
do with the various directory entries, but let's not forget  some   of   the   directory   manipulations,   like   with   the 
the entry point address! imports, and fashion a protector of sorts to resist reverse 
The entry point is computed as the stub 'dll's entry point  engineering.   Other   extended   applications   may   come   to 
after being translated with the translation device I hope  mind as well.
you created.
The   image   size   needs   to   be   recomputed   as   the   last  26 conclusions
section's   VirtualAddress   plus   its   VirtualSize.
I hoped you found some useful information in this article. 
I enjoyed having the opportunity to write it.
Most of the directory entries need to be copied over from 
the stub 'dll' after being passed through the translator. 
Exceptions   include   the   Resource   directory.   If   you 
processed   resources   you   should   point   it   to   the   new 
section you created. If you did not leave it as it was in the 
original. Resources will be available at runtime, but not 
to explorer or OLE (or ResHacker).
If you made exports/relocations, setup those entries (that 
was discussed earlier).
Some  directory   entries  should   definitely   be   zeroed   out:

© CodeBreakers Journal, http://www.CodeBreakers­Journal.com

You might also like