You are on page 1of 20

CodeBreakersMagazineVol.1,No.

2,2006

HowtoWriteYourOwnPacker
byBigBoote
Vol.1,No.2,2006

Abstract:
Whywriteyourownpackerwhentherearesomanyexistingonestochoosefrom?Well,asidefrommakingyourexecutablessmaller,
packingisagoodwaytoquicklyandeasilyobfuscateyourwork.

CodeBreakersJournal,http://www.CodeBreakersJournal.com
CodeBreakersMagazineVol.1,No.2,2006

How to Write Your Own Packer produced took about 1.5 weeks to produce including
researchand bugfixing. Subsequentones tookfarless
ByBigBoote sinceIhadalreadydonethehardpart,whichisfiguring
outhow.Hopefullythisdocumentwillsaveyouthattime
aswell!
Youdonothavetouseassemblerforthemostpart.Ifyou
Why write your own packer when there are so many
can part with supporting some esoteric features, you
existing onesto choose from? Well, aside from making
won'thavetouseitatall.Allofthatisrelevantforthe
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
LogoorObjectOrientedCOBOLifyoulike.
knowpackerseitherhaveanexplicit'unpack'function,
or there are readily available procdump scripts for OK,enoughoftheblahblahblah,ontotechnicalstuff....
generatinganunpackedversion.

3 Big Picture
1 Intro Simple. Executable is analyzed, transformed, and an
extrapieceofcodeisattachedwhichgetsinvokedinstead
Why write your own packer when there are so many oftheoriginalprogram.Thispieceiscalleda'stub'and
existing ones to choose from? Well, aside from making decompressestheimagetoitsoriginallocation.Thenit
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.
knowpackerseitherhaveanexplicit'unpack'function,or
there are readily available procdump scripts for Sounds simple, but there are pitfalls that await you.
generatinganunpackedversion. Someoftheseinclude:
SincethisdocumenthasquicklyexplodedinlengthI'm
SupportforsimplifiedThreadLocalStorage,whichis
goingtobreakitupintoseparateinstallments.Inthis
keyinsupportingDelphiapplications
installment I will cover the qualitative aspects of
Supportforcoderelocationfixupsindllsifyoucare
producingapacker.I'lldiscusswhatyou'regettinginto
aboutpackingdlls.RecallActiveXcontrolsaredlls
andhowthepackerisstructuredingeneral.I'llbriefly
too,asareothercommonthingsyoumightbe
discusssomepitfalls,andI'llgivesomelinkstotechnical
interestedinpacking
informationyouwillneedtobefamiliarwithbeforegoing
intothenextinstallments.
Supportforsomestuffthatmustbeavailableevenin
InthenexttwoinstallmentsI'llgointodetailsofhowto thecompressedform.Thisincludessomeofyour
implement the components of the packer and how I resourcesandexportnamesindlls
usuallygoaboutproducingthem. Dealingwithboundimports

2 What You're Getting Into Supportforstrippingoutdirectoryentriesthatwill


confusetheWindowsloadersincethedecompression
It'snotreallyhard,perse,butitisrathertediouscode. won'thavehappenedandtheywillpointtonothing
Lots of pointer manipulation and translation to keep useful,liketheIATanddebuginfo
trackof.Asidefromthat,ifyoucanwritecodetoaddand
subtractintegersanddofileIO,you'vegotalltheskill
Supportfordoingrelocationfixupsmanuallyonyour
needed! As mentioned, it is tedious code so you will
decompressionstubsinceitwillcertainlybeina
probably do well to not attempt this coding on a
differentmemorylocationthanwherethelinker
hangover;trustme,Iknow.
thoughtitwouldbewhenitwascompiled
FYI,thelastpackerIproducedwasfairlyfullfunctioned
(exes and dlls, several compression algorithms with DealingwithdifferencesininterpretationofthePE
debug capability and advanced support such as TLS specbetweendifferentvendor'slinkers.Borland
(criticalforDelphiapps))anditweighedinatabout3700 linkersinterpretaspectsofthespecdifferentlyfrom
lines for the packer tool and about 1000 lines for the Microsoft'ssoyouneedtobereadyforthat.
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

CodeBreakersJournal,http://www.CodeBreakersJournal.com
CodeBreakersMagazineVol.1,No.2,2006

WorkingaroundbugsinMicrosoftcode.Thereisan MakingthePackerApplication
infamousonerelatingtoOLEandtheresource Thepackerapplicationdoesallthehardwork.This
section.Manypackersdonotaccommodatethisand makessincewhenyourealizethestubissupposedto
thisisimportantforActiveXsupport. doaslittleaspossibletohaveaminimumimpacton
runtime.

4 First Step I'lltrytokeepcodeexamplestoaminimumbutthere


may be some reference to structure members when
OK,enoughofthehorrorstories.Thefirststepistoget describingwhat'sgoingonandmaybeasnippetortwo
painfully familiar with the file format of executables. wherecodeisclearerthanhumanlanguage.Mostofthe
Thisiscalledthe'PortableExecutable'format,orPEfor importantstructurescanbefoundinWINNT.Hforthose
short.Iwilldiscussitbrieflyhere.Youwillneedmore whowishtoreadahead.
detail in reality. Rather than attempting to duplicate
that,herearesomereferencesyouwillfindhelpful:
6 Continuo
ThePortableExecutableFileFormatfromTopto
Bottom Last installment I mentioned some of the bigpicture
aspectsofcreatinganexepacker.InthisinstallmentI
http://mup.anticrack.de/Randy%20Kath%20 amgoingtotalkaboutaparticularpartofthepacker,
%20PE%20Format.html/]http://mup.anticrack.de/Randy thedecompressionstub.Thisisthesimplerpart.Inthe
%20Kath%20%20PE%20Format.html
nextinstallment(s)I'lltalkaboutthepackerapplication
itself.Again,thisisn'tgoingtobesourceforapacker,but
agoodandreadablediscussion,butnottotallyaccurate I might do a straightforward one and publish it as an
when it comes to the import section. Dead wrong in addendumtothisseriesiffolksareinterestedinhaving
implying that thesesectionsalways exist they easily someworkingsourceasastartingpoint.
cannotexist.Still,agoodread.
Thedecompressionstubhasseveralresponsibilities:
AnInDepthLookintotheWin32PortableExecutable
Findthepackeddata
FileFormatpts1and2
//h**p://www.msdnaa.net/Resources/Display.aspx?ResID Restoredatacontents
=1083]http://www.msdnaa.net/Resources/Display.aspx? Performrelocationfixups
ResID=1083
//h**p://www.msdnaa.net/Resources/display.aspx?ResID ResolveallimportssincetheWindowsloadercouldn't
=1323]http://www.msdnaa.net/Resources/display.aspx?R doit
esID=1323
Performthreadlocalstoragedutiessincethe
greatarticle,weakondiscussionofresourcesection Windowsloadercouldn'tdoit
Boinkovertotheoriginalprogram
MicrosoftPortableExecutableandCommonObjectFile Youmayalsohavetohandlebeingreenteredifyou
FormatSpecification arepackingadll
//h**p://www.microsoft.com/whdc/hwdev/hardware/pecof
f.mspx]http://www.microsoft.com/whdc/hwdev/hardware Oh,anditalsohastorun.Soletsstartwiththat...
/pecoff.mspx

horse'smouth.Dry.Accurate.
7 A Stub That Runs
It'susefultorememberthatyourdecompressionstubis
5 Next Step actually a parasite onto a program that was never
OK,afteryou'vegottenfamiliarwiththose,wecanstart expectingforittobethere.Assuch,youshouldtryto
towritesomecode.I'mgoingtosavethatforthenext minimize your impact on the runtime environment in
installments(probablytwo).Theywilldetail: yourpacker.Ihadmentionedbeforethatyoucouldmake
a packer in Logo or ObjectOriented COBOL, and that
MakingtheUnpackerStub reallywasonlypartiallytrue.Youcanmakethepacker
Thestubhasseveralresponsibilitiesasidefromthe applicationthatwayfersureandyoumightevenbe
obviousdecompression.Italsohastoperformduties abletomaketheunpackerthatwaysometimesbutyou
normallydonebytheWindowsloader. willreallybemuchhappierwithC/C++/ASMforthestub
part.IpersonallylikeC++.Anyway,itwillbesmaller.If
youdon'tcareaboutthesize,stillusingstufflikeDelphi
orVBforthestubwouldbeproblematicbecauseithoists

CodeBreakersJournal,http://www.CodeBreakersJournal.com
CodeBreakersMagazineVol.1,No.2,2006

in subtle stuff like TLS and runtimes, and they don't assumptions.Inreallifeyoudon'thavetodoitthisway,
have primitives needed to thunk over to the original butlet'stemporarily pretend we are and at the end of
program. Plusit can hose COM stuff that the original this series you'll know how you might like to do it
appisn'texpecting.Solet'sassumetheunpackerwillbe different.
inthe lowerlevel languagesIspoke ofandtakesolace
that this is pretty straightforward code, and that the
Bigpictureisthattherewillbetwoprojects,producing
packerstillcanbeinwhatever. twodistinctexecutablesthepackerstubandthepacker
application. Their configuration will be significantly
Sincethestubisaparasite,andsinceitwillhavetobe
different.
locatedinaspotattheoriginalapplication'sconvenience,
wewillhavetoberelocatingitdynamicallyinthepacker
Wearegoingtodoabitofledgerdemainwiththestub
application.Tohelpwiththiswewillmakethestubwith
project which will be explained later, but for now,
relocationrecords.Theseareusuallyusedfordllswhen
configureaboilerplateprojectforyourstubthusly:
theycan'tbeloadedattheirpreferredaddress.Wewill
makeuseofthemwhenbindingthestubtotheoriginal ProduceaDLL
application.
Usestaticmultithreadedruntimelibraries

If you're an avid ASM coder, many things are more DisableRTTIandexceptionsupport


straightforward since you can take care to produce
positionindependent code. This won't necessarily free Ifthereareoptionsfortheboilerplatecodeitgenerates,
you from all relocation concerns, however. The make it simple, so that there is just DllMain being
decompressionlibraryofchoicemaywellnotbeposition implemented.We'regoingtothrowallthatawayanyway.
independent. Also, and references to global data will Goaheadandbuilditasasanitycheck,whichshouldgo
needtobefixedup. throughfine.
We'remakingthepackerstubaDLLnotbecauseitwill
ultimatelybeaDLLitwon't.We'redoingthisbecause
8 Choice of Compressor wewanttherelocationrecords.You_can_createitasan
You can pretty much use whatever compressor library exeprojectandcauseittohaverelocationrecords(linker
youwant,solongasyoubuilditinawaythatdoesn't option/FIXED:no),butIfindtheMicrosoft'slinkerwill
spewoutstuffviaprintforotherUIfunctions.Thereare crashrandomlyinthatconfiguration.StickwiththeDLL
plenty free compressors out there. You might want to configandyou'llbeOK.
startwithsomethinglikezlib.Itwon'tgiveyouthebest
Next, change the config thusly (this is for Microsoft's
compression,butIknowitworksinthisscenario.Also,
tools,you'llhavetolookuptheequivalentsforBorland's
another is UCL. This compresses better and is much
orgcc):
smaller codewise. It is GPL, however, and maybe you
careaboutthelicensingimplications.
Check the docs to the compressor you want for Linkeroptions:
configuration options and related stuff. For example,
BZip2requiresBZ_NO_STDIOtobedefinedtohaveno addanylibrarypathsyourcompressorlibwillbe
printfstuff. needing
/nodefaultlibdon'tusedefaultlibs
Configurethebuildtobecompatiblewiththestuband /map(filename)DOgenerateamapfile
compression library. For me, I disable RTTI and make remove/debugdon'tgeneratedebuginfo
sure I am linking the static runtime library, change/incremental:yesto/incremental:nodisable
incrementallinking
multithreaded. I optimize for size. The output should
produceastaticlibrary,ofcourse,ratherthanadll,since
the goal is to add no dependencies beyond the apps
originalones. Compileroptions:

SettingUpProjectsandnowforsomethingcompletely addanydefinesorheaderpathsyourcompressorlib
different willbeneeding
/FAcsgeneratelistingfileswithsource,assembly,and
machinecode
OK, I am going to take a brief break from code and
/Fa(filename)specifywheretheselistingsgo
technologicalstuffandtalkaboutprojectconfiguration. remove/GZcompilergeneratedstackchecks
Normally I wouldn't since that's a personal choice, removeanydebugoptions,itwon'thelpuswherewe're
howeverthistimeIwillbecausethingsItalkaboutlater going
will be dependent upon some of the configuration

CodeBreakersJournal,http://www.CodeBreakersJournal.com
CodeBreakersMagazineVol.1,No.2,2006

BOOLWINAPI_DllMainCRTStartup(HANDLE,
theseoptionsareprobablyavailableascheckboxes,so
DWORD,LPVOID)
youwon'thavetomanuallyaddthem.
{
//(programwillgohere)
returnTRUE;
}
Thegististhatwearenotgoingtohavenormaldebug
capabilitessoweturnoffthatstuff.Instead,wewillbe
relyingonthelistingofthecompilergeneratedassembly Thisshouldresolvethelinkererrorandwillbeourentry
toseecodeandthelinkergeneratedmapfiletoseeactual point. The place our program will ultimately go is
addresses. All this is interesting stuff in any project indicatedbythecomment.Ultimatelywe'llneverhitthe
'return TRUE' statement; it's just there to make the
really, but it is all we have for debugging in this one.
compilerhappy,andthefunctionsignatureiswhatitis
If you build now you will should get a linker error tomakethelinkerhappy.
complaining about an unresolved external symbol
Ifyouwanttobemorearty,youcandothefollowing:
DllMainCRTStartup@12. This is good! If you don't get
thatthenthedefaultlibsarecomingin.Thesymbolis #pragmacomment(linker,"/entry:\"StubEntryPoint\"")
possibledifferentforBorlandstuff.Othererrorsprobably voiddeclspec(naked)StubEntryPoint(){
meansomethingelseneedstobefixed;thisistheonly //(programwillgohere)
oneyoushouldgetforMicrosoft'scompiler. }

whichissyntacticallyclearer.
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
originalapphas.Thus,youcannotmakecallstofunky
pervertswhatthecompilernormallythinksaboutandI
dlls(vbrunX.dll,etc).Youhavenoideaiftheyarethere.
have seen it crash randomly. I have found when the
Youwilldowelltostaticallylinkyourruntimelibrary.
compiler gets in a crashing mood, that putting in:
Youwilldomuch(much)better,however,tonotlinkany
runtimelibrariesatall!ASMcoderswilltakedelightin
this fact already, because they are hardcore, but this asmnop
neednotdissuadetheC/C++coderswhoareaccustomed
tomalloc()strcmp()newstd::vector<>orsuch.Allthisis
inacoupleplacesseemstogetitbackontrack.Ain'tthat
doable. You will just have to provide your own
alaugh?!Whatever...
implementation of these functions. Fortunately, this is
prettyeasysinceyoucancallnativefunctionsexported As code is added, you should periodically build. The
by Kernel32.dll. /That/ dll is certainly present, and linkerwilladdmoreandmorecomplaintslikeaboveand
certainlyonethatisalreadyusedbytheoriginalappyou wewillhavetoimplementtheunderlyingmethodsthe
arepackingsofeelfreetouseitwhenyoulike. compilerisemittingreferencesto.Here'satip:whenyou
installedyourdevtools,youmayhavehadtheoptionto
installthesourcetotheCRuntime.Itwillbehelpfulin
10 Making a Trivial C somecasessinceyoucancutandpastesomeparts.In
Runtime to Link Instead of the particular,afunction:
Proper One extern"C"declspec(naked)void_chkstk(void)
Replacing the C Runtime might sound scary but
rememberweonlywanttoimplementwhatisnecessary; issometimesemittedbythecompilerquietly(ifyouhave
thiswillturnouttobeasmallsetofthings.Thelinker alargearrayonthestack,likeforabuffer).Justcutand
willhelpyoufigureoutwhattheseare.Recallthatwe pastethatone;it'sfunky.
turnedoffdefaultlibrarysearchingwiththe/nodefault
switch (or equivalent for your linker, that's for FYI,Itypicallyhavetoimplement:
Microsoft's).IfyouconfiguredasIsuggestedabove,we've
memcpy
gotalinkererroralready:DllMainCRTStartup@12We'll memset
fixthatonefirst. memcmp
malloc
Discard your boilerplate DllMain. Replace it with:
free
realloc

CodeBreakersJournal,http://www.CodeBreakersJournal.com
CodeBreakersMagazineVol.1,No.2,2006

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


operatornew(unsignedintnSize) application to help me out. That way the same stub
operatordelete(void*pv) worksforexesanddllsandinthesameway.Itinvolvesa
globalvariable,andtherearegoingtobeseveralofthose,
soletmediscussthatfirst.
Togetyougoingonwhatitmeanstodothissortofroll
yourownCruntime,pleaseseethefollowingarticle.It's
good and will save me from repeating the infomation 12 Packer Parameter Globals
here.There'ssampleimplementationaswell.
Therearegoingtobeparametersthatarecomputedby
ReduceEXEandDLLSizewithLIBCTINY.LIB thepackingapplicationandthatwillbeembeddedinthe
http://msdn.microsoft.com/msdnmag/issues/01/01/hood/d stubsoitcandoit'sworkatruntime.Theserequireabit
efault.aspx]http://msdn.microsoft.com/msdnmag/issues/ ofspecialhandlingbecausethepackerapplicationneeds
0...od/default.aspx
tofindtheseitemsatpacktime.Youcouldhardcodein
OK,we'renowsetuptodothework! the addresses into the packer. You would get these
addressesfromthemapfilegeneratedbythelinker.This
isa bittacky because youwillhave to doublecheckit
11 Unpacking Stub each time you alter the stub, which will be quite
Responsibilities frequentlywhiledeveloping.Instead,Iprefertodoabit
oflegerdemainwithstructures,sections,andsegments.
I mentioned way back that the stub has the following
Thisonlyneedstobedoneforthevariablespublishedto
duties:
thepacker.Regularglobalsyoumightwanttohavecan
Findthepackeddata bedoneasusualwithoutconcern.
Restoredatacontents First, simple stuff. I make one structure with all the
Performrelocationfixups,ifneeded important globals. Then one global instance of that
structure.Thusthereisonlyoneobjectthepackerhasto
ResolveallimportssincetheWindowsloadercouldn't
locateatpacktime.Let'scallthatstructure:
doit
Performthreadlocalstoragedutiessincethe //inaheader,sayGlobalExternVars.h
Windowsloadercouldn'tdoit structGlobalExternVars
{
Boinkovertotheoriginalprogram //stuffgoeshere
Youmayalsohavetohandlebeingreenteredifyou };
arepackingadll
Nowwewilldosomekookinessinourmain.cppfile:
It'simportantthatthestubrestoretheoriginaldatato
it'sexactoriginallocation.Thisisbecausewedon'tknow #pragmadata_seg(".A$A")
aboutwhatreferencesareintheoriginalcodetothings declspec(allocate(".A$A"))externstruct
like global data structures and functions pointers in GlobalExternVarsgev=
{
thingslikevtables.
//initializersgohere
Recall that the format of the PE file (links to good };
discussionswereprovidedinthepreviousinstallment)is #pragmadata_seg()

organizedintosections,whichhavealocationandsize.
Thisinformationisstoredinthesectionheaders,which What the Hell is that? Well, it creates a special data
describewherethesectionsgoinmemory(relativetothe sectionforourglobalvariables.Dirtylittlesecretabout
loadaddress). thelinkeristhatitsortsthesectionnameslexically,and
Todothisproperly,wewillbeneedingtoknowourload discardstheportionatthe'$'andafter.Bynamingthe
address.Ifweareastubforanexewecansimplydoa section'.A$A'wewillbeforcingtheglobalvarsstructure
GetModuleHandle(NULL) and the returned module tobeattheverybeginningofthedatasection,whichwill
handleisthebaseloadaddress.Thiswon'tworkforadll be easy for the packing application to locate. Next, we
however.Themodulehandleforthedllisonthestack. will merge some sections with the following linker
Wecanwritesomecodetogetit,orwecanchoosenotto options.Youcanputtheseonthelinkline,oryoucanbe
do the 'arty entry point' and it is referenceble as a fancierandplacetheminthecodewithapragma(ifyour
parameter(donotattempttoreferencethoseparameters toolssupportsuch).Ithinkputtingtheminthepragma
ifitisthestubforanexeunlessyouarefondofcrashes). makesitmoreobviousfromthecodestandpointthatthe
stuffisfragileandshouldbehandledcarefullyifchanges

CodeBreakersJournal,http://www.CodeBreakersJournal.com
CodeBreakersMagazineVol.1,No.2,2006

areneeded. 14 Decompressing the


#pragmacomment(linker,"/MERGE:.rdata=.A") Original Data
#pragmacomment(linker,"/MERGE:.data=.A")
#pragmacomment(linker,"/MERGE:.bss=.A") Thecompresseddataisstuffattachedbythepacker.Like
thestub,itwillhavestuckitsomewhere.Itcanlocated
mostanywhereyoulike.Apopularchoiceistolocateitat
Sotheglobaldata(anddon'tforgetyourcompressionlib the_end_ofwheretheoriginaldatawaslocated.Then,
mighthavesometoo)willallbemergedintoonesection, 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,noticethatImerged.bssintoo.Thishas overwritten.Fancy.Thiswillonlyworkofcourseifthe
a subtle simplifying effect. .bss is used to hold compressed data is smaller than the original, but we
_uninitialized_globals.Thesedon'tnormallytakeupfile generally hope that our compressor actually, uh,
space(sincetheyareuninitialized)buttheydotakeup compresses,andmakesthingssmaller.
memory. The packer will have to take this in
considerationwhenlayingouttheactualstubitbuilds. Thecompresseddataislocatedsomewhereplacedbythe
Bymergingitintothedatasection,itwilltakeupactual packingapplication.Where? Whoknows. There will be
filespaceandthusthepackerwon'thavetoworryabout neededapublishedexternalglobalspecifyingwhereand
it. There will be very little .bss at all so don't be setup at pack time. So add a
disturbedaboutittakingupspace;we'retalkingbytes. DWORD RVA_compressed_data_start;
DWORDcompressed_data_size;
totheGlobalExternVarsstruct.TransformingtheRVAto
13 Computing the Load theVAbyaddingtheload_addressspreviouslycomputed
Address will tell you where the compressed data is located at
OK,regardlessofwhetheryouhaveusedmytechnique runtime.
for publishing packer globals or rolled your own, let's
assumethatitisdone.Now,theoriginalpointwasthat Thespecificformatofyourcompresseddataiscompletely
wewouldbeneedingthethebaseaddressatruntimein uptoyou.Sinceessentiallywewillberestoringdatato
the stub so that we can convert Relative Virtual originallocations,whicharechunks(thesectionsofthe
Addresses (RVAs) to actual Virtual Addresses (VAs). originalPEfile),thesimplestreamformatof:
RecalltheVA=RVA+baseaddress.
structoriginal_directory_information
Mytechniqueistohaveapublishedglobalwhichisthe dwordsection_count
RVAofthestubentrypoint.Thepackersetsthisup.The section1header
{
stub then takes the address of the actual entry point,
dwordRVA_location
subtractsthe RVA computed and stored by the packer, dwordsize
and the result is the load location of the packed }
executable.Istorethisresultina'regular'global(which (section1compresseddata)
doesn't need to be part of the GlobalExternVars). ...
Idothisfirstthinginthemainstubentrypointthusly: The original_directory_information is the stuff in the
DataDirectory member of the
//globalvar
DWORDload_address=0;//computedactualload
IMAGE_OPTIONAL_HEADERofthePEheadersofthe
addressforconvenience originalapp.Thepackerwillhavechangedthesevalues
to be suitable for the stub, so it will need tostick the
//inthestubentrypointfunction originalinthecompressedstreamsowecangettothose
load_address=(DWORD)StubEntryPoint valuesatruntime.Thiswillsufficeforthestream.Feel
gev.RVA_stub_entry;
free to add whatever you might like to it as well. The
decompressionroutinepseudocodeis:
Note,ifyoudidnotdomyentrypointrenametrick,you
would use the name of your funtion instead, possibly structsection_header{
DWORDRVA_location;
_DllMainCRTStartup. This technique always works
DWORDsize;
regardlessofwethertheappliatonisaDLLorEXE. };
Once you have the load address you are all set up to
decompresstotheproperlocation.
//'regular'nonpublishedglobal
IMAGE_DATA_DIRECTORYorigdirinfo
IMAGE_NUMBEROF_DIRECTORY_ENTRIES;

CodeBreakersJournal,http://www.CodeBreakersJournal.com
CodeBreakersMagazineVol.1,No.2,2006

whizthroughtherecordsgettingtheDWORDatthe
addresstheyindicateandaddtheoffset
voiddecompress_original_data(){
void*pvCompData=(void*)
Pretty straightforward. The format of the relocation
(gev.RVA_compressed_data_start+load_address);
initialize_compressor(pvCompData,
recordsisalittlebitoddandisstructuredthewayitis
gev.compressed_data_size;); presumably for size considerations. The records are
organizedasaseriesofchunksofrecords,onechunkper
decompress_data(&origdirinfo,sizeof(origdirinfo)); page.Therecordsinthechunkreferenceanoffsetinto
the page. Additionally, for padding consideration there
intsection_count;
are records that are essentially noops and should be
decompress_data(&section_count,sizeof(section_count))
;
ignored.Pseudocodefollows:

for(inti=0;i<section_count;++i){ voidperform_relocations(){
section_headerhdr; //seeifnorelocationrecords
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;
}
//computeoffset
cleanup_compressor(); IMAGE_DOS_HEADER*dos_header=
} (IMAGE_DOS_HEADER*)load_address;
IMAGE_NT_HEADERS32*nt_hdr=
Thiswillbecalledinthemainentrypointofthestub (IMAGE_NT_HEADERS32*)
rightaftercomputingtheactualloadaddress. &((unsignedchar*)load_address)dos_header>e_lfanew;
DWORDreloc_offset=load_addressnt_hdr
That'sit!Whatcouldbeeasier?Well,noticethatwe're >OptionalHeader.ImageBase;
using a stream model for our compressor. Most
compressionlibrariescomeprettyclosetoimplementing //ifwe'rewherewewanttobe,nothingfurthertodo
thatbutyouhavetodoeversoslightlymoretomakeit if(reloc_offset==0)
return;
thatsimple.Iwrapupmycompressorsinaclasssothat
they all implement the above interface to make things //gottadoit,computethestart
simple like above. Swaping out compressors then just IMAGE_BASE_RELOCATION*ibr_current=
meansmakinganewadaptorclass.Therestofthestub (IMAGE_BASE_RELOCATION*)
need not be touched to put in different (origdirinfoIMAGE_DIRECTORY_ENTRY_BASERELOC
compressors/encryptors. .VirtualAddress+load_address);

//computetheend
Nowthatalltheoriginaldataisdecompressedintoit's IMAGE_BASE_RELOCATION*ibr_end=
originallocation,wehavetodostuffthattheWindows (IMAGE_BASE_RELOCATION*)
loader normally does. This includes relocation fixups, &((unsigned
importslookup,andTLSinitialization/thunking. char*)ibr_current)origdirinfo[IMAGE_DIRECTORY_EN
TRY_BASERELOC.Size];

15 Performing Relocation //loopthroughthechunks


while(ibr_current<ibr_end&&ibr_current
Fixups >VirtualAddress){
DWORDRVA_page=ibr_current>VirtualAddress;
ThisisreallyonlynecessaryforpackedDLLssinceEXEs intcount_reloc=(ibr_current>SizeOfBlock
aresupposedtobealwaysloadedattheirpreferredbase IMAGE_SIZEOF_BASE_RELOCATION)/
address.Infact,relocationrecordsareusuallystripped sizeof(WORD);
fromEXEssothere'snothingtoprocess. WORD*awRelType=(WORD*)((unsigned
char*)ibr_current+
Details of the relocation record format are sufficiently IMAGE_SIZEOF_BASE_RELOCATION);
detailedinthearticlesreferenceinthefirstinstallment. for(inti=0;i<nCountReloc;++i){
Forustoprocessthemwe: WORDwType=awRelTypenIdx>>12;
WORDwValue=awRelTypenIdx&0x0fff;
computeanoffsetofthepreferredbaseaddressand if(wType==IMAGE_REL_BASED_HIGHLOW){//doit
theactualloadaddress *((DWORD*)(RVA_page+wValue+load_address))+=
reloc_offset;
findtherelocationrecordsfromtheoriginaldirectory }
informationwejustdecompressed

CodeBreakersJournal,http://www.CodeBreakersJournal.com
CodeBreakersMagazineVol.1,No.2,2006

ibr_current=(IMAGE_BASE_RELOCATION*) IMAGE_IMPORT_MODULE_DIRECTORY.dwModuleNa
&((unsignedchar*)ibr_current)ibr_current>SizeOfBlock; meRVA has the name of the dll you will need to
} LoadLibrary()on).Onceyougettheaddress,youstickit
} intheparallellocationintheImportAddressTablearray.
Youdothisforeachmember.
ThisisthemajorityofwhatisneededtosupportDLLs. InthecasewhentheImportNameTableisnotpresent,
Thereisalittlebitmorediscussedlater.Giventhatthis however, as with Borland's linker, you must get the
issostraightforward,I'malittlesurprisedatthenumber address of the function name from the
ofpackersouttherethatdonotsupportDLLs. ImportAddressTable itself. Then you overwrite it with
Thenextmajorthingwehavetodoistoresolveallthe thefunctionaddress.
imports. This is only a little more involved that the It is important to use the ImportNameTable in
relocationrecords. preferencetotheImportAddressTablebecauseofathing
called'boundexecutables'.Ifyouwanttotestyourwork
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 AfterprocessingeachDLLyoumayormaynotwishto
doingGetProcAddresstoresolvetheimports.Thisisvery do a FreeLibrary. It's going to depend on how you
similar to the relocation record logic that I won't do a implementyourpackerapplication.We'lldiscussthatin
pseudocode example. Details of these structures are thenextinstallment,anditrelatesto'mergedimports'.
giveninthelinksprovidedinthefirstinstallment.The For now, suffice it to say that if you perform merged
structuresallstartat: imports,youcancallFreeLibrary,butifyoudonot,you
mustnotcallit.Youmightwanttoputthecallinand
origdirinfoIMAGE_DIRECTORY_ENTRY_IMPORT.Virtual commentitoutwhiledevelopinguntilyouhavemerged
Address
imports implemented. Merged imports is important for
properly supporting TLS that potentially exist in
ThereareacouplecaveatsIshouldmentionhowever: implicitly loaded DLLs. This leads into the final
responsibility for the stub, which is handling TLS
ThestructuresarewiredtogetherviaRVApointers. support.
Theseneedtohavetheload_addressaddedtomakea
realpointer
Thepointersinthestructuretostringsarereal 17 Supporting TLS
pointers.These_do_not_needtheload_address ThreadLocalStorage,orTLS,isahandyprogramming
added.Relocationprocessingwillhavealreadyfixed mechanism.Wedon'tcaremostly,sincewe'renotusing
theseup. it, but the original application to be packed might be
usingitindeed.Infact,Delphialwaysusesit,andsoif
Don'tforgetaboutimportingbyordinal.Youwillknow we're going to support packing Delphi apps, we better
thisishappeningbecausethepointertothestringwill accomodateit.
have the high bit set ( (ptr & 0x8000000) != 0 ).
BorlandandMicrosoftlinkersdodifferentthings,soyou TLSfundamentallyisdoneviaAPIcalls.Ingeneral,you
have to be prepared to get the string from either of allocatean'index'whichyoustoreinaglobalvariable.
differentspots.Basically,therearetwoparallelarrays, WiththisindexyoucangetaDWORDvaluespecificto
theImportNameTablewhichyougetfrom: 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,youcanwritecodelikethis:
andtheImportAddressTablewhichyougetfrom:
declspec(thread)inttls_int_value=0;
IMAGE_IMPORT_MODULE_DIRECTORY.dwIATPortio andeachthreadcanaccessit'sdistinctinstancebyname
nRVA likeanyothervariable.Idon'tknowifthereisanofficial
nameforthisformofTLS,soI'llcallit'simplifiedTLS'.
TheImportNameTableisoptional.Borlanddoesn'tuseit. Thisisdoneincooperationoftheoperatingsystem,and
Ifitispresent,youshoulduseittogetthenameofthe there are structures within the PE file that makes it
function and GetProcAddress() it's pointer (the happen.Thosestructuresarecontainedinachunkthat

CodeBreakersJournal,http://www.CodeBreakersJournal.com
CodeBreakersMagazineVol.1,No.2,2006

ispointedtobyyetanotherdirectoryentry: to invoke the original callbacks, and to indicated that


thereisadeferredcall.Initializetheseglobalsthusly:
origdirinfoIMAGE_DIRECTORY_ENTRY_TLS.VirtualA
ddress boolsafe_to_callback_tls=false;
booldelayed_tls_callback=false;

The problem is that the processing of this information


happensbytheOSonthecreationofeverythreadprior andprovidesomeauxilliaryglobalstoholddatathatis
to execution being passed to the thread start address. delayed:
Thiswouldnotnormallybeaconcernforus,exceptthat
at least one thread has been started before we can PVOIDTLS_dll_handle=NULL;
DWORDTLS_reason=0;
unpackthedata:ourthread!Whatwehavetodoisset
PVOIDTLS_reserved=NULL;
upafakeTLSmanagementsectiontocapturewhatthe
OShasdonebeforewestarted,thenmanuallycopythis
informationtotheoriginalappasourlaststep. thethunkimplementationproceedsassuch:

Forthis,Iaddtwoitemstotheexternalglobalpacker extern"C"voidNTAPITLS_callback(PVOIDDllHandle,
datastructure: DWORDReason,PVOIDReserved){
if(safe_to_callback_tls){
GlobalExternVars PIMAGE_TLS_CALLBACK*ppfn=
{ g_pkrdat.m_tlsdirOrig.AddressOfCallBacks;
//(otherstuffwepreviouslydescribed) if(ppfn){
IMAGE_TLS_DIRECTORYtls_original; while(*ppfn){
IMAGE_TLS_DIRECTORYtls_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;
modifiedfromthestub: TLS_reason=Reason;
TLS_reserved=Reserved;
tls_proxy.AddressOfIndex }
tls_proxy.AddressOfCallBacks }

InthestubwewillinializetheAddressOfIndextopoint ThiswillprovideaplacefortheOStostoretheslotinfo,
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 thenwewillcapturetheparametersforlaterwhenwe
functionpointersinthestub.Thefunctionpointersarray will invoke the original thunks after decompression.
isalistofthingsthatiscalledwheneveranewthreadis Again,thisisalldonebecausetheOSwillbedoingthis
created. It is intended to be used for user defined stuff before we have a chance to decompress. After we
initializationoftheTLSobjects.Alas,nocompilerIhave decompress, we pass the call straight to the original
seenhaseverusedthem.Moreover,ontheWindows9x application.
line,thesefunctionsarenotevencalled.Still,wesupport
it in case one day they are used. We point the Wehandlethislaststeplikeso:
AddressOfCallbacks to an array of two items, one
voidFinalizeTLSStuff(){
pointing to a function of our implementation, and the if
secondbeingNULLtoindicatetheendofthelist. (origdirinfoIMAGE_DIRECTORY_ENTRY_TLS.Virtual
Address!=0){
TherewillbeaglobalDWORDfortheTLSslot:
*gev.tls_original.AddressOfIndex=TLS_slot_index;
void*TLS_data;
DWORDTLS_slot_index;
asm
{
The TLS callback function must be of the form: movecx,DWORDPTRTLS_slot_index;
movedx,DWORDPTRfs:02ch
movecx,DWORDPTRedx+ecx*4
movpvTLSData,ecx
extern"C"voidNTAPITLS_callback(PVOIDDllHandle,
}
DWORDReason,PVOIDReserved);
intsize=gev.tls_original.EndAddressOfRawData
gev.tls_original.StartAddressOfRawData;
alsoyouaddtwoglobalbooleansindicatingthatitissafe memcpy(pvTLSData,(void*)

CodeBreakersJournal,http://www.CodeBreakersJournal.com
CodeBreakersMagazineVol.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); differentstubtechniques.Ithinkthemostinterestingis
} UPX, where the packer actually acts somewhat like a
linker,buildingthestubcodedynamicallyandincluding
safe_to_callback_tls=true;
onlywhatisnecessaryatpacktime.
if(delayed_tls_callback){
TLSCallbackThunk(TLS_dll_handleTLS_reason You can implement the stub in the fashion of your
TLS_reserved); choosing,andyoucanomitfeaturesyoudon'tthinkwill
}
benecessaryinyourparticularapplication.
}

Onceyouhavedonethat,itisfinallysafetocalloverto 20 What's Next


the original program. You should have a published OK,thiswasagoodbitlongerthanIexpected.Still,I
external global that will be set up by the packing wantedtocommunicateasmuchaspossiblethedetails
application that specifies the original program's entry sothatotherswon'thavehadtospendasmuchtimein
point.Iwillcallit thedebuggerasIhad.Debuggingacompressedexeisa
majorpainbecausethedebugginginfoisalluselessso
DWORDorig_entry;
youhavetodoitinassembly.
Nextinstallmentwillcoverthepackerapplication,which
whichwillbeamemberofGlobalExternVars.Itwillbe
willbemuchmorestraightforwardfromthestandpoint
initializedtoanRVAandwe willfix itupto aVAby
of configuration, but will have much more work to do
addingtheload_address.Thisdoneonlyonceonthefirst
thanthestub.
pass,ofcourse.
ForEXEs,theentrypointwillneverreturn.ForDLLsit
will. Moreover for DLLs there are the original 21 Continuo
parameterswhichmustbepushed.Thisbringsustothe ThisseriesisaboutcreatingexepackersforWindows32
finaltopic,thelastbitneededforDLLsupport. bitPEfiles.
InthepreviousinstallmentIdescribedhowtocreatea
18 Last Bit for DLL Support decompressionstubthatwouldbeboundtoanexisting
executable. In this (final?) installment I'm going to
EXEs gointotheir entry pointonly once, and withno
describetheactualpackerapplication,whichbindsthe
parameters(remember,thisisnotmain(),butwellbefore
stubtoanarbitraryexecutableandsetsupparameters
that).DLLs,ontheotherhandenteratleasttwiceand
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
needstobedoneonce.Easyenough,addaglobalboolean
The packer application will wind up being the biggest
thatindicatesthatstuffwasdoneandsetittotrueafter
hunkofcodefortheproject.Fortunately,itwillbefairly
thefirstpass.
straightforward.
Theslightlymoretediousthingisproducingastubthat
worksforDLLsandexes,sinceyouwillwanttoreturn
thevalue. 22 First Things
WhatIliketodoismakeuseofthedeclspec(naked) Therearesomebasicthingstosetuporconsiderbefore
attibuteIappliedtotheStubEntryPoint.Thiscausesthe wegetmovingwiththeactualpacker.
compilertoemitnoprologandepilogcode.Consequently,
ifwedon'tmesswiththestack,wecandoandassembly 22.1 Project Configuration
jmptotheoriginalentrypoint,andtherightbehaviour
willhappenifweareanEXEoraDLL.Thusly: Asmentionedinthepreviousinstallment,configuration
isafunctionofyourparticulardesign.Forthesakeof
asmjmpgevt.orig_entry; discussioninthisarticleweareassumingadesignwhere
Andallshouldberunning. thedecompressionstubisproducedasadll.Thebinary
of that dll will be incorporated into the packer
applicationasabinaryresource.Noneofthisisstrictly
necessary.Thestub'dll'willneverexistintherealworld

CodeBreakersJournal,http://www.CodeBreakersJournal.com
CodeBreakersMagazineVol.1,No.2,2006

assuchsincewearegoingtosnipoutinterestingpieces. memorysinceithasmappedthesectionsappropriately.
You could just as easily use a tool to spew just the
interestingpiecestobinaryresources,orencodethemas So,Iwouldfurthersuggestcreatingautilityclassthat
staticdatainaCsourcefile.Thischoiceispertasteand 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 commandline 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 commandline (console) physicalmemorypointer.Wecanusethisdeviceforboth
application.CreateaRCfileandincludearesourcethat thememorymappedoriginal,andalsofortheresource
isthestub'dll'producedbyyourpreviousproject.That's loadedstub.Youdon'thavetodothisbutitwillmake
reallyitforconfiguration.I'msurethatwillbeawelcome yourlifeeasier.Thisisaplusbecauseit'salreadygoing
simplificationafterhavingsetthestubproject! togetalittleharderasitiswink.gif.Youmaywishto
throwinacoupleotherconvenientPEspecificitems,like
pointers to the image headers. We'll be using various
22.2 Utility Code fieldsintheseheadersatseveralpointsthroughoutthe
packingprocess.
Therearegoingtobesomethingsthataresimple,but Oneotherthingthatwillmakeyouhappierinthelong
verytedious,andyouwillprobablyliketoproducesome run is to produce some sort of wrapper for your
machinerytotendtothesetasks. compressionlibraryofchoice.Indoingsoyoucanboth
Onesuchtaskrelatestotranslatingaddresses.Wehave simplifyuseofthelibraryandalsobeabletoswapouta
todothisinacoupleplacesfordifferentreasons,soyou differentcompressorshouldyouchoose.Forexample:
might consider making some sort of general purpose
classCompressor{
addresstranslator.Itwillneedtohandleseveraldistinct
public:
rangesofaddressesbeingmappedindependentlytoother Compressor(HANDLEhFile);//create;writetogiven
ranges.Inpracticalterms,therewon'tbeahugenumber fileatcurrentfilepos
ofrangemappings(likeabout5),soifyouwanttojust voidInsertData(constvoid*pv,intsize);//sticksome
keepalistofrangemappingsanddoalinearsearchno uncompresseddatain
onewillchastiseyou. voidFinish();//finishanypendingwrites
DWORDCompressedCount();//countofoutput
Anothertediousthing(Ifind)isreadinglittlebitsand (compressed)data
pieces from the original executable file. This is };
particularly true when navigating a network of objects
sinceyouhavetorunalongpointerpaths.Tomakethis ThissortofinterfaceIhavefoundtobesuitableforall
muchmorebearableIuseamemorymappedfileforthe compressionlibrariesIhaveconsidered,thoughofcourse
original executable. Readonly access is fine since we Iwouldn'tuseitforthings otherthanthisexepacker.
won'tbealteringtheoriginal(BTW,ifforsomereason
you do want to write to the mapped image, but not Other than that you might like to make a general
disruptyouroriginalfile,rememberyoucanmapitcopy purposeresourcetreewalker,butwe'lldiscussthatlater
onwrite.I'vedonethisforsomeprotectors.)Idon'tuse in the implementation. Making this part generic is
thisapproachfortheoutputfile,however,becausemost mostly useful if you wish to reuse it in other projects.
ofthatwillbesequentialwrite.
Lastly,Iwouldliketoreiteratethatthepointersinthe Withthatbeingsaid,wearereadytomoveontothe...
executable are RVA's. This means you will need to do
_two_thingstotransformthemtorealpointers.First,if
you'vemappedtheimagetoanaddress,youwillneedto
23 Basic Tasks
add that base address. The stub 'dll' compiled in as a Herearethefundamentalthingsthepackerwillneedto
resourcewillbeaccessedthroughamemoryaddressonce do.
we LockResource() on it. That address is the base
DetermineSizeoforiginal
address. Now, that's all you have to do on a running
module(i.e.onetheOSloadermappedin),butthat'snot Setupnewsection(s);modifyoriginals
all we have to do. The second thing we have to do is Createandaddstuboutsidethisregion
considerthefileandsectionalignmentoftheexecutable Preserveexportinfo
(do_not_assumetheyaretheysame).Thenetresultof
FixupTLSstuff
thisisthattherewillneedtobeanadjustmentonaper
sectionbasistotheresultantpointer.Again,thisisnot RelocatetheRelocations
necessary for a module loaded by the OS loader into

CodeBreakersJournal,http://www.CodeBreakersJournal.com
CodeBreakersMagazineVol.1,No.2,2006

Compressandstoworiginaldata implementation finds the resources via section name


(.rsrc) rather than looking up in the directory (at
Processtheresourcedirectory
IMAGE_NT_HEADERS::OptionalHeader.DataDirectoryI
writeouttheresults MAGE_DIRECTORY_ENTRY_RESOURCE.VirtualAddr
ess).Theresultofthisisthatyoutakecareinhandling
andacoupleminorfixupslikechangingtheentrypoint thisone.Moredetailsonthatwhenwediscussresources.
andsomeofthedirectoryentries.
There are a variety of choices in determining how you
24 Details wantthenewsectionstobelaidout.Somepackerskeep
alltheoriginalones,makingthem0size,andaddingthe
Herearethedetailsofeachofthesetasks. 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 ForexampleUPXconsolidatesthesectionsandsplitsthe


headeroftheoriginal.Itislocatedat: consolidationintwo.Theloweraddressedpartisnamed
UPX0andisuninitialized.Thehigheraddressedpartis
IMAGE_NT_HEADERS::OptionalHeader.SizeOfImage
namedUPX1andcontainsthecompresseddataandthe
Thisisimportant,becausethisdeterminesthestartof stub.Thereasoningbehindthischoiceisapparentlythat
wherewewillbindourstub.Afterwebindourstubwe ithaslessruntimeimpactsincethecompresseddatawill
will update this value to include the stub's additional ultimately overwrite itself. ASPack on the other hand
size. leavesthe originalsections in place and adds two new
ones, one for the stub and one for the stub data
(compresseddatapresumably).Manypackersallowyou
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 ASPackallowsyoutodothisforthestubcodesection(by
loaderallocatesandpossiblememorymapstoregionsin default.aspack) butthe datasectionhasafixedname
therunningprocess'addressspace,aredescribedinthe (.adata).Gofigure.
PEheader.Theycantakeupzerodiskspace,whentells If you're making a new packer then for development
theloadertoallocatethememory,butnottomappartof purposes you may wish to simply keep allthesections
the file in (e.g. this is routinely done for sections like and append your new one. Later you can tweak the
uninitializeddata.) sectionhandlingstuffsinceitstrivial.
Sincewearegoingtopacktheapplication,andsincewe Inourexample,we'regoingtostickallthestubcodeand
willhavetoinitializeitourselves(i.e.theloadercan'tdo compresseddataintoonesectionwhichwewillappendto
it for us) we will need to modify the existing section theend.Ifyou'regoingtodosomeresourcepreservation
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
writeablesincewewillbewritingwhendecompressing yetanothersectionaddedafterthestub(becauseofthe
(IMAGE_SECTION_HEADER::Characteristics).Also,we MicrosoftOLEbug).
need to modify the size of compressed sections to 0
(IMAGE_SECTION_HEADER::SizeOfRawData). The Ikeepalistofthesectionheaders,keepingareferenceto
PointerToRawData need not be modified, but I usually thestubsectiononhand.TheoriginalsectionsIsetup
setitto0anyway. once and forget about. The stub section will be
manipulatedaswegosincewereallydon'tknowhowbig
It's worth noting that the section names have not it'sgoingtobeyetuntilwecompressthedata.Isetupthe
meaning whatsoever (with one exception I shall note), name,characteristicsandvirtualaddressnowsincewe
and you can change them atwill. They are purely know them. I use the following for characteristics
mnemonic. The important bits of data that may be
brokenintosections(orcombinedwithexistingsections)
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
resourcesection,mustpreserveitsname.Thedefective

CodeBreakersJournal,http://www.CodeBreakersJournal.com
CodeBreakersMagazineVol.1,No.2,2006

which pretty much turns on all access. The reasons.


IMAGE_SECTION_HEADER::VirtualAddressIinitialize
to: Thedatasectionshouldbedistinctbecausewetookpains
toputthepublicstubdata(thestuffwewillbefixingup
IMAGE_NT_HEADERS::ImageBase+ for the stub to use at runtime) located at the very
IMAGE_NT_HEADERS::OptionalHeader.SizeOfImage
beginning.Havingitbrokenoutinthesourcestub'dll'
makes it easy to find this important area.
whichsticksitattheendoftheoriginalexe'sPEmapped
addressspace.(We'llhavetofixupSizeOfImageforthe The.idatasectionusuallycanbepartofthedatasection,
resultlater,whenweknowhowbethestubanddatais). but we want it separate because we are going to
completelyregenerateit.Havingitinaseparatesection
Thiswillleaveuntillatertheneedtofixupthefields: 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.relocsectioncontainstherelocationdata.Similarto
memory. .idata,wearegoingtoprocesstheoriginalandreplaceit
withthenewcontents.
24.3 Create and Add Stub The.textsectionisnotspecialinitself.Itisjustwhatis
Outside this Region left over from not being in the other sections.

OK,thisisthetwistiestpart,mostlybecausethereisa If you don't have three sections containing the above


lotofpointerstofollowandalsoabitoftranslationof information you may want to revisit your stub
thosepointers(inonecasewewillhavetotranslatethe configurations. Again, the particular name is not
pointers twice!). Hope you implemented some of that important,justthecontents.
utilitycodewementioned!Youcantakesolaceinthefact
that the big picture is quite simple, and so all the
complexityisinmanagingtheindirection. 24.4 Starting to Process the
Thebigpictureofstubcreationisappendingchunksof
Original Stub
memory,thenwhizzingthroughthatmemoryandfixing Thestubsectionwillbesmall,soIbuilditupcompletely
uppointers.Wehavetodothefixupsbecausewhenwe inmemorybeforetransferringittothefinaldiskimage.
builtthestubprojectthelinkercalculatedwhereitems 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
appropriatefortheparticularexewearepacking,sothe reallocationsasthesectiongrows.IfyouuseC++youcan
original calculations will be invalid and must be free yourself from this minor chore by using a
corrected. std::vector<unsignedchar>asyourbuffer.Thatwayyou
canappendwithpush_back()orinsert()orresize()asyou
Thewaywedescribedthedecompressionstubprojectin chose.
installment 2 of this series possibly made you want to
wretchbecauseofthefunkylinkeroptionsandvarious We mentioned earlier in Utility Code how you could
pragmas.Well,allthatwasdonetomakethisoperation create a class for handling the details of accessing
moremanageable.Ifyousetuptheprojectasdescribed portionsofPEfiles.Boththeoriginalapplicationandthe
your resultingstub'dll'(whichIshallcall'stub.dll'for stub'dll'arePEfilesandyoucanuseaninstanceofthis
convenience) should have four sections. This was utilityclassforeachofthese.
achievedviathevarious/MERGEoptionsforthelinker.
Wearegoingtocreatethenewstubsectionbyappending
Thenamesarenotreallyimportant,butwewantfourto
thecode(.textinthecitedexample),thedata(.Ainthe
makeiteasiertofindtheimportantstuff.Youcanuse
example), then imports (.idata) and finally the
DUMPBIN.EXEtoseewhatthesectionsare.Iamgoing
relocations (.reloc). As mentioned earlier, since these
toassumethatyouhavefoursectionsnamed:
sections will be in a location than what the linker
.textcodesection thought,wemustfixupinternalpointerstoreferencethe
.Aspeciallyorganizeddatasectioninstub new location. Happily, the linker provided what is
.idataimportsection essentially a list of 32bit values that are virtual
.relocrelocationdata addresses(_not_RVA's)ofallsuchpointers.Wejustadd
adeltatothatvaluethatwecompute.Tocomputethat
Ultimatelywewillmergeallthesetogether,butwewant delta you will need to know where the section it
them separate in the original stub 'dll' for special originallypointedtocamefrom,andwhereitmovedto.

CodeBreakersJournal,http://www.CodeBreakersJournal.com
CodeBreakersMagazineVol.1,No.2,2006

Youthenaddthistothe32bitvaluelocatedattheplace 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 advisesagainstusingit,andIhaveneverseenitdonein
AddressTranslatorutilityclassmentionedearlier.Then productioncode.
you just stick your address in and get back what it
TheproblemisthattheOSloaderallocatesTLSatload
translatesto.Tousethis,however,itmustbesetup.You
timeandstuffspointersinappropriateplaces.However,
setupthetranslatorasyouappendyoursections.Hereis
this is a oneshot opportunity and it does not perform
somepseudocodeofhowtodoitforthisexamplepacker:
this action if a DLL is loaded later, like when the
application calls LoadLibrary() and such. Consequently
Mergingthecodeanddatasectionsgiven:
folksarecautionedagainstusingitinaDLLunlessyou
bufferofbytesfordestinationstubsection(empty) absolutely,positively,knowtheDLLwillonlybeloaded
implicitly,notexplicitly.
translator(empty)
originalstub'dll'w/mechanismtoaccesssectionsby Well,guesswhat?Unlesswetakepainstochangeaffairs,
RVA _all_ the original applications DLLs will be loaded
explicitly (by thestub)andthus TLSintheDLLswill
listofsectionsinstub'dll'
fail. We can change the affairs by manipulating the
RVAofstartofstubsectionindestinationexe importssectiontoloadtheapplication'soriginalDLLs.
(computedearlier) Wedothisbyaddingbogusimportstructuresthatmakes
preferredloadaddressofdestinationexe it look like the stub is going to use all the dlls the
originalapplicationdid.
thendothefollowing:

24.5 Merging Imports Data


for.textsectioninstub'dll' Ifyou'redevelopinganewpacker,Iadviseinitiallydoing
appendall.textsectiontobuffer thestraightforwardappendoftheimportslikeyoudid
for the code and data for starters. This will work for
addentryintranslatortranslatingfrom(original
everyrealapplicationIhaveeverseen.Afteryourpacker
stub.textRVAstart,originalstub.textlength)to
is working, then you might consider adding import
(RVAdest+buffer.size(),
mergingtosupportthenonexistentbutpossibleTLSin
originalstub.textlength)
aDLLclients.
resizebufferasneededtoalignon32bitboundary
I'm going to handwave through this because it's so
remembercurrentsizeofbuffer;thiswillbetheindex
excruciatingly boring and virtually never needs to be
topublicdata
done.Iwill,however,tellyouwhatyouneedtodoand
for.A(data)sectioninstub'dll' youcansiftthroughtheheaders.Ifyougettherestof
appendall.Asectiontobuffer yourpackerworking,performingthistaskwillinvolveno
addentryintranslatortranslatingfrom(original new technology just more pointers, translations, and
stub.ARVAstart, appends.Briefly,todothisyoumust:
originalstub.Alength)to(RVAdest+buffer.size(),
Go through the stub's imports; collect this
originalstub.Alength)
information
resizebufferasneededtoalignon32bitboundary
Go through the application's imports; merge this
information(selectively)
OK,atthispointwehavemergedourcodeanddata.We synthesizeanewimportsection
also have an index that corresponds to where in the
bufferthepackerpublicdataislocated.Keepthatasan appenditwithlimitedfixups
indexratherthanpointerbecauseaswegrowthebuffer, Going through the stub's imports we only really care
the pointer will become invalid whereas the index will aboutthemodulename.Thislistofnameswillforma
not.Wealsohavesetupthefirsttwosectionentriesin 'stop list' which will inhibit merging the original
the translator that will allow us to transform (stub) applicationstuff.Noneedtoforceanimportofamodule
original pointers into (stub) destination pointers. thatisalreadycomingin,andwhowantstofixupallthe
pointersanyway.
Youcanseetheprocessisprettymuchthesameforthe
codeanddataportionsofthestub.Really,itcanalsobe Going through the application's imports, we ignore
thesamefortheimportsection.Thatis,unlessyouwant modulesthatcomeinthroughthestub.Forallotherswe

CodeBreakersJournal,http://www.CodeBreakersJournal.com
CodeBreakersMagazineVol.1,No.2,2006

arbitrarily select an import (I usually just choose the existsyoumustprocessitandcopytheresulttothe


first)andcreateanewimportdescriptor,ImportName parallelitemintheIAT,oryoumustprocesstheIAT
Table,ImportAddressTable,andstringsforsuch.Your only.
addresstranslatorwillbeofinvaluablehelpinkeeping
trackofwherealltheindividualdescriptorsmoved.The Someoftheitemsareanordinal,whichmeansyoudo
majorissueisthatyouwillhavetoinsertdata_intothe nothingsinceitisnotapointer.Don'tforgettocheckfor
middle_of theoriginal stub's importtable.Thiscomes this.
fromtheextraimportmoduledescriptorsforthebogus
imports. The result is all the pointers from the stub's AfteryouhaveappendedtheImportsection(eitherthe
original descriptors become skewed by the amount easywayorthehard)andfixedupthepointers,setthe:
additional descriptors. If you stuff in two translation
recordsforeachhalfyouwillbeOK. IMAGE_NT_HEADERS::OptionalHeader.DataDirectoryI
MAGE_DIRECTORY_ENTRY_IMPORT.VirtualAddress
Regardlessofwhetherornotyoudotheimportmerging IMAGE_NT_HEADERS::OptionalHeader.DataDirectoryI
or simple append, you must still perform a special MAGE_DIRECTORY_ENTRY_IMPORT.Size
relocationpassontheimportsdata.Thereasonisthat
thepointersintheimportsectiondonothaverelocation torefertothenewlyappendedarea.Goaheadandalign
records!ThesepointersareRVAs,andthusrelativeand upthesizeofthebuffertoaDWORDboundaryforthe
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.
relativehaschanged,andsowemustfixthemup.It's
prettystraightforward.
24.6 Exports
Fixinguptheimport'sRVAsmeanswhizzingthroughthe
structures, using the translator to get the translated Ifyou'rejustpackingexe's(andnotdlls)youwon'thave
address,andsavingbacktheresult. to worry about this since exe don't typically export
anything.Ontheotherhand,ifyou_do_intendtopack
The structure of the imports section is adequately dlls,youwilldefinitelyhavetodealwithit.Theexports
described in the articles I referenced in installment 1, sectionneedstobeavailableevenbeforethestubhasa
and I refer you there for details, however there are a chancetodecompresstheoriginaldata.
coupleitemsIwouldliketopointout:
Thiswouldbeastraightforwardappendexceptwehave
to fixup RVAs, so we have to traverse the structures
IhaveneverfoundthedeclarationoftheImportModule anyway.Fortunately,thisismuchsimplerthanwhatwe
Directorystructureinthe headers.Ifanyonefindsthe (potentially)didforimportmerging.
'official'declarationIwouldliketoknowitsnameand There is one root structure
location. Anyway, it's a simple struct, and here is the IMAGE_EXPORT_DIRECTORYwhichisindicatedin
handcraftedversionIuse: thedirectoryoftheoriginalexeat:
structIMP_MOD_DIR{
IMAGE_NT_HEADERS::OptionalHeader.DataDirectoryI
DWORDdwINTRVA;/*nametable;maynotexist*/
MAGE_DIRECTORY_ENTRY_EXPORT.VirtualAddress
DWORDdwTimeDateStamp;/*forboundexes,ignore*/
DWORDdwForwarderChainRVA;/*forboundexes,
ignore*/ Aftercopyingthatstructureoverwewillneedtofixup
DWORDdwModNameRVA;/*nameofdll*/ thethreemembers:
DWORDdwIATRVA;/*importaddresstable,mustexist*/
};
AddressOfFunctions
AddressOfNames
AddressOfNameOrdinals
The import section consists of an array of these
terminatedbyanemptyone.
toreflecttheRVAsofwheretheywillbecopied.Append
TheINTcontainsalistoftheordinal,ornameand
themverbatimoverfromtheoriginal,oneaftertheother,
hint,ofanimportedsymbol. followingtheIMAGE_EXPORT_DIRECTORYstructure.
TheINTmaynotexist.BorlandshunstheINT The contents are largely OK asis except for
apparently,whereasMicrosoftembraces AddressOfNames and some special cases of
TheIAT,foranunboundexe,containsthesame AddressOfFunctions.
informationastheINTforanunboundexe.For
Borland(whichshunstheINT)theIATmustcontain First, we will need to travel across the original
thisinformation.TheneteffectisthatiftheINT application's AddressOfNames array, appending the

CodeBreakersJournal,http://www.CodeBreakersJournal.com
CodeBreakersMagazineVol.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.Thatdecisionshouldbemadeonthebasisof
whethertheoriginalisrelocatable.Therearetwowaysto
Second,wewillneedtodosomethingabitodd.Wewill tellthis;thepresenceofarelocationdirectoryentryisa
travelacrosstheoriginalAddressOfFunctionsarrayand goodone.Thereisalsoacharacteristicsbitthatindicates
look for pointers (that are RVAs) that are within the thatrelocationrecordshavebeenstripped.Sufficeitto
export section. What is this for? Forwarded functions! saythatiftheoriginalisnotrelocatable,thenwedon't
Wack! Anyway, these are not addresses of exported needtomaketheresultrelocatable.Ifitisrelocatablewe
objects (functions, data) but are strings that must be needtocreatearelocationsectionforthestub.Thestub
copied. In this special case, do like we did for the will handle doing the application at runtime.
AddressOfNamesarrayandcopythestringandsetthe
pointertopointtothatcopy. Tocreatearelocationsectionforthestubwefirstsort
the array of fixedup relocation addresses we created
whiledoingstubfixups.Thesortingisneededtohandle
Setup:
the quirky format of the relocation section.

IMAGE_NT_HEADERS::OptionalHeader.DataDirectoryI Recallfrominstallment2thattherelocationrecordsare
MAGE_DIRECTORY_ENTRY_EXPORT.VirtualAddress stored in chunks, one chunk per page, and as 16bit
IMAGE_NT_HEADERS::OptionalHeader.DataDirectoryI recordsthatareessentiallyoffsetsintothepage.Irefer
MAGE_DIRECTORY_ENTRY_EXPORT.Size youtoinstallment2fordetails,andtothereferencesin
installment1.Sufficeittosay,wetravelalongournow
towherewestuckitandhowbigitbecame.Finallyalign sorted array, emitting chunk headers whenever a page
upthebuffertoaDWORDboundaryforfurtherappends, changeisdetectedandemitting16bitrecordsotherwise.
andyou'redonewiththispart. Apageisona4096boundaryfor32bitPEfilessoyou
can AND the address with 0xfffff000 to find it's page
FYI,weare50%throughourtodolist.Andwehaven't
value,andyoucanANDtheaddresswith0x00000fffto
compressedanydatayet!It'salldownhillfromhere...
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 32bit boundary by adding a noop
Relocating the Relocations relocation record (IMAGE_REL_BASED_ABSOLUTE).

Atthispointmostofthestub'sstuffhasbeenbuiltup Afterprocessingallrecordssetthe
andwecanfixupit'spointerstoreflectthefactthatwe
haveextractedandmovedit'soriginalcomponents.This IMAGE_NT_HEADERS::OptionalHeader.DataDirectoryI
taskisverysimilartotherelocationfixupsperformedin 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

Innormalrelocation,likewhatthestubperforms,there
isonlyonedelta.Thisisbecausetheimageasawhole to reflect this new chunk that we added. We should
moves,andallitemsarerelocatedbythesameamount. alreadybealignedtoa32bitboundary.
Inourcase,differentsectionshavemoveddifferently,and
thuseachitemmustbetreatedashavingit'sowndelta. 24.8 Setup for TLS Stuff

The delta computation in this case iscomputed as the IftheoriginalapplicationusedTLSweneedtosetsome


changebetweentheRVAoftheoriginalitemtobefixed things up so that the stub can help out. This is fairly
up(RVAFixupOrig)andtheRVAoftheitemafterithas straightforward. Especially if there is none!
beenmoved(RVAFixupDest).TheitematRVAFixupDest
must then be adjusted by this delta. TLS information is communicated to the stub through
thepublicdata.Wayback,whenwewereappendingthe
SincethistranslatedRVADestisarelocatedrelocation,I datasectiontooknoteoftheindexthatstartsthedata
saveitintoanarrayofDWORDsforthenextstep.This section.Also,sincewhenwebuildthestubtohavethat
saves me from going through the relocation structure structureatthebeginning,nowwecancasttheaddress
twice. ofthebufferoffsetbytheindextoapointertothepublic
structure. Again, we can't stow this pointer since

CodeBreakersJournal,http://www.CodeBreakersJournal.com
CodeBreakersMagazineVol.1,No.2,2006

wheneverweappend to the buffer we risk reallocating development in order to isolate problems. Not useful
memory, but we can recompute the pointer as needed otherwise.
fromtheindexbetweenappends.
OK, assuming you have implemented the wrapper
Anyway, if there is no TLS, as evidenced by:
interfaceIsuggestedinUtilityCode,above,weareready
todosomecompressing!Wellalmost.Thecompressionof
IMAGE_NT_HEADERS::OptionalHeader.DataDirectoryI theoriginaldatacouldbelarge,soIprefernottodoitto
MAGE_DIRECTORY_ENTRY_TLS.VirtualAddress memoryandratherdirectlycompresstotheoutputfile
(ergo the HANDLE constructor argument in the
Compressorclass).Sowemustcomputethefileposition
being0,thenwecansimplyclearoutthecopyofthetls
ofwherethisdatagoes.
directoryinthepublicdata(wecalledittls_originalin
installment2). WezeroedthesizeoftheoriginalPEsections,sothefirst
realoneisournewstubsection.Weneedtocomputethe
If there _is_ TLS, then we copy the original TLS
file offset to this new section (PointerToRawData).
directorystructuretothetls_originalinthepublicdata,
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 Thencalculate:
EndAddressOfRawData
nthdrDest.FileHeader.NumberOfSections=(newsection
count)
Note,theaddressesdonotneedtobetranslated(shock intnSectionHeadersPos=
ofshocks) 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. intnFirstSectionPos=nSectionHeadersPos+
(newsectioncount)*
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
thatvalueintothesectioninformationthatwecreated
waybackinthebeginning(itwasthelastiteminthe
to refer to the tls_proxy structure. You compute the list).
VirtualAddresswithsomethinglike:
Doaseektothisposition+thesizeofthebufferwehave
StubSectionRVA+dwIdxStubPublicData+offsetof
(GlobalExternVars,tls_original) beenbuildingup.Theneteffectofthisistocausethe
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
properlypositioned).
24.9 Compressing the Original Aswementionedininstallment2,theexactformatofthe
Data data stream is a matter of design. I had made a
suggestion of using:
Finally!Wecompressdata!Therearemanycompression
librariestochoosefrom,takeyourpicksolongasyoucan
use in the decompression stub. Recall that means structoriginal_directory_information
operatingwithaminimalCruntime(whichweproduced dwordsection_count
ourselves).Theoldstandbyofzlibworksjustfineforthis section1header
purpose, but don't expect spectacular compression. {
dwordRVA_location
dwordsize
Youmayalsochoosetoimplementadummycompressor
that does no compression at all. This is useful during

CodeBreakersJournal,http://www.CodeBreakersJournal.com
CodeBreakersMagazineVol.1,No.2,2006

} version information so that one's experience with


(section1compresseddata) Explorercanbegratifyingandfulfilling,butalsosowe
... cansupportvariousOLEfeatures.
ifweweretousethat,thenwewouldinvokethefollowing Ifyoudon'tcareaboutthesethingssimplycarryon.If
usingthe_original_exe'sNTheaders: youdocare,thenmore'fun'awaits.

InsertData The'fun'thatawaitsissimilartowhatwedidforexports
(IMAGE_NT_HEADERS::OptionalHeader.DataDirectory earlierinthatwewalkastructureandoptionallycopy
, stuffover,adjustingthepointerwhenwedoandleaving
sizeof(IMAGE_NT_HEADERS::OptionalHeader.DataDire itpointingtotheoriginaldatainthecompressedsection
ctory)); otherwise.
DWORDdwSectionCount=
IMAGE_NT_HEADERS::FileHeader.NumberOfSections;
InsertData(&dwSectionCount,sizeof(dwSectionCount)) The difference is that this structure is more complex,
; withmoreobjectsandamorecomplexdecisiononwhat
foreachsectionIMAGE_SECTION_HEADER tokeep.Firstletmebrieflytellyouwhatyouwantto
InsertData(& keepuncompressedbecausethat'stheeasyparttoknow
IMAGE_SECTION_HEADER::VirtualAddress, andtediouspartto figureoutexperimentally.Youwill
sizeof(IMAGE_SECTION_HEADER::VirtualAddress));
want to keep uncompressed the following resources:
InsertData(&
IMAGE_SECTION_HEADER::SizeOfRawData,
sizeof(IMAGE_SECTION_HEADER::SizeOfRawData));
firstRT_ICONshouldbekept
InsertData((actualpointertooriginaldata),
IMAGE_SECTION_HEADER::SizeOfRawData); firstRT_GROUP_ICONshouldbekept
firstRT_VERSIONshouldbekept
In other words, we are pushing the RVA of where the
datagoes,thephysical(uncompressed)size,andthenthe first"TYPELIB"shouldbekept
physicaldata.Wedothisforeachsectionoftheoriginal.
all"REGISTRY"shouldbekept

WhenwearedoneweinvokeFinish()onthecompressor
to flush any remaining data not written. OK,thatbeingsaid,keepinmindthatresourcesarea
multileveltreeofdirectories.Youneedtokeeptrackof
We get the number of actual compressed bytes with atwhatlevelyouaretomakeyourcomparisonsinorder
CompressedCount(). This we add to the size of the todeterminewhethertokeeparesourceornot.Also,asa
buffer we were building and store it in the perceivedconvenience,allthefixedsizedstructuresare
SizeOfRawDatafieldofthesectionheaderforthestub. coalesced at the beginning with variable length ones
afterwards.Thismeansallthedirectorystructuresare
Finally, get a pointer to the structure containing the atthebeginning,withthingslikestringidentifiersand
public data (this is why we didn't write out this until resourcedataafterwards.
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 sectioninmemorywithamanagedarrayofbytes.Once
compresseddata(whichistheRVAofthestub+thesize itisconstructedIwriteitoutlater.
ofthestubbuffer)andthesizeofthecompresseddata
(which we got from the compressor when done). Youcanwalkthetreeoncetofindwherethisboundary
betweenfixedandvariablesizeddatalays,thencopythe
Then seek back to the position PointerToRawData we fixeddataverbatim.It'sinterestingtonotthatmostof
justcomputedandwriteoutthestubbuffer.Basicallywe the pointers in this section are relative to the section
just concatenated the two in reverse order. itself,andthusdonotrequiretranslation.Theexception
tothisisthepointerstotheactualresourcedata,which
Finishedwithgeneratingandwritingoutthestub! isanRVA.
Walkthetreeasecondtimeandappendallthestring
24.10 Processing the Resource identifiers.Adjustthepointerstothesestringskeeping
inmindthattheyare_not_RVAs,butareratherrelative
Directory offsetsintotheresourcesection.
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 everimportant application icon and
above.Keepinmindthattheseactually_are_RVAs,so

CodeBreakersJournal,http://www.CodeBreakersJournal.com
CodeBreakersMagazineVol.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 kissitgoodbye
section, plus its size, aligned up to the
IMAGE_DIRECTORY_ENTRY_IATexpungeit
NT_HEADERS::OptionalHeader.SectionAlignment. The
resource chunks should be aligned between appends. IMAGE_DIRECTORY_ENTRY_DEBUG(wedon't
reallyhavebugs,anyway)
Setup the section header for this additional section. It
_must_havethename.rsrc.SetuptheVirtualAddressof 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
PointerToRawDatainasimilarmanner,exceptusethe harms nothing. The last one might be nice, but the
last sections PointerToRawData + SizeOfRawData and debuggercan'tgettothedatauntilaftertheapplication
align the result up by the value of isrunning,whichistoolate.
IMAGE_NT_HEADERS::OptionalHeader.FileAlignment WritingouttheRemainder
instead. Set the SizeOfRawData to the size of the
CopyovertheoriginalDOSstub.
resulting chunk, and the VirtualSize to the same. You
can align these values up if you like. WriteoutthemodifiedPEheader.

Similar to what we did with the stub, seek to the Position to the section header offset we computed
PointerToRawDataandwriteoutthedatainthebuffer (nSectionHeadersPos)Loopthroughthesectionheaders
we'vebeenbuilding. wehavebeenkeepingonhandandwritethemout.Ifyou
haveamodifiedresourcesection,takecaretorenamethe
Finally,set:
originalandmakethenewonebenamed.rsrctowork
IMAGE_NT_HEADERS::OptionalHeader.DataDirectoryI
aroundtheMicrosoftOLEautomationbug.
MAGE_DIRECTORY_ENTRY_RESOURCE.VirtualAddr Closeyourfile.
ess
IMAGE_NT_HEADERS::OptionalHeader.DataDirectoryI
MAGE_DIRECTORY_ENTRY_RESOURCE.Size 25 Beyond Packers
Ithinkit'susefultoconsiderfromabigpictureofwhata
andwearedonewiththat.
packeris,becausesubsetsofthetechnologycanbeused
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 compressionandalotofthemanipulationofdirectories
beforewritingtherestofthestuffout.Mostlythishasto and you can produce one. Similarly, one could retain
dowiththevariousdirectoryentries,butlet'snotforget some of the directory manipulations, like with the
theentrypointaddress! imports,andfashionaprotectorofsortstoresistreverse
Theentrypointiscomputedasthestub'dll'sentrypoint engineering. Other extended applications may come to
afterbeingtranslatedwiththetranslationdeviceIhope mindaswell.
youcreated.
The image size needs to be recomputed as the last 26 conclusions
section's VirtualAddress plus its VirtualSize.
Ihopedyoufoundsomeusefulinformationinthisarticle.
Ienjoyedhavingtheopportunitytowriteit.
Mostofthedirectoryentriesneedtobecopiedoverfrom
thestub'dll'afterbeingpassedthroughthetranslator.
Exceptions include the Resource directory. If you
processed resources you should point it to the new
sectionyoucreated.Ifyoudidnotleaveitasitwasinthe
original.Resourceswillbeavailableatruntime,butnot
toexplorerorOLE(orResHacker).
Ifyoumadeexports/relocations,setupthoseentries(that
wasdiscussedearlier).
Some directory entries should definitely be zeroed out:

CodeBreakersJournal,http://www.CodeBreakersJournal.com

You might also like