pefile.py 295 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034
  1. #!/usr/bin/python
  2. """pefile, Portable Executable reader module
  3. All the PE file basic structures are available with their default names as
  4. attributes of the instance returned.
  5. Processed elements such as the import table are made available with lowercase
  6. names, to differentiate them from the upper case basic structure names.
  7. pefile has been tested against many edge cases such as corrupted and malformed
  8. PEs as well as malware, which often attempts to abuse the format way beyond its
  9. standard use. To the best of my knowledge most of the abuse is handled
  10. gracefully.
  11. Copyright (c) 2005-2024 Ero Carrera <ero.carrera@gmail.com>
  12. """
  13. __author__ = "Ero Carrera"
  14. __version__ = "2024.8.26"
  15. __contact__ = "ero.carrera@gmail.com"
  16. import codecs
  17. import collections
  18. import copy as copymod
  19. import functools
  20. import gc
  21. import math
  22. import mmap
  23. import os
  24. import string
  25. import struct
  26. import time
  27. import uuid
  28. from collections import Counter
  29. from hashlib import md5, sha1, sha256, sha512
  30. from typing import Union
  31. import ordlookup
  32. codecs.register_error("backslashreplace_", codecs.lookup_error("backslashreplace"))
  33. long = int
  34. # lru_cache with a shallow copy of the objects returned (list, dicts, ..)
  35. # we don't use deepcopy as it's _really_ slow and the data we retrieved using
  36. # this is enough with copy.copy taken from
  37. # https://stackoverflow.com/questions/54909357
  38. def lru_cache(maxsize=128, typed=False, copy=False):
  39. if not copy:
  40. return functools.lru_cache(maxsize, typed)
  41. def decorator(f):
  42. cached_func = functools.lru_cache(maxsize, typed)(f)
  43. @functools.wraps(f)
  44. def wrapper(*args, **kwargs):
  45. # return copymod.deepcopy(cached_func(*args, **kwargs))
  46. return copymod.copy(cached_func(*args, **kwargs))
  47. return wrapper
  48. return decorator
  49. @lru_cache(maxsize=2048)
  50. def cache_adjust_SectionAlignment(val, section_alignment, file_alignment):
  51. if section_alignment < 0x1000: # page size
  52. section_alignment = file_alignment
  53. # 0x200 is the minimum valid FileAlignment according to the documentation
  54. # although ntoskrnl.exe has an alignment of 0x80 in some Windows versions
  55. #
  56. # elif section_alignment < 0x80:
  57. # section_alignment = 0x80
  58. if section_alignment and val % section_alignment:
  59. return section_alignment * (int(val / section_alignment))
  60. return val
  61. def count_zeroes(data):
  62. return data.count(0)
  63. fast_load = False
  64. # This will set a maximum length of a string to be retrieved from the file.
  65. # It's there to prevent loading massive amounts of data from memory mapped
  66. # files. Strings longer than 1MB should be rather rare.
  67. MAX_STRING_LENGTH = 0x100000 # 2^20
  68. # Maximum number of imports to parse.
  69. MAX_IMPORT_SYMBOLS = 0x2000
  70. # Limit maximum length for specific string types separately
  71. MAX_IMPORT_NAME_LENGTH = 0x200
  72. MAX_DLL_LENGTH = 0x200
  73. MAX_SYMBOL_NAME_LENGTH = 0x200
  74. # Limit maximum number of sections before processing of sections will stop
  75. MAX_SECTIONS = 0x800
  76. # The global maximum number of resource entries to parse per file
  77. MAX_RESOURCE_ENTRIES = 0x8000
  78. # The maximum depth of nested resource tables
  79. MAX_RESOURCE_DEPTH = 32
  80. # Limit number of exported symbols
  81. MAX_SYMBOL_EXPORT_COUNT = 0x2000
  82. IMAGE_DOS_SIGNATURE = 0x5A4D
  83. IMAGE_DOSZM_SIGNATURE = 0x4D5A
  84. IMAGE_NE_SIGNATURE = 0x454E
  85. IMAGE_LE_SIGNATURE = 0x454C
  86. IMAGE_LX_SIGNATURE = 0x584C
  87. IMAGE_TE_SIGNATURE = 0x5A56 # Terse Executables have a 'VZ' signature
  88. IMAGE_NT_SIGNATURE = 0x00004550
  89. IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16
  90. IMAGE_ORDINAL_FLAG = 0x80000000
  91. IMAGE_ORDINAL_FLAG64 = 0x8000000000000000
  92. OPTIONAL_HEADER_MAGIC_PE = 0x10B
  93. OPTIONAL_HEADER_MAGIC_PE_PLUS = 0x20B
  94. def two_way_dict(pairs):
  95. return dict([(e[1], e[0]) for e in pairs] + pairs)
  96. directory_entry_types = [
  97. ("IMAGE_DIRECTORY_ENTRY_EXPORT", 0),
  98. ("IMAGE_DIRECTORY_ENTRY_IMPORT", 1),
  99. ("IMAGE_DIRECTORY_ENTRY_RESOURCE", 2),
  100. ("IMAGE_DIRECTORY_ENTRY_EXCEPTION", 3),
  101. ("IMAGE_DIRECTORY_ENTRY_SECURITY", 4),
  102. ("IMAGE_DIRECTORY_ENTRY_BASERELOC", 5),
  103. ("IMAGE_DIRECTORY_ENTRY_DEBUG", 6),
  104. # Architecture on non-x86 platforms
  105. ("IMAGE_DIRECTORY_ENTRY_COPYRIGHT", 7),
  106. ("IMAGE_DIRECTORY_ENTRY_GLOBALPTR", 8),
  107. ("IMAGE_DIRECTORY_ENTRY_TLS", 9),
  108. ("IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG", 10),
  109. ("IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT", 11),
  110. ("IMAGE_DIRECTORY_ENTRY_IAT", 12),
  111. ("IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT", 13),
  112. ("IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR", 14),
  113. ("IMAGE_DIRECTORY_ENTRY_RESERVED", 15),
  114. ]
  115. DIRECTORY_ENTRY = two_way_dict(directory_entry_types)
  116. image_characteristics = [
  117. ("IMAGE_FILE_RELOCS_STRIPPED", 0x0001),
  118. ("IMAGE_FILE_EXECUTABLE_IMAGE", 0x0002),
  119. ("IMAGE_FILE_LINE_NUMS_STRIPPED", 0x0004),
  120. ("IMAGE_FILE_LOCAL_SYMS_STRIPPED", 0x0008),
  121. ("IMAGE_FILE_AGGRESIVE_WS_TRIM", 0x0010),
  122. ("IMAGE_FILE_LARGE_ADDRESS_AWARE", 0x0020),
  123. ("IMAGE_FILE_16BIT_MACHINE", 0x0040),
  124. ("IMAGE_FILE_BYTES_REVERSED_LO", 0x0080),
  125. ("IMAGE_FILE_32BIT_MACHINE", 0x0100),
  126. ("IMAGE_FILE_DEBUG_STRIPPED", 0x0200),
  127. ("IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP", 0x0400),
  128. ("IMAGE_FILE_NET_RUN_FROM_SWAP", 0x0800),
  129. ("IMAGE_FILE_SYSTEM", 0x1000),
  130. ("IMAGE_FILE_DLL", 0x2000),
  131. ("IMAGE_FILE_UP_SYSTEM_ONLY", 0x4000),
  132. ("IMAGE_FILE_BYTES_REVERSED_HI", 0x8000),
  133. ]
  134. IMAGE_CHARACTERISTICS = two_way_dict(image_characteristics)
  135. section_characteristics = [
  136. ("IMAGE_SCN_TYPE_REG", 0x00000000), # reserved
  137. ("IMAGE_SCN_TYPE_DSECT", 0x00000001), # reserved
  138. ("IMAGE_SCN_TYPE_NOLOAD", 0x00000002), # reserved
  139. ("IMAGE_SCN_TYPE_GROUP", 0x00000004), # reserved
  140. ("IMAGE_SCN_TYPE_NO_PAD", 0x00000008), # reserved
  141. ("IMAGE_SCN_TYPE_COPY", 0x00000010), # reserved
  142. ("IMAGE_SCN_CNT_CODE", 0x00000020),
  143. ("IMAGE_SCN_CNT_INITIALIZED_DATA", 0x00000040),
  144. ("IMAGE_SCN_CNT_UNINITIALIZED_DATA", 0x00000080),
  145. ("IMAGE_SCN_LNK_OTHER", 0x00000100),
  146. ("IMAGE_SCN_LNK_INFO", 0x00000200),
  147. ("IMAGE_SCN_LNK_OVER", 0x00000400), # reserved
  148. ("IMAGE_SCN_LNK_REMOVE", 0x00000800),
  149. ("IMAGE_SCN_LNK_COMDAT", 0x00001000),
  150. ("IMAGE_SCN_MEM_PROTECTED", 0x00004000), # obsolete
  151. ("IMAGE_SCN_NO_DEFER_SPEC_EXC", 0x00004000),
  152. ("IMAGE_SCN_GPREL", 0x00008000),
  153. ("IMAGE_SCN_MEM_FARDATA", 0x00008000),
  154. ("IMAGE_SCN_MEM_SYSHEAP", 0x00010000), # obsolete
  155. ("IMAGE_SCN_MEM_PURGEABLE", 0x00020000),
  156. ("IMAGE_SCN_MEM_16BIT", 0x00020000),
  157. ("IMAGE_SCN_MEM_LOCKED", 0x00040000),
  158. ("IMAGE_SCN_MEM_PRELOAD", 0x00080000),
  159. ("IMAGE_SCN_ALIGN_1BYTES", 0x00100000),
  160. ("IMAGE_SCN_ALIGN_2BYTES", 0x00200000),
  161. ("IMAGE_SCN_ALIGN_4BYTES", 0x00300000),
  162. ("IMAGE_SCN_ALIGN_8BYTES", 0x00400000),
  163. ("IMAGE_SCN_ALIGN_16BYTES", 0x00500000), # default alignment
  164. ("IMAGE_SCN_ALIGN_32BYTES", 0x00600000),
  165. ("IMAGE_SCN_ALIGN_64BYTES", 0x00700000),
  166. ("IMAGE_SCN_ALIGN_128BYTES", 0x00800000),
  167. ("IMAGE_SCN_ALIGN_256BYTES", 0x00900000),
  168. ("IMAGE_SCN_ALIGN_512BYTES", 0x00A00000),
  169. ("IMAGE_SCN_ALIGN_1024BYTES", 0x00B00000),
  170. ("IMAGE_SCN_ALIGN_2048BYTES", 0x00C00000),
  171. ("IMAGE_SCN_ALIGN_4096BYTES", 0x00D00000),
  172. ("IMAGE_SCN_ALIGN_8192BYTES", 0x00E00000),
  173. ("IMAGE_SCN_ALIGN_MASK", 0x00F00000),
  174. ("IMAGE_SCN_LNK_NRELOC_OVFL", 0x01000000),
  175. ("IMAGE_SCN_MEM_DISCARDABLE", 0x02000000),
  176. ("IMAGE_SCN_MEM_NOT_CACHED", 0x04000000),
  177. ("IMAGE_SCN_MEM_NOT_PAGED", 0x08000000),
  178. ("IMAGE_SCN_MEM_SHARED", 0x10000000),
  179. ("IMAGE_SCN_MEM_EXECUTE", 0x20000000),
  180. ("IMAGE_SCN_MEM_READ", 0x40000000),
  181. ("IMAGE_SCN_MEM_WRITE", 0x80000000),
  182. ]
  183. SECTION_CHARACTERISTICS = two_way_dict(section_characteristics)
  184. debug_types = [
  185. ("IMAGE_DEBUG_TYPE_UNKNOWN", 0),
  186. ("IMAGE_DEBUG_TYPE_COFF", 1),
  187. ("IMAGE_DEBUG_TYPE_CODEVIEW", 2),
  188. ("IMAGE_DEBUG_TYPE_FPO", 3),
  189. ("IMAGE_DEBUG_TYPE_MISC", 4),
  190. ("IMAGE_DEBUG_TYPE_EXCEPTION", 5),
  191. ("IMAGE_DEBUG_TYPE_FIXUP", 6),
  192. ("IMAGE_DEBUG_TYPE_OMAP_TO_SRC", 7),
  193. ("IMAGE_DEBUG_TYPE_OMAP_FROM_SRC", 8),
  194. ("IMAGE_DEBUG_TYPE_BORLAND", 9),
  195. ("IMAGE_DEBUG_TYPE_RESERVED10", 10),
  196. ("IMAGE_DEBUG_TYPE_CLSID", 11),
  197. ("IMAGE_DEBUG_TYPE_VC_FEATURE", 12),
  198. ("IMAGE_DEBUG_TYPE_POGO", 13),
  199. ("IMAGE_DEBUG_TYPE_ILTCG", 14),
  200. ("IMAGE_DEBUG_TYPE_MPX", 15),
  201. ("IMAGE_DEBUG_TYPE_REPRO", 16),
  202. ("IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS", 20),
  203. ]
  204. DEBUG_TYPE = two_way_dict(debug_types)
  205. subsystem_types = [
  206. ("IMAGE_SUBSYSTEM_UNKNOWN", 0),
  207. ("IMAGE_SUBSYSTEM_NATIVE", 1),
  208. ("IMAGE_SUBSYSTEM_WINDOWS_GUI", 2),
  209. ("IMAGE_SUBSYSTEM_WINDOWS_CUI", 3),
  210. ("IMAGE_SUBSYSTEM_OS2_CUI", 5),
  211. ("IMAGE_SUBSYSTEM_POSIX_CUI", 7),
  212. ("IMAGE_SUBSYSTEM_NATIVE_WINDOWS", 8),
  213. ("IMAGE_SUBSYSTEM_WINDOWS_CE_GUI", 9),
  214. ("IMAGE_SUBSYSTEM_EFI_APPLICATION", 10),
  215. ("IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER", 11),
  216. ("IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER", 12),
  217. ("IMAGE_SUBSYSTEM_EFI_ROM", 13),
  218. ("IMAGE_SUBSYSTEM_XBOX", 14),
  219. ("IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION", 16),
  220. ]
  221. SUBSYSTEM_TYPE = two_way_dict(subsystem_types)
  222. machine_types = [
  223. ("IMAGE_FILE_MACHINE_UNKNOWN", 0x0),
  224. ("IMAGE_FILE_MACHINE_I386", 0x014C),
  225. ("IMAGE_FILE_MACHINE_R3000", 0x0162),
  226. ("IMAGE_FILE_MACHINE_R4000", 0x0166),
  227. ("IMAGE_FILE_MACHINE_R10000", 0x0168),
  228. ("IMAGE_FILE_MACHINE_WCEMIPSV2", 0x0169),
  229. ("IMAGE_FILE_MACHINE_ALPHA", 0x0184),
  230. ("IMAGE_FILE_MACHINE_SH3", 0x01A2),
  231. ("IMAGE_FILE_MACHINE_SH3DSP", 0x01A3),
  232. ("IMAGE_FILE_MACHINE_SH3E", 0x01A4),
  233. ("IMAGE_FILE_MACHINE_SH4", 0x01A6),
  234. ("IMAGE_FILE_MACHINE_SH5", 0x01A8),
  235. ("IMAGE_FILE_MACHINE_ARM", 0x01C0),
  236. ("IMAGE_FILE_MACHINE_THUMB", 0x01C2),
  237. ("IMAGE_FILE_MACHINE_ARMNT", 0x01C4),
  238. ("IMAGE_FILE_MACHINE_AM33", 0x01D3),
  239. ("IMAGE_FILE_MACHINE_POWERPC", 0x01F0),
  240. ("IMAGE_FILE_MACHINE_POWERPCFP", 0x01F1),
  241. ("IMAGE_FILE_MACHINE_IA64", 0x0200),
  242. ("IMAGE_FILE_MACHINE_MIPS16", 0x0266),
  243. ("IMAGE_FILE_MACHINE_ALPHA64", 0x0284),
  244. ("IMAGE_FILE_MACHINE_AXP64", 0x0284), # same
  245. ("IMAGE_FILE_MACHINE_MIPSFPU", 0x0366),
  246. ("IMAGE_FILE_MACHINE_MIPSFPU16", 0x0466),
  247. ("IMAGE_FILE_MACHINE_TRICORE", 0x0520),
  248. ("IMAGE_FILE_MACHINE_CEF", 0x0CEF),
  249. ("IMAGE_FILE_MACHINE_EBC", 0x0EBC),
  250. ("IMAGE_FILE_MACHINE_RISCV32", 0x5032),
  251. ("IMAGE_FILE_MACHINE_RISCV64", 0x5064),
  252. ("IMAGE_FILE_MACHINE_RISCV128", 0x5128),
  253. ("IMAGE_FILE_MACHINE_LOONGARCH32", 0x6232),
  254. ("IMAGE_FILE_MACHINE_LOONGARCH64", 0x6264),
  255. ("IMAGE_FILE_MACHINE_AMD64", 0x8664),
  256. ("IMAGE_FILE_MACHINE_M32R", 0x9041),
  257. ("IMAGE_FILE_MACHINE_ARM64", 0xAA64),
  258. ("IMAGE_FILE_MACHINE_CEE", 0xC0EE),
  259. ]
  260. MACHINE_TYPE = two_way_dict(machine_types)
  261. relocation_types = [
  262. ("IMAGE_REL_BASED_ABSOLUTE", 0),
  263. ("IMAGE_REL_BASED_HIGH", 1),
  264. ("IMAGE_REL_BASED_LOW", 2),
  265. ("IMAGE_REL_BASED_HIGHLOW", 3),
  266. ("IMAGE_REL_BASED_HIGHADJ", 4),
  267. ("IMAGE_REL_BASED_MIPS_JMPADDR", 5),
  268. ("IMAGE_REL_BASED_SECTION", 6),
  269. ("IMAGE_REL_BASED_REL", 7),
  270. ("IMAGE_REL_BASED_MIPS_JMPADDR16", 9),
  271. ("IMAGE_REL_BASED_IA64_IMM64", 9),
  272. ("IMAGE_REL_BASED_DIR64", 10),
  273. ("IMAGE_REL_BASED_HIGH3ADJ", 11),
  274. ]
  275. RELOCATION_TYPE = two_way_dict(relocation_types)
  276. dll_characteristics = [
  277. ("IMAGE_LIBRARY_PROCESS_INIT", 0x0001), # reserved
  278. ("IMAGE_LIBRARY_PROCESS_TERM", 0x0002), # reserved
  279. ("IMAGE_LIBRARY_THREAD_INIT", 0x0004), # reserved
  280. ("IMAGE_LIBRARY_THREAD_TERM", 0x0008), # reserved
  281. ("IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA", 0x0020),
  282. ("IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE", 0x0040),
  283. ("IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY", 0x0080),
  284. ("IMAGE_DLLCHARACTERISTICS_NX_COMPAT", 0x0100),
  285. ("IMAGE_DLLCHARACTERISTICS_NO_ISOLATION", 0x0200),
  286. ("IMAGE_DLLCHARACTERISTICS_NO_SEH", 0x0400),
  287. ("IMAGE_DLLCHARACTERISTICS_NO_BIND", 0x0800),
  288. ("IMAGE_DLLCHARACTERISTICS_APPCONTAINER", 0x1000),
  289. ("IMAGE_DLLCHARACTERISTICS_WDM_DRIVER", 0x2000),
  290. ("IMAGE_DLLCHARACTERISTICS_GUARD_CF", 0x4000),
  291. ("IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE", 0x8000),
  292. ]
  293. DLL_CHARACTERISTICS = two_way_dict(dll_characteristics)
  294. ex_dll_characteristics = [
  295. ("IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT", 0x0001),
  296. ("IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT_STRICT_MODE", 0x0002),
  297. ("IMAGE_DLLCHARACTERISTICS_EX_CET_SET_CONTEXT_IP_VALIDATION_RELAXED_MODE", 0x0004),
  298. ("IMAGE_DLLCHARACTERISTICS_EX_CET_DYNAMIC_APIS_ALLOW_IN_PROC", 0x0008),
  299. ("IMAGE_DLLCHARACTERISTICS_EX_CET_RESERVED_1", 0x0010),
  300. ("IMAGE_DLLCHARACTERISTICS_EX_CET_RESERVED_2", 0x0020),
  301. ]
  302. EX_DLL_CHARACTERISTICS = two_way_dict(ex_dll_characteristics)
  303. MIN_VALID_FILE_ALIGNMENT = 0x200
  304. SECTOR_SIZE = 0x200
  305. # Unwind info-related enums
  306. unwind_info_flags = [
  307. ("UNW_FLAG_EHANDLER", 0x01),
  308. ("UNW_FLAG_UHANDLER", 0x02),
  309. ("UNW_FLAG_CHAININFO", 0x04),
  310. ]
  311. UNWIND_INFO_FLAGS = two_way_dict(unwind_info_flags)
  312. registers = [
  313. ("RAX", 0),
  314. ("RCX", 1),
  315. ("RDX", 2),
  316. ("RBX", 3),
  317. ("RSP", 4),
  318. ("RBP", 5),
  319. ("RSI", 6),
  320. ("RDI", 7),
  321. ("R8", 8),
  322. ("R9", 9),
  323. ("R10", 10),
  324. ("R11", 11),
  325. ("R12", 12),
  326. ("R13", 13),
  327. ("R14", 14),
  328. ("R15", 15),
  329. ]
  330. REGISTERS = two_way_dict(registers)
  331. # enum _UNWIND_OP_CODES
  332. UWOP_PUSH_NONVOL = 0
  333. UWOP_ALLOC_LARGE = 1
  334. UWOP_ALLOC_SMALL = 2
  335. UWOP_SET_FPREG = 3
  336. UWOP_SAVE_NONVOL = 4
  337. UWOP_SAVE_NONVOL_FAR = 5
  338. UWOP_EPILOG = 6
  339. UWOP_SAVE_XMM128 = 8
  340. UWOP_SAVE_XMM128_FAR = 9
  341. UWOP_PUSH_MACHFRAME = 10
  342. # Resource types
  343. resource_type = [
  344. ("RT_CURSOR", 1),
  345. ("RT_BITMAP", 2),
  346. ("RT_ICON", 3),
  347. ("RT_MENU", 4),
  348. ("RT_DIALOG", 5),
  349. ("RT_STRING", 6),
  350. ("RT_FONTDIR", 7),
  351. ("RT_FONT", 8),
  352. ("RT_ACCELERATOR", 9),
  353. ("RT_RCDATA", 10),
  354. ("RT_MESSAGETABLE", 11),
  355. ("RT_GROUP_CURSOR", 12),
  356. ("RT_GROUP_ICON", 14),
  357. ("RT_VERSION", 16),
  358. ("RT_DLGINCLUDE", 17),
  359. ("RT_PLUGPLAY", 19),
  360. ("RT_VXD", 20),
  361. ("RT_ANICURSOR", 21),
  362. ("RT_ANIICON", 22),
  363. ("RT_HTML", 23),
  364. ("RT_MANIFEST", 24),
  365. ]
  366. RESOURCE_TYPE = two_way_dict(resource_type)
  367. # Language definitions
  368. lang = [
  369. ("LANG_NEUTRAL", 0x00),
  370. ("LANG_INVARIANT", 0x7F),
  371. ("LANG_AFRIKAANS", 0x36),
  372. ("LANG_ALBANIAN", 0x1C),
  373. ("LANG_ARABIC", 0x01),
  374. ("LANG_ARMENIAN", 0x2B),
  375. ("LANG_ASSAMESE", 0x4D),
  376. ("LANG_AZERI", 0x2C),
  377. ("LANG_BASQUE", 0x2D),
  378. ("LANG_BELARUSIAN", 0x23),
  379. ("LANG_BENGALI", 0x45),
  380. ("LANG_BULGARIAN", 0x02),
  381. ("LANG_CATALAN", 0x03),
  382. ("LANG_CHINESE", 0x04),
  383. ("LANG_CROATIAN", 0x1A),
  384. ("LANG_CZECH", 0x05),
  385. ("LANG_DANISH", 0x06),
  386. ("LANG_DIVEHI", 0x65),
  387. ("LANG_DUTCH", 0x13),
  388. ("LANG_ENGLISH", 0x09),
  389. ("LANG_ESTONIAN", 0x25),
  390. ("LANG_FAEROESE", 0x38),
  391. ("LANG_FARSI", 0x29),
  392. ("LANG_FINNISH", 0x0B),
  393. ("LANG_FRENCH", 0x0C),
  394. ("LANG_GALICIAN", 0x56),
  395. ("LANG_GEORGIAN", 0x37),
  396. ("LANG_GERMAN", 0x07),
  397. ("LANG_GREEK", 0x08),
  398. ("LANG_GUJARATI", 0x47),
  399. ("LANG_HEBREW", 0x0D),
  400. ("LANG_HINDI", 0x39),
  401. ("LANG_HUNGARIAN", 0x0E),
  402. ("LANG_ICELANDIC", 0x0F),
  403. ("LANG_INDONESIAN", 0x21),
  404. ("LANG_ITALIAN", 0x10),
  405. ("LANG_JAPANESE", 0x11),
  406. ("LANG_KANNADA", 0x4B),
  407. ("LANG_KASHMIRI", 0x60),
  408. ("LANG_KAZAK", 0x3F),
  409. ("LANG_KONKANI", 0x57),
  410. ("LANG_KOREAN", 0x12),
  411. ("LANG_KYRGYZ", 0x40),
  412. ("LANG_LATVIAN", 0x26),
  413. ("LANG_LITHUANIAN", 0x27),
  414. ("LANG_MACEDONIAN", 0x2F),
  415. ("LANG_MALAY", 0x3E),
  416. ("LANG_MALAYALAM", 0x4C),
  417. ("LANG_MANIPURI", 0x58),
  418. ("LANG_MARATHI", 0x4E),
  419. ("LANG_MONGOLIAN", 0x50),
  420. ("LANG_NEPALI", 0x61),
  421. ("LANG_NORWEGIAN", 0x14),
  422. ("LANG_ORIYA", 0x48),
  423. ("LANG_POLISH", 0x15),
  424. ("LANG_PORTUGUESE", 0x16),
  425. ("LANG_PUNJABI", 0x46),
  426. ("LANG_ROMANIAN", 0x18),
  427. ("LANG_RUSSIAN", 0x19),
  428. ("LANG_SANSKRIT", 0x4F),
  429. ("LANG_SERBIAN", 0x1A),
  430. ("LANG_SINDHI", 0x59),
  431. ("LANG_SLOVAK", 0x1B),
  432. ("LANG_SLOVENIAN", 0x24),
  433. ("LANG_SPANISH", 0x0A),
  434. ("LANG_SWAHILI", 0x41),
  435. ("LANG_SWEDISH", 0x1D),
  436. ("LANG_SYRIAC", 0x5A),
  437. ("LANG_TAMIL", 0x49),
  438. ("LANG_TATAR", 0x44),
  439. ("LANG_TELUGU", 0x4A),
  440. ("LANG_THAI", 0x1E),
  441. ("LANG_TURKISH", 0x1F),
  442. ("LANG_UKRAINIAN", 0x22),
  443. ("LANG_URDU", 0x20),
  444. ("LANG_UZBEK", 0x43),
  445. ("LANG_VIETNAMESE", 0x2A),
  446. ("LANG_GAELIC", 0x3C),
  447. ("LANG_MALTESE", 0x3A),
  448. ("LANG_MAORI", 0x28),
  449. ("LANG_RHAETO_ROMANCE", 0x17),
  450. ("LANG_SAAMI", 0x3B),
  451. ("LANG_SORBIAN", 0x2E),
  452. ("LANG_SUTU", 0x30),
  453. ("LANG_TSONGA", 0x31),
  454. ("LANG_TSWANA", 0x32),
  455. ("LANG_VENDA", 0x33),
  456. ("LANG_XHOSA", 0x34),
  457. ("LANG_ZULU", 0x35),
  458. ("LANG_ESPERANTO", 0x8F),
  459. ("LANG_WALON", 0x90),
  460. ("LANG_CORNISH", 0x91),
  461. ("LANG_WELSH", 0x92),
  462. ("LANG_BRETON", 0x93),
  463. ]
  464. LANG = two_way_dict(lang)
  465. # Sublanguage definitions
  466. sublang = [
  467. ("SUBLANG_NEUTRAL", 0x00),
  468. ("SUBLANG_DEFAULT", 0x01),
  469. ("SUBLANG_SYS_DEFAULT", 0x02),
  470. ("SUBLANG_ARABIC_SAUDI_ARABIA", 0x01),
  471. ("SUBLANG_ARABIC_IRAQ", 0x02),
  472. ("SUBLANG_ARABIC_EGYPT", 0x03),
  473. ("SUBLANG_ARABIC_LIBYA", 0x04),
  474. ("SUBLANG_ARABIC_ALGERIA", 0x05),
  475. ("SUBLANG_ARABIC_MOROCCO", 0x06),
  476. ("SUBLANG_ARABIC_TUNISIA", 0x07),
  477. ("SUBLANG_ARABIC_OMAN", 0x08),
  478. ("SUBLANG_ARABIC_YEMEN", 0x09),
  479. ("SUBLANG_ARABIC_SYRIA", 0x0A),
  480. ("SUBLANG_ARABIC_JORDAN", 0x0B),
  481. ("SUBLANG_ARABIC_LEBANON", 0x0C),
  482. ("SUBLANG_ARABIC_KUWAIT", 0x0D),
  483. ("SUBLANG_ARABIC_UAE", 0x0E),
  484. ("SUBLANG_ARABIC_BAHRAIN", 0x0F),
  485. ("SUBLANG_ARABIC_QATAR", 0x10),
  486. ("SUBLANG_AZERI_LATIN", 0x01),
  487. ("SUBLANG_AZERI_CYRILLIC", 0x02),
  488. ("SUBLANG_CHINESE_TRADITIONAL", 0x01),
  489. ("SUBLANG_CHINESE_SIMPLIFIED", 0x02),
  490. ("SUBLANG_CHINESE_HONGKONG", 0x03),
  491. ("SUBLANG_CHINESE_SINGAPORE", 0x04),
  492. ("SUBLANG_CHINESE_MACAU", 0x05),
  493. ("SUBLANG_DUTCH", 0x01),
  494. ("SUBLANG_DUTCH_BELGIAN", 0x02),
  495. ("SUBLANG_ENGLISH_US", 0x01),
  496. ("SUBLANG_ENGLISH_UK", 0x02),
  497. ("SUBLANG_ENGLISH_AUS", 0x03),
  498. ("SUBLANG_ENGLISH_CAN", 0x04),
  499. ("SUBLANG_ENGLISH_NZ", 0x05),
  500. ("SUBLANG_ENGLISH_EIRE", 0x06),
  501. ("SUBLANG_ENGLISH_SOUTH_AFRICA", 0x07),
  502. ("SUBLANG_ENGLISH_JAMAICA", 0x08),
  503. ("SUBLANG_ENGLISH_CARIBBEAN", 0x09),
  504. ("SUBLANG_ENGLISH_BELIZE", 0x0A),
  505. ("SUBLANG_ENGLISH_TRINIDAD", 0x0B),
  506. ("SUBLANG_ENGLISH_ZIMBABWE", 0x0C),
  507. ("SUBLANG_ENGLISH_PHILIPPINES", 0x0D),
  508. ("SUBLANG_FRENCH", 0x01),
  509. ("SUBLANG_FRENCH_BELGIAN", 0x02),
  510. ("SUBLANG_FRENCH_CANADIAN", 0x03),
  511. ("SUBLANG_FRENCH_SWISS", 0x04),
  512. ("SUBLANG_FRENCH_LUXEMBOURG", 0x05),
  513. ("SUBLANG_FRENCH_MONACO", 0x06),
  514. ("SUBLANG_GERMAN", 0x01),
  515. ("SUBLANG_GERMAN_SWISS", 0x02),
  516. ("SUBLANG_GERMAN_AUSTRIAN", 0x03),
  517. ("SUBLANG_GERMAN_LUXEMBOURG", 0x04),
  518. ("SUBLANG_GERMAN_LIECHTENSTEIN", 0x05),
  519. ("SUBLANG_ITALIAN", 0x01),
  520. ("SUBLANG_ITALIAN_SWISS", 0x02),
  521. ("SUBLANG_KASHMIRI_SASIA", 0x02),
  522. ("SUBLANG_KASHMIRI_INDIA", 0x02),
  523. ("SUBLANG_KOREAN", 0x01),
  524. ("SUBLANG_LITHUANIAN", 0x01),
  525. ("SUBLANG_MALAY_MALAYSIA", 0x01),
  526. ("SUBLANG_MALAY_BRUNEI_DARUSSALAM", 0x02),
  527. ("SUBLANG_NEPALI_INDIA", 0x02),
  528. ("SUBLANG_NORWEGIAN_BOKMAL", 0x01),
  529. ("SUBLANG_NORWEGIAN_NYNORSK", 0x02),
  530. ("SUBLANG_PORTUGUESE", 0x02),
  531. ("SUBLANG_PORTUGUESE_BRAZILIAN", 0x01),
  532. ("SUBLANG_SERBIAN_LATIN", 0x02),
  533. ("SUBLANG_SERBIAN_CYRILLIC", 0x03),
  534. ("SUBLANG_SPANISH", 0x01),
  535. ("SUBLANG_SPANISH_MEXICAN", 0x02),
  536. ("SUBLANG_SPANISH_MODERN", 0x03),
  537. ("SUBLANG_SPANISH_GUATEMALA", 0x04),
  538. ("SUBLANG_SPANISH_COSTA_RICA", 0x05),
  539. ("SUBLANG_SPANISH_PANAMA", 0x06),
  540. ("SUBLANG_SPANISH_DOMINICAN_REPUBLIC", 0x07),
  541. ("SUBLANG_SPANISH_VENEZUELA", 0x08),
  542. ("SUBLANG_SPANISH_COLOMBIA", 0x09),
  543. ("SUBLANG_SPANISH_PERU", 0x0A),
  544. ("SUBLANG_SPANISH_ARGENTINA", 0x0B),
  545. ("SUBLANG_SPANISH_ECUADOR", 0x0C),
  546. ("SUBLANG_SPANISH_CHILE", 0x0D),
  547. ("SUBLANG_SPANISH_URUGUAY", 0x0E),
  548. ("SUBLANG_SPANISH_PARAGUAY", 0x0F),
  549. ("SUBLANG_SPANISH_BOLIVIA", 0x10),
  550. ("SUBLANG_SPANISH_EL_SALVADOR", 0x11),
  551. ("SUBLANG_SPANISH_HONDURAS", 0x12),
  552. ("SUBLANG_SPANISH_NICARAGUA", 0x13),
  553. ("SUBLANG_SPANISH_PUERTO_RICO", 0x14),
  554. ("SUBLANG_SWEDISH", 0x01),
  555. ("SUBLANG_SWEDISH_FINLAND", 0x02),
  556. ("SUBLANG_URDU_PAKISTAN", 0x01),
  557. ("SUBLANG_URDU_INDIA", 0x02),
  558. ("SUBLANG_UZBEK_LATIN", 0x01),
  559. ("SUBLANG_UZBEK_CYRILLIC", 0x02),
  560. ("SUBLANG_DUTCH_SURINAM", 0x03),
  561. ("SUBLANG_ROMANIAN", 0x01),
  562. ("SUBLANG_ROMANIAN_MOLDAVIA", 0x02),
  563. ("SUBLANG_RUSSIAN", 0x01),
  564. ("SUBLANG_RUSSIAN_MOLDAVIA", 0x02),
  565. ("SUBLANG_CROATIAN", 0x01),
  566. ("SUBLANG_LITHUANIAN_CLASSIC", 0x02),
  567. ("SUBLANG_GAELIC", 0x01),
  568. ("SUBLANG_GAELIC_SCOTTISH", 0x02),
  569. ("SUBLANG_GAELIC_MANX", 0x03),
  570. ]
  571. SUBLANG = two_way_dict(sublang)
  572. # Initialize the dictionary with all the name->value pairs
  573. SUBLANG = dict(sublang)
  574. # Now add all the value->name information, handling duplicates appropriately
  575. for sublang_name, sublang_value in sublang:
  576. if sublang_value in SUBLANG:
  577. SUBLANG[sublang_value].append(sublang_name)
  578. else:
  579. SUBLANG[sublang_value] = [sublang_name]
  580. # Resolve a sublang name given the main lang name
  581. #
  582. def get_sublang_name_for_lang(lang_value, sublang_value):
  583. lang_name = LANG.get(lang_value, "*unknown*")
  584. for sublang_name in SUBLANG.get(sublang_value, []):
  585. # if the main language is a substring of sublang's name, then
  586. # return that
  587. if lang_name in sublang_name:
  588. return sublang_name
  589. # otherwise return the first sublang name
  590. return SUBLANG.get(sublang_value, ["*unknown*"])[0]
  591. # Ange Albertini's code to process resources' strings
  592. #
  593. def parse_strings(data, counter, l):
  594. i = 0
  595. error_count = 0
  596. while i < len(data):
  597. data_slice = data[i : i + 2]
  598. if len(data_slice) < 2:
  599. break
  600. len_ = struct.unpack("<h", data_slice)[0]
  601. i += 2
  602. if len_ != 0 and 0 <= len_ * 2 <= len(data):
  603. try:
  604. l[counter] = data[i : i + len_ * 2].decode("utf-16le")
  605. except UnicodeDecodeError:
  606. error_count += 1
  607. if error_count >= 3:
  608. break
  609. i += len_ * 2
  610. counter += 1
  611. def retrieve_flags(flag_dict, flag_filter):
  612. """Read the flags from a dictionary and return them in a usable form.
  613. Will return a list of (flag, value) for all flags in "flag_dict"
  614. matching the filter "flag_filter".
  615. """
  616. return [
  617. (flag, flag_dict[flag])
  618. for flag in flag_dict.keys()
  619. if isinstance(flag, (str, bytes)) and flag.startswith(flag_filter)
  620. ]
  621. def set_flags(obj, flag_field, flags):
  622. """Will process the flags and set attributes in the object accordingly.
  623. The object "obj" will gain attributes named after the flags provided in
  624. "flags" and valued True/False, matching the results of applying each
  625. flag value from "flags" to flag_field.
  626. """
  627. for flag, value in flags:
  628. if value & flag_field:
  629. obj.__dict__[flag] = True
  630. else:
  631. obj.__dict__[flag] = False
  632. def power_of_two(val):
  633. return val != 0 and (val & (val - 1)) == 0
  634. class AddressSet(set):
  635. def __init__(self):
  636. super().__init__()
  637. self.min = None
  638. self.max = None
  639. def add(self, value):
  640. super().add(value)
  641. self.min = value if self.min is None else min(self.min, value)
  642. self.max = value if self.max is None else max(self.max, value)
  643. def diff(self):
  644. return 0 if self.min is None or self.max is None else self.max - self.min
  645. class UnicodeStringWrapperPostProcessor:
  646. """This class attempts to help the process of identifying strings
  647. that might be plain Unicode or Pascal. A list of strings will be
  648. wrapped on it with the hope the overlappings will help make the
  649. decision about their type."""
  650. def __init__(self, pe, rva_ptr):
  651. self.pe = pe
  652. self.rva_ptr = rva_ptr
  653. self.string = None
  654. def get_rva(self):
  655. """Get the RVA of the string."""
  656. return self.rva_ptr
  657. def __str__(self):
  658. """Return the escaped UTF-8 representation of the string."""
  659. return self.decode("utf-8", "backslashreplace_")
  660. def decode(self, *args):
  661. if not self.string:
  662. return ""
  663. return self.string.decode(*args)
  664. def invalidate(self):
  665. """Make this instance None, to express it's no known string type."""
  666. self = None
  667. def render_pascal_16(self):
  668. try:
  669. self.string = self.pe.get_string_u_at_rva(
  670. self.rva_ptr + 2, max_length=self.get_pascal_16_length()
  671. )
  672. except PEFormatError:
  673. self.pe.get_warnings().append(
  674. "Failed rendering pascal string, "
  675. "attempting to read from RVA 0x{0:x}".format(self.rva_ptr + 2)
  676. )
  677. def get_pascal_16_length(self):
  678. return self.__get_word_value_at_rva(self.rva_ptr)
  679. def __get_word_value_at_rva(self, rva):
  680. try:
  681. data = self.pe.get_data(rva, 2)
  682. except PEFormatError:
  683. return False
  684. if len(data) < 2:
  685. return False
  686. return struct.unpack("<H", data)[0]
  687. def ask_unicode_16(self, next_rva_ptr):
  688. """The next RVA is taken to be the one immediately following this one.
  689. Such RVA could indicate the natural end of the string and will be checked
  690. to see if there's a Unicode NULL character there.
  691. """
  692. if self.__get_word_value_at_rva(next_rva_ptr - 2) == 0:
  693. self.length = next_rva_ptr - self.rva_ptr
  694. return True
  695. return False
  696. def render_unicode_16(self):
  697. try:
  698. self.string = self.pe.get_string_u_at_rva(self.rva_ptr)
  699. except PEFormatError:
  700. self.pe.get_warnings().append(
  701. "Failed rendering unicode string, "
  702. "attempting to read from RVA 0x{0:x}".format(self.rva_ptr)
  703. )
  704. class PEFormatError(Exception):
  705. """Generic PE format error exception."""
  706. def __init__(self, value):
  707. self.value = value
  708. def __str__(self):
  709. return repr(self.value)
  710. class Dump:
  711. """Convenience class for dumping the PE information."""
  712. def __init__(self):
  713. self.text = []
  714. def add_lines(self, txt, indent=0):
  715. """Adds a list of lines.
  716. The list can be indented with the optional argument 'indent'.
  717. """
  718. for line in txt:
  719. self.add_line(line, indent)
  720. def add_line(self, txt, indent=0):
  721. """Adds a line.
  722. The line can be indented with the optional argument 'indent'.
  723. """
  724. self.add(txt + "\n", indent)
  725. def add(self, txt, indent=0):
  726. """Adds some text, no newline will be appended.
  727. The text can be indented with the optional argument 'indent'.
  728. """
  729. self.text.append("{0}{1}".format(" " * indent, txt))
  730. def add_header(self, txt):
  731. """Adds a header element."""
  732. self.add_line("{0}{1}{0}\n".format("-" * 10, txt))
  733. def add_newline(self):
  734. """Adds a newline."""
  735. self.text.append("\n")
  736. def get_text(self):
  737. """Get the text in its current state."""
  738. return "".join("{0}".format(b) for b in self.text)
  739. STRUCT_SIZEOF_TYPES = {
  740. "x": 1,
  741. "c": 1,
  742. "b": 1,
  743. "B": 1,
  744. "h": 2,
  745. "H": 2,
  746. "i": 4,
  747. "I": 4,
  748. "l": 4,
  749. "L": 4,
  750. "f": 4,
  751. "q": 8,
  752. "Q": 8,
  753. "d": 8,
  754. "s": 1,
  755. }
  756. @lru_cache(maxsize=2048)
  757. def sizeof_type(t):
  758. count = 1
  759. _t = t
  760. if t[0] in string.digits:
  761. # extract the count
  762. count = int("".join([d for d in t if d in string.digits]))
  763. _t = "".join([d for d in t if d not in string.digits])
  764. return STRUCT_SIZEOF_TYPES[_t] * count
  765. @lru_cache(maxsize=2048, copy=True)
  766. def set_format(format):
  767. __format_str__ = "<"
  768. __unpacked_data_elms__ = []
  769. __field_offsets__ = {}
  770. __keys__ = []
  771. __format_length__ = 0
  772. offset = 0
  773. for elm in format:
  774. if "," in elm:
  775. elm_type, elm_name = elm.split(",", 1)
  776. __format_str__ += elm_type
  777. __unpacked_data_elms__.append(None)
  778. elm_names = elm_name.split(",")
  779. names = []
  780. for elm_name in elm_names:
  781. if elm_name in __keys__:
  782. search_list = [x[: len(elm_name)] for x in __keys__]
  783. occ_count = search_list.count(elm_name)
  784. elm_name = "{0}_{1:d}".format(elm_name, occ_count)
  785. names.append(elm_name)
  786. __field_offsets__[elm_name] = offset
  787. offset += sizeof_type(elm_type)
  788. # Some PE header structures have unions on them, so a certain
  789. # value might have different names, so each key has a list of
  790. # all the possible members referring to the data.
  791. __keys__.append(names)
  792. __format_length__ = struct.calcsize(__format_str__)
  793. return (
  794. __format_str__,
  795. __unpacked_data_elms__,
  796. __field_offsets__,
  797. __keys__,
  798. __format_length__,
  799. )
  800. class Structure:
  801. """Prepare structure object to extract members from data.
  802. Format is a list containing definitions for the elements
  803. of the structure.
  804. """
  805. def __init__(self, format, name=None, file_offset=None):
  806. # Format is forced little endian, for big endian non Intel platforms
  807. self.__format_str__ = "<"
  808. self.__keys__ = []
  809. self.__format_length__ = 0
  810. self.__field_offsets__ = {}
  811. self.__unpacked_data_elms__ = []
  812. d = format[1]
  813. # need a tuple to be hashable in set_format using lru cache
  814. if not isinstance(d, tuple):
  815. d = tuple(d)
  816. (
  817. self.__format_str__,
  818. self.__unpacked_data_elms__,
  819. self.__field_offsets__,
  820. self.__keys__,
  821. self.__format_length__,
  822. ) = set_format(d)
  823. self.__all_zeroes__ = False
  824. self.__file_offset__ = file_offset
  825. if name:
  826. self.name = name
  827. else:
  828. self.name = format[0]
  829. def __get_format__(self) -> str:
  830. return self.__format_str__
  831. def get_field_absolute_offset(self, field_name):
  832. """Return the offset within the field for the requested field in the structure."""
  833. return self.__file_offset__ + self.__field_offsets__[field_name]
  834. def get_field_relative_offset(self, field_name):
  835. """Return the offset within the structure for the requested field."""
  836. return self.__field_offsets__[field_name]
  837. def get_file_offset(self):
  838. return self.__file_offset__
  839. def set_file_offset(self, offset):
  840. self.__file_offset__ = offset
  841. def all_zeroes(self):
  842. """Returns true is the unpacked data is all zeros."""
  843. return self.__all_zeroes__
  844. def sizeof(self):
  845. """Return size of the structure."""
  846. return self.__format_length__
  847. def __unpack__(self, data):
  848. data = data
  849. if len(data) > self.__format_length__:
  850. data = data[: self.__format_length__]
  851. # Some malware have incorrect header lengths.
  852. # Fail gracefully if this occurs
  853. # Buggy malware: a29b0118af8b7408444df81701ad5a7f
  854. #
  855. elif len(data) < self.__format_length__:
  856. raise PEFormatError("Data length less than expected header length.")
  857. if count_zeroes(data) == len(data):
  858. self.__all_zeroes__ = True
  859. self.__unpacked_data_elms__ = struct.unpack(self.__format_str__, data)
  860. for idx, val in enumerate(self.__unpacked_data_elms__):
  861. for key in self.__keys__[idx]:
  862. setattr(self, key, val)
  863. def __pack__(self):
  864. new_values = []
  865. for idx, val in enumerate(self.__unpacked_data_elms__):
  866. new_val = None
  867. for key in self.__keys__[idx]:
  868. new_val = getattr(self, key)
  869. # In the case of unions, when the first changed value
  870. # is picked the loop is exited
  871. if new_val != val:
  872. break
  873. new_values.append(new_val)
  874. return struct.pack(self.__format_str__, *new_values)
  875. def __str__(self):
  876. return "\n".join(self.dump())
  877. def __repr__(self):
  878. return "<Structure: %s>" % (
  879. " ".join([" ".join(s.split()) for s in self.dump()])
  880. )
  881. def dump(self, indentation=0):
  882. """Returns a string representation of the structure."""
  883. dump = []
  884. dump.append("[{0}]".format(self.name))
  885. printable_bytes = [
  886. ord(i) for i in string.printable if i not in string.whitespace
  887. ]
  888. # Refer to the __set_format__ method for an explanation
  889. # of the following construct.
  890. for keys in self.__keys__:
  891. for key in keys:
  892. val = getattr(self, key)
  893. if isinstance(val, (int, long)):
  894. if key.startswith("Signature_"):
  895. val_str = "{:<8X}".format(val)
  896. else:
  897. val_str = "0x{:<8X}".format(val)
  898. if key == "TimeDateStamp" or key == "dwTimeStamp":
  899. try:
  900. val_str += " [%s UTC]" % time.asctime(time.gmtime(val))
  901. except ValueError:
  902. val_str += " [INVALID TIME]"
  903. else:
  904. val_str = bytearray(val)
  905. if key.startswith("Signature"):
  906. val_str = "".join(
  907. ["{:02X}".format(i) for i in val_str.rstrip(b"\x00")]
  908. )
  909. else:
  910. val_str = "".join(
  911. [
  912. chr(i)
  913. if (i in printable_bytes)
  914. else "\\x{0:02x}".format(i)
  915. for i in val_str.rstrip(b"\x00")
  916. ]
  917. )
  918. dump.append(
  919. "0x%-8X 0x%-3X %-30s %s"
  920. % (
  921. self.__field_offsets__[key] + self.__file_offset__,
  922. self.__field_offsets__[key],
  923. key + ":",
  924. val_str,
  925. )
  926. )
  927. return dump
  928. def dump_dict(self):
  929. """Returns a dictionary representation of the structure."""
  930. dump_dict = {}
  931. dump_dict["Structure"] = self.name
  932. # Refer to the __set_format__ method for an explanation
  933. # of the following construct.
  934. for keys in self.__keys__:
  935. for key in keys:
  936. val = getattr(self, key)
  937. if isinstance(val, (int, long)):
  938. if key == "TimeDateStamp" or key == "dwTimeStamp":
  939. try:
  940. val = "0x%-8X [%s UTC]" % (
  941. val,
  942. time.asctime(time.gmtime(val)),
  943. )
  944. except ValueError:
  945. val = "0x%-8X [INVALID TIME]" % val
  946. else:
  947. val = "".join(
  948. chr(d) if chr(d) in string.printable else "\\x%02x" % d
  949. for d in [ord(c) if not isinstance(c, int) else c for c in val]
  950. )
  951. dump_dict[key] = {
  952. "FileOffset": self.__field_offsets__[key] + self.__file_offset__,
  953. "Offset": self.__field_offsets__[key],
  954. "Value": val,
  955. }
  956. return dump_dict
  957. class SectionStructure(Structure):
  958. """Convenience section handling class."""
  959. def __init__(self, *args, **kwargs):
  960. if "pe" in kwargs:
  961. self.pe = kwargs["pe"]
  962. del kwargs["pe"]
  963. self.PointerToRawData = None
  964. self.VirtualAddress = None
  965. self.SizeOfRawData = None
  966. self.Misc_VirtualSize = None
  967. super().__init__(*args, **kwargs)
  968. self.PointerToRawData_adj = None
  969. self.VirtualAddress_adj = None
  970. self.section_min_addr = None
  971. self.section_max_addr = None
  972. def get_PointerToRawData_adj(self):
  973. if self.PointerToRawData_adj is None and self.PointerToRawData is not None:
  974. ptrd = self.pe.adjust_PointerToRawData(self.PointerToRawData)
  975. # When the SectionAligment is smaller than the native page-size if the
  976. # section’s PointerToRawData and VirtualAddress match, the section's data
  977. # will be read at that offset. Implemented in the Window's function:
  978. # LdrpWx86FormatVirtualImage.
  979. if self.pe.OPTIONAL_HEADER.SectionAlignment < 0x1000:
  980. if self.PointerToRawData == self.VirtualAddress:
  981. ptrd = self.VirtualAddress
  982. self.PointerToRawData_adj = ptrd
  983. return self.PointerToRawData_adj
  984. def get_VirtualAddress_adj(self):
  985. if self.VirtualAddress_adj is None:
  986. if self.VirtualAddress is not None:
  987. self.VirtualAddress_adj = self.pe.adjust_SectionAlignment(
  988. self.VirtualAddress,
  989. self.pe.OPTIONAL_HEADER.SectionAlignment,
  990. self.pe.OPTIONAL_HEADER.FileAlignment,
  991. )
  992. return self.VirtualAddress_adj
  993. def get_data(self, start=None, length=None, ignore_padding=False):
  994. """Get data chunk from a section.
  995. Allows to query data from the section by passing the
  996. addresses where the PE file would be loaded by default.
  997. It is then possible to retrieve code and data by their real
  998. addresses as they would be if loaded.
  999. Note that sections on disk can include padding that would
  1000. not be loaded to memory. That is the case if `section.SizeOfRawData`
  1001. is greater than `section.Misc_VirtualSize`, and that means
  1002. that data past `section.Misc_VirtualSize` is padding.
  1003. In case you are not interested in this padding, passing
  1004. `ignore_padding=True` will truncate the result in order
  1005. not to return the padding (if any).
  1006. Returns bytes().
  1007. """
  1008. if start is None:
  1009. offset = self.get_PointerToRawData_adj()
  1010. else:
  1011. offset = (
  1012. start - self.get_VirtualAddress_adj()
  1013. ) + self.get_PointerToRawData_adj()
  1014. if length is not None:
  1015. end = offset + length
  1016. elif self.SizeOfRawData is not None:
  1017. end = offset + self.SizeOfRawData
  1018. else:
  1019. end = offset
  1020. if ignore_padding and end is not None and offset is not None:
  1021. end = min(end, offset + self.Misc_VirtualSize)
  1022. # PointerToRawData is not adjusted here as we might want to read any possible
  1023. # extra bytes that might get cut off by aligning the start (and hence cutting
  1024. # something off the end)
  1025. if self.PointerToRawData is not None and self.SizeOfRawData is not None:
  1026. if end > self.PointerToRawData + self.SizeOfRawData:
  1027. end = self.PointerToRawData + self.SizeOfRawData
  1028. return self.pe.__data__[offset:end]
  1029. def __setattr__(self, name, val):
  1030. if name == "Characteristics":
  1031. section_flags = retrieve_flags(SECTION_CHARACTERISTICS, "IMAGE_SCN_")
  1032. # Set the section's flags according to the Characteristics member
  1033. set_flags(self, val, section_flags)
  1034. elif "IMAGE_SCN_" in name and hasattr(self, name):
  1035. if val:
  1036. self.__dict__["Characteristics"] |= SECTION_CHARACTERISTICS[name]
  1037. else:
  1038. self.__dict__["Characteristics"] ^= SECTION_CHARACTERISTICS[name]
  1039. self.__dict__[name] = val
  1040. def get_rva_from_offset(self, offset):
  1041. return offset - self.get_PointerToRawData_adj() + self.get_VirtualAddress_adj()
  1042. def get_offset_from_rva(self, rva):
  1043. return rva - self.get_VirtualAddress_adj() + self.get_PointerToRawData_adj()
  1044. def contains_offset(self, offset):
  1045. """Check whether the section contains the file offset provided."""
  1046. if self.PointerToRawData is None:
  1047. # bss and other sections containing only uninitialized data must have 0
  1048. # and do not take space in the file
  1049. return False
  1050. PointerToRawData_adj = self.get_PointerToRawData_adj()
  1051. return (
  1052. PointerToRawData_adj <= offset < PointerToRawData_adj + self.SizeOfRawData
  1053. )
  1054. def contains_rva(self, rva):
  1055. """Check whether the section contains the address provided."""
  1056. # speedup
  1057. if self.section_min_addr is not None and self.section_max_addr is not None:
  1058. return self.section_min_addr <= rva < self.section_max_addr
  1059. VirtualAddress_adj = self.get_VirtualAddress_adj()
  1060. # Check if the SizeOfRawData is realistic. If it's bigger than the size of
  1061. # the whole PE file minus the start address of the section it could be
  1062. # either truncated or the SizeOfRawData contains a misleading value.
  1063. # In either of those cases we take the VirtualSize
  1064. #
  1065. if len(self.pe.__data__) - self.get_PointerToRawData_adj() < self.SizeOfRawData:
  1066. # PECOFF documentation v8 says:
  1067. # VirtualSize: The total size of the section when loaded into memory.
  1068. # If this value is greater than SizeOfRawData, the section is zero-padded.
  1069. # This field is valid only for executable images and should be set to zero
  1070. # for object files.
  1071. #
  1072. size = self.Misc_VirtualSize
  1073. else:
  1074. size = max(self.SizeOfRawData, self.Misc_VirtualSize)
  1075. # Check whether there's any section after the current one that starts before
  1076. # the calculated end for the current one. If so, cut the current section's size
  1077. # to fit in the range up to where the next section starts.
  1078. if (
  1079. self.next_section_virtual_address is not None
  1080. and self.next_section_virtual_address > self.VirtualAddress
  1081. and VirtualAddress_adj + size > self.next_section_virtual_address
  1082. ):
  1083. size = self.next_section_virtual_address - VirtualAddress_adj
  1084. self.section_min_addr = VirtualAddress_adj
  1085. self.section_max_addr = VirtualAddress_adj + size
  1086. return VirtualAddress_adj <= rva < VirtualAddress_adj + size
  1087. def contains(self, rva):
  1088. return self.contains_rva(rva)
  1089. def get_entropy(self):
  1090. """Calculate and return the entropy for the section."""
  1091. return self.entropy_H(self.get_data())
  1092. def get_hash_sha1(self):
  1093. """Get the SHA-1 hex-digest of the section's data."""
  1094. if sha1 is not None:
  1095. return sha1(self.get_data()).hexdigest()
  1096. def get_hash_sha256(self):
  1097. """Get the SHA-256 hex-digest of the section's data."""
  1098. if sha256 is not None:
  1099. return sha256(self.get_data()).hexdigest()
  1100. def get_hash_sha512(self):
  1101. """Get the SHA-512 hex-digest of the section's data."""
  1102. if sha512 is not None:
  1103. return sha512(self.get_data()).hexdigest()
  1104. def get_hash_md5(self):
  1105. """Get the MD5 hex-digest of the section's data."""
  1106. if md5 is not None:
  1107. return md5(self.get_data()).hexdigest()
  1108. def entropy_H(self, data):
  1109. """Calculate the entropy of a chunk of data."""
  1110. if not data:
  1111. return 0.0
  1112. occurences = Counter(bytearray(data))
  1113. entropy = 0
  1114. for x in occurences.values():
  1115. p_x = float(x) / len(data)
  1116. entropy -= p_x * math.log(p_x, 2)
  1117. return entropy
  1118. @lru_cache(maxsize=2048, copy=False)
  1119. def set_bitfields_format(format):
  1120. class Accumulator:
  1121. def __init__(self, fmt, comp_fields):
  1122. self._subfields = []
  1123. # add a prefix to distinguish the artificially created compound field
  1124. # from regular fields
  1125. self._name = "~"
  1126. self._type = None
  1127. self._bits_left = 0
  1128. self._comp_fields = comp_fields
  1129. self._format = fmt
  1130. def wrap_up(self):
  1131. if self._type is None:
  1132. return
  1133. self._format.append(self._type + "," + self._name)
  1134. self._comp_fields[len(self._format) - 1] = (self._type, self._subfields)
  1135. self._name = "~"
  1136. self._type = None
  1137. self._subfields = []
  1138. def new_type(self, tp):
  1139. self._bits_left = STRUCT_SIZEOF_TYPES[tp] * 8
  1140. self._type = tp
  1141. def add_subfield(self, name, bitcnt):
  1142. self._name += name
  1143. self._bits_left -= bitcnt
  1144. self._subfields.append((name, bitcnt))
  1145. def get_type(self):
  1146. return self._type
  1147. def get_name(self):
  1148. return self._name
  1149. def get_bits_left(self):
  1150. return self._bits_left
  1151. old_fmt = []
  1152. comp_fields = {}
  1153. ac = Accumulator(old_fmt, comp_fields)
  1154. for elm in format[1]:
  1155. if ":" not in elm:
  1156. ac.wrap_up()
  1157. old_fmt.append(elm)
  1158. continue
  1159. elm_type, elm_name = elm.split(",", 1)
  1160. if "," in elm_name:
  1161. raise NotImplementedError(
  1162. "Structures with bitfields do not support unions yet"
  1163. )
  1164. elm_type, elm_bits = elm_type.split(":", 1)
  1165. elm_bits = int(elm_bits)
  1166. if elm_type != ac.get_type() or elm_bits > ac.get_bits_left():
  1167. ac.wrap_up()
  1168. ac.new_type(elm_type)
  1169. ac.add_subfield(elm_name, elm_bits)
  1170. ac.wrap_up()
  1171. format_str, _, field_offsets, keys, format_length = set_format(tuple(old_fmt))
  1172. extended_keys = []
  1173. for idx, val in enumerate(keys):
  1174. if idx not in comp_fields:
  1175. extended_keys.append(val)
  1176. continue
  1177. _, sbf = comp_fields[idx]
  1178. bf_names = [[f[StructureWithBitfields.BTF_NAME_IDX]] for f in sbf]
  1179. extended_keys.extend(bf_names)
  1180. for n in bf_names:
  1181. field_offsets[n[0]] = field_offsets[val[0]]
  1182. return (format_str, format_length, field_offsets, keys, extended_keys, comp_fields)
  1183. class StructureWithBitfields(Structure):
  1184. """
  1185. Extends Structure's functionality with support for bitfields such as:
  1186. ('B:4,LowerHalf', 'B:4,UpperHalf')
  1187. To this end, two lists are maintained:
  1188. * self.__keys__ that contains compound fields, for example
  1189. ('B,~LowerHalfUpperHalf'), and is used during packing/unpaking
  1190. * self.__keys_ext__ containing a separate key for each field (ex., LowerHalf,
  1191. UpperHalf) to simplify implementation of dump()
  1192. This way the implementation of unpacking/packing and dump() from Structure can be
  1193. reused.
  1194. In addition, we create a dictionary:
  1195. <comound_field_index_in_keys> -->
  1196. (data type, [ (subfield name, length in bits)+ ] )
  1197. that facilitates bitfield paking and unpacking.
  1198. With lru_cache() creating only once instance per format string, the memory
  1199. overhead is negligible.
  1200. """
  1201. BTF_NAME_IDX = 0
  1202. BTF_BITCNT_IDX = 1
  1203. CF_TYPE_IDX = 0
  1204. CF_SUBFLD_IDX = 1
  1205. def __init__(self, format, name=None, file_offset=None):
  1206. (
  1207. self.__format_str__,
  1208. self.__format_length__,
  1209. self.__field_offsets__,
  1210. self.__keys__,
  1211. self.__keys_ext__,
  1212. self.__compound_fields__,
  1213. ) = set_bitfields_format(format)
  1214. # create our own unpacked_data_elms to ensure they are not shared among
  1215. # StructureWithBitfields instances with the same format string
  1216. self.__unpacked_data_elms__ = [None for i in range(self.__format_length__)]
  1217. self.__all_zeroes__ = False
  1218. self.__file_offset__ = file_offset
  1219. self.name = name if name != None else format[0]
  1220. def __unpack__(self, data):
  1221. # calling the original routine to deal with special cases/spurious data
  1222. # structures
  1223. super().__unpack__(data)
  1224. self._unpack_bitfield_attributes()
  1225. def __pack__(self):
  1226. self._pack_bitfield_attributes()
  1227. try:
  1228. data = super().__pack__()
  1229. finally:
  1230. self._unpack_bitfield_attributes()
  1231. return data
  1232. def dump(self, indentation=0):
  1233. tk = self.__keys__
  1234. self.__keys__ = self.__keys_ext__
  1235. try:
  1236. ret = super().dump(indentation)
  1237. finally:
  1238. self.__keys__ = tk
  1239. return ret
  1240. def dump_dict(self):
  1241. tk = self.__keys__
  1242. self.__keys__ = self.__keys_ext__
  1243. try:
  1244. ret = super().dump_dict()
  1245. finally:
  1246. self.__keys__ = tk
  1247. return ret
  1248. def _unpack_bitfield_attributes(self):
  1249. """Replace compound attributes corresponding to bitfields with separate
  1250. sub-fields.
  1251. """
  1252. for i in self.__compound_fields__.keys():
  1253. cf_name = self.__keys__[i][0]
  1254. cval = getattr(self, cf_name)
  1255. delattr(self, cf_name)
  1256. offst = 0
  1257. for sf in self.__compound_fields__[i][StructureWithBitfields.CF_SUBFLD_IDX]:
  1258. mask = (1 << sf[StructureWithBitfields.BTF_BITCNT_IDX]) - 1
  1259. mask <<= offst
  1260. setattr(
  1261. self,
  1262. sf[StructureWithBitfields.BTF_NAME_IDX],
  1263. (cval & mask) >> offst,
  1264. )
  1265. offst += sf[StructureWithBitfields.BTF_BITCNT_IDX]
  1266. def _pack_bitfield_attributes(self):
  1267. """Pack attributes into a compound bitfield"""
  1268. for i in self.__compound_fields__.keys():
  1269. cf_name = self.__keys__[i][0]
  1270. offst, acc_val = 0, 0
  1271. for sf in self.__compound_fields__[i][StructureWithBitfields.CF_SUBFLD_IDX]:
  1272. mask = (1 << sf[StructureWithBitfields.BTF_BITCNT_IDX]) - 1
  1273. field_val = (
  1274. getattr(self, sf[StructureWithBitfields.BTF_NAME_IDX]) & mask
  1275. )
  1276. acc_val |= field_val << offst
  1277. offst += sf[StructureWithBitfields.BTF_BITCNT_IDX]
  1278. setattr(self, cf_name, acc_val)
  1279. class DataContainer:
  1280. """Generic data container."""
  1281. def __init__(self, **kwargs):
  1282. bare_setattr = super().__setattr__
  1283. for key, value in kwargs.items():
  1284. bare_setattr(key, value)
  1285. class ImportDescData(DataContainer):
  1286. """Holds import descriptor information.
  1287. dll: name of the imported DLL
  1288. imports: list of imported symbols (ImportData instances)
  1289. struct: IMAGE_IMPORT_DESCRIPTOR structure
  1290. """
  1291. class ImportData(DataContainer):
  1292. """Holds imported symbol's information.
  1293. ordinal: Ordinal of the symbol
  1294. name: Name of the symbol
  1295. bound: If the symbol is bound, this contains
  1296. the address.
  1297. """
  1298. def __setattr__(self, name, val):
  1299. # If the instance doesn't yet have an ordinal attribute
  1300. # it's not fully initialized so can't do any of the
  1301. # following
  1302. #
  1303. if (
  1304. hasattr(self, "ordinal")
  1305. and hasattr(self, "bound")
  1306. and hasattr(self, "name")
  1307. ):
  1308. if name == "ordinal":
  1309. if self.pe.PE_TYPE == OPTIONAL_HEADER_MAGIC_PE:
  1310. ordinal_flag = IMAGE_ORDINAL_FLAG
  1311. elif self.pe.PE_TYPE == OPTIONAL_HEADER_MAGIC_PE_PLUS:
  1312. ordinal_flag = IMAGE_ORDINAL_FLAG64
  1313. # Set the ordinal and flag the entry as importing by ordinal
  1314. self.struct_table.Ordinal = ordinal_flag | (val & 0xFFFF)
  1315. self.struct_table.AddressOfData = self.struct_table.Ordinal
  1316. self.struct_table.Function = self.struct_table.Ordinal
  1317. self.struct_table.ForwarderString = self.struct_table.Ordinal
  1318. elif name == "bound":
  1319. if self.struct_iat is not None:
  1320. self.struct_iat.AddressOfData = val
  1321. self.struct_iat.AddressOfData = self.struct_iat.AddressOfData
  1322. self.struct_iat.Function = self.struct_iat.AddressOfData
  1323. self.struct_iat.ForwarderString = self.struct_iat.AddressOfData
  1324. elif name == "address":
  1325. self.struct_table.AddressOfData = val
  1326. self.struct_table.Ordinal = self.struct_table.AddressOfData
  1327. self.struct_table.Function = self.struct_table.AddressOfData
  1328. self.struct_table.ForwarderString = self.struct_table.AddressOfData
  1329. elif name == "name":
  1330. # Make sure we reset the entry in case the import had been set to
  1331. # import by ordinal
  1332. if self.name_offset:
  1333. name_rva = self.pe.get_rva_from_offset(self.name_offset)
  1334. self.pe.set_dword_at_offset(
  1335. self.ordinal_offset, (0 << 31) | name_rva
  1336. )
  1337. # Complain if the length of the new name is longer than the
  1338. # existing one
  1339. if len(val) > len(self.name):
  1340. raise PEFormatError(
  1341. "The export name provided is longer than the existing one."
  1342. )
  1343. pass
  1344. self.pe.set_bytes_at_offset(self.name_offset, val)
  1345. self.__dict__[name] = val
  1346. class ExportDirData(DataContainer):
  1347. """Holds export directory information.
  1348. struct: IMAGE_EXPORT_DIRECTORY structure
  1349. symbols: list of exported symbols (ExportData instances)"""
  1350. class ExportData(DataContainer):
  1351. """Holds exported symbols' information.
  1352. ordinal: ordinal of the symbol
  1353. address: address of the symbol
  1354. name: name of the symbol (None if the symbol is
  1355. exported by ordinal only)
  1356. forwarder: if the symbol is forwarded it will
  1357. contain the name of the target symbol,
  1358. None otherwise.
  1359. """
  1360. def __setattr__(self, name, val):
  1361. # If the instance doesn't yet have an ordinal attribute
  1362. # it's not fully initialized so can't do any of the
  1363. # following
  1364. #
  1365. if (
  1366. hasattr(self, "ordinal")
  1367. and hasattr(self, "address")
  1368. and hasattr(self, "forwarder")
  1369. and hasattr(self, "name")
  1370. ):
  1371. if name == "ordinal":
  1372. self.pe.set_word_at_offset(self.ordinal_offset, val)
  1373. elif name == "address":
  1374. self.pe.set_dword_at_offset(self.address_offset, val)
  1375. elif name == "name":
  1376. # Complain if the length of the new name is longer than the
  1377. # existing one
  1378. if len(val) > len(self.name):
  1379. raise PEFormatError(
  1380. "The export name provided is longer than the existing one."
  1381. )
  1382. self.pe.set_bytes_at_offset(self.name_offset, val)
  1383. elif name == "forwarder":
  1384. # Complain if the length of the new name is longer than the
  1385. # existing one
  1386. if len(val) > len(self.forwarder):
  1387. raise PEFormatError(
  1388. "The forwarder name provided is longer than the existing one."
  1389. )
  1390. self.pe.set_bytes_at_offset(self.forwarder_offset, val)
  1391. self.__dict__[name] = val
  1392. class ResourceDirData(DataContainer):
  1393. """Holds resource directory information.
  1394. struct: IMAGE_RESOURCE_DIRECTORY structure
  1395. entries: list of entries (ResourceDirEntryData instances)
  1396. """
  1397. class ResourceDirEntryData(DataContainer):
  1398. """Holds resource directory entry data.
  1399. struct: IMAGE_RESOURCE_DIRECTORY_ENTRY structure
  1400. name: If the resource is identified by name this
  1401. attribute will contain the name string. None
  1402. otherwise. If identified by id, the id is
  1403. available at 'struct.Id'
  1404. id: the id, also in struct.Id
  1405. directory: If this entry has a lower level directory
  1406. this attribute will point to the
  1407. ResourceDirData instance representing it.
  1408. data: If this entry has no further lower directories
  1409. and points to the actual resource data, this
  1410. attribute will reference the corresponding
  1411. ResourceDataEntryData instance.
  1412. (Either of the 'directory' or 'data' attribute will exist,
  1413. but not both.)
  1414. """
  1415. class ResourceDataEntryData(DataContainer):
  1416. """Holds resource data entry information.
  1417. struct: IMAGE_RESOURCE_DATA_ENTRY structure
  1418. lang: Primary language ID
  1419. sublang: Sublanguage ID
  1420. """
  1421. class DebugData(DataContainer):
  1422. """Holds debug information.
  1423. struct: IMAGE_DEBUG_DIRECTORY structure
  1424. entries: list of entries (IMAGE_DEBUG_TYPE instances)
  1425. """
  1426. class DynamicRelocationData(DataContainer):
  1427. """Holds dynamic relocation information.
  1428. struct: IMAGE_DYNAMIC_RELOCATION structure
  1429. symbol: Symbol to which dynamic relocations must be applied
  1430. relocations: List of dynamic relocations for this symbol (BaseRelocationData instances)
  1431. """
  1432. class FunctionOverrideData(DataContainer):
  1433. """Holds Function and bdd dynamic relocation information.
  1434. struct: IMAGE_DYNAMIC_RELOCATION structure
  1435. symbol: Symbol to which dynamic relocations must be applied
  1436. bdd_relocs: List of bdd dynamic relocations (BddDynamicRelocationData instances)
  1437. func_relocs: List of function override dynamic relocations (FunctionOverrideDynamicRelocationData instances)
  1438. """
  1439. class FunctionOverrideDynamicRelocationData(DataContainer):
  1440. """Holds Function override dynamic relocation information.
  1441. struct: IMAGE_FUNCTION_OVERRIDE_DYNAMIC_RELOCATION structure
  1442. func_rva: Original function rva
  1443. override_rvas: List of overriding function rvas
  1444. relocations: List of dynamic relocations (BaseRelocationData instances)
  1445. """
  1446. class BddDynamicRelocationData(DataContainer):
  1447. """Holds Bdd dynamic relocation information.
  1448. struct: IMAGE_BDD_DYNAMIC_RELOCATION structure
  1449. """
  1450. class BaseRelocationData(DataContainer):
  1451. """Holds base relocation information.
  1452. struct: IMAGE_BASE_RELOCATION structure
  1453. entries: list of relocation data (RelocationData instances)
  1454. """
  1455. class RelocationData(DataContainer):
  1456. """Holds relocation information.
  1457. type: Type of relocation
  1458. The type string can be obtained by
  1459. RELOCATION_TYPE[type]
  1460. rva: RVA of the relocation
  1461. """
  1462. def __setattr__(self, name, val):
  1463. # If the instance doesn't yet have a struct attribute
  1464. # it's not fully initialized so can't do any of the
  1465. # following
  1466. #
  1467. if hasattr(self, "struct"):
  1468. # Get the word containing the type and data
  1469. #
  1470. word = self.struct.Data
  1471. if name == "type":
  1472. word = (val << 12) | (word & 0xFFF)
  1473. elif name == "rva":
  1474. offset = max(val - self.base_rva, 0)
  1475. word = (word & 0xF000) | (offset & 0xFFF)
  1476. # Store the modified data
  1477. #
  1478. self.struct.Data = word
  1479. self.__dict__[name] = val
  1480. class TlsData(DataContainer):
  1481. """Holds TLS information.
  1482. struct: IMAGE_TLS_DIRECTORY structure
  1483. """
  1484. class BoundImportDescData(DataContainer):
  1485. """Holds bound import descriptor data.
  1486. This directory entry will provide information on the
  1487. DLLs this PE file has been bound to (if bound at all).
  1488. The structure will contain the name and timestamp of the
  1489. DLL at the time of binding so that the loader can know
  1490. whether it differs from the one currently present in the
  1491. system and must, therefore, re-bind the PE's imports.
  1492. struct: IMAGE_BOUND_IMPORT_DESCRIPTOR structure
  1493. name: DLL name
  1494. entries: list of entries (BoundImportRefData instances)
  1495. the entries will exist if this DLL has forwarded
  1496. symbols. If so, the destination DLL will have an
  1497. entry in this list.
  1498. """
  1499. class LoadConfigData(DataContainer):
  1500. """Holds Load Config data.
  1501. struct: IMAGE_LOAD_CONFIG_DIRECTORY structure
  1502. name: dll name
  1503. dynamic_relocations: dynamic relocation information, if present
  1504. """
  1505. class BoundImportRefData(DataContainer):
  1506. """Holds bound import forwarder reference data.
  1507. Contains the same information as the bound descriptor but
  1508. for forwarded DLLs, if any.
  1509. struct: IMAGE_BOUND_FORWARDER_REF structure
  1510. name: dll name
  1511. """
  1512. class ExceptionsDirEntryData(DataContainer):
  1513. """Holds the data related to SEH (and stack unwinding, in particular)
  1514. struct an instance of RUNTIME_FUNTION
  1515. unwindinfo an instance of UNWIND_INFO
  1516. """
  1517. class UnwindInfo(StructureWithBitfields):
  1518. """Handles the complexities of UNWIND_INFO structure:
  1519. * variable number of UWIND_CODEs
  1520. * optional ExceptionHandler and FunctionEntry fields
  1521. """
  1522. def __init__(self, file_offset=0):
  1523. super().__init__(
  1524. (
  1525. "UNWIND_INFO",
  1526. (
  1527. "B:3,Version",
  1528. "B:5,Flags",
  1529. "B,SizeOfProlog",
  1530. "B,CountOfCodes",
  1531. "B:4,FrameRegister",
  1532. "B:4,FrameOffset",
  1533. ),
  1534. ),
  1535. file_offset=file_offset,
  1536. )
  1537. self._full_size = super().sizeof()
  1538. self._opt_field_name = None
  1539. self._code_info = StructureWithBitfields(
  1540. ("UNWIND_CODE", ("B,CodeOffset", "B:4,UnwindOp", "B:4,OpInfo")),
  1541. file_offset=0,
  1542. )
  1543. self._chained_entry = None
  1544. self._finished_unpacking = False
  1545. def unpack_in_stages(self, data):
  1546. """Unpacks the UNWIND_INFO "in two calls", with the first call establishing
  1547. a full size of the structure and the second, performing the actual unpacking.
  1548. """
  1549. if self._finished_unpacking:
  1550. return None
  1551. super().__unpack__(data)
  1552. codes_cnt_max = (self.CountOfCodes + 1) & ~1
  1553. hdlr_offset = super().sizeof() + codes_cnt_max * self._code_info.sizeof()
  1554. self._full_size = hdlr_offset + (
  1555. 0 if self.Flags == 0 else STRUCT_SIZEOF_TYPES["I"]
  1556. )
  1557. if len(data) < self._full_size:
  1558. return None
  1559. if self.Version != 1 and self.Version != 2:
  1560. return "Unsupported version of UNWIND_INFO at " + hex(self.__file_offset__)
  1561. self.UnwindCodes = []
  1562. ro = super().sizeof()
  1563. codes_left = self.CountOfCodes
  1564. while codes_left > 0:
  1565. self._code_info.__unpack__(data[ro : ro + self._code_info.sizeof()])
  1566. ucode = PrologEpilogOpsFactory.create(self._code_info)
  1567. if ucode is None:
  1568. return "Unknown UNWIND_CODE at " + hex(self.__file_offset__ + ro)
  1569. len_in_codes = ucode.length_in_code_structures(self._code_info, self)
  1570. opc_size = self._code_info.sizeof() * len_in_codes
  1571. ucode.initialize(
  1572. self._code_info,
  1573. data[ro : ro + opc_size],
  1574. self,
  1575. self.__file_offset__ + ro,
  1576. )
  1577. ro += opc_size
  1578. codes_left -= len_in_codes
  1579. self.UnwindCodes.append(ucode)
  1580. if self.UNW_FLAG_EHANDLER or self.UNW_FLAG_UHANDLER:
  1581. self._opt_field_name = "ExceptionHandler"
  1582. if self.UNW_FLAG_CHAININFO:
  1583. self._opt_field_name = "FunctionEntry"
  1584. if self._opt_field_name != None:
  1585. setattr(
  1586. self,
  1587. self._opt_field_name,
  1588. struct.unpack(
  1589. "<I", data[hdlr_offset : hdlr_offset + STRUCT_SIZEOF_TYPES["I"]]
  1590. )[0],
  1591. )
  1592. self._finished_unpacking = True
  1593. return None
  1594. def dump(self, indentation=0):
  1595. # Because __keys_ext__ are shared among all the instances with the same
  1596. # format string, we have to add and sunsequently remove the optional field
  1597. # each time.
  1598. # It saves space (as compared to keeping a copy self.__keys_ext__ per
  1599. # UnwindInfo instance), but makes our dump() implementation thread-unsafe.
  1600. if self._opt_field_name != None:
  1601. self.__field_offsets__[self._opt_field_name] = (
  1602. self._full_size - STRUCT_SIZEOF_TYPES["I"]
  1603. )
  1604. self.__keys_ext__.append([self._opt_field_name])
  1605. try:
  1606. dump = super().dump(indentation)
  1607. finally:
  1608. if self._opt_field_name != None:
  1609. self.__keys_ext__.pop()
  1610. dump.append(
  1611. "Flags: "
  1612. + ", ".join([s[0] for s in unwind_info_flags if getattr(self, s[0])])
  1613. )
  1614. dump.append(
  1615. "Unwind codes: "
  1616. + "; ".join([str(c) for c in self.UnwindCodes if c.is_valid()])
  1617. )
  1618. return dump
  1619. def dump_dict(self):
  1620. if self._opt_field_name != None:
  1621. self.__field_offsets__[self._opt_field_name] = (
  1622. self._full_size - STRUCT_SIZEOF_TYPES["I"]
  1623. )
  1624. self.__keys_ext__.append([self._opt_field_name])
  1625. try:
  1626. ret = super().dump_dict()
  1627. finally:
  1628. if self._opt_field_name != None:
  1629. self.__keys_ext__.pop()
  1630. return ret
  1631. def __setattr__(self, name, val):
  1632. if name == "Flags":
  1633. set_flags(self, val, unwind_info_flags)
  1634. elif "UNW_FLAG_" in name and hasattr(self, name):
  1635. if val:
  1636. self.__dict__["Flags"] |= UNWIND_INFO_FLAGS[name]
  1637. else:
  1638. self.__dict__["Flags"] ^= UNWIND_INFO_FLAGS[name]
  1639. self.__dict__[name] = val
  1640. def sizeof(self):
  1641. return self._full_size
  1642. def __pack__(self):
  1643. data = bytearray(self._full_size)
  1644. data[0 : super().sizeof()] = super().__pack__()
  1645. cur_offset = super().sizeof()
  1646. for uc in self.UnwindCodes:
  1647. if cur_offset + uc.struct.sizeof() > self._full_size:
  1648. break
  1649. data[cur_offset : cur_offset + uc.struct.sizeof()] = uc.struct.__pack__()
  1650. cur_offset += uc.struct.sizeof()
  1651. if self._opt_field_name != None:
  1652. data[
  1653. self._full_size - STRUCT_SIZEOF_TYPES["I"] : self._full_size
  1654. ] = struct.pack("<I", getattr(self, self._opt_field_name))
  1655. return data
  1656. def get_chained_function_entry(self):
  1657. return self._chained_entry
  1658. def set_chained_function_entry(self, entry):
  1659. if self._chained_entry != None:
  1660. raise PEFormatError("Chained function entry cannot be changed")
  1661. self._chained_entry = entry
  1662. class PrologEpilogOp:
  1663. """Meant as an abstract class representing a generic unwind code.
  1664. There is a subclass of PrologEpilogOp for each member of UNWIND_OP_CODES enum.
  1665. """
  1666. def initialize(self, unw_code, data, unw_info, file_offset):
  1667. self.struct = StructureWithBitfields(
  1668. self._get_format(unw_code), file_offset=file_offset
  1669. )
  1670. self.struct.__unpack__(data)
  1671. def length_in_code_structures(self, unw_code, unw_info):
  1672. """Computes how many UNWIND_CODE structures UNWIND_CODE occupies.
  1673. May be called before initialize() and, for that reason, should not rely on
  1674. the values of intance attributes.
  1675. """
  1676. return 1
  1677. def is_valid(self):
  1678. return True
  1679. def _get_format(self, unw_code):
  1680. return ("UNWIND_CODE", ("B,CodeOffset", "B:4,UnwindOp", "B:4,OpInfo"))
  1681. class PrologEpilogOpPushReg(PrologEpilogOp):
  1682. """UWOP_PUSH_NONVOL"""
  1683. def _get_format(self, unw_code):
  1684. return ("UNWIND_CODE_PUSH_NONVOL", ("B,CodeOffset", "B:4,UnwindOp", "B:4,Reg"))
  1685. def __str__(self):
  1686. return ".PUSHREG " + REGISTERS[self.struct.Reg]
  1687. class PrologEpilogOpAllocLarge(PrologEpilogOp):
  1688. """UWOP_ALLOC_LARGE"""
  1689. def _get_format(self, unw_code):
  1690. return (
  1691. "UNWIND_CODE_ALLOC_LARGE",
  1692. (
  1693. "B,CodeOffset",
  1694. "B:4,UnwindOp",
  1695. "B:4,OpInfo",
  1696. "H,AllocSizeInQwords" if unw_code.OpInfo == 0 else "I,AllocSize",
  1697. ),
  1698. )
  1699. def length_in_code_structures(self, unw_code, unw_info):
  1700. return 2 if unw_code.OpInfo == 0 else 3
  1701. def get_alloc_size(self):
  1702. return (
  1703. self.struct.AllocSizeInQwords * 8
  1704. if self.struct.OpInfo == 0
  1705. else self.struct.AllocSize
  1706. )
  1707. def __str__(self):
  1708. return ".ALLOCSTACK " + hex(self.get_alloc_size())
  1709. class PrologEpilogOpAllocSmall(PrologEpilogOp):
  1710. """UWOP_ALLOC_SMALL"""
  1711. def _get_format(self, unw_code):
  1712. return (
  1713. "UNWIND_CODE_ALLOC_SMALL",
  1714. ("B,CodeOffset", "B:4,UnwindOp", "B:4,AllocSizeInQwordsMinus8"),
  1715. )
  1716. def get_alloc_size(self):
  1717. return self.struct.AllocSizeInQwordsMinus8 * 8 + 8
  1718. def __str__(self):
  1719. return ".ALLOCSTACK " + hex(self.get_alloc_size())
  1720. class PrologEpilogOpSetFP(PrologEpilogOp):
  1721. """UWOP_SET_FPREG"""
  1722. def initialize(self, unw_code, data, unw_info, file_offset):
  1723. super().initialize(unw_code, data, unw_info, file_offset)
  1724. self._frame_register = unw_info.FrameRegister
  1725. self._frame_offset = unw_info.FrameOffset * 16
  1726. def __str__(self):
  1727. return (
  1728. ".SETFRAME "
  1729. + REGISTERS[self._frame_register]
  1730. + ", "
  1731. + hex(self._frame_offset)
  1732. )
  1733. class PrologEpilogOpSaveReg(PrologEpilogOp):
  1734. """UWOP_SAVE_NONVOL"""
  1735. def length_in_code_structures(self, unwcode, unw_info):
  1736. return 2
  1737. def get_offset(self):
  1738. return self.struct.OffsetInQwords * 8
  1739. def _get_format(self, unw_code):
  1740. return (
  1741. "UNWIND_CODE_SAVE_NONVOL",
  1742. ("B,CodeOffset", "B:4,UnwindOp", "B:4,Reg", "H,OffsetInQwords"),
  1743. )
  1744. def __str__(self):
  1745. return ".SAVEREG " + REGISTERS[self.struct.Reg] + ", " + hex(self.get_offset())
  1746. class PrologEpilogOpSaveRegFar(PrologEpilogOp):
  1747. """UWOP_SAVE_NONVOL_FAR"""
  1748. def length_in_code_structures(self, unw_code, unw_info):
  1749. return 3
  1750. def get_offset(self):
  1751. return self.struct.Offset
  1752. def _get_format(self, unw_code):
  1753. return (
  1754. "UNWIND_CODE_SAVE_NONVOL_FAR",
  1755. ("B,CodeOffset", "B:4,UnwindOp", "B:4,Reg", "I,Offset"),
  1756. )
  1757. def __str__(self):
  1758. return ".SAVEREG " + REGISTERS[self.struct.Reg] + ", " + hex(self.struct.Offset)
  1759. class PrologEpilogOpSaveXMM(PrologEpilogOp):
  1760. """UWOP_SAVE_XMM128"""
  1761. def _get_format(self, unw_code):
  1762. return (
  1763. "UNWIND_CODE_SAVE_XMM128",
  1764. ("B,CodeOffset", "B:4,UnwindOp", "B:4,Reg", "H,OffsetIn2Qwords"),
  1765. )
  1766. def length_in_code_structures(self, unw_code, unw_info):
  1767. return 2
  1768. def get_offset(self):
  1769. return self.struct.OffsetIn2Qwords * 16
  1770. def __str__(self):
  1771. return ".SAVEXMM128 XMM" + str(self.struct.Reg) + ", " + hex(self.get_offset())
  1772. class PrologEpilogOpSaveXMMFar(PrologEpilogOp):
  1773. """UWOP_SAVE_XMM128_FAR"""
  1774. def _get_format(self, unw_code):
  1775. return (
  1776. "UNWIND_CODE_SAVE_XMM128_FAR",
  1777. ("B,CodeOffset", "B:4,UnwindOp", "B:4,Reg", "I,Offset"),
  1778. )
  1779. def length_in_code_structures(self, unw_code, unw_info):
  1780. return 3
  1781. def get_offset(self):
  1782. return self.struct.Offset
  1783. def __str__(self):
  1784. return ".SAVEXMM128 XMM" + str(self.struct.Reg) + ", " + hex(self.struct.Offset)
  1785. class PrologEpilogOpPushFrame(PrologEpilogOp):
  1786. """UWOP_PUSH_MACHFRAME"""
  1787. def __str__(self):
  1788. return ".PUSHFRAME" + (" <code>" if self.struct.OpInfo else "")
  1789. class PrologEpilogOpEpilogMarker(PrologEpilogOp):
  1790. """UWOP_EPILOG"""
  1791. def initialize(self, unw_code, data, unw_info, file_offset):
  1792. self._long_offst = True
  1793. self._first = not hasattr(unw_info, "SizeOfEpilog")
  1794. super().initialize(unw_code, data, unw_info, file_offset)
  1795. if self._first:
  1796. setattr(unw_info, "SizeOfEpilog", self.struct.Size)
  1797. self._long_offst = unw_code.OpInfo & 1 == 0
  1798. self._epilog_size = unw_info.SizeOfEpilog
  1799. def _get_format(self, unw_code):
  1800. # check if it is the first epilog code among encountered; then its record
  1801. # will contain size of the epilog
  1802. if self._first:
  1803. return (
  1804. "UNWIND_CODE_EPILOG",
  1805. ("B,OffsetLow,Size", "B:4,UnwindOp", "B:4,Flags")
  1806. if unw_code.OpInfo & 1 == 1
  1807. else (
  1808. "B,Size",
  1809. "B:4,UnwindOp",
  1810. "B:4,Flags",
  1811. "B,OffsetLow",
  1812. "B:4,Unused",
  1813. "B:4,OffsetHigh",
  1814. ),
  1815. )
  1816. else:
  1817. return (
  1818. "UNWIND_CODE_EPILOG",
  1819. ("B,OffsetLow", "B:4,UnwindOp", "B:4,OffsetHigh"),
  1820. )
  1821. def length_in_code_structures(self, unw_code, unw_info):
  1822. return (
  1823. 2
  1824. if not hasattr(unw_info, "SizeOfEpilog") and (unw_code.OpInfo & 1) == 0
  1825. else 1
  1826. )
  1827. def get_offset(self):
  1828. return self.struct.OffsetLow | (
  1829. self.struct.OffsetHigh << 8 if self._long_offst else 0
  1830. )
  1831. def is_valid(self):
  1832. return self.get_offset() > 0
  1833. def __str__(self):
  1834. # the EPILOG sequence may have a terminating all-zeros entry
  1835. return (
  1836. "EPILOG: size="
  1837. + hex(self._epilog_size)
  1838. + ", offset from the end=-"
  1839. + hex(self.get_offset())
  1840. if self.get_offset() > 0
  1841. else ""
  1842. )
  1843. class PrologEpilogOpsFactory:
  1844. """A factory for creating unwind codes based on the value of UnwindOp"""
  1845. _class_dict = {
  1846. UWOP_PUSH_NONVOL: PrologEpilogOpPushReg,
  1847. UWOP_ALLOC_LARGE: PrologEpilogOpAllocLarge,
  1848. UWOP_ALLOC_SMALL: PrologEpilogOpAllocSmall,
  1849. UWOP_SET_FPREG: PrologEpilogOpSetFP,
  1850. UWOP_SAVE_NONVOL: PrologEpilogOpSaveReg,
  1851. UWOP_SAVE_NONVOL_FAR: PrologEpilogOpSaveRegFar,
  1852. UWOP_SAVE_XMM128: PrologEpilogOpSaveXMM,
  1853. UWOP_SAVE_XMM128_FAR: PrologEpilogOpSaveXMMFar,
  1854. UWOP_PUSH_MACHFRAME: PrologEpilogOpPushFrame,
  1855. UWOP_EPILOG: PrologEpilogOpEpilogMarker,
  1856. }
  1857. @staticmethod
  1858. def create(unwcode):
  1859. code = unwcode.UnwindOp
  1860. return (
  1861. PrologEpilogOpsFactory._class_dict[code]()
  1862. if code in PrologEpilogOpsFactory._class_dict
  1863. else None
  1864. )
  1865. # Valid FAT32 8.3 short filename characters according to:
  1866. # https://en.wikipedia.org/wiki/8.3_filename
  1867. # This will help decide whether DLL ASCII names are likely
  1868. # to be valid or otherwise corrupt data
  1869. #
  1870. # The filename length is not checked because the DLLs filename
  1871. # can be longer that the 8.3
  1872. allowed_filename = (
  1873. string.ascii_lowercase
  1874. + string.ascii_uppercase
  1875. + string.digits
  1876. + "!#$%&'()-@^_`{}~+,.;=[]:"
  1877. ).encode()
  1878. def is_valid_dos_filename(s):
  1879. if s is None or not isinstance(s, (str, bytes, bytearray)):
  1880. return False
  1881. # Allow path separators as import names can contain directories.
  1882. allowed = allowed_filename + b"\\/"
  1883. return all(c in allowed for c in set(s))
  1884. # Check if an imported name uses the valid accepted characters expected in
  1885. # mangled function names. If the symbol's characters don't fall within this
  1886. # charset we will assume the name is invalid.
  1887. # The dot "." character comes from: https://github.com/erocarrera/pefile/pull/346
  1888. # All other symbols can be inserted by adding a name with that symbol to a .def file,
  1889. # and passing it to link.exe (See export_test.py)
  1890. allowed_function_name = (
  1891. string.ascii_lowercase + string.ascii_uppercase + string.digits
  1892. ).encode()
  1893. @lru_cache(maxsize=2048)
  1894. def is_valid_function_name(
  1895. s: Union[str, bytes, bytearray], relax_allowed_characters: bool = False
  1896. ) -> bool:
  1897. allowed_extra = b"._?@$()<>"
  1898. if relax_allowed_characters:
  1899. allowed_extra = b"!\"#$%&'()*+,-./:<>?[\\]^_`{|}~@"
  1900. return (
  1901. s is not None
  1902. and isinstance(s, (str, bytes, bytearray))
  1903. and all((c in allowed_function_name or c in allowed_extra) for c in set(s))
  1904. )
  1905. class PE:
  1906. """A Portable Executable representation.
  1907. This class provides access to most of the information in a PE file.
  1908. It expects to be supplied the name of the file to load or PE data
  1909. to process and an optional argument 'fast_load' (False by default)
  1910. which controls whether to load all the directories information,
  1911. which can be quite time consuming.
  1912. pe = pefile.PE('module.dll')
  1913. pe = pefile.PE(name='module.dll')
  1914. would load 'module.dll' and process it. If the data is already
  1915. available in a buffer the same can be achieved with:
  1916. pe = pefile.PE(data=module_dll_data)
  1917. The "fast_load" can be set to a default by setting its value in the
  1918. module itself by means, for instance, of a "pefile.fast_load = True".
  1919. That will make all the subsequent instances not to load the
  1920. whole PE structure. The "full_load" method can be used to parse
  1921. the missing data at a later stage.
  1922. Basic headers information will be available in the attributes:
  1923. DOS_HEADER
  1924. NT_HEADERS
  1925. FILE_HEADER
  1926. OPTIONAL_HEADER
  1927. All of them will contain among their attributes the members of the
  1928. corresponding structures as defined in WINNT.H
  1929. The raw data corresponding to the header (from the beginning of the
  1930. file up to the start of the first section) will be available in the
  1931. instance's attribute 'header' as a string.
  1932. The sections will be available as a list in the 'sections' attribute.
  1933. Each entry will contain as attributes all the structure's members.
  1934. Directory entries will be available as attributes (if they exist):
  1935. (no other entries are processed at this point)
  1936. DIRECTORY_ENTRY_IMPORT (list of ImportDescData instances)
  1937. DIRECTORY_ENTRY_EXPORT (ExportDirData instance)
  1938. DIRECTORY_ENTRY_RESOURCE (ResourceDirData instance)
  1939. DIRECTORY_ENTRY_DEBUG (list of DebugData instances)
  1940. DIRECTORY_ENTRY_BASERELOC (list of BaseRelocationData instances)
  1941. DIRECTORY_ENTRY_TLS
  1942. DIRECTORY_ENTRY_BOUND_IMPORT (list of BoundImportData instances)
  1943. The following dictionary attributes provide ways of mapping different
  1944. constants. They will accept the numeric value and return the string
  1945. representation and the opposite, feed in the string and get the
  1946. numeric constant:
  1947. DIRECTORY_ENTRY
  1948. IMAGE_CHARACTERISTICS
  1949. SECTION_CHARACTERISTICS
  1950. DEBUG_TYPE
  1951. SUBSYSTEM_TYPE
  1952. MACHINE_TYPE
  1953. RELOCATION_TYPE
  1954. RESOURCE_TYPE
  1955. LANG
  1956. SUBLANG
  1957. """
  1958. #
  1959. # Format specifications for PE structures.
  1960. #
  1961. __IMAGE_DOS_HEADER_format__ = (
  1962. "IMAGE_DOS_HEADER",
  1963. (
  1964. "H,e_magic",
  1965. "H,e_cblp",
  1966. "H,e_cp",
  1967. "H,e_crlc",
  1968. "H,e_cparhdr",
  1969. "H,e_minalloc",
  1970. "H,e_maxalloc",
  1971. "H,e_ss",
  1972. "H,e_sp",
  1973. "H,e_csum",
  1974. "H,e_ip",
  1975. "H,e_cs",
  1976. "H,e_lfarlc",
  1977. "H,e_ovno",
  1978. "8s,e_res",
  1979. "H,e_oemid",
  1980. "H,e_oeminfo",
  1981. "20s,e_res2",
  1982. "I,e_lfanew",
  1983. ),
  1984. )
  1985. __IMAGE_FILE_HEADER_format__ = (
  1986. "IMAGE_FILE_HEADER",
  1987. (
  1988. "H,Machine",
  1989. "H,NumberOfSections",
  1990. "I,TimeDateStamp",
  1991. "I,PointerToSymbolTable",
  1992. "I,NumberOfSymbols",
  1993. "H,SizeOfOptionalHeader",
  1994. "H,Characteristics",
  1995. ),
  1996. )
  1997. __IMAGE_DATA_DIRECTORY_format__ = (
  1998. "IMAGE_DATA_DIRECTORY",
  1999. ("I,VirtualAddress", "I,Size"),
  2000. )
  2001. __IMAGE_OPTIONAL_HEADER_format__ = (
  2002. "IMAGE_OPTIONAL_HEADER",
  2003. (
  2004. "H,Magic",
  2005. "B,MajorLinkerVersion",
  2006. "B,MinorLinkerVersion",
  2007. "I,SizeOfCode",
  2008. "I,SizeOfInitializedData",
  2009. "I,SizeOfUninitializedData",
  2010. "I,AddressOfEntryPoint",
  2011. "I,BaseOfCode",
  2012. "I,BaseOfData",
  2013. "I,ImageBase",
  2014. "I,SectionAlignment",
  2015. "I,FileAlignment",
  2016. "H,MajorOperatingSystemVersion",
  2017. "H,MinorOperatingSystemVersion",
  2018. "H,MajorImageVersion",
  2019. "H,MinorImageVersion",
  2020. "H,MajorSubsystemVersion",
  2021. "H,MinorSubsystemVersion",
  2022. "I,Reserved1",
  2023. "I,SizeOfImage",
  2024. "I,SizeOfHeaders",
  2025. "I,CheckSum",
  2026. "H,Subsystem",
  2027. "H,DllCharacteristics",
  2028. "I,SizeOfStackReserve",
  2029. "I,SizeOfStackCommit",
  2030. "I,SizeOfHeapReserve",
  2031. "I,SizeOfHeapCommit",
  2032. "I,LoaderFlags",
  2033. "I,NumberOfRvaAndSizes",
  2034. ),
  2035. )
  2036. __IMAGE_OPTIONAL_HEADER64_format__ = (
  2037. "IMAGE_OPTIONAL_HEADER64",
  2038. (
  2039. "H,Magic",
  2040. "B,MajorLinkerVersion",
  2041. "B,MinorLinkerVersion",
  2042. "I,SizeOfCode",
  2043. "I,SizeOfInitializedData",
  2044. "I,SizeOfUninitializedData",
  2045. "I,AddressOfEntryPoint",
  2046. "I,BaseOfCode",
  2047. "Q,ImageBase",
  2048. "I,SectionAlignment",
  2049. "I,FileAlignment",
  2050. "H,MajorOperatingSystemVersion",
  2051. "H,MinorOperatingSystemVersion",
  2052. "H,MajorImageVersion",
  2053. "H,MinorImageVersion",
  2054. "H,MajorSubsystemVersion",
  2055. "H,MinorSubsystemVersion",
  2056. "I,Reserved1",
  2057. "I,SizeOfImage",
  2058. "I,SizeOfHeaders",
  2059. "I,CheckSum",
  2060. "H,Subsystem",
  2061. "H,DllCharacteristics",
  2062. "Q,SizeOfStackReserve",
  2063. "Q,SizeOfStackCommit",
  2064. "Q,SizeOfHeapReserve",
  2065. "Q,SizeOfHeapCommit",
  2066. "I,LoaderFlags",
  2067. "I,NumberOfRvaAndSizes",
  2068. ),
  2069. )
  2070. __IMAGE_NT_HEADERS_format__ = ("IMAGE_NT_HEADERS", ("I,Signature",))
  2071. __IMAGE_SECTION_HEADER_format__ = (
  2072. "IMAGE_SECTION_HEADER",
  2073. (
  2074. "8s,Name",
  2075. "I,Misc,Misc_PhysicalAddress,Misc_VirtualSize",
  2076. "I,VirtualAddress",
  2077. "I,SizeOfRawData",
  2078. "I,PointerToRawData",
  2079. "I,PointerToRelocations",
  2080. "I,PointerToLinenumbers",
  2081. "H,NumberOfRelocations",
  2082. "H,NumberOfLinenumbers",
  2083. "I,Characteristics",
  2084. ),
  2085. )
  2086. __IMAGE_DELAY_IMPORT_DESCRIPTOR_format__ = (
  2087. "IMAGE_DELAY_IMPORT_DESCRIPTOR",
  2088. (
  2089. "I,grAttrs",
  2090. "I,szName",
  2091. "I,phmod",
  2092. "I,pIAT",
  2093. "I,pINT",
  2094. "I,pBoundIAT",
  2095. "I,pUnloadIAT",
  2096. "I,dwTimeStamp",
  2097. ),
  2098. )
  2099. __IMAGE_IMPORT_DESCRIPTOR_format__ = (
  2100. "IMAGE_IMPORT_DESCRIPTOR",
  2101. (
  2102. "I,OriginalFirstThunk,Characteristics",
  2103. "I,TimeDateStamp",
  2104. "I,ForwarderChain",
  2105. "I,Name",
  2106. "I,FirstThunk",
  2107. ),
  2108. )
  2109. __IMAGE_EXPORT_DIRECTORY_format__ = (
  2110. "IMAGE_EXPORT_DIRECTORY",
  2111. (
  2112. "I,Characteristics",
  2113. "I,TimeDateStamp",
  2114. "H,MajorVersion",
  2115. "H,MinorVersion",
  2116. "I,Name",
  2117. "I,Base",
  2118. "I,NumberOfFunctions",
  2119. "I,NumberOfNames",
  2120. "I,AddressOfFunctions",
  2121. "I,AddressOfNames",
  2122. "I,AddressOfNameOrdinals",
  2123. ),
  2124. )
  2125. __IMAGE_RESOURCE_DIRECTORY_format__ = (
  2126. "IMAGE_RESOURCE_DIRECTORY",
  2127. (
  2128. "I,Characteristics",
  2129. "I,TimeDateStamp",
  2130. "H,MajorVersion",
  2131. "H,MinorVersion",
  2132. "H,NumberOfNamedEntries",
  2133. "H,NumberOfIdEntries",
  2134. ),
  2135. )
  2136. __IMAGE_RESOURCE_DIRECTORY_ENTRY_format__ = (
  2137. "IMAGE_RESOURCE_DIRECTORY_ENTRY",
  2138. ("I,Name", "I,OffsetToData"),
  2139. )
  2140. __IMAGE_RESOURCE_DATA_ENTRY_format__ = (
  2141. "IMAGE_RESOURCE_DATA_ENTRY",
  2142. ("I,OffsetToData", "I,Size", "I,CodePage", "I,Reserved"),
  2143. )
  2144. __VS_VERSIONINFO_format__ = (
  2145. "VS_VERSIONINFO",
  2146. ("H,Length", "H,ValueLength", "H,Type"),
  2147. )
  2148. __VS_FIXEDFILEINFO_format__ = (
  2149. "VS_FIXEDFILEINFO",
  2150. (
  2151. "I,Signature",
  2152. "I,StrucVersion",
  2153. "I,FileVersionMS",
  2154. "I,FileVersionLS",
  2155. "I,ProductVersionMS",
  2156. "I,ProductVersionLS",
  2157. "I,FileFlagsMask",
  2158. "I,FileFlags",
  2159. "I,FileOS",
  2160. "I,FileType",
  2161. "I,FileSubtype",
  2162. "I,FileDateMS",
  2163. "I,FileDateLS",
  2164. ),
  2165. )
  2166. __StringFileInfo_format__ = (
  2167. "StringFileInfo",
  2168. ("H,Length", "H,ValueLength", "H,Type"),
  2169. )
  2170. __StringTable_format__ = ("StringTable", ("H,Length", "H,ValueLength", "H,Type"))
  2171. __String_format__ = ("String", ("H,Length", "H,ValueLength", "H,Type"))
  2172. __Var_format__ = ("Var", ("H,Length", "H,ValueLength", "H,Type"))
  2173. __IMAGE_THUNK_DATA_format__ = (
  2174. "IMAGE_THUNK_DATA",
  2175. ("I,ForwarderString,Function,Ordinal,AddressOfData",),
  2176. )
  2177. __IMAGE_THUNK_DATA64_format__ = (
  2178. "IMAGE_THUNK_DATA",
  2179. ("Q,ForwarderString,Function,Ordinal,AddressOfData",),
  2180. )
  2181. __IMAGE_DEBUG_DIRECTORY_format__ = (
  2182. "IMAGE_DEBUG_DIRECTORY",
  2183. (
  2184. "I,Characteristics",
  2185. "I,TimeDateStamp",
  2186. "H,MajorVersion",
  2187. "H,MinorVersion",
  2188. "I,Type",
  2189. "I,SizeOfData",
  2190. "I,AddressOfRawData",
  2191. "I,PointerToRawData",
  2192. ),
  2193. )
  2194. __IMAGE_BASE_RELOCATION_format__ = (
  2195. "IMAGE_BASE_RELOCATION",
  2196. ("I,VirtualAddress", "I,SizeOfBlock"),
  2197. )
  2198. __IMAGE_BASE_RELOCATION_ENTRY_format__ = (
  2199. "IMAGE_BASE_RELOCATION_ENTRY",
  2200. ("H,Data",),
  2201. )
  2202. __IMAGE_IMPORT_CONTROL_TRANSFER_DYNAMIC_RELOCATION_format__ = (
  2203. "IMAGE_IMPORT_CONTROL_TRANSFER_DYNAMIC_RELOCATION",
  2204. ("I:12,PageRelativeOffset", "I:1,IndirectCall", "I:19,IATIndex"),
  2205. )
  2206. __IMAGE_INDIR_CONTROL_TRANSFER_DYNAMIC_RELOCATION_format__ = (
  2207. "IMAGE_INDIR_CONTROL_TRANSFER_DYNAMIC_RELOCATION",
  2208. (
  2209. "H:12,PageRelativeOffset",
  2210. "H:1,IndirectCall",
  2211. "H:1,RexWPrefix",
  2212. "H:1,CfgCheck",
  2213. "H:1,Reserved",
  2214. ),
  2215. )
  2216. __IMAGE_SWITCHTABLE_BRANCH_DYNAMIC_RELOCATION_format__ = (
  2217. "IMAGE_SWITCHTABLE_BRANCH_DYNAMIC_RELOCATION",
  2218. ("H:12,PageRelativeOffset", "H:4,RegisterNumber"),
  2219. )
  2220. __IMAGE_FUNCTION_OVERRIDE_HEADER_format__ = (
  2221. "IMAGE_FUNCTION_OVERRIDE_HEADER",
  2222. ("I,FuncOverrideSize",),
  2223. )
  2224. __IMAGE_FUNCTION_OVERRIDE_DYNAMIC_RELOCATION_format__ = (
  2225. "IMAGE_FUNCTION_OVERRIDE_DYNAMIC_RELOCATION",
  2226. (
  2227. "I,OriginalRva",
  2228. "I,BDDOffset",
  2229. "I,RvaSize",
  2230. "I,BaseRelocSize",
  2231. ),
  2232. )
  2233. __IMAGE_BDD_INFO_format__ = (
  2234. "IMAGE_BDD_INFO",
  2235. ("I,Version", "I,BDDSize"),
  2236. )
  2237. __IMAGE_BDD_DYNAMIC_RELOCATION_format__ = (
  2238. "IMAGE_BDD_DYNAMIC_RELOCATION",
  2239. ("H,Left", "H,Right", "I,Value"),
  2240. )
  2241. __IMAGE_TLS_DIRECTORY_format__ = (
  2242. "IMAGE_TLS_DIRECTORY",
  2243. (
  2244. "I,StartAddressOfRawData",
  2245. "I,EndAddressOfRawData",
  2246. "I,AddressOfIndex",
  2247. "I,AddressOfCallBacks",
  2248. "I,SizeOfZeroFill",
  2249. "I,Characteristics",
  2250. ),
  2251. )
  2252. __IMAGE_TLS_DIRECTORY64_format__ = (
  2253. "IMAGE_TLS_DIRECTORY",
  2254. (
  2255. "Q,StartAddressOfRawData",
  2256. "Q,EndAddressOfRawData",
  2257. "Q,AddressOfIndex",
  2258. "Q,AddressOfCallBacks",
  2259. "I,SizeOfZeroFill",
  2260. "I,Characteristics",
  2261. ),
  2262. )
  2263. __IMAGE_LOAD_CONFIG_DIRECTORY_format__ = (
  2264. "IMAGE_LOAD_CONFIG_DIRECTORY",
  2265. (
  2266. "I,Size",
  2267. "I,TimeDateStamp",
  2268. "H,MajorVersion",
  2269. "H,MinorVersion",
  2270. "I,GlobalFlagsClear",
  2271. "I,GlobalFlagsSet",
  2272. "I,CriticalSectionDefaultTimeout",
  2273. "I,DeCommitFreeBlockThreshold",
  2274. "I,DeCommitTotalFreeThreshold",
  2275. "I,LockPrefixTable",
  2276. "I,MaximumAllocationSize",
  2277. "I,VirtualMemoryThreshold",
  2278. "I,ProcessHeapFlags",
  2279. "I,ProcessAffinityMask",
  2280. "H,CSDVersion",
  2281. "H,DependentLoadFlags",
  2282. "I,EditList",
  2283. "I,SecurityCookie",
  2284. "I,SEHandlerTable",
  2285. "I,SEHandlerCount",
  2286. "I,GuardCFCheckFunctionPointer",
  2287. "I,GuardCFDispatchFunctionPointer",
  2288. "I,GuardCFFunctionTable",
  2289. "I,GuardCFFunctionCount",
  2290. "I,GuardFlags",
  2291. "H,CodeIntegrityFlags",
  2292. "H,CodeIntegrityCatalog",
  2293. "I,CodeIntegrityCatalogOffset",
  2294. "I,CodeIntegrityReserved",
  2295. "I,GuardAddressTakenIatEntryTable",
  2296. "I,GuardAddressTakenIatEntryCount",
  2297. "I,GuardLongJumpTargetTable",
  2298. "I,GuardLongJumpTargetCount",
  2299. "I,DynamicValueRelocTable",
  2300. "I,CHPEMetadataPointer",
  2301. "I,GuardRFFailureRoutine",
  2302. "I,GuardRFFailureRoutineFunctionPointer",
  2303. "I,DynamicValueRelocTableOffset",
  2304. "H,DynamicValueRelocTableSection",
  2305. "H,Reserved2",
  2306. "I,GuardRFVerifyStackPointerFunctionPointer",
  2307. "I,HotPatchTableOffset",
  2308. "I,Reserved3",
  2309. "I,EnclaveConfigurationPointer",
  2310. "I,VolatileMetadataPointer",
  2311. "I,GuardEHContinuationTable",
  2312. "I,GuardEHContinuationCount",
  2313. "I,GuardXFGCheckFunctionPointer",
  2314. "I,GuardXFGDispatchFunctionPointer",
  2315. "I,GuardXFGTableDispatchFunctionPointer",
  2316. "I,CastGuardOsDeterminedFailureMode",
  2317. "I,GuardMemcpyFunctionPointer",
  2318. ),
  2319. )
  2320. __IMAGE_LOAD_CONFIG_DIRECTORY64_format__ = (
  2321. "IMAGE_LOAD_CONFIG_DIRECTORY",
  2322. (
  2323. "I,Size",
  2324. "I,TimeDateStamp",
  2325. "H,MajorVersion",
  2326. "H,MinorVersion",
  2327. "I,GlobalFlagsClear",
  2328. "I,GlobalFlagsSet",
  2329. "I,CriticalSectionDefaultTimeout",
  2330. "Q,DeCommitFreeBlockThreshold",
  2331. "Q,DeCommitTotalFreeThreshold",
  2332. "Q,LockPrefixTable",
  2333. "Q,MaximumAllocationSize",
  2334. "Q,VirtualMemoryThreshold",
  2335. "Q,ProcessAffinityMask",
  2336. "I,ProcessHeapFlags",
  2337. "H,CSDVersion",
  2338. "H,DependentLoadFlags",
  2339. "Q,EditList",
  2340. "Q,SecurityCookie",
  2341. "Q,SEHandlerTable",
  2342. "Q,SEHandlerCount",
  2343. "Q,GuardCFCheckFunctionPointer",
  2344. "Q,GuardCFDispatchFunctionPointer",
  2345. "Q,GuardCFFunctionTable",
  2346. "Q,GuardCFFunctionCount",
  2347. "I,GuardFlags",
  2348. "H,CodeIntegrityFlags",
  2349. "H,CodeIntegrityCatalog",
  2350. "I,CodeIntegrityCatalogOffset",
  2351. "I,CodeIntegrityReserved",
  2352. "Q,GuardAddressTakenIatEntryTable",
  2353. "Q,GuardAddressTakenIatEntryCount",
  2354. "Q,GuardLongJumpTargetTable",
  2355. "Q,GuardLongJumpTargetCount",
  2356. "Q,DynamicValueRelocTable",
  2357. "Q,CHPEMetadataPointer",
  2358. "Q,GuardRFFailureRoutine",
  2359. "Q,GuardRFFailureRoutineFunctionPointer",
  2360. "I,DynamicValueRelocTableOffset",
  2361. "H,DynamicValueRelocTableSection",
  2362. "H,Reserved2",
  2363. "Q,GuardRFVerifyStackPointerFunctionPointer",
  2364. "I,HotPatchTableOffset",
  2365. "I,Reserved3",
  2366. "Q,EnclaveConfigurationPointer",
  2367. "Q,VolatileMetadataPointer",
  2368. "Q,GuardEHContinuationTable",
  2369. "Q,GuardEHContinuationCount",
  2370. "Q,GuardXFGCheckFunctionPointer",
  2371. "Q,GuardXFGDispatchFunctionPointer",
  2372. "Q,GuardXFGTableDispatchFunctionPointer",
  2373. "Q,CastGuardOsDeterminedFailureMode",
  2374. "Q,GuardMemcpyFunctionPointer",
  2375. ),
  2376. )
  2377. __IMAGE_DYNAMIC_RELOCATION_TABLE_format__ = (
  2378. "IMAGE_DYNAMIC_RELOCATION_TABLE",
  2379. ("I,Version", "I,Size"),
  2380. )
  2381. __IMAGE_DYNAMIC_RELOCATION_format__ = (
  2382. "IMAGE_DYNAMIC_RELOCATION",
  2383. ("I,Symbol", "I,BaseRelocSize"),
  2384. )
  2385. __IMAGE_DYNAMIC_RELOCATION64_format__ = (
  2386. "IMAGE_DYNAMIC_RELOCATION64",
  2387. ("Q,Symbol", "I,BaseRelocSize"),
  2388. )
  2389. __IMAGE_DYNAMIC_RELOCATION_V2_format__ = (
  2390. "IMAGE_DYNAMIC_RELOCATION_V2",
  2391. ("I,HeaderSize", "I,FixupInfoSize", "I,Symbol", "I,SymbolGroup", "I,Flags"),
  2392. )
  2393. __IMAGE_DYNAMIC_RELOCATION64_V2_format__ = (
  2394. "IMAGE_DYNAMIC_RELOCATION64_V2",
  2395. ("I,HeaderSize", "I,FixupInfoSize", "Q,Symbol", "I,SymbolGroup", "I,Flags"),
  2396. )
  2397. __IMAGE_BOUND_IMPORT_DESCRIPTOR_format__ = (
  2398. "IMAGE_BOUND_IMPORT_DESCRIPTOR",
  2399. ("I,TimeDateStamp", "H,OffsetModuleName", "H,NumberOfModuleForwarderRefs"),
  2400. )
  2401. __IMAGE_BOUND_FORWARDER_REF_format__ = (
  2402. "IMAGE_BOUND_FORWARDER_REF",
  2403. ("I,TimeDateStamp", "H,OffsetModuleName", "H,Reserved"),
  2404. )
  2405. __RUNTIME_FUNCTION_format__ = (
  2406. "RUNTIME_FUNCTION",
  2407. ("I,BeginAddress", "I,EndAddress", "I,UnwindData"),
  2408. )
  2409. def __init__(
  2410. self,
  2411. name=None,
  2412. data=None,
  2413. fast_load=None,
  2414. max_symbol_exports=MAX_SYMBOL_EXPORT_COUNT,
  2415. max_repeated_symbol=120,
  2416. ):
  2417. self.max_symbol_exports = max_symbol_exports
  2418. self.max_repeated_symbol = max_repeated_symbol
  2419. self._get_section_by_rva_last_used = None
  2420. self.sections = []
  2421. self.__warnings = []
  2422. self.PE_TYPE = None
  2423. if name is None and data is None:
  2424. raise ValueError("Must supply either name or data")
  2425. # This list will keep track of all the structures created.
  2426. # That will allow for an easy iteration through the list
  2427. # in order to save the modifications made
  2428. self.__structures__ = []
  2429. self.__from_file = None
  2430. # We only want to print these warnings once
  2431. self.FileAlignment_Warning = False
  2432. self.SectionAlignment_Warning = False
  2433. # Count of total resource entries across nested tables
  2434. self.__total_resource_entries_count = 0
  2435. # Sum of the size of all resource entries parsed, which should not
  2436. # exceed the file size.
  2437. self.__total_resource_bytes = 0
  2438. # The number of imports parsed in this file
  2439. self.__total_import_symbols = 0
  2440. self.dynamic_relocation_format_by_symbol = {
  2441. 3: PE.__IMAGE_IMPORT_CONTROL_TRANSFER_DYNAMIC_RELOCATION_format__,
  2442. 4: PE.__IMAGE_INDIR_CONTROL_TRANSFER_DYNAMIC_RELOCATION_format__,
  2443. 5: PE.__IMAGE_SWITCHTABLE_BRANCH_DYNAMIC_RELOCATION_format__,
  2444. }
  2445. fast_load = fast_load if fast_load is not None else globals()["fast_load"]
  2446. try:
  2447. self.__parse__(name, data, fast_load)
  2448. except:
  2449. self.close()
  2450. raise
  2451. def __enter__(self):
  2452. return self
  2453. def __exit__(self, type, value, traceback):
  2454. self.close()
  2455. def _close_data(self):
  2456. if (
  2457. self.__from_file is True
  2458. and hasattr(self, "__data__")
  2459. and (
  2460. (isinstance(mmap.mmap, type) and isinstance(self.__data__, mmap.mmap))
  2461. or "mmap.mmap" in repr(type(self.__data__))
  2462. )
  2463. ):
  2464. self.__data__.close()
  2465. del self.__data__
  2466. # Run a full garbage collection to find unreachable objects and free them.
  2467. gc.collect()
  2468. def close(self):
  2469. self._close_data()
  2470. def __unpack_data__(self, format, data, file_offset):
  2471. """Apply structure format to raw data.
  2472. Returns an unpacked structure object if successful, None otherwise.
  2473. """
  2474. structure = Structure(format, file_offset=file_offset)
  2475. try:
  2476. structure.__unpack__(data)
  2477. except PEFormatError as err:
  2478. self.__warnings.append(
  2479. 'Corrupt header "{0}" at file offset {1}. Exception: {2}'.format(
  2480. format[0], file_offset, err
  2481. )
  2482. )
  2483. return None
  2484. self.__structures__.append(structure)
  2485. return structure
  2486. def __unpack_data_with_bitfields__(self, format, data, file_offset):
  2487. """Apply structure format to raw data.
  2488. Returns an unpacked structure object if successful, None otherwise.
  2489. """
  2490. structure = StructureWithBitfields(format, file_offset=file_offset)
  2491. try:
  2492. structure.__unpack__(data)
  2493. except PEFormatError as err:
  2494. self.__warnings.append(
  2495. 'Corrupt header "{0}" at file offset {1}. Exception: {2}'.format(
  2496. format[0], file_offset, err
  2497. )
  2498. )
  2499. return None
  2500. self.__structures__.append(structure)
  2501. return structure
  2502. def __parse__(self, fname, data, fast_load):
  2503. """Parse a Portable Executable file.
  2504. Loads a PE file, parsing all its structures and making them available
  2505. through the instance's attributes.
  2506. """
  2507. if fname is not None:
  2508. stat = os.stat(fname)
  2509. if stat.st_size == 0:
  2510. raise PEFormatError("The file is empty")
  2511. fd = None
  2512. try:
  2513. fd = open(fname, "rb")
  2514. self.fileno = fd.fileno()
  2515. if hasattr(mmap, "MAP_PRIVATE"):
  2516. # Unix
  2517. self.__data__ = mmap.mmap(self.fileno, 0, mmap.MAP_PRIVATE)
  2518. else:
  2519. # Windows
  2520. self.__data__ = mmap.mmap(self.fileno, 0, access=mmap.ACCESS_READ)
  2521. self.__from_file = True
  2522. except IOError as excp:
  2523. exception_msg = "{0}".format(excp)
  2524. exception_msg = exception_msg and (": %s" % exception_msg)
  2525. raise Exception(
  2526. "Unable to access file '{0}'{1}".format(fname, exception_msg)
  2527. )
  2528. finally:
  2529. if fd is not None:
  2530. fd.close()
  2531. elif data is not None:
  2532. self.__data__ = data
  2533. self.__from_file = False
  2534. # Resources should not overlap each other, so they should not exceed the
  2535. # file size.
  2536. self.__resource_size_limit_upperbounds = len(self.__data__)
  2537. self.__resource_size_limit_reached = False
  2538. if not fast_load:
  2539. for byte, byte_count in Counter(bytearray(self.__data__)).items():
  2540. # Only report the cases where a byte makes up for more than 50% (if
  2541. # zero) or 15% (if non-zero) of the file's contents. There are
  2542. # legitimate PEs where 0x00 bytes are close to 50% of the whole
  2543. # file's contents.
  2544. if (byte == 0 and byte_count / len(self.__data__) > 0.5) or (
  2545. byte != 0 and byte_count / len(self.__data__) > 0.15
  2546. ):
  2547. self.__warnings.append(
  2548. (
  2549. "Byte 0x{0:02x} makes up {1:.4f}% of the file's contents."
  2550. " This may indicate truncation / malformation."
  2551. ).format(byte, 100.0 * byte_count / len(self.__data__))
  2552. )
  2553. dos_header_data = self.__data__[:64]
  2554. if len(dos_header_data) != 64:
  2555. raise PEFormatError(
  2556. "Unable to read the DOS Header, possibly a truncated file."
  2557. )
  2558. self.DOS_HEADER = self.__unpack_data__(
  2559. self.__IMAGE_DOS_HEADER_format__, dos_header_data, file_offset=0
  2560. )
  2561. if self.DOS_HEADER.e_magic == IMAGE_DOSZM_SIGNATURE:
  2562. raise PEFormatError("Probably a ZM Executable (not a PE file).")
  2563. if not self.DOS_HEADER or self.DOS_HEADER.e_magic != IMAGE_DOS_SIGNATURE:
  2564. raise PEFormatError("DOS Header magic not found.")
  2565. # Check for sane value in e_lfanew
  2566. #
  2567. if self.DOS_HEADER.e_lfanew > len(self.__data__):
  2568. raise PEFormatError("Invalid e_lfanew value, probably not a PE file")
  2569. nt_headers_offset = self.DOS_HEADER.e_lfanew
  2570. self.NT_HEADERS = self.__unpack_data__(
  2571. self.__IMAGE_NT_HEADERS_format__,
  2572. self.__data__[nt_headers_offset : nt_headers_offset + 8],
  2573. file_offset=nt_headers_offset,
  2574. )
  2575. # We better check the signature right here, before the file screws
  2576. # around with sections:
  2577. # Some malware will cause the Signature value to not exist at all
  2578. if not self.NT_HEADERS or not self.NT_HEADERS.Signature:
  2579. raise PEFormatError("NT Headers not found.")
  2580. if (0xFFFF & self.NT_HEADERS.Signature) == IMAGE_NE_SIGNATURE:
  2581. raise PEFormatError("Invalid NT Headers signature. Probably a NE file")
  2582. if (0xFFFF & self.NT_HEADERS.Signature) == IMAGE_LE_SIGNATURE:
  2583. raise PEFormatError("Invalid NT Headers signature. Probably a LE file")
  2584. if (0xFFFF & self.NT_HEADERS.Signature) == IMAGE_LX_SIGNATURE:
  2585. raise PEFormatError("Invalid NT Headers signature. Probably a LX file")
  2586. if (0xFFFF & self.NT_HEADERS.Signature) == IMAGE_TE_SIGNATURE:
  2587. raise PEFormatError("Invalid NT Headers signature. Probably a TE file")
  2588. if self.NT_HEADERS.Signature != IMAGE_NT_SIGNATURE:
  2589. raise PEFormatError("Invalid NT Headers signature.")
  2590. self.FILE_HEADER = self.__unpack_data__(
  2591. self.__IMAGE_FILE_HEADER_format__,
  2592. self.__data__[nt_headers_offset + 4 : nt_headers_offset + 4 + 32],
  2593. file_offset=nt_headers_offset + 4,
  2594. )
  2595. image_flags = retrieve_flags(IMAGE_CHARACTERISTICS, "IMAGE_FILE_")
  2596. if not self.FILE_HEADER:
  2597. raise PEFormatError("File Header missing")
  2598. # Set the image's flags according to the Characteristics member
  2599. set_flags(self.FILE_HEADER, self.FILE_HEADER.Characteristics, image_flags)
  2600. optional_header_offset = nt_headers_offset + 4 + self.FILE_HEADER.sizeof()
  2601. # Note: location of sections can be controlled from PE header:
  2602. sections_offset = optional_header_offset + self.FILE_HEADER.SizeOfOptionalHeader
  2603. self.OPTIONAL_HEADER = self.__unpack_data__(
  2604. self.__IMAGE_OPTIONAL_HEADER_format__,
  2605. # Read up to 256 bytes to allow creating a copy of too much data
  2606. self.__data__[optional_header_offset : optional_header_offset + 256],
  2607. file_offset=optional_header_offset,
  2608. )
  2609. # According to solardesigner's findings for his
  2610. # Tiny PE project, the optional header does not
  2611. # need fields beyond "Subsystem" in order to be
  2612. # loadable by the Windows loader (given that zeros
  2613. # are acceptable values and the header is loaded
  2614. # in a zeroed memory page)
  2615. # If trying to parse a full Optional Header fails
  2616. # we try to parse it again with some 0 padding
  2617. #
  2618. MINIMUM_VALID_OPTIONAL_HEADER_RAW_SIZE = 69
  2619. if (
  2620. self.OPTIONAL_HEADER is None
  2621. and len(
  2622. self.__data__[optional_header_offset : optional_header_offset + 0x200]
  2623. )
  2624. >= MINIMUM_VALID_OPTIONAL_HEADER_RAW_SIZE
  2625. ):
  2626. # Add enough zeros to make up for the unused fields
  2627. #
  2628. padding_length = 128
  2629. # Create padding
  2630. #
  2631. padded_data = self.__data__[
  2632. optional_header_offset : optional_header_offset + 0x200
  2633. ] + (b"\0" * padding_length)
  2634. self.OPTIONAL_HEADER = self.__unpack_data__(
  2635. self.__IMAGE_OPTIONAL_HEADER_format__,
  2636. padded_data,
  2637. file_offset=optional_header_offset,
  2638. )
  2639. # Check the Magic in the OPTIONAL_HEADER and set the PE file
  2640. # type accordingly
  2641. #
  2642. if self.OPTIONAL_HEADER is not None:
  2643. if self.OPTIONAL_HEADER.Magic == OPTIONAL_HEADER_MAGIC_PE:
  2644. self.PE_TYPE = OPTIONAL_HEADER_MAGIC_PE
  2645. elif self.OPTIONAL_HEADER.Magic == OPTIONAL_HEADER_MAGIC_PE_PLUS:
  2646. self.PE_TYPE = OPTIONAL_HEADER_MAGIC_PE_PLUS
  2647. self.OPTIONAL_HEADER = self.__unpack_data__(
  2648. self.__IMAGE_OPTIONAL_HEADER64_format__,
  2649. self.__data__[
  2650. optional_header_offset : optional_header_offset + 0x200
  2651. ],
  2652. file_offset=optional_header_offset,
  2653. )
  2654. # Again, as explained above, we try to parse
  2655. # a reduced form of the Optional Header which
  2656. # is still valid despite not including all
  2657. # structure members
  2658. #
  2659. MINIMUM_VALID_OPTIONAL_HEADER_RAW_SIZE = 69 + 4
  2660. if (
  2661. self.OPTIONAL_HEADER is None
  2662. and len(
  2663. self.__data__[
  2664. optional_header_offset : optional_header_offset + 0x200
  2665. ]
  2666. )
  2667. >= MINIMUM_VALID_OPTIONAL_HEADER_RAW_SIZE
  2668. ):
  2669. padding_length = 128
  2670. padded_data = self.__data__[
  2671. optional_header_offset : optional_header_offset + 0x200
  2672. ] + (b"\0" * padding_length)
  2673. self.OPTIONAL_HEADER = self.__unpack_data__(
  2674. self.__IMAGE_OPTIONAL_HEADER64_format__,
  2675. padded_data,
  2676. file_offset=optional_header_offset,
  2677. )
  2678. if not self.FILE_HEADER:
  2679. raise PEFormatError("File Header missing")
  2680. # Die gracefully if there is no OPTIONAL_HEADER field
  2681. # 975440f5ad5e2e4a92c4d9a5f22f75c1
  2682. if self.OPTIONAL_HEADER is None:
  2683. raise PEFormatError("No Optional Header found, invalid PE32 or PE32+ file.")
  2684. if self.PE_TYPE is None:
  2685. self.__warnings.append(
  2686. "Invalid type 0x{0:04x} in Optional Header.".format(
  2687. self.OPTIONAL_HEADER.Magic
  2688. )
  2689. )
  2690. dll_characteristics_flags = retrieve_flags(
  2691. DLL_CHARACTERISTICS, "IMAGE_DLLCHARACTERISTICS_"
  2692. )
  2693. # Set the Dll Characteristics flags according to the DllCharacteristics member
  2694. set_flags(
  2695. self.OPTIONAL_HEADER,
  2696. self.OPTIONAL_HEADER.DllCharacteristics,
  2697. dll_characteristics_flags,
  2698. )
  2699. self.OPTIONAL_HEADER.DATA_DIRECTORY = []
  2700. offset = optional_header_offset + self.OPTIONAL_HEADER.sizeof()
  2701. self.NT_HEADERS.FILE_HEADER = self.FILE_HEADER
  2702. self.NT_HEADERS.OPTIONAL_HEADER = self.OPTIONAL_HEADER
  2703. # Windows 8 specific check
  2704. #
  2705. if (
  2706. self.OPTIONAL_HEADER.AddressOfEntryPoint
  2707. < self.OPTIONAL_HEADER.SizeOfHeaders
  2708. ):
  2709. self.__warnings.append(
  2710. "SizeOfHeaders is smaller than AddressOfEntryPoint: this file "
  2711. "cannot run under Windows 8."
  2712. )
  2713. # The NumberOfRvaAndSizes is sanitized to stay within
  2714. # reasonable limits so can be casted to an int
  2715. #
  2716. if self.OPTIONAL_HEADER.NumberOfRvaAndSizes > 0x10:
  2717. self.__warnings.append(
  2718. "Suspicious NumberOfRvaAndSizes in the Optional Header. "
  2719. "Normal values are never larger than 0x10, the value is: 0x%x"
  2720. % self.OPTIONAL_HEADER.NumberOfRvaAndSizes
  2721. )
  2722. MAX_ASSUMED_VALID_NUMBER_OF_RVA_AND_SIZES = 0x100
  2723. for i in range(int(0x7FFFFFFF & self.OPTIONAL_HEADER.NumberOfRvaAndSizes)):
  2724. if len(self.__data__) - offset == 0:
  2725. break
  2726. if len(self.__data__) - offset < 8:
  2727. data = self.__data__[offset:] + b"\0" * 8
  2728. else:
  2729. data = self.__data__[
  2730. offset : offset + MAX_ASSUMED_VALID_NUMBER_OF_RVA_AND_SIZES
  2731. ]
  2732. dir_entry = self.__unpack_data__(
  2733. self.__IMAGE_DATA_DIRECTORY_format__, data, file_offset=offset
  2734. )
  2735. if dir_entry is None:
  2736. break
  2737. # Would fail if missing an entry
  2738. # 1d4937b2fa4d84ad1bce0309857e70ca offending sample
  2739. try:
  2740. dir_entry.name = DIRECTORY_ENTRY[i]
  2741. except (KeyError, AttributeError):
  2742. break
  2743. offset += dir_entry.sizeof()
  2744. self.OPTIONAL_HEADER.DATA_DIRECTORY.append(dir_entry)
  2745. # If the offset goes outside the optional header,
  2746. # the loop is broken, regardless of how many directories
  2747. # NumberOfRvaAndSizes says there are
  2748. #
  2749. # We assume a normally sized optional header, hence that we do
  2750. # a sizeof() instead of reading SizeOfOptionalHeader.
  2751. # Then we add a default number of directories times their size,
  2752. # if we go beyond that, we assume the number of directories
  2753. # is wrong and stop processing
  2754. if offset >= (
  2755. optional_header_offset + self.OPTIONAL_HEADER.sizeof() + 8 * 16
  2756. ):
  2757. break
  2758. offset = self.parse_sections(sections_offset)
  2759. # There could be a problem if there are no raw data sections
  2760. # greater than 0
  2761. # fc91013eb72529da005110a3403541b6 example
  2762. # Should this throw an exception in the minimum header offset
  2763. # can't be found?
  2764. #
  2765. rawDataPointers = [
  2766. self.adjust_PointerToRawData(s.PointerToRawData)
  2767. for s in self.sections
  2768. if s.PointerToRawData > 0
  2769. ]
  2770. if len(rawDataPointers) > 0:
  2771. lowest_section_offset = min(rawDataPointers)
  2772. else:
  2773. lowest_section_offset = None
  2774. if not lowest_section_offset or lowest_section_offset < offset:
  2775. self.header = self.__data__[:offset]
  2776. else:
  2777. self.header = self.__data__[:lowest_section_offset]
  2778. # Check whether the entry point lies within a section
  2779. #
  2780. if (
  2781. self.get_section_by_rva(self.OPTIONAL_HEADER.AddressOfEntryPoint)
  2782. is not None
  2783. ):
  2784. # Check whether the entry point lies within the file
  2785. #
  2786. ep_offset = self.get_offset_from_rva(
  2787. self.OPTIONAL_HEADER.AddressOfEntryPoint
  2788. )
  2789. if ep_offset > len(self.__data__):
  2790. self.__warnings.append(
  2791. "Possibly corrupt file. AddressOfEntryPoint lies outside the"
  2792. " file. AddressOfEntryPoint: 0x%x"
  2793. % self.OPTIONAL_HEADER.AddressOfEntryPoint
  2794. )
  2795. else:
  2796. self.__warnings.append(
  2797. "AddressOfEntryPoint lies outside the sections' boundaries. "
  2798. "AddressOfEntryPoint: 0x%x" % self.OPTIONAL_HEADER.AddressOfEntryPoint
  2799. )
  2800. if not fast_load:
  2801. self.full_load()
  2802. def parse_rich_header(self):
  2803. """Parses the rich header
  2804. see https://www.ntcore.com/files/richsign.htm for more information
  2805. Structure:
  2806. 00 DanS ^ checksum, checksum, checksum, checksum
  2807. 10 Symbol RVA ^ checksum, Symbol size ^ checksum...
  2808. ...
  2809. XX Rich, checksum, 0, 0,...
  2810. """
  2811. # Rich Header constants
  2812. #
  2813. DANS = 0x536E6144 # 'DanS' as dword
  2814. RICH = 0x68636952 # 'Rich' as dword
  2815. rich_index = self.__data__.find(
  2816. b"Rich", 0x80, self.OPTIONAL_HEADER.get_file_offset()
  2817. )
  2818. if rich_index == -1:
  2819. return None
  2820. # Read a block of data
  2821. try:
  2822. # The end of the structure is 8 bytes after the start of the Rich
  2823. # string (although there is padding after this).
  2824. rich_data = self.__data__[0x80 : rich_index + 8]
  2825. # Make the data have length a multiple of 4, otherwise the
  2826. # subsequent parsing will fail. It's not impossible that we retrieve
  2827. # truncated data that is not a multiple.
  2828. rich_data = rich_data[: 4 * (len(rich_data) // 4)]
  2829. data = list(struct.unpack("<{0}I".format(len(rich_data) // 4), rich_data))
  2830. if RICH not in data:
  2831. return None
  2832. except PEFormatError:
  2833. return None
  2834. # get key, raw_data and clear_data
  2835. key = struct.pack("<L", data[data.index(RICH) + 1])
  2836. result = {"key": key}
  2837. raw_data = rich_data[: rich_data.find(b"Rich")]
  2838. result["raw_data"] = raw_data
  2839. ord_ = lambda c: ord(c) if not isinstance(c, int) else c
  2840. clear_data = bytearray()
  2841. for idx, val in enumerate(raw_data):
  2842. clear_data.append(ord_(val) ^ ord_(key[idx % len(key)]))
  2843. result["clear_data"] = bytes(clear_data)
  2844. # PE files are stored in little-endian order, the same byte order as an x86
  2845. # https://wiki.osdev.org/PE
  2846. checksum = int.from_bytes(key, "little")
  2847. # the checksum should be present 3 times after the DanS signature
  2848. if (
  2849. data[0] ^ checksum != DANS
  2850. or data[1] != checksum
  2851. or data[2] != checksum
  2852. or data[3] != checksum
  2853. ):
  2854. self.__warnings.append(
  2855. "Rich Header is not in Microsoft format, possibly malformed"
  2856. )
  2857. result["checksum"] = checksum
  2858. headervalues = []
  2859. result["values"] = headervalues
  2860. data = data[4:]
  2861. for i in range(len(data) // 2):
  2862. # Stop until the Rich footer signature is found
  2863. #
  2864. if data[2 * i] == RICH:
  2865. # it should be followed by the checksum
  2866. #
  2867. if data[2 * i + 1] != checksum:
  2868. self.__warnings.append("Rich Header is malformed")
  2869. break
  2870. # header values come by pairs
  2871. #
  2872. headervalues += [data[2 * i] ^ checksum, data[2 * i + 1] ^ checksum]
  2873. return result
  2874. def get_warnings(self):
  2875. """Return the list of warnings.
  2876. Non-critical problems found when parsing the PE file are
  2877. appended to a list of warnings. This method returns the
  2878. full list.
  2879. """
  2880. return self.__warnings
  2881. def show_warnings(self):
  2882. """Print the list of warnings.
  2883. Non-critical problems found when parsing the PE file are
  2884. appended to a list of warnings. This method prints the
  2885. full list to standard output.
  2886. """
  2887. for warning in self.__warnings:
  2888. print(">", warning)
  2889. def full_load(self):
  2890. """Process the data directories.
  2891. This method will load the data directories which might not have
  2892. been loaded if the "fast_load" option was used.
  2893. It also parses the rich header, which may or may not present.
  2894. """
  2895. self.parse_data_directories()
  2896. class RichHeader:
  2897. pass
  2898. rich_header = self.parse_rich_header()
  2899. if rich_header:
  2900. self.RICH_HEADER = RichHeader()
  2901. self.RICH_HEADER.checksum = rich_header.get("checksum", None)
  2902. self.RICH_HEADER.values = rich_header.get("values", None)
  2903. self.RICH_HEADER.key = rich_header.get("key", None)
  2904. self.RICH_HEADER.raw_data = rich_header.get("raw_data", None)
  2905. self.RICH_HEADER.clear_data = rich_header.get("clear_data", None)
  2906. else:
  2907. self.RICH_HEADER = None
  2908. def write(self, filename=None):
  2909. """Write the PE file.
  2910. This function will process all headers and components
  2911. of the PE file and include all changes made (by just
  2912. assigning to attributes in the PE objects) and write
  2913. the changes back to a file whose name is provided as
  2914. an argument. The filename is optional, if not
  2915. provided the data will be returned as a 'str' object.
  2916. """
  2917. file_data = bytearray(self.__data__)
  2918. for structure in self.__structures__:
  2919. struct_data = bytearray(structure.__pack__())
  2920. offset = structure.get_file_offset()
  2921. file_data[offset : offset + len(struct_data)] = struct_data
  2922. if hasattr(self, "VS_VERSIONINFO"):
  2923. if hasattr(self, "FileInfo"):
  2924. for finfo in self.FileInfo:
  2925. for entry in finfo:
  2926. if hasattr(entry, "StringTable"):
  2927. for st_entry in entry.StringTable:
  2928. for key, entry in list(st_entry.entries.items()):
  2929. # Offsets and lengths of the keys and values.
  2930. # Each value in the dictionary is a tuple:
  2931. # (key length, value length)
  2932. # The lengths are in characters, not in bytes.
  2933. offsets = st_entry.entries_offsets[key]
  2934. lengths = st_entry.entries_lengths[key]
  2935. if len(entry) > lengths[1]:
  2936. l = entry.decode("utf-8").encode("utf-16le")
  2937. file_data[
  2938. offsets[1] : offsets[1] + lengths[1] * 2
  2939. ] = l[: lengths[1] * 2]
  2940. else:
  2941. encoded_data = entry.decode("utf-8").encode(
  2942. "utf-16le"
  2943. )
  2944. file_data[
  2945. offsets[1] : offsets[1] + len(encoded_data)
  2946. ] = encoded_data
  2947. new_file_data = file_data
  2948. if not filename:
  2949. return new_file_data
  2950. with open(filename, "wb+") as f:
  2951. f.write(new_file_data)
  2952. def parse_sections(self, offset):
  2953. """Fetch the PE file sections.
  2954. The sections will be readily available in the "sections" attribute.
  2955. Its attributes will contain all the section information plus "data"
  2956. a buffer containing the section's data.
  2957. The "Characteristics" member will be processed and attributes
  2958. representing the section characteristics (with the 'IMAGE_SCN_'
  2959. string trimmed from the constant's names) will be added to the
  2960. section instance.
  2961. Refer to the SectionStructure class for additional info.
  2962. """
  2963. self.sections = []
  2964. MAX_SIMULTANEOUS_ERRORS = 3
  2965. for i in range(self.FILE_HEADER.NumberOfSections):
  2966. if i >= MAX_SECTIONS:
  2967. self.__warnings.append(
  2968. "Too many sections {0} (>={1})".format(
  2969. self.FILE_HEADER.NumberOfSections, MAX_SECTIONS
  2970. )
  2971. )
  2972. break
  2973. simultaneous_errors = 0
  2974. section = SectionStructure(self.__IMAGE_SECTION_HEADER_format__, pe=self)
  2975. if not section:
  2976. break
  2977. section_offset = offset + section.sizeof() * i
  2978. section.set_file_offset(section_offset)
  2979. section_data = self.__data__[
  2980. section_offset : section_offset + section.sizeof()
  2981. ]
  2982. # Check if the section is all nulls and stop if so.
  2983. if count_zeroes(section_data) == section.sizeof():
  2984. self.__warnings.append(f"Invalid section {i}. Contents are null-bytes.")
  2985. break
  2986. if not section_data:
  2987. self.__warnings.append(
  2988. f"Invalid section {i}. No data in the file (is this corkami's "
  2989. "virtsectblXP?)."
  2990. )
  2991. break
  2992. section.__unpack__(section_data)
  2993. self.__structures__.append(section)
  2994. if section.SizeOfRawData + section.PointerToRawData > len(self.__data__):
  2995. simultaneous_errors += 1
  2996. self.__warnings.append(
  2997. f"Error parsing section {i}. SizeOfRawData is larger than file."
  2998. )
  2999. if self.adjust_PointerToRawData(section.PointerToRawData) > len(
  3000. self.__data__
  3001. ):
  3002. simultaneous_errors += 1
  3003. self.__warnings.append(
  3004. f"Error parsing section {i}. PointerToRawData points beyond "
  3005. "the end of the file."
  3006. )
  3007. if section.Misc_VirtualSize > 0x10000000:
  3008. simultaneous_errors += 1
  3009. self.__warnings.append(
  3010. f"Suspicious value found parsing section {i}. VirtualSize is "
  3011. "extremely large > 256MiB."
  3012. )
  3013. if (
  3014. self.adjust_SectionAlignment(
  3015. section.VirtualAddress,
  3016. self.OPTIONAL_HEADER.SectionAlignment,
  3017. self.OPTIONAL_HEADER.FileAlignment,
  3018. )
  3019. > 0x10000000
  3020. ):
  3021. simultaneous_errors += 1
  3022. self.__warnings.append(
  3023. f"Suspicious value found parsing section {i}. VirtualAddress is "
  3024. "beyond 0x10000000."
  3025. )
  3026. if (
  3027. self.OPTIONAL_HEADER.FileAlignment != 0
  3028. and (section.PointerToRawData % self.OPTIONAL_HEADER.FileAlignment) != 0
  3029. ):
  3030. simultaneous_errors += 1
  3031. self.__warnings.append(
  3032. (
  3033. f"Error parsing section {i}. "
  3034. "PointerToRawData should normally be "
  3035. "a multiple of FileAlignment, this might imply the file "
  3036. "is trying to confuse tools which parse this incorrectly."
  3037. )
  3038. )
  3039. if simultaneous_errors >= MAX_SIMULTANEOUS_ERRORS:
  3040. self.__warnings.append("Too many warnings parsing section. Aborting.")
  3041. break
  3042. section_flags = retrieve_flags(SECTION_CHARACTERISTICS, "IMAGE_SCN_")
  3043. # Set the section's flags according to the Characteristics member
  3044. set_flags(section, section.Characteristics, section_flags)
  3045. if section.__dict__.get(
  3046. "IMAGE_SCN_MEM_WRITE", False
  3047. ) and section.__dict__.get("IMAGE_SCN_MEM_EXECUTE", False):
  3048. if section.Name.rstrip(b"\x00") == b"PAGE" and self.is_driver():
  3049. # Drivers can have a PAGE section with those flags set without
  3050. # implying that it is malicious
  3051. pass
  3052. else:
  3053. self.__warnings.append(
  3054. f"Suspicious flags set for section {i}. "
  3055. "Both IMAGE_SCN_MEM_WRITE and IMAGE_SCN_MEM_EXECUTE are set. "
  3056. "This might indicate a packed executable."
  3057. )
  3058. self.sections.append(section)
  3059. # Sort the sections by their VirtualAddress and add a field to each of them
  3060. # with the VirtualAddress of the next section. This will allow to check
  3061. # for potentially overlapping sections in badly constructed PEs.
  3062. self.sections.sort(key=lambda a: a.VirtualAddress)
  3063. for idx, section in enumerate(self.sections):
  3064. if idx == len(self.sections) - 1:
  3065. section.next_section_virtual_address = None
  3066. else:
  3067. section.next_section_virtual_address = self.sections[
  3068. idx + 1
  3069. ].VirtualAddress
  3070. if self.FILE_HEADER.NumberOfSections > 0 and self.sections:
  3071. return (
  3072. offset + self.sections[0].sizeof() * self.FILE_HEADER.NumberOfSections
  3073. )
  3074. else:
  3075. return offset
  3076. def parse_data_directories(
  3077. self, directories=None, forwarded_exports_only=False, import_dllnames_only=False
  3078. ):
  3079. """Parse and process the PE file's data directories.
  3080. If the optional argument 'directories' is given, only
  3081. the directories at the specified indexes will be parsed.
  3082. Such functionality allows parsing of areas of interest
  3083. without the burden of having to parse all others.
  3084. The directories can then be specified as:
  3085. For export / import only:
  3086. directories = [ 0, 1 ]
  3087. or (more verbosely):
  3088. directories = [ DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT'],
  3089. DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_EXPORT'] ]
  3090. If 'directories' is a list, the ones that are processed will be removed,
  3091. leaving only the ones that are not present in the image.
  3092. If `forwarded_exports_only` is True, the IMAGE_DIRECTORY_ENTRY_EXPORT
  3093. attribute will only contain exports that are forwarded to another DLL.
  3094. If `import_dllnames_only` is True, symbols will not be parsed from
  3095. the import table and the entries in the IMAGE_DIRECTORY_ENTRY_IMPORT
  3096. attribute will not have a `symbols` attribute.
  3097. """
  3098. directory_parsing = (
  3099. ("IMAGE_DIRECTORY_ENTRY_IMPORT", self.parse_import_directory),
  3100. ("IMAGE_DIRECTORY_ENTRY_EXPORT", self.parse_export_directory),
  3101. ("IMAGE_DIRECTORY_ENTRY_RESOURCE", self.parse_resources_directory),
  3102. ("IMAGE_DIRECTORY_ENTRY_DEBUG", self.parse_debug_directory),
  3103. ("IMAGE_DIRECTORY_ENTRY_BASERELOC", self.parse_relocations_directory),
  3104. ("IMAGE_DIRECTORY_ENTRY_TLS", self.parse_directory_tls),
  3105. ("IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG", self.parse_directory_load_config),
  3106. ("IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT", self.parse_delay_import_directory),
  3107. ("IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT", self.parse_directory_bound_imports),
  3108. ("IMAGE_DIRECTORY_ENTRY_EXCEPTION", self.parse_exceptions_directory),
  3109. )
  3110. if directories is not None:
  3111. if not isinstance(directories, (tuple, list)):
  3112. directories = [directories]
  3113. for entry in directory_parsing:
  3114. try:
  3115. directory_index = DIRECTORY_ENTRY[entry[0]]
  3116. dir_entry = self.OPTIONAL_HEADER.DATA_DIRECTORY[directory_index]
  3117. except IndexError:
  3118. break
  3119. # Only process all the directories if no individual ones have
  3120. # been chosen
  3121. #
  3122. if directories is None or directory_index in directories:
  3123. value = None
  3124. if dir_entry.VirtualAddress:
  3125. if (
  3126. forwarded_exports_only
  3127. and entry[0] == "IMAGE_DIRECTORY_ENTRY_EXPORT"
  3128. ):
  3129. value = entry[1](
  3130. dir_entry.VirtualAddress,
  3131. dir_entry.Size,
  3132. forwarded_only=True,
  3133. )
  3134. elif (
  3135. import_dllnames_only
  3136. and entry[0] == "IMAGE_DIRECTORY_ENTRY_IMPORT"
  3137. ):
  3138. value = entry[1](
  3139. dir_entry.VirtualAddress, dir_entry.Size, dllnames_only=True
  3140. )
  3141. else:
  3142. try:
  3143. value = entry[1](dir_entry.VirtualAddress, dir_entry.Size)
  3144. except PEFormatError as excp:
  3145. self.__warnings.append(
  3146. f'Failed to process directory "{entry[0]}": {excp}'
  3147. )
  3148. if value:
  3149. setattr(self, entry[0][6:], value)
  3150. if (
  3151. (directories is not None)
  3152. and isinstance(directories, list)
  3153. and (entry[0] in directories)
  3154. ):
  3155. directories.remove(directory_index)
  3156. def parse_exceptions_directory(self, rva, size):
  3157. """Parses exception directory
  3158. All the code related to handling exception directories is documented in
  3159. https://auscitte.github.io/systems%20blog/Exception-Directory-pefile#implementation-details
  3160. """
  3161. # "For x64 and Itanium platforms; the format is different for other platforms"
  3162. if (
  3163. self.FILE_HEADER.Machine != MACHINE_TYPE["IMAGE_FILE_MACHINE_AMD64"]
  3164. and self.FILE_HEADER.Machine != MACHINE_TYPE["IMAGE_FILE_MACHINE_IA64"]
  3165. ):
  3166. return None
  3167. rf = Structure(self.__RUNTIME_FUNCTION_format__)
  3168. rf_size = rf.sizeof()
  3169. rva2rt = {}
  3170. rt_funcs = []
  3171. rva2infos = {}
  3172. for _ in range(size // rf_size):
  3173. rf = self.__unpack_data__(
  3174. self.__RUNTIME_FUNCTION_format__,
  3175. self.get_data(rva, rf_size),
  3176. file_offset=self.get_offset_from_rva(rva),
  3177. )
  3178. if rf is None:
  3179. break
  3180. ui = None
  3181. if (rf.UnwindData & 0x1) == 0:
  3182. # according to "Improving Automated Analysis of Windows x64 Binaries",
  3183. # if the lowest bit is set, (UnwindData & ~0x1) should point to the
  3184. # chained RUNTIME_FUNCTION instead of UNWIND_INFO
  3185. if (
  3186. rf.UnwindData in rva2infos
  3187. ): # unwind info data structures can be shared among functions
  3188. ui = rva2infos[rf.UnwindData]
  3189. else:
  3190. ui = UnwindInfo(file_offset=self.get_offset_from_rva(rf.UnwindData))
  3191. rva2infos[rf.UnwindData] = ui
  3192. ws = ui.unpack_in_stages(self.get_data(rf.UnwindData, ui.sizeof()))
  3193. if ws != None:
  3194. self.__warnings.append(ws)
  3195. break
  3196. ws = ui.unpack_in_stages(self.get_data(rf.UnwindData, ui.sizeof()))
  3197. if ws != None:
  3198. self.__warnings.append(ws)
  3199. break
  3200. self.__structures__.append(ui)
  3201. entry = ExceptionsDirEntryData(struct=rf, unwindinfo=ui)
  3202. rt_funcs.append(entry)
  3203. rva2rt[rf.BeginAddress] = entry
  3204. rva += rf_size
  3205. # each chained function entry holds a reference to the function first in chain
  3206. for rf in rt_funcs:
  3207. if rf.unwindinfo is None:
  3208. # TODO: have not encountered such a binary yet;
  3209. # in theory, (UnwindData & ~0x1) should point to the chained
  3210. # RUNTIME_FUNCTION which could be used to locate the corresponding
  3211. # ExceptionsDirEntryData and set_chained_function_entry()
  3212. continue
  3213. if not hasattr(rf.unwindinfo, "FunctionEntry"):
  3214. continue
  3215. if rf.unwindinfo.FunctionEntry not in rva2rt:
  3216. self.__warnings.append(
  3217. f"FunctionEntry of UNWIND_INFO at {rf.struct.get_file_offset():x}"
  3218. " points to an entry that does not exist"
  3219. )
  3220. continue
  3221. try:
  3222. rf.unwindinfo.set_chained_function_entry(
  3223. rva2rt[rf.unwindinfo.FunctionEntry]
  3224. )
  3225. except PEFormatError as excp:
  3226. self.__warnings.append(
  3227. "Failed parsing FunctionEntry of UNWIND_INFO at "
  3228. f"{rf.struct.get_file_offset():x}: {excp}"
  3229. )
  3230. continue
  3231. return rt_funcs
  3232. def parse_directory_bound_imports(self, rva, size):
  3233. """"""
  3234. bnd_descr = Structure(self.__IMAGE_BOUND_IMPORT_DESCRIPTOR_format__)
  3235. bnd_descr_size = bnd_descr.sizeof()
  3236. start = rva
  3237. bound_imports = []
  3238. while True:
  3239. bnd_descr = self.__unpack_data__(
  3240. self.__IMAGE_BOUND_IMPORT_DESCRIPTOR_format__,
  3241. self.__data__[rva : rva + bnd_descr_size],
  3242. file_offset=rva,
  3243. )
  3244. if bnd_descr is None:
  3245. # If can't parse directory then silently return.
  3246. # This directory does not necessarily have to be valid to
  3247. # still have a valid PE file
  3248. self.__warnings.append(
  3249. "The Bound Imports directory exists but can't be parsed."
  3250. )
  3251. return
  3252. if bnd_descr.all_zeroes():
  3253. break
  3254. rva += bnd_descr.sizeof()
  3255. section = self.get_section_by_offset(rva)
  3256. file_offset = self.get_offset_from_rva(rva)
  3257. if section is None:
  3258. safety_boundary = len(self.__data__) - file_offset
  3259. sections_after_offset = [
  3260. s.PointerToRawData
  3261. for s in self.sections
  3262. if s.PointerToRawData > file_offset
  3263. ]
  3264. if sections_after_offset:
  3265. # Find the first section starting at a later offset than that
  3266. # specified by 'rva'
  3267. first_section_after_offset = min(sections_after_offset)
  3268. section = self.get_section_by_offset(first_section_after_offset)
  3269. if section is not None:
  3270. safety_boundary = section.PointerToRawData - file_offset
  3271. else:
  3272. safety_boundary = (
  3273. section.PointerToRawData + len(section.get_data()) - file_offset
  3274. )
  3275. if not section:
  3276. self.__warnings.append(
  3277. (
  3278. "RVA of IMAGE_BOUND_IMPORT_DESCRIPTOR points "
  3279. "to an invalid address: {0:x}"
  3280. ).format(rva)
  3281. )
  3282. return
  3283. forwarder_refs = []
  3284. # 8 is the size of __IMAGE_BOUND_IMPORT_DESCRIPTOR_format__
  3285. for _ in range(
  3286. min(bnd_descr.NumberOfModuleForwarderRefs, int(safety_boundary / 8))
  3287. ):
  3288. # Both structures IMAGE_BOUND_IMPORT_DESCRIPTOR and
  3289. # IMAGE_BOUND_FORWARDER_REF have the same size.
  3290. bnd_frwd_ref = self.__unpack_data__(
  3291. self.__IMAGE_BOUND_FORWARDER_REF_format__,
  3292. self.__data__[rva : rva + bnd_descr_size],
  3293. file_offset=rva,
  3294. )
  3295. if not bnd_frwd_ref:
  3296. raise PEFormatError("IMAGE_BOUND_FORWARDER_REF cannot be read")
  3297. rva += bnd_frwd_ref.sizeof()
  3298. offset = start + bnd_frwd_ref.OffsetModuleName
  3299. name_str = self.get_string_from_data(
  3300. 0, self.__data__[offset : offset + MAX_STRING_LENGTH]
  3301. )
  3302. # OffsetModuleName points to a DLL name. These shouldn't be too long.
  3303. # Anything longer than a safety length of 128 will be taken to indicate
  3304. # a corrupt entry and abort the processing of these entries.
  3305. # Names shorter than 4 characters will be taken as invalid as well.
  3306. if name_str:
  3307. invalid_chars = [
  3308. c for c in bytearray(name_str) if chr(c) not in string.printable
  3309. ]
  3310. if len(name_str) > 256 or invalid_chars:
  3311. break
  3312. forwarder_refs.append(
  3313. BoundImportRefData(struct=bnd_frwd_ref, name=name_str)
  3314. )
  3315. offset = start + bnd_descr.OffsetModuleName
  3316. name_str = self.get_string_from_data(
  3317. 0, self.__data__[offset : offset + MAX_STRING_LENGTH]
  3318. )
  3319. if name_str:
  3320. invalid_chars = [
  3321. c for c in bytearray(name_str) if chr(c) not in string.printable
  3322. ]
  3323. if len(name_str) > 256 or invalid_chars:
  3324. break
  3325. if not name_str:
  3326. break
  3327. bound_imports.append(
  3328. BoundImportDescData(
  3329. struct=bnd_descr, name=name_str, entries=forwarder_refs
  3330. )
  3331. )
  3332. return bound_imports
  3333. def parse_directory_tls(self, rva, size):
  3334. """"""
  3335. # By default let's pretend the format is a 32-bit PE. It may help
  3336. # produce some output for files where the Magic in the Optional Header
  3337. # is incorrect.
  3338. format = self.__IMAGE_TLS_DIRECTORY_format__
  3339. if self.PE_TYPE == OPTIONAL_HEADER_MAGIC_PE_PLUS:
  3340. format = self.__IMAGE_TLS_DIRECTORY64_format__
  3341. try:
  3342. tls_struct = self.__unpack_data__(
  3343. format,
  3344. self.get_data(rva, Structure(format).sizeof()),
  3345. file_offset=self.get_offset_from_rva(rva),
  3346. )
  3347. except PEFormatError:
  3348. self.__warnings.append(
  3349. "Invalid TLS information. Can't read " "data at RVA: 0x%x" % rva
  3350. )
  3351. tls_struct = None
  3352. if not tls_struct:
  3353. return None
  3354. return TlsData(struct=tls_struct)
  3355. def parse_directory_load_config(self, rva, size):
  3356. """"""
  3357. if self.PE_TYPE == OPTIONAL_HEADER_MAGIC_PE:
  3358. load_config_dir_sz = self.get_dword_at_rva(rva)
  3359. format = self.__IMAGE_LOAD_CONFIG_DIRECTORY_format__
  3360. elif self.PE_TYPE == OPTIONAL_HEADER_MAGIC_PE_PLUS:
  3361. load_config_dir_sz = self.get_dword_at_rva(rva)
  3362. format = self.__IMAGE_LOAD_CONFIG_DIRECTORY64_format__
  3363. else:
  3364. self.__warnings.append(
  3365. "Don't know how to parse LOAD_CONFIG information for non-PE32/"
  3366. "PE32+ file"
  3367. )
  3368. return None
  3369. # load config directory size can be less than represented by 'format' variable,
  3370. # generate truncated format which correspond load config directory size
  3371. fields_counter = 0
  3372. cumulative_sz = 0
  3373. for field in format[1]:
  3374. fields_counter += 1
  3375. cumulative_sz += STRUCT_SIZEOF_TYPES[field.split(",")[0]]
  3376. if cumulative_sz == load_config_dir_sz:
  3377. break
  3378. format = (format[0], format[1][:fields_counter])
  3379. load_config_struct = None
  3380. try:
  3381. load_config_struct = self.__unpack_data__(
  3382. format,
  3383. self.get_data(rva, Structure(format).sizeof()),
  3384. file_offset=self.get_offset_from_rva(rva),
  3385. )
  3386. except PEFormatError:
  3387. self.__warnings.append(
  3388. "Invalid LOAD_CONFIG information. Can't read " "data at RVA: 0x%x" % rva
  3389. )
  3390. if not load_config_struct:
  3391. return None
  3392. dynamic_relocations = None
  3393. if fields_counter > 35:
  3394. dynamic_relocations = self.parse_dynamic_relocations(
  3395. load_config_struct.DynamicValueRelocTableOffset,
  3396. load_config_struct.DynamicValueRelocTableSection,
  3397. )
  3398. return LoadConfigData(
  3399. struct=load_config_struct, dynamic_relocations=dynamic_relocations
  3400. )
  3401. def parse_dynamic_relocations(
  3402. self, dynamic_value_reloc_table_offset, dynamic_value_reloc_table_section
  3403. ):
  3404. if not dynamic_value_reloc_table_offset:
  3405. return None
  3406. if not dynamic_value_reloc_table_section:
  3407. return None
  3408. if dynamic_value_reloc_table_section > len(self.sections):
  3409. return None
  3410. section = self.sections[dynamic_value_reloc_table_section - 1]
  3411. rva = section.VirtualAddress + dynamic_value_reloc_table_offset
  3412. image_dynamic_reloc_table_struct = None
  3413. reloc_table_size = Structure(
  3414. self.__IMAGE_DYNAMIC_RELOCATION_TABLE_format__
  3415. ).sizeof()
  3416. try:
  3417. image_dynamic_reloc_table_struct = self.__unpack_data__(
  3418. self.__IMAGE_DYNAMIC_RELOCATION_TABLE_format__,
  3419. self.get_data(rva, reloc_table_size),
  3420. file_offset=self.get_offset_from_rva(rva),
  3421. )
  3422. except PEFormatError:
  3423. self.__warnings.append(
  3424. "Invalid IMAGE_DYNAMIC_RELOCATION_TABLE information. Can't read "
  3425. "data at RVA: 0x%x" % rva
  3426. )
  3427. return None
  3428. if image_dynamic_reloc_table_struct.Version != 1:
  3429. self.__warnings.append(
  3430. "No parsing available for IMAGE_DYNAMIC_RELOCATION_TABLE.Version = %d"
  3431. % image_dynamic_reloc_table_struct.Version
  3432. )
  3433. return None
  3434. rva += reloc_table_size
  3435. end = rva + image_dynamic_reloc_table_struct.Size
  3436. dynamic_relocations = []
  3437. while rva < end:
  3438. format = self.__IMAGE_DYNAMIC_RELOCATION_format__
  3439. if self.PE_TYPE == OPTIONAL_HEADER_MAGIC_PE_PLUS:
  3440. format = self.__IMAGE_DYNAMIC_RELOCATION64_format__
  3441. rlc_size = Structure(format).sizeof()
  3442. try:
  3443. dynamic_rlc = self.__unpack_data__(
  3444. format,
  3445. self.get_data(rva, rlc_size),
  3446. file_offset=self.get_offset_from_rva(rva),
  3447. )
  3448. except PEFormatError:
  3449. self.__warnings.append(
  3450. "Invalid relocation information. Can't read "
  3451. "data at RVA: 0x%x" % rva
  3452. )
  3453. dynamic_rlc = None
  3454. if not dynamic_rlc:
  3455. break
  3456. rva += rlc_size
  3457. symbol = dynamic_rlc.Symbol
  3458. size = dynamic_rlc.BaseRelocSize
  3459. if 3 <= symbol <= 5:
  3460. relocations = self.parse_image_base_relocation_list(
  3461. rva, size, self.dynamic_relocation_format_by_symbol[symbol]
  3462. )
  3463. dynamic_relocations.append(
  3464. DynamicRelocationData(
  3465. struct=dynamic_rlc, symbol=symbol, relocations=relocations
  3466. )
  3467. )
  3468. elif symbol == 7:
  3469. func_relocs, bdd_relocs = self.parse_function_override_data(rva)
  3470. dynamic_relocations.append(
  3471. FunctionOverrideData(
  3472. struct=dynamic_rlc,
  3473. symbol=symbol,
  3474. bdd_relocs=bdd_relocs,
  3475. func_relocs=func_relocs,
  3476. )
  3477. )
  3478. elif symbol > 5:
  3479. relocations = self.parse_image_base_relocation_list(rva, size)
  3480. dynamic_relocations.append(
  3481. DynamicRelocationData(
  3482. struct=dynamic_rlc, symbol=symbol, relocations=relocations
  3483. )
  3484. )
  3485. rva += size
  3486. return dynamic_relocations
  3487. def parse_function_override_data(self, rva):
  3488. """"""
  3489. func_relocs = []
  3490. bdd_relocs = []
  3491. format = self.__IMAGE_FUNCTION_OVERRIDE_HEADER_format__
  3492. func_header = self.__unpack_data__(
  3493. format,
  3494. self.get_data(rva, Structure(format).sizeof()),
  3495. self.get_offset_from_rva(rva),
  3496. )
  3497. if not func_header:
  3498. self.__warnings.append(
  3499. "Invalid function override header. Can't read "
  3500. "data at RVA: 0x%x" % rva
  3501. )
  3502. return func_relocs, bdd_relocs
  3503. rva += Structure(format).sizeof()
  3504. func_end = rva + func_header.FuncOverrideSize
  3505. while rva < func_end:
  3506. format = self.__IMAGE_FUNCTION_OVERRIDE_DYNAMIC_RELOCATION_format__
  3507. func_info = self.__unpack_data__(
  3508. format,
  3509. self.get_data(rva, Structure(format).sizeof()),
  3510. self.get_offset_from_rva(rva),
  3511. )
  3512. if not func_info:
  3513. self.__warnings.append(
  3514. "Invalid function override info. Can't read "
  3515. "data at RVA: 0x%x" % rva
  3516. )
  3517. return func_relocs, bdd_relocs
  3518. rva += Structure(format).sizeof()
  3519. override_rvas = []
  3520. for i in range(func_info.RvaSize // 4):
  3521. override_rvas.append(struct.unpack("<I", self.get_data(rva, 4))[0])
  3522. rva += 4
  3523. relocations = self.parse_image_base_relocation_list(
  3524. rva, func_info.BaseRelocSize
  3525. )
  3526. rva += func_info.BaseRelocSize
  3527. func_relocs.append(
  3528. FunctionOverrideDynamicRelocationData(
  3529. struct=func_info,
  3530. func_rva=func_info.OriginalRva,
  3531. override_rvas=override_rvas,
  3532. relocations=relocations,
  3533. )
  3534. )
  3535. format = self.__IMAGE_BDD_INFO_format__
  3536. bdd_info = self.__unpack_data__(
  3537. format,
  3538. self.get_data(rva, Structure(format).sizeof()),
  3539. self.get_offset_from_rva(rva),
  3540. )
  3541. if not bdd_info:
  3542. self.__warnings.append(
  3543. "Invalid bdd info. Can't read " "data at RVA: 0x%x" % rva
  3544. )
  3545. return func_relocs, bdd_relocs
  3546. rva += Structure(format).sizeof()
  3547. for i in range(bdd_info.BDDSize // 8):
  3548. format = self.__IMAGE_BDD_DYNAMIC_RELOCATION_format__
  3549. bdd_reloc = self.__unpack_data__(
  3550. format,
  3551. self.get_data(rva, Structure(format).sizeof()),
  3552. self.get_offset_from_rva(rva),
  3553. )
  3554. if not bdd_reloc:
  3555. self.__warnings.append(
  3556. "Invalid bdd dynamic relocation. Can't read "
  3557. "data at RVA: 0x%x" % rva
  3558. )
  3559. return func_relocs, bdd_relocs
  3560. rva += Structure(format).sizeof()
  3561. bdd_relocs.append(BddDynamicRelocationData(struct=bdd_reloc))
  3562. return func_relocs, bdd_relocs
  3563. def parse_relocations_directory(self, rva, size):
  3564. """"""
  3565. return self.parse_image_base_relocation_list(rva, size)
  3566. def parse_image_base_relocation_list(self, rva, size, fmt=None):
  3567. rlc_size = Structure(self.__IMAGE_BASE_RELOCATION_format__).sizeof()
  3568. end = rva + size
  3569. relocations = []
  3570. while rva < end:
  3571. # Malware that has bad RVA entries will cause an error.
  3572. # Just continue on after an exception
  3573. #
  3574. try:
  3575. rlc = self.__unpack_data__(
  3576. self.__IMAGE_BASE_RELOCATION_format__,
  3577. self.get_data(rva, rlc_size),
  3578. file_offset=self.get_offset_from_rva(rva),
  3579. )
  3580. except PEFormatError:
  3581. self.__warnings.append(
  3582. "Invalid relocation information. Can't read "
  3583. "data at RVA: 0x%x" % rva
  3584. )
  3585. rlc = None
  3586. if not rlc:
  3587. break
  3588. # rlc.VirtualAddress must lie within the Image
  3589. if rlc.VirtualAddress > self.OPTIONAL_HEADER.SizeOfImage:
  3590. self.__warnings.append(
  3591. "Invalid relocation information. VirtualAddress outside"
  3592. " of Image: 0x%x" % rlc.VirtualAddress
  3593. )
  3594. break
  3595. # rlc.SizeOfBlock must be less or equal than the size of the image
  3596. # (It's a rather loose sanity test)
  3597. if rlc.SizeOfBlock > self.OPTIONAL_HEADER.SizeOfImage:
  3598. self.__warnings.append(
  3599. "Invalid relocation information. SizeOfBlock too large"
  3600. ": %d" % rlc.SizeOfBlock
  3601. )
  3602. break
  3603. if fmt is None:
  3604. reloc_entries = self.parse_relocations(
  3605. rva + rlc_size, rlc.VirtualAddress, rlc.SizeOfBlock - rlc_size
  3606. )
  3607. else:
  3608. reloc_entries = self.parse_relocations_with_format(
  3609. rva + rlc_size, rlc.VirtualAddress, rlc.SizeOfBlock - rlc_size, fmt
  3610. )
  3611. relocations.append(BaseRelocationData(struct=rlc, entries=reloc_entries))
  3612. if not rlc.SizeOfBlock:
  3613. break
  3614. rva += rlc.SizeOfBlock
  3615. return relocations
  3616. def parse_relocations(self, data_rva, rva, size):
  3617. """"""
  3618. try:
  3619. data = self.get_data(data_rva, size)
  3620. file_offset = self.get_offset_from_rva(data_rva)
  3621. except PEFormatError:
  3622. self.__warnings.append(f"Bad RVA in relocation data: 0x{data_rva:x}")
  3623. return []
  3624. entries = []
  3625. offsets_and_type = set()
  3626. for idx in range(len(data) // 2):
  3627. entry = self.__unpack_data__(
  3628. self.__IMAGE_BASE_RELOCATION_ENTRY_format__,
  3629. data[idx * 2 : (idx + 1) * 2],
  3630. file_offset=file_offset,
  3631. )
  3632. if not entry:
  3633. break
  3634. word = entry.Data
  3635. reloc_type = word >> 12
  3636. reloc_offset = word & 0x0FFF
  3637. if (reloc_offset, reloc_type) in offsets_and_type:
  3638. self.__warnings.append(
  3639. "Overlapping offsets in relocation data "
  3640. "at RVA: 0x%x" % (reloc_offset + rva)
  3641. )
  3642. break
  3643. offsets_and_type.add((reloc_offset, reloc_type))
  3644. entries.append(
  3645. RelocationData(
  3646. struct=entry, type=reloc_type, base_rva=rva, rva=reloc_offset + rva
  3647. )
  3648. )
  3649. file_offset += entry.sizeof()
  3650. return entries
  3651. def parse_relocations_with_format(self, data_rva, rva, size, format):
  3652. """"""
  3653. try:
  3654. data = self.get_data(data_rva, size)
  3655. file_offset = self.get_offset_from_rva(data_rva)
  3656. except PEFormatError:
  3657. self.__warnings.append(f"Bad RVA in relocation data: 0x{data_rva:x}")
  3658. return []
  3659. entry_size = StructureWithBitfields(format).sizeof()
  3660. entries = []
  3661. offsets = set()
  3662. for idx in range(len(data) // entry_size):
  3663. entry = self.__unpack_data_with_bitfields__(
  3664. format,
  3665. data[idx * entry_size : (idx + 1) * entry_size],
  3666. file_offset=file_offset,
  3667. )
  3668. if not entry:
  3669. break
  3670. reloc_offset = entry.PageRelativeOffset
  3671. if reloc_offset in offsets:
  3672. self.__warnings.append(
  3673. "Overlapping offsets in relocation data "
  3674. "at RVA: 0x%x" % (reloc_offset + rva)
  3675. )
  3676. break
  3677. offsets.add(reloc_offset)
  3678. entries.append(
  3679. RelocationData(struct=entry, base_rva=rva, rva=reloc_offset + rva)
  3680. )
  3681. file_offset += entry_size
  3682. return entries
  3683. def parse_debug_directory(self, rva, size):
  3684. """"""
  3685. dbg_size = Structure(self.__IMAGE_DEBUG_DIRECTORY_format__).sizeof()
  3686. debug = []
  3687. for idx in range(int(size / dbg_size)):
  3688. try:
  3689. data = self.get_data(rva + dbg_size * idx, dbg_size)
  3690. except PEFormatError:
  3691. self.__warnings.append(
  3692. "Invalid debug information. Can't read " "data at RVA: 0x%x" % rva
  3693. )
  3694. return None
  3695. dbg = self.__unpack_data__(
  3696. self.__IMAGE_DEBUG_DIRECTORY_format__,
  3697. data,
  3698. file_offset=self.get_offset_from_rva(rva + dbg_size * idx),
  3699. )
  3700. if not dbg:
  3701. return None
  3702. # apply structure according to DEBUG_TYPE
  3703. # https://www.debuginfo.com/articles/debuginfomatch.html
  3704. #
  3705. dbg_type = None
  3706. if dbg.Type == 1:
  3707. # IMAGE_DEBUG_TYPE_COFF
  3708. pass
  3709. elif dbg.Type == 2:
  3710. # if IMAGE_DEBUG_TYPE_CODEVIEW
  3711. dbg_type_offset = dbg.PointerToRawData
  3712. dbg_type_size = dbg.SizeOfData
  3713. dbg_type_data = self.__data__[
  3714. dbg_type_offset : dbg_type_offset + dbg_type_size
  3715. ]
  3716. if dbg_type_data[:4] == b"RSDS":
  3717. # pdb7.0
  3718. __CV_INFO_PDB70_format__ = [
  3719. "CV_INFO_PDB70",
  3720. [
  3721. "4s,CvSignature",
  3722. "I,Signature_Data1", # Signature is of GUID type
  3723. "H,Signature_Data2",
  3724. "H,Signature_Data3",
  3725. "B,Signature_Data4",
  3726. "B,Signature_Data5",
  3727. "6s,Signature_Data6",
  3728. "I,Age",
  3729. ],
  3730. ]
  3731. pdbFileName_size = (
  3732. dbg_type_size - Structure(__CV_INFO_PDB70_format__).sizeof()
  3733. )
  3734. # pdbFileName_size can be negative here, as seen in the malware
  3735. # sample with hash
  3736. # MD5: 7c297600870d026c014d42596bb9b5fd
  3737. # SHA256:
  3738. # 83f4e63681fcba8a9d7bbb1688c71981b1837446514a1773597e0192bba9fac3
  3739. # Checking for positive size here to ensure proper parsing.
  3740. if pdbFileName_size > 0:
  3741. __CV_INFO_PDB70_format__[1].append(
  3742. "{0}s,PdbFileName".format(pdbFileName_size)
  3743. )
  3744. dbg_type = self.__unpack_data__(
  3745. __CV_INFO_PDB70_format__, dbg_type_data, dbg_type_offset
  3746. )
  3747. if dbg_type is not None:
  3748. dbg_type.Signature_Data6_value = struct.unpack(
  3749. ">Q", b"\0\0" + dbg_type.Signature_Data6
  3750. )[0]
  3751. dbg_type.Signature_String = (
  3752. str(
  3753. uuid.UUID(
  3754. fields=(
  3755. dbg_type.Signature_Data1,
  3756. dbg_type.Signature_Data2,
  3757. dbg_type.Signature_Data3,
  3758. dbg_type.Signature_Data4,
  3759. dbg_type.Signature_Data5,
  3760. dbg_type.Signature_Data6_value,
  3761. )
  3762. )
  3763. )
  3764. .replace("-", "")
  3765. .upper()
  3766. + f"{dbg_type.Age:X}"
  3767. )
  3768. elif dbg_type_data[:4] == b"NB10":
  3769. # pdb2.0
  3770. __CV_INFO_PDB20_format__ = [
  3771. "CV_INFO_PDB20",
  3772. [
  3773. "I,CvHeaderSignature",
  3774. "I,CvHeaderOffset",
  3775. "I,Signature",
  3776. "I,Age",
  3777. ],
  3778. ]
  3779. pdbFileName_size = (
  3780. dbg_type_size - Structure(__CV_INFO_PDB20_format__).sizeof()
  3781. )
  3782. # As with the PDB 7.0 case, ensuring a positive size for
  3783. # pdbFileName_size to ensure proper parsing.
  3784. if pdbFileName_size > 0:
  3785. # Add the last variable-length string field.
  3786. __CV_INFO_PDB20_format__[1].append(
  3787. "{0}s,PdbFileName".format(pdbFileName_size)
  3788. )
  3789. dbg_type = self.__unpack_data__(
  3790. __CV_INFO_PDB20_format__, dbg_type_data, dbg_type_offset
  3791. )
  3792. elif dbg.Type == 4:
  3793. # IMAGE_DEBUG_TYPE_MISC
  3794. dbg_type_offset = dbg.PointerToRawData
  3795. dbg_type_size = dbg.SizeOfData
  3796. dbg_type_data = self.__data__[
  3797. dbg_type_offset : dbg_type_offset + dbg_type_size
  3798. ]
  3799. ___IMAGE_DEBUG_MISC_format__ = [
  3800. "IMAGE_DEBUG_MISC",
  3801. [
  3802. "I,DataType",
  3803. "I,Length",
  3804. "B,Unicode",
  3805. "B,Reserved1",
  3806. "H,Reserved2",
  3807. ],
  3808. ]
  3809. dbg_type_partial = self.__unpack_data__(
  3810. ___IMAGE_DEBUG_MISC_format__, dbg_type_data, dbg_type_offset
  3811. )
  3812. # Need to check that dbg_type_partial contains a correctly unpacked data
  3813. # structure, as the malware sample with the following hash
  3814. # MD5: 5e7d6707d693108de5a303045c17d95b
  3815. # SHA256:
  3816. # 5dd94a95025f3b6e3dd440d52f7c6d2964fdd1aa119e0ee92e38c7bf83829e5c
  3817. # contains a value of None for dbg_type_partial after unpacking,
  3818. # presumably due to a malformed DEBUG entry.
  3819. if dbg_type_partial:
  3820. # The Unicode bool should be set to 0 or 1.
  3821. if dbg_type_partial.Unicode in (0, 1):
  3822. data_size = (
  3823. dbg_type_size
  3824. - Structure(___IMAGE_DEBUG_MISC_format__).sizeof()
  3825. )
  3826. # As with the PDB case, ensuring a positive size for data_size
  3827. # here to ensure proper parsing.
  3828. if data_size > 0:
  3829. ___IMAGE_DEBUG_MISC_format__[1].append(
  3830. "{0}s,Data".format(data_size)
  3831. )
  3832. dbg_type = self.__unpack_data__(
  3833. ___IMAGE_DEBUG_MISC_format__, dbg_type_data, dbg_type_offset
  3834. )
  3835. elif dbg.Type == 20:
  3836. # IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS
  3837. dbg_type_offset = dbg.PointerToRawData
  3838. dbg_type_size = dbg.SizeOfData
  3839. dbg_type_data = self.__data__[
  3840. dbg_type_offset : dbg_type_offset + dbg_type_size
  3841. ]
  3842. # Note: the names for these formats and structure members are made up.
  3843. # They are not documented properly.
  3844. ___IMAGE_DEBUG_EX_DLLCHARACTERISTICS_format__ = [
  3845. "IMAGE_DEBUG_EX_DLLCHARACTERISTICS",
  3846. [
  3847. "I,ExDllCharacteristics",
  3848. ],
  3849. ]
  3850. dbg_type = self.__unpack_data__(
  3851. ___IMAGE_DEBUG_EX_DLLCHARACTERISTICS_format__,
  3852. dbg_type_data,
  3853. dbg_type_offset,
  3854. )
  3855. ex_dll_characteristics_flags = retrieve_flags(
  3856. EX_DLL_CHARACTERISTICS, "IMAGE_DLLCHARACTERISTICS_EX_"
  3857. )
  3858. set_flags(
  3859. dbg_type,
  3860. dbg_type.ExDllCharacteristics,
  3861. ex_dll_characteristics_flags,
  3862. )
  3863. debug.append(DebugData(struct=dbg, entry=dbg_type))
  3864. return debug
  3865. def parse_resources_directory(self, rva, size=0, base_rva=None, level=0, dirs=None):
  3866. """Parse the resources directory.
  3867. Given the RVA of the resources directory, it will process all
  3868. its entries.
  3869. The root will have the corresponding member of its structure,
  3870. IMAGE_RESOURCE_DIRECTORY plus 'entries', a list of all the
  3871. entries in the directory.
  3872. Those entries will have, correspondingly, all the structure's
  3873. members (IMAGE_RESOURCE_DIRECTORY_ENTRY) and an additional one,
  3874. "directory", pointing to the IMAGE_RESOURCE_DIRECTORY structure
  3875. representing upper layers of the tree. This one will also have
  3876. an 'entries' attribute, pointing to the 3rd, and last, level.
  3877. Another directory with more entries. Those last entries will
  3878. have a new attribute (both 'leaf' or 'data_entry' can be used to
  3879. access it). This structure finally points to the resource data.
  3880. All the members of this structure, IMAGE_RESOURCE_DATA_ENTRY,
  3881. are available as its attributes.
  3882. """
  3883. if dirs is None:
  3884. dirs = [rva]
  3885. if base_rva is None:
  3886. base_rva = rva
  3887. if level > MAX_RESOURCE_DEPTH:
  3888. self.__warnings.append(
  3889. "Error parsing the resources directory. "
  3890. "Excessively nested table depth %d (>%s)" % (level, MAX_RESOURCE_DEPTH)
  3891. )
  3892. return None
  3893. try:
  3894. # If the RVA is invalid all would blow up. Some EXEs seem to be
  3895. # specially nasty and have an invalid RVA.
  3896. data = self.get_data(
  3897. rva, Structure(self.__IMAGE_RESOURCE_DIRECTORY_format__).sizeof()
  3898. )
  3899. except PEFormatError:
  3900. self.__warnings.append(
  3901. "Invalid resources directory. Can't read "
  3902. "directory data at RVA: 0x%x" % rva
  3903. )
  3904. return None
  3905. # Get the resource directory structure, that is, the header
  3906. # of the table preceding the actual entries
  3907. #
  3908. resource_dir = self.__unpack_data__(
  3909. self.__IMAGE_RESOURCE_DIRECTORY_format__,
  3910. data,
  3911. file_offset=self.get_offset_from_rva(rva),
  3912. )
  3913. if resource_dir is None:
  3914. # If we can't parse resources directory then silently return.
  3915. # This directory does not necessarily have to be valid to
  3916. # still have a valid PE file
  3917. self.__warnings.append(
  3918. "Invalid resources directory. Can't parse "
  3919. "directory data at RVA: 0x%x" % rva
  3920. )
  3921. return None
  3922. dir_entries = []
  3923. # Advance the RVA to the position immediately following the directory
  3924. # table header and pointing to the first entry in the table
  3925. #
  3926. rva += resource_dir.sizeof()
  3927. number_of_entries = (
  3928. resource_dir.NumberOfNamedEntries + resource_dir.NumberOfIdEntries
  3929. )
  3930. # Set a hard limit on the maximum reasonable number of entries
  3931. MAX_ALLOWED_ENTRIES = 4096
  3932. if number_of_entries > MAX_ALLOWED_ENTRIES:
  3933. self.__warnings.append(
  3934. "Error parsing the resources directory. "
  3935. "The directory contains %d entries (>%s)"
  3936. % (number_of_entries, MAX_ALLOWED_ENTRIES)
  3937. )
  3938. return None
  3939. self.__total_resource_entries_count += number_of_entries
  3940. if self.__total_resource_entries_count > MAX_RESOURCE_ENTRIES:
  3941. self.__warnings.append(
  3942. "Error parsing the resources directory. "
  3943. "The file contains at least %d entries (>%d)"
  3944. % (self.__total_resource_entries_count, MAX_RESOURCE_ENTRIES)
  3945. )
  3946. return None
  3947. strings_to_postprocess = []
  3948. # Keep track of the last name's start and end offsets in order
  3949. # to be able to detect overlapping entries that might suggest
  3950. # and invalid or corrupt directory.
  3951. last_name_begin_end = None
  3952. for idx in range(number_of_entries):
  3953. if (
  3954. not self.__resource_size_limit_reached
  3955. and self.__total_resource_bytes > self.__resource_size_limit_upperbounds
  3956. ):
  3957. self.__resource_size_limit_reached = True
  3958. self.__warnings.append(
  3959. "Resource size 0x%x exceeds file size 0x%x, overlapping "
  3960. "resources found."
  3961. % (
  3962. self.__total_resource_bytes,
  3963. self.__resource_size_limit_upperbounds,
  3964. )
  3965. )
  3966. res = self.parse_resource_entry(rva)
  3967. if res is None:
  3968. self.__warnings.append(
  3969. "Error parsing the resources directory, "
  3970. "Entry %d is invalid, RVA = 0x%x. " % (idx, rva)
  3971. )
  3972. break
  3973. entry_name = None
  3974. entry_id = None
  3975. name_is_string = (res.Name & 0x80000000) >> 31
  3976. if not name_is_string:
  3977. entry_id = res.Name
  3978. else:
  3979. ustr_offset = base_rva + res.NameOffset
  3980. try:
  3981. entry_name = UnicodeStringWrapperPostProcessor(self, ustr_offset)
  3982. self.__total_resource_bytes += entry_name.get_pascal_16_length()
  3983. # If the last entry's offset points before the current's but its end
  3984. # is past the current's beginning, assume the overlap indicates a
  3985. # corrupt name.
  3986. if last_name_begin_end and (
  3987. last_name_begin_end[0] < ustr_offset
  3988. and last_name_begin_end[1] >= ustr_offset
  3989. ):
  3990. # Remove the previous overlapping entry as it's likely to be
  3991. # already corrupt data.
  3992. strings_to_postprocess.pop()
  3993. self.__warnings.append(
  3994. "Error parsing the resources directory, "
  3995. "attempting to read entry name. "
  3996. "Entry names overlap 0x%x" % (ustr_offset)
  3997. )
  3998. break
  3999. last_name_begin_end = (
  4000. ustr_offset,
  4001. ustr_offset + entry_name.get_pascal_16_length(),
  4002. )
  4003. strings_to_postprocess.append(entry_name)
  4004. except PEFormatError:
  4005. self.__warnings.append(
  4006. "Error parsing the resources directory, "
  4007. "attempting to read entry name. "
  4008. "Can't read unicode string at offset 0x%x" % (ustr_offset)
  4009. )
  4010. if res.DataIsDirectory:
  4011. # One trick malware can do is to recursively reference
  4012. # the next directory. This causes hilarity to ensue when
  4013. # trying to parse everything correctly.
  4014. # If the original RVA given to this function is equal to
  4015. # the next one to parse, we assume that it's a trick.
  4016. # Instead of raising a PEFormatError this would skip some
  4017. # reasonable data so we just break.
  4018. #
  4019. # 9ee4d0a0caf095314fd7041a3e4404dc is the offending sample
  4020. if base_rva + res.OffsetToDirectory in dirs:
  4021. break
  4022. entry_directory = self.parse_resources_directory(
  4023. base_rva + res.OffsetToDirectory,
  4024. size - (rva - base_rva), # size
  4025. base_rva=base_rva,
  4026. level=level + 1,
  4027. dirs=dirs + [base_rva + res.OffsetToDirectory],
  4028. )
  4029. if not entry_directory:
  4030. break
  4031. # Ange Albertini's code to process resources' strings
  4032. #
  4033. strings = None
  4034. if entry_id == RESOURCE_TYPE["RT_STRING"]:
  4035. strings = {}
  4036. for resource_id in entry_directory.entries:
  4037. if hasattr(resource_id, "directory"):
  4038. resource_strings = {}
  4039. for resource_lang in resource_id.directory.entries:
  4040. if (
  4041. resource_lang is None
  4042. or not hasattr(resource_lang, "data")
  4043. or resource_lang.data.struct.Size is None
  4044. or resource_id.id is None
  4045. ):
  4046. continue
  4047. string_entry_rva = (
  4048. resource_lang.data.struct.OffsetToData
  4049. )
  4050. string_entry_size = resource_lang.data.struct.Size
  4051. string_entry_id = resource_id.id
  4052. # XXX: has been raising exceptions preventing parsing
  4053. try:
  4054. string_entry_data = self.get_data(
  4055. string_entry_rva, string_entry_size
  4056. )
  4057. except PEFormatError:
  4058. self.__warnings.append(
  4059. f"Error parsing resource of type RT_STRING at "
  4060. f"RVA 0x{string_entry_rva:x} with "
  4061. f"size {string_entry_size}"
  4062. )
  4063. continue
  4064. parse_strings(
  4065. string_entry_data,
  4066. (int(string_entry_id) - 1) * 16,
  4067. resource_strings,
  4068. )
  4069. strings.update(resource_strings)
  4070. resource_id.directory.strings = resource_strings
  4071. dir_entries.append(
  4072. ResourceDirEntryData(
  4073. struct=res,
  4074. name=entry_name,
  4075. id=entry_id,
  4076. directory=entry_directory,
  4077. )
  4078. )
  4079. else:
  4080. struct = self.parse_resource_data_entry(
  4081. base_rva + res.OffsetToDirectory
  4082. )
  4083. if struct:
  4084. self.__total_resource_bytes += struct.Size
  4085. entry_data = ResourceDataEntryData(
  4086. struct=struct, lang=res.Name & 0x3FF, sublang=res.Name >> 10
  4087. )
  4088. dir_entries.append(
  4089. ResourceDirEntryData(
  4090. struct=res, name=entry_name, id=entry_id, data=entry_data
  4091. )
  4092. )
  4093. else:
  4094. break
  4095. # Check if this entry contains version information
  4096. #
  4097. if level == 0 and res.Id == RESOURCE_TYPE["RT_VERSION"]:
  4098. if dir_entries:
  4099. last_entry = dir_entries[-1]
  4100. try:
  4101. version_entries = last_entry.directory.entries[0].directory.entries
  4102. except:
  4103. # Maybe a malformed directory structure...?
  4104. # Let's ignore it
  4105. pass
  4106. else:
  4107. for version_entry in version_entries:
  4108. rt_version_struct = None
  4109. try:
  4110. rt_version_struct = version_entry.data.struct
  4111. except:
  4112. # Maybe a malformed directory structure...?
  4113. # Let's ignore it
  4114. pass
  4115. if rt_version_struct is not None:
  4116. self.parse_version_information(rt_version_struct)
  4117. rva += res.sizeof()
  4118. string_rvas = [s.get_rva() for s in strings_to_postprocess]
  4119. string_rvas.sort()
  4120. for idx, s in enumerate(strings_to_postprocess):
  4121. s.render_pascal_16()
  4122. resource_directory_data = ResourceDirData(
  4123. struct=resource_dir, entries=dir_entries
  4124. )
  4125. return resource_directory_data
  4126. def parse_resource_data_entry(self, rva):
  4127. """Parse a data entry from the resources directory."""
  4128. try:
  4129. # If the RVA is invalid all would blow up. Some EXEs seem to be
  4130. # specially nasty and have an invalid RVA.
  4131. data = self.get_data(
  4132. rva, Structure(self.__IMAGE_RESOURCE_DATA_ENTRY_format__).sizeof()
  4133. )
  4134. except PEFormatError:
  4135. self.__warnings.append(
  4136. "Error parsing a resource directory data entry, "
  4137. "the RVA is invalid: 0x%x" % (rva)
  4138. )
  4139. return None
  4140. data_entry = self.__unpack_data__(
  4141. self.__IMAGE_RESOURCE_DATA_ENTRY_format__,
  4142. data,
  4143. file_offset=self.get_offset_from_rva(rva),
  4144. )
  4145. return data_entry
  4146. def parse_resource_entry(self, rva):
  4147. """Parse a directory entry from the resources directory."""
  4148. try:
  4149. data = self.get_data(
  4150. rva, Structure(self.__IMAGE_RESOURCE_DIRECTORY_ENTRY_format__).sizeof()
  4151. )
  4152. except PEFormatError:
  4153. # A warning will be added by the caller if this method returns None
  4154. return None
  4155. resource = self.__unpack_data__(
  4156. self.__IMAGE_RESOURCE_DIRECTORY_ENTRY_format__,
  4157. data,
  4158. file_offset=self.get_offset_from_rva(rva),
  4159. )
  4160. if resource is None:
  4161. return None
  4162. # resource.NameIsString = (resource.Name & 0x80000000L) >> 31
  4163. resource.NameOffset = resource.Name & 0x7FFFFFFF
  4164. resource.__pad = resource.Name & 0xFFFF0000
  4165. resource.Id = resource.Name & 0x0000FFFF
  4166. resource.DataIsDirectory = (resource.OffsetToData & 0x80000000) >> 31
  4167. resource.OffsetToDirectory = resource.OffsetToData & 0x7FFFFFFF
  4168. return resource
  4169. def parse_version_information(self, version_struct):
  4170. """Parse version information structure.
  4171. The date will be made available in three attributes of the PE object.
  4172. VS_VERSIONINFO will contain the first three fields of the main structure:
  4173. 'Length', 'ValueLength', and 'Type'
  4174. VS_FIXEDFILEINFO will hold the rest of the fields, accessible as sub-attributes:
  4175. 'Signature', 'StrucVersion', 'FileVersionMS', 'FileVersionLS',
  4176. 'ProductVersionMS', 'ProductVersionLS', 'FileFlagsMask', 'FileFlags',
  4177. 'FileOS', 'FileType', 'FileSubtype', 'FileDateMS', 'FileDateLS'
  4178. FileInfo is a list of all StringFileInfo and VarFileInfo structures.
  4179. StringFileInfo structures will have a list as an attribute named 'StringTable'
  4180. containing all the StringTable structures. Each of those structures contains a
  4181. dictionary 'entries' with all the key / value version information string pairs.
  4182. VarFileInfo structures will have a list as an attribute named 'Var' containing
  4183. all Var structures. Each Var structure will have a dictionary as an attribute
  4184. named 'entry' which will contain the name and value of the Var.
  4185. """
  4186. # Retrieve the data for the version info resource
  4187. #
  4188. try:
  4189. start_offset = self.get_offset_from_rva(version_struct.OffsetToData)
  4190. except PEFormatError:
  4191. self.__warnings.append(
  4192. "Error parsing the version information, "
  4193. "attempting to read OffsetToData with RVA: 0x{:x}".format(
  4194. version_struct.OffsetToData
  4195. )
  4196. )
  4197. return
  4198. raw_data = self.__data__[start_offset : start_offset + version_struct.Size]
  4199. # Map the main structure and the subsequent string
  4200. #
  4201. versioninfo_struct = self.__unpack_data__(
  4202. self.__VS_VERSIONINFO_format__, raw_data, file_offset=start_offset
  4203. )
  4204. if versioninfo_struct is None:
  4205. return
  4206. ustr_offset = version_struct.OffsetToData + versioninfo_struct.sizeof()
  4207. section = self.get_section_by_rva(ustr_offset)
  4208. section_end = None
  4209. if section:
  4210. section_end = section.VirtualAddress + max(
  4211. section.SizeOfRawData, section.Misc_VirtualSize
  4212. )
  4213. versioninfo_string = None
  4214. # These should return 'ascii' decoded data. For the case when it's
  4215. # garbled data the ascii string will retain the byte values while
  4216. # encoding it to something else may yield values that don't match the
  4217. # file's contents.
  4218. try:
  4219. if section_end is None:
  4220. versioninfo_string = self.get_string_u_at_rva(
  4221. ustr_offset, encoding="ascii"
  4222. )
  4223. else:
  4224. versioninfo_string = self.get_string_u_at_rva(
  4225. ustr_offset, (section_end - ustr_offset) >> 1, encoding="ascii"
  4226. )
  4227. except PEFormatError:
  4228. self.__warnings.append(
  4229. "Error parsing the version information, "
  4230. "attempting to read VS_VERSION_INFO string. Can't "
  4231. "read unicode string at offset 0x%x" % (ustr_offset)
  4232. )
  4233. if versioninfo_string is None:
  4234. self.__warnings.append(
  4235. "Invalid VS_VERSION_INFO block: {0}".format(versioninfo_string)
  4236. )
  4237. return
  4238. # If the structure does not contain the expected name, it's assumed to
  4239. # be invalid
  4240. if versioninfo_string is not None and versioninfo_string != b"VS_VERSION_INFO":
  4241. if len(versioninfo_string) > 128:
  4242. excerpt = versioninfo_string[:128].decode("ascii")
  4243. # Don't leave any half-escaped characters
  4244. excerpt = excerpt[: excerpt.rfind("\\u")]
  4245. versioninfo_string = "{0} ... ({1} bytes, too long to display)".format(
  4246. excerpt, len(versioninfo_string)
  4247. ).encode()
  4248. self.__warnings.append(
  4249. "Invalid VS_VERSION_INFO block: {0}".format(
  4250. versioninfo_string.decode("ascii").replace("\00", "\\00")
  4251. )
  4252. )
  4253. return
  4254. if not hasattr(self, "VS_VERSIONINFO"):
  4255. self.VS_VERSIONINFO = []
  4256. # Set the PE object's VS_VERSIONINFO to this one
  4257. vinfo = versioninfo_struct
  4258. # Set the Key attribute to point to the unicode string identifying the structure
  4259. vinfo.Key = versioninfo_string
  4260. self.VS_VERSIONINFO.append(vinfo)
  4261. if versioninfo_string is None:
  4262. versioninfo_string = ""
  4263. # Process the fixed version information, get the offset and structure
  4264. fixedfileinfo_offset = self.dword_align(
  4265. versioninfo_struct.sizeof() + 2 * (len(versioninfo_string) + 1),
  4266. version_struct.OffsetToData,
  4267. )
  4268. fixedfileinfo_struct = self.__unpack_data__(
  4269. self.__VS_FIXEDFILEINFO_format__,
  4270. raw_data[fixedfileinfo_offset:],
  4271. file_offset=start_offset + fixedfileinfo_offset,
  4272. )
  4273. if not fixedfileinfo_struct:
  4274. return
  4275. if not hasattr(self, "VS_FIXEDFILEINFO"):
  4276. self.VS_FIXEDFILEINFO = []
  4277. # Set the PE object's VS_FIXEDFILEINFO to this one
  4278. self.VS_FIXEDFILEINFO.append(fixedfileinfo_struct)
  4279. # Start parsing all the StringFileInfo and VarFileInfo structures
  4280. # Get the first one
  4281. stringfileinfo_offset = self.dword_align(
  4282. fixedfileinfo_offset + fixedfileinfo_struct.sizeof(),
  4283. version_struct.OffsetToData,
  4284. )
  4285. # Set the PE object's attribute that will contain them all.
  4286. if not hasattr(self, "FileInfo"):
  4287. self.FileInfo = []
  4288. finfo = []
  4289. while True:
  4290. # Process the StringFileInfo/VarFileInfo structure
  4291. stringfileinfo_struct = self.__unpack_data__(
  4292. self.__StringFileInfo_format__,
  4293. raw_data[stringfileinfo_offset:],
  4294. file_offset=start_offset + stringfileinfo_offset,
  4295. )
  4296. if stringfileinfo_struct is None:
  4297. self.__warnings.append(
  4298. "Error parsing StringFileInfo/VarFileInfo struct"
  4299. )
  4300. return None
  4301. # Get the subsequent string defining the structure.
  4302. ustr_offset = (
  4303. version_struct.OffsetToData
  4304. + stringfileinfo_offset
  4305. + versioninfo_struct.sizeof()
  4306. )
  4307. try:
  4308. stringfileinfo_string = self.get_string_u_at_rva(ustr_offset)
  4309. except PEFormatError:
  4310. self.__warnings.append(
  4311. "Error parsing the version information, "
  4312. "attempting to read StringFileInfo string. Can't "
  4313. "read unicode string at offset 0x{0:x}".format(ustr_offset)
  4314. )
  4315. break
  4316. # Set such string as the Key attribute
  4317. stringfileinfo_struct.Key = stringfileinfo_string
  4318. # Append the structure to the PE object's list
  4319. finfo.append(stringfileinfo_struct)
  4320. # Parse a StringFileInfo entry
  4321. if stringfileinfo_string and stringfileinfo_string.startswith(
  4322. b"StringFileInfo"
  4323. ):
  4324. if (
  4325. stringfileinfo_struct.Type in (0, 1)
  4326. and stringfileinfo_struct.ValueLength == 0
  4327. ):
  4328. stringtable_offset = self.dword_align(
  4329. stringfileinfo_offset
  4330. + stringfileinfo_struct.sizeof()
  4331. + 2 * (len(stringfileinfo_string) + 1),
  4332. version_struct.OffsetToData,
  4333. )
  4334. stringfileinfo_struct.StringTable = []
  4335. # Process the String Table entries
  4336. while True:
  4337. stringtable_struct = self.__unpack_data__(
  4338. self.__StringTable_format__,
  4339. raw_data[stringtable_offset:],
  4340. file_offset=start_offset + stringtable_offset,
  4341. )
  4342. if not stringtable_struct:
  4343. break
  4344. ustr_offset = (
  4345. version_struct.OffsetToData
  4346. + stringtable_offset
  4347. + stringtable_struct.sizeof()
  4348. )
  4349. try:
  4350. stringtable_string = self.get_string_u_at_rva(ustr_offset)
  4351. except PEFormatError:
  4352. self.__warnings.append(
  4353. "Error parsing the version information, "
  4354. "attempting to read StringTable string. Can't "
  4355. "read unicode string at offset 0x{0:x}".format(
  4356. ustr_offset
  4357. )
  4358. )
  4359. break
  4360. stringtable_struct.LangID = stringtable_string
  4361. stringtable_struct.entries = {}
  4362. stringtable_struct.entries_offsets = {}
  4363. stringtable_struct.entries_lengths = {}
  4364. stringfileinfo_struct.StringTable.append(stringtable_struct)
  4365. entry_offset = self.dword_align(
  4366. stringtable_offset
  4367. + stringtable_struct.sizeof()
  4368. + 2 * (len(stringtable_string) + 1),
  4369. version_struct.OffsetToData,
  4370. )
  4371. # Process all entries in the string table
  4372. while (
  4373. entry_offset
  4374. < stringtable_offset + stringtable_struct.Length
  4375. ):
  4376. string_struct = self.__unpack_data__(
  4377. self.__String_format__,
  4378. raw_data[entry_offset:],
  4379. file_offset=start_offset + entry_offset,
  4380. )
  4381. if not string_struct:
  4382. break
  4383. ustr_offset = (
  4384. version_struct.OffsetToData
  4385. + entry_offset
  4386. + string_struct.sizeof()
  4387. )
  4388. try:
  4389. key = self.get_string_u_at_rva(ustr_offset)
  4390. key_offset = self.get_offset_from_rva(ustr_offset)
  4391. except PEFormatError:
  4392. self.__warnings.append(
  4393. "Error parsing the version information, "
  4394. "attempting to read StringTable Key string. Can't "
  4395. "read unicode string at offset 0x{0:x}".format(
  4396. ustr_offset
  4397. )
  4398. )
  4399. break
  4400. value_offset = self.dword_align(
  4401. 2 * (len(key) + 1)
  4402. + entry_offset
  4403. + string_struct.sizeof(),
  4404. version_struct.OffsetToData,
  4405. )
  4406. ustr_offset = version_struct.OffsetToData + value_offset
  4407. try:
  4408. value = self.get_string_u_at_rva(
  4409. ustr_offset, max_length=string_struct.ValueLength
  4410. )
  4411. value_offset = self.get_offset_from_rva(ustr_offset)
  4412. except PEFormatError:
  4413. self.__warnings.append(
  4414. "Error parsing the version information, attempting "
  4415. "to read StringTable Value string. Can't read "
  4416. f"unicode string at offset 0x{ustr_offset:x}"
  4417. )
  4418. break
  4419. if string_struct.Length == 0:
  4420. entry_offset = (
  4421. stringtable_offset + stringtable_struct.Length
  4422. )
  4423. else:
  4424. entry_offset = self.dword_align(
  4425. string_struct.Length + entry_offset,
  4426. version_struct.OffsetToData,
  4427. )
  4428. stringtable_struct.entries[key] = value
  4429. stringtable_struct.entries_offsets[key] = (
  4430. key_offset,
  4431. value_offset,
  4432. )
  4433. stringtable_struct.entries_lengths[key] = (
  4434. len(key),
  4435. len(value),
  4436. )
  4437. new_stringtable_offset = self.dword_align(
  4438. stringtable_struct.Length + stringtable_offset,
  4439. version_struct.OffsetToData,
  4440. )
  4441. # Check if the entry is crafted in a way that would lead
  4442. # to an infinite loop and break if so.
  4443. if new_stringtable_offset == stringtable_offset:
  4444. break
  4445. stringtable_offset = new_stringtable_offset
  4446. if stringtable_offset >= stringfileinfo_struct.Length:
  4447. break
  4448. # Parse a VarFileInfo entry
  4449. elif stringfileinfo_string and stringfileinfo_string.startswith(
  4450. b"VarFileInfo"
  4451. ):
  4452. varfileinfo_struct = stringfileinfo_struct
  4453. varfileinfo_struct.name = "VarFileInfo"
  4454. if (
  4455. varfileinfo_struct.Type in (0, 1)
  4456. and varfileinfo_struct.ValueLength == 0
  4457. ):
  4458. var_offset = self.dword_align(
  4459. stringfileinfo_offset
  4460. + varfileinfo_struct.sizeof()
  4461. + 2 * (len(stringfileinfo_string) + 1),
  4462. version_struct.OffsetToData,
  4463. )
  4464. varfileinfo_struct.Var = []
  4465. # Process all entries
  4466. while True:
  4467. var_struct = self.__unpack_data__(
  4468. self.__Var_format__,
  4469. raw_data[var_offset:],
  4470. file_offset=start_offset + var_offset,
  4471. )
  4472. if not var_struct:
  4473. break
  4474. ustr_offset = (
  4475. version_struct.OffsetToData
  4476. + var_offset
  4477. + var_struct.sizeof()
  4478. )
  4479. try:
  4480. var_string = self.get_string_u_at_rva(ustr_offset)
  4481. except PEFormatError:
  4482. self.__warnings.append(
  4483. "Error parsing the version information, "
  4484. "attempting to read VarFileInfo Var string. "
  4485. "Can't read unicode string at offset 0x{0:x}".format(
  4486. ustr_offset
  4487. )
  4488. )
  4489. break
  4490. if var_string is None:
  4491. break
  4492. varfileinfo_struct.Var.append(var_struct)
  4493. varword_offset = self.dword_align(
  4494. 2 * (len(var_string) + 1)
  4495. + var_offset
  4496. + var_struct.sizeof(),
  4497. version_struct.OffsetToData,
  4498. )
  4499. orig_varword_offset = varword_offset
  4500. while (
  4501. varword_offset
  4502. < orig_varword_offset + var_struct.ValueLength
  4503. ):
  4504. word1 = self.get_word_from_data(
  4505. raw_data[varword_offset : varword_offset + 2], 0
  4506. )
  4507. word2 = self.get_word_from_data(
  4508. raw_data[varword_offset + 2 : varword_offset + 4], 0
  4509. )
  4510. varword_offset += 4
  4511. if isinstance(word1, int) and isinstance(word2, int):
  4512. var_struct.entry = {
  4513. var_string: "0x%04x 0x%04x" % (word1, word2)
  4514. }
  4515. var_offset = self.dword_align(
  4516. var_offset + var_struct.Length, version_struct.OffsetToData
  4517. )
  4518. if var_offset <= var_offset + var_struct.Length:
  4519. break
  4520. # Increment and align the offset
  4521. stringfileinfo_offset = self.dword_align(
  4522. stringfileinfo_struct.Length + stringfileinfo_offset,
  4523. version_struct.OffsetToData,
  4524. )
  4525. # Check if all the StringFileInfo and VarFileInfo items have been processed
  4526. if (
  4527. stringfileinfo_struct.Length == 0
  4528. or stringfileinfo_offset >= versioninfo_struct.Length
  4529. ):
  4530. break
  4531. self.FileInfo.append(finfo)
  4532. def parse_export_directory(self, rva, size, forwarded_only=False):
  4533. """Parse the export directory.
  4534. Given the RVA of the export directory, it will process all
  4535. its entries.
  4536. The exports will be made available as a list of ExportData
  4537. instances in the 'IMAGE_DIRECTORY_ENTRY_EXPORT' PE attribute.
  4538. """
  4539. try:
  4540. export_dir = self.__unpack_data__(
  4541. self.__IMAGE_EXPORT_DIRECTORY_format__,
  4542. self.get_data(
  4543. rva, Structure(self.__IMAGE_EXPORT_DIRECTORY_format__).sizeof()
  4544. ),
  4545. file_offset=self.get_offset_from_rva(rva),
  4546. )
  4547. except PEFormatError:
  4548. self.__warnings.append(
  4549. "Error parsing export directory at RVA: 0x%x" % (rva)
  4550. )
  4551. return
  4552. if not export_dir:
  4553. return
  4554. # We keep track of the bytes left in the file and use it to set a upper
  4555. # bound in the number of items that can be read from the different
  4556. # arrays.
  4557. def length_until_eof(rva):
  4558. return len(self.__data__) - self.get_offset_from_rva(rva)
  4559. try:
  4560. address_of_names = self.get_data(
  4561. export_dir.AddressOfNames,
  4562. min(
  4563. length_until_eof(export_dir.AddressOfNames),
  4564. export_dir.NumberOfNames * 4,
  4565. ),
  4566. )
  4567. address_of_name_ordinals = self.get_data(
  4568. export_dir.AddressOfNameOrdinals,
  4569. min(
  4570. length_until_eof(export_dir.AddressOfNameOrdinals),
  4571. export_dir.NumberOfNames * 4,
  4572. ),
  4573. )
  4574. address_of_functions = self.get_data(
  4575. export_dir.AddressOfFunctions,
  4576. min(
  4577. length_until_eof(export_dir.AddressOfFunctions),
  4578. export_dir.NumberOfFunctions * 4,
  4579. ),
  4580. )
  4581. except PEFormatError:
  4582. self.__warnings.append(
  4583. "Error parsing export directory at RVA: 0x%x" % (rva)
  4584. )
  4585. return
  4586. exports = []
  4587. max_failed_entries_before_giving_up = 10
  4588. section = self.get_section_by_rva(export_dir.AddressOfNames)
  4589. # Overly generous upper bound
  4590. safety_boundary = len(self.__data__)
  4591. if section:
  4592. safety_boundary = (
  4593. section.VirtualAddress
  4594. + len(section.get_data())
  4595. - export_dir.AddressOfNames
  4596. )
  4597. symbol_counts = collections.defaultdict(int)
  4598. export_parsing_loop_completed_normally = True
  4599. for i in range(min(export_dir.NumberOfNames, int(safety_boundary / 4))):
  4600. symbol_ordinal = self.get_word_from_data(address_of_name_ordinals, i)
  4601. if symbol_ordinal is not None and symbol_ordinal * 4 < len(
  4602. address_of_functions
  4603. ):
  4604. symbol_address = self.get_dword_from_data(
  4605. address_of_functions, symbol_ordinal
  4606. )
  4607. else:
  4608. # Corrupt? a bad pointer... we assume it's all
  4609. # useless, no exports
  4610. return None
  4611. if symbol_address is None or symbol_address == 0:
  4612. continue
  4613. # If the function's RVA points within the export directory
  4614. # it will point to a string with the forwarded symbol's string
  4615. # instead of pointing to the function start address.
  4616. if symbol_address >= rva and symbol_address < rva + size:
  4617. forwarder_str = self.get_string_at_rva(symbol_address)
  4618. try:
  4619. forwarder_offset = self.get_offset_from_rva(symbol_address)
  4620. except PEFormatError:
  4621. continue
  4622. else:
  4623. if forwarded_only:
  4624. continue
  4625. forwarder_str = None
  4626. forwarder_offset = None
  4627. symbol_name_address = self.get_dword_from_data(address_of_names, i)
  4628. if symbol_name_address is None:
  4629. max_failed_entries_before_giving_up -= 1
  4630. if max_failed_entries_before_giving_up <= 0:
  4631. export_parsing_loop_completed_normally = False
  4632. break
  4633. symbol_name = self.get_string_at_rva(
  4634. symbol_name_address, MAX_SYMBOL_NAME_LENGTH
  4635. )
  4636. if not is_valid_function_name(symbol_name, relax_allowed_characters=True):
  4637. export_parsing_loop_completed_normally = False
  4638. break
  4639. try:
  4640. symbol_name_offset = self.get_offset_from_rva(symbol_name_address)
  4641. except PEFormatError:
  4642. max_failed_entries_before_giving_up -= 1
  4643. if max_failed_entries_before_giving_up <= 0:
  4644. export_parsing_loop_completed_normally = False
  4645. break
  4646. try:
  4647. symbol_name_offset = self.get_offset_from_rva(symbol_name_address)
  4648. except PEFormatError:
  4649. max_failed_entries_before_giving_up -= 1
  4650. if max_failed_entries_before_giving_up <= 0:
  4651. export_parsing_loop_completed_normally = False
  4652. break
  4653. continue
  4654. # File 0b1d3d3664915577ab9a32188d29bbf3542b86c7b9ce333e245496c3018819f1
  4655. # was being parsed as potentially containing millions of exports.
  4656. # Checking for duplicates addresses the issue.
  4657. symbol_counts[(symbol_name, symbol_address)] += 1
  4658. if symbol_counts[(symbol_name, symbol_address)] > 10:
  4659. self.__warnings.append(
  4660. f"Export directory contains more than 10 repeated entries "
  4661. f"({symbol_name}, {symbol_address:#02x}). Assuming corrupt."
  4662. )
  4663. break
  4664. elif len(symbol_counts) > self.max_symbol_exports:
  4665. self.__warnings.append(
  4666. "Export directory contains more than {} symbol entries. "
  4667. "Assuming corrupt.".format(self.max_symbol_exports)
  4668. )
  4669. break
  4670. exports.append(
  4671. ExportData(
  4672. pe=self,
  4673. ordinal=export_dir.Base + symbol_ordinal,
  4674. ordinal_offset=self.get_offset_from_rva(
  4675. export_dir.AddressOfNameOrdinals + 2 * i
  4676. ),
  4677. address=symbol_address,
  4678. address_offset=self.get_offset_from_rva(
  4679. export_dir.AddressOfFunctions + 4 * symbol_ordinal
  4680. ),
  4681. name=symbol_name,
  4682. name_offset=symbol_name_offset,
  4683. forwarder=forwarder_str,
  4684. forwarder_offset=forwarder_offset,
  4685. )
  4686. )
  4687. if not export_parsing_loop_completed_normally:
  4688. self.__warnings.append(
  4689. f"RVA AddressOfNames in the export directory points to an invalid "
  4690. f"address: {export_dir.AddressOfNames:x}"
  4691. )
  4692. ordinals = {exp.ordinal for exp in exports}
  4693. max_failed_entries_before_giving_up = 10
  4694. section = self.get_section_by_rva(export_dir.AddressOfFunctions)
  4695. # Overly generous upper bound
  4696. safety_boundary = len(self.__data__)
  4697. if section:
  4698. safety_boundary = (
  4699. section.VirtualAddress
  4700. + len(section.get_data())
  4701. - export_dir.AddressOfFunctions
  4702. )
  4703. symbol_counts = collections.defaultdict(int)
  4704. export_parsing_loop_completed_normally = True
  4705. for idx in range(min(export_dir.NumberOfFunctions, int(safety_boundary / 4))):
  4706. if idx + export_dir.Base not in ordinals:
  4707. try:
  4708. symbol_address = self.get_dword_from_data(address_of_functions, idx)
  4709. except PEFormatError:
  4710. symbol_address = None
  4711. if symbol_address is None:
  4712. max_failed_entries_before_giving_up -= 1
  4713. if max_failed_entries_before_giving_up <= 0:
  4714. export_parsing_loop_completed_normally = False
  4715. break
  4716. if symbol_address == 0:
  4717. continue
  4718. # Checking for forwarder again.
  4719. if (
  4720. symbol_address is not None
  4721. and symbol_address >= rva
  4722. and symbol_address < rva + size
  4723. ):
  4724. forwarder_str = self.get_string_at_rva(symbol_address)
  4725. else:
  4726. forwarder_str = None
  4727. # File 0b1d3d3664915577ab9a32188d29bbf3542b86c7b9ce333e245496c3018819f1
  4728. # was being parsed as potentially containing millions of exports.
  4729. # Checking for duplicates addresses the issue.
  4730. symbol_counts[symbol_address] += 1
  4731. if symbol_counts[symbol_address] > self.max_repeated_symbol:
  4732. # if most_common and most_common[0][1] > 10:
  4733. self.__warnings.append(
  4734. "Export directory contains more than {} repeated "
  4735. "ordinal entries (0x{:x}). Assuming corrupt.".format(
  4736. self.max_repeated_symbol, symbol_address
  4737. )
  4738. )
  4739. break
  4740. elif len(symbol_counts) > self.max_symbol_exports:
  4741. self.__warnings.append(
  4742. "Export directory contains more than "
  4743. f"{self.max_symbol_exports} ordinal entries. Assuming corrupt."
  4744. )
  4745. break
  4746. exports.append(
  4747. ExportData(
  4748. ordinal=export_dir.Base + idx,
  4749. address=symbol_address,
  4750. name=None,
  4751. forwarder=forwarder_str,
  4752. )
  4753. )
  4754. if not export_parsing_loop_completed_normally:
  4755. self.__warnings.append(
  4756. "RVA AddressOfFunctions in the export directory points to an invalid "
  4757. f"address: {export_dir.AddressOfFunctions:x}"
  4758. )
  4759. return
  4760. if not exports and export_dir.all_zeroes():
  4761. return None
  4762. return ExportDirData(
  4763. struct=export_dir,
  4764. symbols=exports,
  4765. name=self.get_string_at_rva(export_dir.Name),
  4766. )
  4767. def dword_align(self, offset, base):
  4768. return ((offset + base + 3) & 0xFFFFFFFC) - (base & 0xFFFFFFFC)
  4769. def normalize_import_va(self, va):
  4770. # Setup image range
  4771. begin_of_image = self.OPTIONAL_HEADER.ImageBase
  4772. end_of_image = self.OPTIONAL_HEADER.ImageBase + self.OPTIONAL_HEADER.SizeOfImage
  4773. # Try to avoid bogus VAs, which are out of the image.
  4774. # This also filters out entries that are zero
  4775. if begin_of_image <= va < end_of_image:
  4776. va -= begin_of_image
  4777. return va
  4778. def parse_delay_import_directory(self, rva, size):
  4779. """Walk and parse the delay import directory."""
  4780. import_descs = []
  4781. error_count = 0
  4782. while True:
  4783. try:
  4784. # If the RVA is invalid all would blow up. Some PEs seem to be
  4785. # specially nasty and have an invalid RVA.
  4786. data = self.get_data(
  4787. rva,
  4788. Structure(self.__IMAGE_DELAY_IMPORT_DESCRIPTOR_format__).sizeof(),
  4789. )
  4790. except PEFormatError:
  4791. self.__warnings.append(
  4792. "Error parsing the Delay import directory at RVA: 0x%x" % (rva)
  4793. )
  4794. break
  4795. file_offset = self.get_offset_from_rva(rva)
  4796. import_desc = self.__unpack_data__(
  4797. self.__IMAGE_DELAY_IMPORT_DESCRIPTOR_format__,
  4798. data,
  4799. file_offset=file_offset,
  4800. )
  4801. # If the structure is all zeros, we reached the end of the list
  4802. if not import_desc or import_desc.all_zeroes():
  4803. break
  4804. contains_addresses = False
  4805. # Handle old import descriptor that has Virtual Addresses instead of RVAs
  4806. # This version of import descriptor is created by old Visual Studio versions
  4807. # (pre 6.0)
  4808. # Can only be present in 32-bit binaries (no 64-bit compiler existed at the
  4809. # time)
  4810. # Sample: e8d3bff0c1a9a6955993f7a441121a2692261421e82fdfadaaded45d3bea9980
  4811. if (
  4812. import_desc.grAttrs == 0
  4813. and self.FILE_HEADER.Machine == MACHINE_TYPE["IMAGE_FILE_MACHINE_I386"]
  4814. ):
  4815. import_desc.pBoundIAT = self.normalize_import_va(import_desc.pBoundIAT)
  4816. import_desc.pIAT = self.normalize_import_va(import_desc.pIAT)
  4817. import_desc.pINT = self.normalize_import_va(import_desc.pINT)
  4818. import_desc.pUnloadIAT = self.normalize_import_va(
  4819. import_desc.pUnloadIAT
  4820. )
  4821. import_desc.phmod = self.normalize_import_va(import_desc.pUnloadIAT)
  4822. import_desc.szName = self.normalize_import_va(import_desc.szName)
  4823. contains_addresses = True
  4824. rva += import_desc.sizeof()
  4825. # If the array of thunks is somewhere earlier than the import
  4826. # descriptor we can set a maximum length for the array. Otherwise
  4827. # just set a maximum length of the size of the file
  4828. max_len = len(self.__data__) - file_offset
  4829. if rva > import_desc.pINT or rva > import_desc.pIAT:
  4830. max_len = max(rva - import_desc.pINT, rva - import_desc.pIAT)
  4831. import_data = []
  4832. try:
  4833. import_data = self.parse_imports(
  4834. import_desc.pINT,
  4835. import_desc.pIAT,
  4836. None,
  4837. max_len,
  4838. contains_addresses,
  4839. )
  4840. except PEFormatError as excp:
  4841. self.__warnings.append(
  4842. "Error parsing the Delay import directory. "
  4843. "Invalid import data at RVA: 0x{0:x} ({1})".format(rva, excp.value)
  4844. )
  4845. if error_count > 5:
  4846. self.__warnings.append(
  4847. "Too many errors parsing the Delay import directory. "
  4848. "Invalid import data at RVA: 0x{0:x}".format(rva)
  4849. )
  4850. break
  4851. if not import_data:
  4852. error_count += 1
  4853. continue
  4854. if self.__total_import_symbols > MAX_IMPORT_SYMBOLS:
  4855. self.__warnings.append(
  4856. "Error, too many imported symbols %d (>%s)"
  4857. % (self.__total_import_symbols, MAX_IMPORT_SYMBOLS)
  4858. )
  4859. break
  4860. dll = self.get_string_at_rva(import_desc.szName, MAX_DLL_LENGTH)
  4861. if not is_valid_dos_filename(dll):
  4862. dll = b"*invalid*"
  4863. if dll:
  4864. for symbol in import_data:
  4865. if symbol.name is None:
  4866. funcname = ordlookup.ordLookup(dll.lower(), symbol.ordinal)
  4867. if funcname:
  4868. symbol.name = funcname
  4869. import_descs.append(
  4870. ImportDescData(struct=import_desc, imports=import_data, dll=dll)
  4871. )
  4872. return import_descs
  4873. def get_rich_header_hash(self, algorithm="md5"):
  4874. if not hasattr(self, "RICH_HEADER") or self.RICH_HEADER is None:
  4875. return ""
  4876. if algorithm == "md5":
  4877. return md5(self.RICH_HEADER.clear_data).hexdigest()
  4878. elif algorithm == "sha1":
  4879. return sha1(self.RICH_HEADER.clear_data).hexdigest()
  4880. elif algorithm == "sha256":
  4881. return sha256(self.RICH_HEADER.clear_data).hexdigest()
  4882. elif algorithm == "sha512":
  4883. return sha512(self.RICH_HEADER.clear_data).hexdigest()
  4884. raise Exception("Invalid hashing algorithm specified")
  4885. def get_imphash(self):
  4886. """Return the imphash of the PE file.
  4887. Creates a hash based on imported symbol names and their specific order within
  4888. the executable:
  4889. https://www.mandiant.com/resources/blog/tracking-malware-import-hashing
  4890. Returns:
  4891. the hexdigest of the MD5 hash of the exported symbols.
  4892. """
  4893. impstrs = []
  4894. exts = ["ocx", "sys", "dll"]
  4895. if not hasattr(self, "DIRECTORY_ENTRY_IMPORT"):
  4896. return ""
  4897. for entry in self.DIRECTORY_ENTRY_IMPORT:
  4898. if isinstance(entry.dll, bytes):
  4899. libname = entry.dll.decode().lower()
  4900. else:
  4901. libname = entry.dll.lower()
  4902. parts = libname.rsplit(".", 1)
  4903. if len(parts) > 1 and parts[1] in exts:
  4904. libname = parts[0]
  4905. entry_dll_lower = entry.dll.lower()
  4906. for imp in entry.imports:
  4907. funcname = None
  4908. if not imp.name:
  4909. funcname = ordlookup.ordLookup(
  4910. entry_dll_lower, imp.ordinal, make_name=True
  4911. )
  4912. if not funcname:
  4913. raise PEFormatError(
  4914. f"Unable to look up ordinal {entry.dll}:{imp.ordinal:04x}"
  4915. )
  4916. else:
  4917. funcname = imp.name
  4918. if not funcname:
  4919. continue
  4920. if isinstance(funcname, bytes):
  4921. funcname = funcname.decode()
  4922. impstrs.append("%s.%s" % (libname.lower(), funcname.lower()))
  4923. return md5(",".join(impstrs).encode()).hexdigest()
  4924. def get_exphash(self):
  4925. """Return the exphash of the PE file.
  4926. Similar to imphash, but based on exported symbol names and their specific order.
  4927. Returns:
  4928. the hexdigest of the SHA256 hash of the exported symbols.
  4929. """
  4930. if not hasattr(self, "DIRECTORY_ENTRY_EXPORT"):
  4931. return ""
  4932. if not hasattr(self.DIRECTORY_ENTRY_EXPORT, "symbols"):
  4933. return ""
  4934. export_list = [
  4935. e.name.decode().lower()
  4936. for e in self.DIRECTORY_ENTRY_EXPORT.symbols
  4937. if e and e.name is not None
  4938. ]
  4939. if len(export_list) == 0:
  4940. return ""
  4941. return md5(",".join(export_list).encode()).hexdigest()
  4942. def parse_import_directory(self, rva, size, dllnames_only=False):
  4943. """Walk and parse the import directory."""
  4944. import_descs = []
  4945. error_count = 0
  4946. image_import_descriptor_size = Structure(
  4947. self.__IMAGE_IMPORT_DESCRIPTOR_format__
  4948. ).sizeof()
  4949. while True:
  4950. try:
  4951. # If the RVA is invalid all would blow up. Some EXEs seem to be
  4952. # specially nasty and have an invalid RVA.
  4953. data = self.get_data(rva, image_import_descriptor_size)
  4954. except PEFormatError:
  4955. self.__warnings.append(
  4956. f"Error parsing the import directory at RVA: 0x{rva:x}"
  4957. )
  4958. break
  4959. file_offset = self.get_offset_from_rva(rva)
  4960. import_desc = self.__unpack_data__(
  4961. self.__IMAGE_IMPORT_DESCRIPTOR_format__, data, file_offset=file_offset
  4962. )
  4963. # If the structure is all zeros, we reached the end of the list
  4964. if not import_desc or import_desc.all_zeroes():
  4965. break
  4966. rva += import_desc.sizeof()
  4967. # If the array of thunks is somewhere earlier than the import
  4968. # descriptor we can set a maximum length for the array. Otherwise
  4969. # just set a maximum length of the size of the file
  4970. max_len = len(self.__data__) - file_offset
  4971. if rva > import_desc.OriginalFirstThunk or rva > import_desc.FirstThunk:
  4972. max_len = max(
  4973. rva - import_desc.OriginalFirstThunk, rva - import_desc.FirstThunk
  4974. )
  4975. import_data = []
  4976. if not dllnames_only:
  4977. try:
  4978. import_data = self.parse_imports(
  4979. import_desc.OriginalFirstThunk,
  4980. import_desc.FirstThunk,
  4981. import_desc.ForwarderChain,
  4982. max_length=max_len,
  4983. )
  4984. except PEFormatError as e:
  4985. self.__warnings.append(
  4986. "Error parsing the import directory. "
  4987. f"Invalid Import data at RVA: 0x{rva:x} ({e.value})"
  4988. )
  4989. if error_count > 5:
  4990. self.__warnings.append(
  4991. "Too many errors parsing the import directory. "
  4992. f"Invalid import data at RVA: 0x{rva:x}"
  4993. )
  4994. break
  4995. if not import_data:
  4996. error_count += 1
  4997. # TODO: do not continue here
  4998. continue
  4999. dll = self.get_string_at_rva(import_desc.Name, MAX_DLL_LENGTH)
  5000. if not is_valid_dos_filename(dll):
  5001. dll = b"*invalid*"
  5002. if dll:
  5003. for symbol in import_data:
  5004. if symbol.name is None:
  5005. funcname = ordlookup.ordLookup(dll.lower(), symbol.ordinal)
  5006. if funcname:
  5007. symbol.name = funcname
  5008. import_descs.append(
  5009. ImportDescData(struct=import_desc, imports=import_data, dll=dll)
  5010. )
  5011. if not dllnames_only:
  5012. suspicious_imports = {"LoadLibrary", "GetProcAddress"}
  5013. suspicious_imports_count = 0
  5014. total_symbols = 0
  5015. for imp_dll in import_descs:
  5016. for symbol in imp_dll.imports:
  5017. for suspicious_symbol in suspicious_imports:
  5018. if not symbol or not symbol.name:
  5019. continue
  5020. name = symbol.name
  5021. if type(symbol.name) == bytes:
  5022. name = symbol.name.decode("utf-8")
  5023. if name.startswith(suspicious_symbol):
  5024. suspicious_imports_count += 1
  5025. break
  5026. total_symbols += 1
  5027. if (
  5028. suspicious_imports_count == len(suspicious_imports)
  5029. and total_symbols < 20
  5030. ):
  5031. self.__warnings.append(
  5032. "Imported symbols contain entries typical of packed executables."
  5033. )
  5034. return import_descs
  5035. def parse_imports(
  5036. self,
  5037. original_first_thunk,
  5038. first_thunk,
  5039. forwarder_chain,
  5040. max_length=None,
  5041. contains_addresses=False,
  5042. ):
  5043. """Parse the imported symbols.
  5044. It will fill a list, which will be available as the dictionary
  5045. attribute "imports". Its keys will be the DLL names and the values
  5046. of all the symbols imported from that object.
  5047. """
  5048. imported_symbols = []
  5049. # Import Lookup Table. Contains ordinals or pointers to strings.
  5050. ilt = self.get_import_table(
  5051. original_first_thunk, max_length, contains_addresses
  5052. )
  5053. # Import Address Table. May have identical content to ILT if
  5054. # PE file is not bound. It will contain the address of the
  5055. # imported symbols once the binary is loaded or if it is already
  5056. # bound.
  5057. iat = self.get_import_table(first_thunk, max_length, contains_addresses)
  5058. # Would crash if IAT or ILT had None type
  5059. if (not iat or len(iat) == 0) and (not ilt or len(ilt) == 0):
  5060. self.__warnings.append(
  5061. "Damaged Import Table information. "
  5062. "ILT and/or IAT appear to be broken. "
  5063. f"OriginalFirstThunk: 0x{original_first_thunk:x} "
  5064. f"FirstThunk: 0x{first_thunk:x}"
  5065. )
  5066. return []
  5067. table = None
  5068. if ilt:
  5069. table = ilt
  5070. elif iat:
  5071. table = iat
  5072. else:
  5073. return None
  5074. imp_offset = 4
  5075. address_mask = 0x7FFFFFFF
  5076. if self.PE_TYPE == OPTIONAL_HEADER_MAGIC_PE:
  5077. ordinal_flag = IMAGE_ORDINAL_FLAG
  5078. elif self.PE_TYPE == OPTIONAL_HEADER_MAGIC_PE_PLUS:
  5079. ordinal_flag = IMAGE_ORDINAL_FLAG64
  5080. imp_offset = 8
  5081. address_mask = 0x7FFFFFFFFFFFFFFF
  5082. else:
  5083. # Some PEs may have an invalid value in the Magic field of the
  5084. # Optional Header. Just in case the remaining file is parseable
  5085. # let's pretend it's a 32bit PE32 by default.
  5086. ordinal_flag = IMAGE_ORDINAL_FLAG
  5087. num_invalid = 0
  5088. for idx, tbl_entry in enumerate(table):
  5089. imp_ord = None
  5090. imp_hint = None
  5091. imp_name = None
  5092. name_offset = None
  5093. hint_name_table_rva = None
  5094. import_by_ordinal = False # declare it here first
  5095. if tbl_entry.AddressOfData:
  5096. # If imported by ordinal, we will append the ordinal number
  5097. #
  5098. if tbl_entry.AddressOfData & ordinal_flag:
  5099. import_by_ordinal = True
  5100. imp_ord = tbl_entry.AddressOfData & 0xFFFF
  5101. imp_name = None
  5102. name_offset = None
  5103. else:
  5104. import_by_ordinal = False
  5105. try:
  5106. hint_name_table_rva = tbl_entry.AddressOfData & address_mask
  5107. data = self.get_data(hint_name_table_rva, 2)
  5108. # Get the Hint
  5109. imp_hint = self.get_word_from_data(data, 0)
  5110. imp_name = self.get_string_at_rva(
  5111. tbl_entry.AddressOfData + 2, MAX_IMPORT_NAME_LENGTH
  5112. )
  5113. if not is_valid_function_name(imp_name):
  5114. imp_name = b"*invalid*"
  5115. name_offset = self.get_offset_from_rva(
  5116. tbl_entry.AddressOfData + 2
  5117. )
  5118. except PEFormatError:
  5119. pass
  5120. # by nriva: we want the ThunkRVA and ThunkOffset
  5121. thunk_offset = tbl_entry.get_file_offset()
  5122. thunk_rva = self.get_rva_from_offset(thunk_offset)
  5123. imp_address = (
  5124. first_thunk + self.OPTIONAL_HEADER.ImageBase + idx * imp_offset
  5125. )
  5126. struct_iat = None
  5127. try:
  5128. if iat and ilt and ilt[idx].AddressOfData != iat[idx].AddressOfData:
  5129. imp_bound = iat[idx].AddressOfData
  5130. struct_iat = iat[idx]
  5131. else:
  5132. imp_bound = None
  5133. except IndexError:
  5134. imp_bound = None
  5135. # The file with hashes:
  5136. #
  5137. # MD5: bfe97192e8107d52dd7b4010d12b2924
  5138. # SHA256: 3d22f8b001423cb460811ab4f4789f277b35838d45c62ec0454c877e7c82c7f5
  5139. #
  5140. # has an invalid table built in a way that it's parseable but contains
  5141. # invalid entries that lead pefile to take extremely long amounts of time to
  5142. # parse. It also leads to extreme memory consumption.
  5143. # To prevent similar cases, if invalid entries are found in the middle of a
  5144. # table the parsing will be aborted
  5145. #
  5146. if imp_ord is None and imp_name is None:
  5147. raise PEFormatError("Invalid entries, aborting parsing.")
  5148. # Some PEs appear to interleave valid and invalid imports. Instead of
  5149. # aborting the parsing altogether we will simply skip the invalid entries.
  5150. # Although if we see 1000 invalid entries and no legit ones, we abort.
  5151. if imp_name == b"*invalid*":
  5152. if num_invalid > 1000 and num_invalid == idx:
  5153. raise PEFormatError("Too many invalid names, aborting parsing.")
  5154. num_invalid += 1
  5155. continue
  5156. if imp_ord or imp_name:
  5157. imported_symbols.append(
  5158. ImportData(
  5159. pe=self,
  5160. struct_table=tbl_entry,
  5161. struct_iat=struct_iat, # for bound imports if any
  5162. import_by_ordinal=import_by_ordinal,
  5163. ordinal=imp_ord,
  5164. ordinal_offset=tbl_entry.get_file_offset(),
  5165. hint=imp_hint,
  5166. name=imp_name,
  5167. name_offset=name_offset,
  5168. bound=imp_bound,
  5169. address=imp_address,
  5170. hint_name_table_rva=hint_name_table_rva,
  5171. thunk_offset=thunk_offset,
  5172. thunk_rva=thunk_rva,
  5173. )
  5174. )
  5175. return imported_symbols
  5176. def get_import_table(self, rva, max_length=None, contains_addresses=False):
  5177. table = []
  5178. # We need the ordinal flag for a simple heuristic
  5179. # we're implementing within the loop
  5180. #
  5181. if self.PE_TYPE == OPTIONAL_HEADER_MAGIC_PE:
  5182. ordinal_flag = IMAGE_ORDINAL_FLAG
  5183. format = self.__IMAGE_THUNK_DATA_format__
  5184. elif self.PE_TYPE == OPTIONAL_HEADER_MAGIC_PE_PLUS:
  5185. ordinal_flag = IMAGE_ORDINAL_FLAG64
  5186. format = self.__IMAGE_THUNK_DATA64_format__
  5187. else:
  5188. # Some PEs may have an invalid value in the Magic field of the
  5189. # Optional Header. Just in case the remaining file is parseable
  5190. # let's pretend it's a 32bit PE32 by default.
  5191. ordinal_flag = IMAGE_ORDINAL_FLAG
  5192. format = self.__IMAGE_THUNK_DATA_format__
  5193. expected_size = Structure(format).sizeof()
  5194. MAX_ADDRESS_SPREAD = 128 * 2**20 # 128 MB
  5195. ADDR_4GB = 2**32
  5196. MAX_REPEATED_ADDRESSES = 15
  5197. repeated_address = 0
  5198. addresses_of_data_set_64 = AddressSet()
  5199. addresses_of_data_set_32 = AddressSet()
  5200. start_rva = rva
  5201. while rva:
  5202. if max_length is not None and rva >= start_rva + max_length:
  5203. self.__warnings.append(
  5204. "Error parsing the import table. Entries go beyond bounds."
  5205. )
  5206. break
  5207. # Enforce an upper bounds on import symbols.
  5208. if self.__total_import_symbols > MAX_IMPORT_SYMBOLS:
  5209. self.__warnings.append(
  5210. "Excessive number of imports %d (>%s)"
  5211. % (self.__total_import_symbols, MAX_IMPORT_SYMBOLS)
  5212. )
  5213. break
  5214. self.__total_import_symbols += 1
  5215. # if we see too many times the same entry we assume it could be
  5216. # a table containing bogus data (with malicious intent or otherwise)
  5217. if repeated_address >= MAX_REPEATED_ADDRESSES:
  5218. return []
  5219. # if the addresses point somewhere but the difference between the highest
  5220. # and lowest address is larger than MAX_ADDRESS_SPREAD we assume a bogus
  5221. # table as the addresses should be contained within a module
  5222. if addresses_of_data_set_32.diff() > MAX_ADDRESS_SPREAD:
  5223. return []
  5224. if addresses_of_data_set_64.diff() > MAX_ADDRESS_SPREAD:
  5225. return []
  5226. failed = False
  5227. try:
  5228. data = self.get_data(rva, expected_size)
  5229. except PEFormatError:
  5230. failed = True
  5231. if failed or len(data) != expected_size:
  5232. self.__warnings.append(
  5233. "Error parsing the import table. " "Invalid data at RVA: 0x%x" % rva
  5234. )
  5235. return None
  5236. thunk_data = self.__unpack_data__(
  5237. format, data, file_offset=self.get_offset_from_rva(rva)
  5238. )
  5239. # If the thunk data contains VAs instead of RVAs, we need to normalize them
  5240. if contains_addresses:
  5241. thunk_data.AddressOfData = self.normalize_import_va(
  5242. thunk_data.AddressOfData
  5243. )
  5244. thunk_data.ForwarderString = self.normalize_import_va(
  5245. thunk_data.ForwarderString
  5246. )
  5247. thunk_data.Function = self.normalize_import_va(thunk_data.Function)
  5248. thunk_data.Ordinal = self.normalize_import_va(thunk_data.Ordinal)
  5249. # Check if the AddressOfData lies within the range of RVAs that it's
  5250. # being scanned, abort if that is the case, as it is very unlikely
  5251. # to be legitimate data.
  5252. # Seen in PE with SHA256:
  5253. # 5945bb6f0ac879ddf61b1c284f3b8d20c06b228e75ae4f571fa87f5b9512902c
  5254. if (
  5255. thunk_data
  5256. and thunk_data.AddressOfData >= start_rva
  5257. and thunk_data.AddressOfData <= rva
  5258. ):
  5259. self.__warnings.append(
  5260. "Error parsing the import table. "
  5261. "AddressOfData overlaps with THUNK_DATA for "
  5262. "THUNK at RVA 0x%x" % (rva)
  5263. )
  5264. break
  5265. if thunk_data and thunk_data.AddressOfData:
  5266. addr_of_data = thunk_data.AddressOfData
  5267. # If the entry looks like could be an ordinal...
  5268. if addr_of_data & ordinal_flag:
  5269. # but its value is beyond 2^16, we will assume it's a
  5270. # corrupted and ignore it altogether
  5271. if addr_of_data & 0x7FFFFFFF > 0xFFFF:
  5272. return []
  5273. # and if it looks like it should be an RVA
  5274. else:
  5275. # keep track of the RVAs seen and store them to study their
  5276. # properties. When certain non-standard features are detected
  5277. # the parsing will be aborted
  5278. if addr_of_data >= ADDR_4GB:
  5279. the_set = addresses_of_data_set_64
  5280. else:
  5281. the_set = addresses_of_data_set_32
  5282. if addr_of_data in the_set:
  5283. repeated_address += 1
  5284. the_set.add(addr_of_data)
  5285. if not thunk_data or thunk_data.all_zeroes():
  5286. break
  5287. rva += thunk_data.sizeof()
  5288. table.append(thunk_data)
  5289. return table
  5290. def get_memory_mapped_image(self, max_virtual_address=0x10000000, ImageBase=None):
  5291. """Returns the data corresponding to the memory layout of the PE file.
  5292. The data includes the PE header and the sections loaded at offsets
  5293. corresponding to their relative virtual addresses. (the VirtualAddress
  5294. section header member).
  5295. Any offset in this data corresponds to the absolute memory address
  5296. ImageBase+offset.
  5297. The optional argument 'max_virtual_address' provides with means of limiting
  5298. which sections are processed.
  5299. Any section with their VirtualAddress beyond this value will be skipped.
  5300. Normally, sections with values beyond this range are just there to confuse
  5301. tools. It's a common trick to see in packed executables.
  5302. If the 'ImageBase' optional argument is supplied, the file's relocations
  5303. will be applied to the image by calling the 'relocate_image()' method. Beware
  5304. that the relocation information is applied permanently.
  5305. """
  5306. # Rebase if requested
  5307. #
  5308. if ImageBase is not None:
  5309. # Keep a copy of the image's data before modifying it by rebasing it
  5310. #
  5311. original_data = self.__data__
  5312. self.relocate_image(ImageBase)
  5313. # Collect all sections in one code block
  5314. mapped_data = self.header
  5315. for section in self.sections:
  5316. # Miscellaneous integrity tests.
  5317. # Some packer will set these to bogus values to make tools go nuts.
  5318. if section.Misc_VirtualSize == 0 and section.SizeOfRawData == 0:
  5319. continue
  5320. srd = section.SizeOfRawData
  5321. prd = self.adjust_PointerToRawData(section.PointerToRawData)
  5322. VirtualAddress_adj = self.adjust_SectionAlignment(
  5323. section.VirtualAddress,
  5324. self.OPTIONAL_HEADER.SectionAlignment,
  5325. self.OPTIONAL_HEADER.FileAlignment,
  5326. )
  5327. if (
  5328. srd > len(self.__data__)
  5329. or prd > len(self.__data__)
  5330. or srd + prd > len(self.__data__)
  5331. or VirtualAddress_adj >= max_virtual_address
  5332. ):
  5333. continue
  5334. padding_length = VirtualAddress_adj - len(mapped_data)
  5335. if padding_length > 0:
  5336. mapped_data += b"\0" * padding_length
  5337. elif padding_length < 0:
  5338. mapped_data = mapped_data[:padding_length]
  5339. mapped_data += section.get_data()
  5340. # If the image was rebased, restore it to its original form
  5341. #
  5342. if ImageBase is not None:
  5343. self.__data__ = original_data
  5344. return mapped_data
  5345. def get_resources_strings(self):
  5346. """Returns a list of all the strings found within the resources (if any).
  5347. This method will scan all entries in the resources directory of the PE, if
  5348. there is one, and will return a [] with the strings.
  5349. An empty list will be returned otherwise.
  5350. """
  5351. resources_strings = []
  5352. if hasattr(self, "DIRECTORY_ENTRY_RESOURCE"):
  5353. for res_type in self.DIRECTORY_ENTRY_RESOURCE.entries:
  5354. if hasattr(res_type, "directory"):
  5355. for resource_id in res_type.directory.entries:
  5356. if hasattr(resource_id, "directory"):
  5357. if (
  5358. hasattr(resource_id.directory, "strings")
  5359. and resource_id.directory.strings
  5360. ):
  5361. for res_string in list(
  5362. resource_id.directory.strings.values()
  5363. ):
  5364. resources_strings.append(res_string)
  5365. return resources_strings
  5366. def get_data(self, rva=0, length=None):
  5367. """Get data regardless of the section where it lies on.
  5368. Given a RVA and the size of the chunk to retrieve, this method
  5369. will find the section where the data lies and return the data.
  5370. """
  5371. s = self.get_section_by_rva(rva)
  5372. if length is None:
  5373. end = None
  5374. else:
  5375. end = rva + length
  5376. if not s:
  5377. if rva < len(self.header):
  5378. return self.header[rva:end]
  5379. # Before we give up we check whether the file might
  5380. # contain the data anyway. There are cases of PE files
  5381. # without sections that rely on windows loading the first
  5382. # 8291 bytes into memory and assume the data will be
  5383. # there
  5384. # A functional file with these characteristics is:
  5385. # MD5: 0008892cdfbc3bda5ce047c565e52295
  5386. # SHA-1: c7116b9ff950f86af256defb95b5d4859d4752a9
  5387. #
  5388. if rva < len(self.__data__):
  5389. return self.__data__[rva:end]
  5390. raise PEFormatError("data at RVA can't be fetched. Corrupt header?")
  5391. return s.get_data(rva, length)
  5392. def get_rva_from_offset(self, offset):
  5393. """Get the RVA corresponding to this file offset."""
  5394. s = self.get_section_by_offset(offset)
  5395. if not s:
  5396. if self.sections:
  5397. lowest_rva = min(
  5398. [
  5399. self.adjust_SectionAlignment(
  5400. s.VirtualAddress,
  5401. self.OPTIONAL_HEADER.SectionAlignment,
  5402. self.OPTIONAL_HEADER.FileAlignment,
  5403. )
  5404. for s in self.sections
  5405. ]
  5406. )
  5407. if offset < lowest_rva:
  5408. # We will assume that the offset lies within the headers, or
  5409. # at least points before where the earliest section starts
  5410. # and we will simply return the offset as the RVA
  5411. #
  5412. # The case illustrating this behavior can be found at:
  5413. # https://corkami.blogspot.com/2010/01/hey-hey-hey-whats-in-your-head.html
  5414. # where the import table is not contained by any section
  5415. # hence the RVA needs to be resolved to a raw offset
  5416. return offset
  5417. return None
  5418. else:
  5419. return offset
  5420. return s.get_rva_from_offset(offset)
  5421. def get_offset_from_rva(self, rva):
  5422. """Get the file offset corresponding to this RVA.
  5423. Given a RVA , this method will find the section where the
  5424. data lies and return the offset within the file.
  5425. """
  5426. s = self.get_section_by_rva(rva)
  5427. if not s:
  5428. # If not found within a section assume it might
  5429. # point to overlay data or otherwise data present
  5430. # but not contained in any section. In those
  5431. # cases the RVA should equal the offset
  5432. if rva < len(self.__data__):
  5433. return rva
  5434. raise PEFormatError(f"data at RVA 0x{rva:x} can't be fetched")
  5435. return s.get_offset_from_rva(rva)
  5436. def get_string_at_rva(self, rva, max_length=MAX_STRING_LENGTH):
  5437. """Get an ASCII string located at the given address."""
  5438. if rva is None:
  5439. return None
  5440. s = self.get_section_by_rva(rva)
  5441. if not s:
  5442. return self.get_string_from_data(0, self.__data__[rva : rva + max_length])
  5443. return self.get_string_from_data(0, s.get_data(rva, length=max_length))
  5444. def get_bytes_from_data(self, offset, data):
  5445. """."""
  5446. if offset > len(data):
  5447. return b""
  5448. d = data[offset:]
  5449. if isinstance(d, bytearray):
  5450. return bytes(d)
  5451. return d
  5452. def get_string_from_data(self, offset, data):
  5453. """Get an ASCII string from data."""
  5454. s = self.get_bytes_from_data(offset, data)
  5455. end = s.find(b"\0")
  5456. if end >= 0:
  5457. s = s[:end]
  5458. return s
  5459. def get_string_u_at_rva(self, rva, max_length=2**16, encoding=None):
  5460. """Get an Unicode string located at the given address."""
  5461. if max_length == 0:
  5462. return b""
  5463. # If the RVA is invalid let the exception reach the callers. All
  5464. # call-sites of get_string_u_at_rva() will handle it.
  5465. data = self.get_data(rva, 2)
  5466. # max_length is the maximum count of 16bit characters needs to be
  5467. # doubled to get size in bytes
  5468. max_length <<= 1
  5469. requested = min(max_length, 256)
  5470. data = self.get_data(rva, requested)
  5471. # try to find null-termination
  5472. null_index = -1
  5473. while True:
  5474. null_index = data.find(b"\x00\x00", null_index + 1)
  5475. if null_index == -1:
  5476. data_length = len(data)
  5477. if data_length < requested or data_length == max_length:
  5478. null_index = len(data) >> 1
  5479. break
  5480. # Request remaining part of data limited by max_length
  5481. data += self.get_data(rva + data_length, max_length - data_length)
  5482. null_index = requested - 1
  5483. requested = max_length
  5484. elif null_index % 2 == 0:
  5485. null_index >>= 1
  5486. break
  5487. # convert selected part of the string to unicode
  5488. uchrs = struct.unpack("<{:d}H".format(null_index), data[: null_index * 2])
  5489. s = "".join(map(chr, uchrs))
  5490. if encoding:
  5491. return s.encode(encoding, "backslashreplace_")
  5492. return s.encode("utf-8", "backslashreplace_")
  5493. def get_section_by_offset(self, offset):
  5494. """Get the section containing the given file offset."""
  5495. for section in self.sections:
  5496. if section.contains_offset(offset):
  5497. return section
  5498. return None
  5499. def get_section_by_rva(self, rva):
  5500. """Get the section containing the given address."""
  5501. # if we look a lot of times at RVA in the same section, "cache" the last used section
  5502. # to speedup lookups (very useful when parsing import table)
  5503. if self._get_section_by_rva_last_used is not None:
  5504. if self._get_section_by_rva_last_used.contains_rva(rva):
  5505. return self._get_section_by_rva_last_used
  5506. for section in self.sections:
  5507. if section.contains_rva(rva):
  5508. self._get_section_by_rva_last_used = section
  5509. return section
  5510. return None
  5511. def __str__(self):
  5512. return self.dump_info()
  5513. def has_relocs(self):
  5514. """Checks if the PE file has relocation directory"""
  5515. return hasattr(self, "DIRECTORY_ENTRY_BASERELOC")
  5516. def has_dynamic_relocs(self):
  5517. if hasattr(self, "DIRECTORY_ENTRY_LOAD_CONFIG"):
  5518. if self.DIRECTORY_ENTRY_LOAD_CONFIG.dynamic_relocations:
  5519. return True
  5520. return False
  5521. def print_info(self, encoding="utf-8"):
  5522. """Print all the PE header information in a human readable from."""
  5523. print(self.dump_info(encoding=encoding))
  5524. def dump_info(self, dump=None, encoding="ascii"):
  5525. """Dump all the PE header information into human readable string."""
  5526. if dump is None:
  5527. dump = Dump()
  5528. warnings = self.get_warnings()
  5529. if warnings:
  5530. dump.add_header("Parsing Warnings")
  5531. for warning in warnings:
  5532. dump.add_line(warning)
  5533. dump.add_newline()
  5534. dump.add_header("DOS_HEADER")
  5535. dump.add_lines(self.DOS_HEADER.dump())
  5536. dump.add_newline()
  5537. dump.add_header("NT_HEADERS")
  5538. dump.add_lines(self.NT_HEADERS.dump())
  5539. dump.add_newline()
  5540. dump.add_header("FILE_HEADER")
  5541. dump.add_lines(self.FILE_HEADER.dump())
  5542. image_flags = retrieve_flags(IMAGE_CHARACTERISTICS, "IMAGE_FILE_")
  5543. dump.add("Flags: ")
  5544. flags = []
  5545. for flag in sorted(image_flags):
  5546. if getattr(self.FILE_HEADER, flag[0]):
  5547. flags.append(flag[0])
  5548. dump.add_line(", ".join(flags))
  5549. dump.add_newline()
  5550. if hasattr(self, "OPTIONAL_HEADER") and self.OPTIONAL_HEADER is not None:
  5551. dump.add_header("OPTIONAL_HEADER")
  5552. dump.add_lines(self.OPTIONAL_HEADER.dump())
  5553. dll_characteristics_flags = retrieve_flags(
  5554. DLL_CHARACTERISTICS, "IMAGE_DLLCHARACTERISTICS_"
  5555. )
  5556. dump.add("DllCharacteristics: ")
  5557. flags = []
  5558. for flag in sorted(dll_characteristics_flags):
  5559. if getattr(self.OPTIONAL_HEADER, flag[0]):
  5560. flags.append(flag[0])
  5561. dump.add_line(", ".join(flags))
  5562. ex_dll_characteristics_flags = retrieve_flags(
  5563. EX_DLL_CHARACTERISTICS, "IMAGE_DLLCHARACTERISTICS_EX_"
  5564. )
  5565. if ex_dll_characteristics_flags:
  5566. flags = []
  5567. if (
  5568. hasattr(self, "DIRECTORY_ENTRY_DEBUG")
  5569. and self.DIRECTORY_ENTRY_DEBUG is not None
  5570. ):
  5571. for debug_entry in self.DIRECTORY_ENTRY_DEBUG:
  5572. if (
  5573. debug_entry.struct.Type
  5574. == DEBUG_TYPE["IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS"]
  5575. ):
  5576. for flag in sorted(ex_dll_characteristics_flags):
  5577. if getattr(debug_entry.entry, flag[0]):
  5578. flags.append(flag[0])
  5579. if flags:
  5580. dump.add("ExDllCharacteristics: ")
  5581. dump.add_line(", ".join(flags))
  5582. dump.add_newline()
  5583. dump.add_header("PE Sections")
  5584. section_flags = retrieve_flags(SECTION_CHARACTERISTICS, "IMAGE_SCN_")
  5585. for section in self.sections:
  5586. dump.add_lines(section.dump())
  5587. dump.add("Flags: ")
  5588. flags = []
  5589. for flag in sorted(section_flags):
  5590. if getattr(section, flag[0]):
  5591. flags.append(flag[0])
  5592. dump.add_line(", ".join(flags))
  5593. dump.add_line(
  5594. "Entropy: {0:f} (Min=0.0, Max=8.0)".format(section.get_entropy())
  5595. )
  5596. if md5 is not None:
  5597. dump.add_line("MD5 hash: {0}".format(section.get_hash_md5()))
  5598. if sha1 is not None:
  5599. dump.add_line("SHA-1 hash: %s" % section.get_hash_sha1())
  5600. if sha256 is not None:
  5601. dump.add_line("SHA-256 hash: %s" % section.get_hash_sha256())
  5602. if sha512 is not None:
  5603. dump.add_line("SHA-512 hash: %s" % section.get_hash_sha512())
  5604. dump.add_newline()
  5605. if hasattr(self, "OPTIONAL_HEADER") and hasattr(
  5606. self.OPTIONAL_HEADER, "DATA_DIRECTORY"
  5607. ):
  5608. dump.add_header("Directories")
  5609. for directory in self.OPTIONAL_HEADER.DATA_DIRECTORY:
  5610. if directory is not None:
  5611. dump.add_lines(directory.dump())
  5612. dump.add_newline()
  5613. if hasattr(self, "VS_VERSIONINFO"):
  5614. for idx, vinfo_entry in enumerate(self.VS_VERSIONINFO):
  5615. if len(self.VS_VERSIONINFO) > 1:
  5616. dump.add_header(f"Version Information {idx + 1}")
  5617. else:
  5618. dump.add_header("Version Information")
  5619. if vinfo_entry is not None:
  5620. dump.add_lines(vinfo_entry.dump())
  5621. dump.add_newline()
  5622. if hasattr(self, "VS_FIXEDFILEINFO"):
  5623. dump.add_lines(self.VS_FIXEDFILEINFO[idx].dump())
  5624. dump.add_newline()
  5625. if hasattr(self, "FileInfo") and len(self.FileInfo) > idx:
  5626. for entry in self.FileInfo[idx]:
  5627. dump.add_lines(entry.dump())
  5628. dump.add_newline()
  5629. if hasattr(entry, "StringTable"):
  5630. for st_entry in entry.StringTable:
  5631. [dump.add_line(" " + line) for line in st_entry.dump()]
  5632. dump.add_line(
  5633. " LangID: {0}".format(
  5634. st_entry.LangID.decode(
  5635. encoding, "backslashreplace_"
  5636. )
  5637. )
  5638. )
  5639. dump.add_newline()
  5640. for str_entry in sorted(list(st_entry.entries.items())):
  5641. # try:
  5642. dump.add_line(
  5643. " {0}: {1}".format(
  5644. str_entry[0].decode(
  5645. encoding, "backslashreplace_"
  5646. ),
  5647. str_entry[1].decode(
  5648. encoding, "backslashreplace_"
  5649. ),
  5650. )
  5651. )
  5652. dump.add_newline()
  5653. elif hasattr(entry, "Var"):
  5654. for var_entry in entry.Var:
  5655. if hasattr(var_entry, "entry"):
  5656. [
  5657. dump.add_line(" " + line)
  5658. for line in var_entry.dump()
  5659. ]
  5660. dump.add_line(
  5661. " {0}: {1}".format(
  5662. list(var_entry.entry.keys())[0].decode(
  5663. "utf-8", "backslashreplace_"
  5664. ),
  5665. list(var_entry.entry.values())[0],
  5666. )
  5667. )
  5668. dump.add_newline()
  5669. if hasattr(self, "DIRECTORY_ENTRY_EXPORT"):
  5670. dump.add_header("Exported symbols")
  5671. dump.add_lines(self.DIRECTORY_ENTRY_EXPORT.struct.dump())
  5672. dump.add_newline()
  5673. dump.add_line("%-10s %-10s %s" % ("Ordinal", "RVA", "Name"))
  5674. for export in self.DIRECTORY_ENTRY_EXPORT.symbols:
  5675. if export.address is not None:
  5676. name = b"None"
  5677. if export.name:
  5678. name = export.name
  5679. dump.add(
  5680. "%-10d 0x%08X %s"
  5681. % (export.ordinal, export.address, name.decode(encoding))
  5682. )
  5683. if export.forwarder:
  5684. dump.add_line(
  5685. " forwarder: {0}".format(
  5686. export.forwarder.decode(encoding, "backslashreplace_")
  5687. )
  5688. )
  5689. else:
  5690. dump.add_newline()
  5691. dump.add_newline()
  5692. if hasattr(self, "DIRECTORY_ENTRY_IMPORT"):
  5693. dump.add_header("Imported symbols")
  5694. for module in self.DIRECTORY_ENTRY_IMPORT:
  5695. dump.add_lines(module.struct.dump())
  5696. # Print the name of the DLL if there are no imports.
  5697. if not module.imports:
  5698. dump.add(
  5699. " Name -> {0}".format(
  5700. self.get_string_at_rva(module.struct.Name).decode(
  5701. encoding, "backslashreplace_"
  5702. )
  5703. )
  5704. )
  5705. dump.add_newline()
  5706. dump.add_newline()
  5707. for symbol in module.imports:
  5708. if symbol.import_by_ordinal is True:
  5709. if symbol.name is not None:
  5710. dump.add(
  5711. "{0}.{1} Ordinal[{2}] (Imported by Ordinal)".format(
  5712. module.dll.decode("utf-8"),
  5713. symbol.name.decode("utf-8"),
  5714. symbol.ordinal,
  5715. )
  5716. )
  5717. else:
  5718. dump.add(
  5719. "{0} Ordinal[{1}] (Imported by Ordinal)".format(
  5720. module.dll.decode("utf-8"), symbol.ordinal
  5721. )
  5722. )
  5723. else:
  5724. dump.add(
  5725. "{0}.{1} Hint[{2:d}]".format(
  5726. module.dll.decode(encoding, "backslashreplace_"),
  5727. symbol.name.decode(encoding, "backslashreplace_"),
  5728. symbol.hint,
  5729. )
  5730. )
  5731. if symbol.bound:
  5732. dump.add_line(" Bound: 0x{0:08X}".format(symbol.bound))
  5733. else:
  5734. dump.add_newline()
  5735. dump.add_newline()
  5736. if hasattr(self, "DIRECTORY_ENTRY_BOUND_IMPORT"):
  5737. dump.add_header("Bound imports")
  5738. for bound_imp_desc in self.DIRECTORY_ENTRY_BOUND_IMPORT:
  5739. dump.add_lines(bound_imp_desc.struct.dump())
  5740. dump.add_line(
  5741. "DLL: {0}".format(
  5742. bound_imp_desc.name.decode(encoding, "backslashreplace_")
  5743. )
  5744. )
  5745. dump.add_newline()
  5746. for bound_imp_ref in bound_imp_desc.entries:
  5747. dump.add_lines(bound_imp_ref.struct.dump(), 4)
  5748. dump.add_line(
  5749. "DLL: {0}".format(
  5750. bound_imp_ref.name.decode(encoding, "backslashreplace_")
  5751. ),
  5752. 4,
  5753. )
  5754. dump.add_newline()
  5755. if hasattr(self, "DIRECTORY_ENTRY_DELAY_IMPORT"):
  5756. dump.add_header("Delay Imported symbols")
  5757. for module in self.DIRECTORY_ENTRY_DELAY_IMPORT:
  5758. dump.add_lines(module.struct.dump())
  5759. dump.add_newline()
  5760. for symbol in module.imports:
  5761. if symbol.import_by_ordinal is True:
  5762. dump.add(
  5763. "{0} Ordinal[{1:d}] (Imported by Ordinal)".format(
  5764. module.dll.decode(encoding, "backslashreplace_"),
  5765. symbol.ordinal,
  5766. )
  5767. )
  5768. else:
  5769. dump.add(
  5770. "{0}.{1} Hint[{2}]".format(
  5771. module.dll.decode(encoding, "backslashreplace_"),
  5772. symbol.name.decode(encoding, "backslashreplace_"),
  5773. symbol.hint,
  5774. )
  5775. )
  5776. if symbol.bound:
  5777. dump.add_line(" Bound: 0x{0:08X}".format(symbol.bound))
  5778. else:
  5779. dump.add_newline()
  5780. dump.add_newline()
  5781. if hasattr(self, "DIRECTORY_ENTRY_RESOURCE"):
  5782. dump.add_header("Resource directory")
  5783. dump.add_lines(self.DIRECTORY_ENTRY_RESOURCE.struct.dump())
  5784. for res_type in self.DIRECTORY_ENTRY_RESOURCE.entries:
  5785. if res_type.name is not None:
  5786. name = res_type.name.decode(encoding, "backslashreplace_")
  5787. dump.add_line(
  5788. f"Name: [{name}]",
  5789. 2,
  5790. )
  5791. else:
  5792. res_type_id = RESOURCE_TYPE.get(res_type.struct.Id, "-")
  5793. dump.add_line(
  5794. f"Id: [0x{res_type.struct.Id:X}] ({res_type_id})",
  5795. 2,
  5796. )
  5797. dump.add_lines(res_type.struct.dump(), 2)
  5798. if hasattr(res_type, "directory"):
  5799. dump.add_lines(res_type.directory.struct.dump(), 4)
  5800. for resource_id in res_type.directory.entries:
  5801. if resource_id.name is not None:
  5802. name = resource_id.name.decode("utf-8", "backslashreplace_")
  5803. dump.add_line(
  5804. f"Name: [{name}]",
  5805. 6,
  5806. )
  5807. else:
  5808. dump.add_line(f"Id: [0x{resource_id.struct.Id:X}]", 6)
  5809. dump.add_lines(resource_id.struct.dump(), 6)
  5810. if hasattr(resource_id, "directory"):
  5811. dump.add_lines(resource_id.directory.struct.dump(), 8)
  5812. for resource_lang in resource_id.directory.entries:
  5813. if hasattr(resource_lang, "data"):
  5814. dump.add_line(
  5815. "\\--- LANG [%d,%d][%s,%s]"
  5816. % (
  5817. resource_lang.data.lang,
  5818. resource_lang.data.sublang,
  5819. LANG.get(
  5820. resource_lang.data.lang, "*unknown*"
  5821. ),
  5822. get_sublang_name_for_lang(
  5823. resource_lang.data.lang,
  5824. resource_lang.data.sublang,
  5825. ),
  5826. ),
  5827. 8,
  5828. )
  5829. dump.add_lines(resource_lang.struct.dump(), 10)
  5830. dump.add_lines(resource_lang.data.struct.dump(), 12)
  5831. if (
  5832. hasattr(resource_id.directory, "strings")
  5833. and resource_id.directory.strings
  5834. ):
  5835. dump.add_line("[STRINGS]", 10)
  5836. for idx, res_string in list(
  5837. sorted(resource_id.directory.strings.items())
  5838. ):
  5839. dump.add_line(
  5840. "{0:6d}: {1}".format(
  5841. idx,
  5842. res_string.encode(
  5843. "unicode-escape", "backslashreplace"
  5844. ).decode("ascii"),
  5845. ),
  5846. 12,
  5847. )
  5848. dump.add_newline()
  5849. dump.add_newline()
  5850. if (
  5851. hasattr(self, "DIRECTORY_ENTRY_TLS")
  5852. and self.DIRECTORY_ENTRY_TLS
  5853. and self.DIRECTORY_ENTRY_TLS.struct
  5854. ):
  5855. dump.add_header("TLS")
  5856. dump.add_lines(self.DIRECTORY_ENTRY_TLS.struct.dump())
  5857. dump.add_newline()
  5858. if (
  5859. hasattr(self, "DIRECTORY_ENTRY_LOAD_CONFIG")
  5860. and self.DIRECTORY_ENTRY_LOAD_CONFIG
  5861. and self.DIRECTORY_ENTRY_LOAD_CONFIG.struct
  5862. ):
  5863. dump.add_header("LOAD_CONFIG")
  5864. dump.add_lines(self.DIRECTORY_ENTRY_LOAD_CONFIG.struct.dump())
  5865. dump.add_newline()
  5866. if hasattr(self, "DIRECTORY_ENTRY_DEBUG"):
  5867. dump.add_header("Debug information")
  5868. for dbg in self.DIRECTORY_ENTRY_DEBUG:
  5869. dump.add_lines(dbg.struct.dump())
  5870. try:
  5871. dump.add_line("Type: " + DEBUG_TYPE[dbg.struct.Type])
  5872. except KeyError:
  5873. dump.add_line("Type: 0x{0:x}(Unknown)".format(dbg.struct.Type))
  5874. dump.add_newline()
  5875. if dbg.entry:
  5876. dump.add_lines(dbg.entry.dump(), 4)
  5877. dump.add_newline()
  5878. if self.has_relocs():
  5879. dump.add_header("Base relocations")
  5880. for base_reloc in self.DIRECTORY_ENTRY_BASERELOC:
  5881. dump.add_lines(base_reloc.struct.dump())
  5882. for reloc in base_reloc.entries:
  5883. try:
  5884. dump.add_line(
  5885. "%08Xh %s" % (reloc.rva, RELOCATION_TYPE[reloc.type][16:]),
  5886. 4,
  5887. )
  5888. except KeyError:
  5889. dump.add_line(
  5890. "0x%08X 0x%x(Unknown)" % (reloc.rva, reloc.type), 4
  5891. )
  5892. dump.add_newline()
  5893. if (
  5894. hasattr(self, "DIRECTORY_ENTRY_EXCEPTION")
  5895. and len(self.DIRECTORY_ENTRY_EXCEPTION) > 0
  5896. ):
  5897. dump.add_header("Unwind data for exception handling")
  5898. for rf in self.DIRECTORY_ENTRY_EXCEPTION:
  5899. dump.add_lines(rf.struct.dump())
  5900. if hasattr(rf, "unwindinfo") and rf.unwindinfo is not None:
  5901. dump.add_lines(rf.unwindinfo.dump(), 4)
  5902. return dump.get_text()
  5903. def dump_dict(self):
  5904. """Dump all the PE header information into a dictionary."""
  5905. dump_dict = {}
  5906. warnings = self.get_warnings()
  5907. if warnings:
  5908. dump_dict["Parsing Warnings"] = warnings
  5909. dump_dict["DOS_HEADER"] = self.DOS_HEADER.dump_dict()
  5910. dump_dict["NT_HEADERS"] = self.NT_HEADERS.dump_dict()
  5911. dump_dict["FILE_HEADER"] = self.FILE_HEADER.dump_dict()
  5912. image_flags = retrieve_flags(IMAGE_CHARACTERISTICS, "IMAGE_FILE_")
  5913. dump_dict["Flags"] = []
  5914. for flag in image_flags:
  5915. if getattr(self.FILE_HEADER, flag[0]):
  5916. dump_dict["Flags"].append(flag[0])
  5917. if hasattr(self, "OPTIONAL_HEADER") and self.OPTIONAL_HEADER is not None:
  5918. dump_dict["OPTIONAL_HEADER"] = self.OPTIONAL_HEADER.dump_dict()
  5919. dll_characteristics_flags = retrieve_flags(
  5920. DLL_CHARACTERISTICS, "IMAGE_DLLCHARACTERISTICS_"
  5921. )
  5922. dump_dict["DllCharacteristics"] = []
  5923. for flag in dll_characteristics_flags:
  5924. if getattr(self.OPTIONAL_HEADER, flag[0]):
  5925. dump_dict["DllCharacteristics"].append(flag[0])
  5926. dump_dict["PE Sections"] = []
  5927. section_flags = retrieve_flags(SECTION_CHARACTERISTICS, "IMAGE_SCN_")
  5928. for section in self.sections:
  5929. section_dict = section.dump_dict()
  5930. dump_dict["PE Sections"].append(section_dict)
  5931. section_dict["Flags"] = []
  5932. for flag in section_flags:
  5933. if getattr(section, flag[0]):
  5934. section_dict["Flags"].append(flag[0])
  5935. section_dict["Entropy"] = section.get_entropy()
  5936. if md5 is not None:
  5937. section_dict["MD5"] = section.get_hash_md5()
  5938. if sha1 is not None:
  5939. section_dict["SHA1"] = section.get_hash_sha1()
  5940. if sha256 is not None:
  5941. section_dict["SHA256"] = section.get_hash_sha256()
  5942. if sha512 is not None:
  5943. section_dict["SHA512"] = section.get_hash_sha512()
  5944. if hasattr(self, "OPTIONAL_HEADER") and hasattr(
  5945. self.OPTIONAL_HEADER, "DATA_DIRECTORY"
  5946. ):
  5947. dump_dict["Directories"] = []
  5948. for idx, directory in enumerate(self.OPTIONAL_HEADER.DATA_DIRECTORY):
  5949. if directory is not None:
  5950. dump_dict["Directories"].append(directory.dump_dict())
  5951. if hasattr(self, "VS_VERSIONINFO"):
  5952. dump_dict["Version Information"] = []
  5953. for idx, vs_vinfo in enumerate(self.VS_VERSIONINFO):
  5954. version_info_list = []
  5955. version_info_list.append(vs_vinfo.dump_dict())
  5956. if hasattr(self, "VS_FIXEDFILEINFO"):
  5957. version_info_list.append(self.VS_FIXEDFILEINFO[idx].dump_dict())
  5958. if hasattr(self, "FileInfo") and len(self.FileInfo) > idx:
  5959. fileinfo_list = []
  5960. version_info_list.append(fileinfo_list)
  5961. for entry in self.FileInfo[idx]:
  5962. fileinfo_list.append(entry.dump_dict())
  5963. if hasattr(entry, "StringTable"):
  5964. stringtable_dict = {}
  5965. for st_entry in entry.StringTable:
  5966. fileinfo_list.extend(st_entry.dump_dict())
  5967. stringtable_dict["LangID"] = st_entry.LangID
  5968. for str_entry in list(st_entry.entries.items()):
  5969. stringtable_dict[str_entry[0]] = str_entry[1]
  5970. fileinfo_list.append(stringtable_dict)
  5971. elif hasattr(entry, "Var"):
  5972. for var_entry in entry.Var:
  5973. var_dict = {}
  5974. if hasattr(var_entry, "entry"):
  5975. fileinfo_list.extend(var_entry.dump_dict())
  5976. var_dict[list(var_entry.entry.keys())[0]] = list(
  5977. var_entry.entry.values()
  5978. )[0]
  5979. fileinfo_list.append(var_dict)
  5980. dump_dict["Version Information"].append(version_info_list)
  5981. if hasattr(self, "DIRECTORY_ENTRY_EXPORT"):
  5982. dump_dict["Exported symbols"] = []
  5983. dump_dict["Exported symbols"].append(
  5984. self.DIRECTORY_ENTRY_EXPORT.struct.dump_dict()
  5985. )
  5986. for export in self.DIRECTORY_ENTRY_EXPORT.symbols:
  5987. export_dict = {}
  5988. if export.address is not None:
  5989. export_dict.update(
  5990. {
  5991. "Ordinal": export.ordinal,
  5992. "RVA": export.address,
  5993. "Name": export.name,
  5994. }
  5995. )
  5996. if export.forwarder:
  5997. export_dict["forwarder"] = export.forwarder
  5998. dump_dict["Exported symbols"].append(export_dict)
  5999. if hasattr(self, "DIRECTORY_ENTRY_IMPORT"):
  6000. dump_dict["Imported symbols"] = []
  6001. for module in self.DIRECTORY_ENTRY_IMPORT:
  6002. import_list = []
  6003. dump_dict["Imported symbols"].append(import_list)
  6004. import_list.append(module.struct.dump_dict())
  6005. for symbol in module.imports:
  6006. symbol_dict = {}
  6007. if symbol.import_by_ordinal is True:
  6008. symbol_dict["DLL"] = module.dll
  6009. symbol_dict["Ordinal"] = symbol.ordinal
  6010. else:
  6011. symbol_dict["DLL"] = module.dll
  6012. symbol_dict["Name"] = symbol.name
  6013. symbol_dict["Hint"] = symbol.hint
  6014. if symbol.bound:
  6015. symbol_dict["Bound"] = symbol.bound
  6016. import_list.append(symbol_dict)
  6017. if hasattr(self, "DIRECTORY_ENTRY_BOUND_IMPORT"):
  6018. dump_dict["Bound imports"] = []
  6019. for bound_imp_desc in self.DIRECTORY_ENTRY_BOUND_IMPORT:
  6020. bound_imp_desc_dict = {}
  6021. dump_dict["Bound imports"].append(bound_imp_desc_dict)
  6022. bound_imp_desc_dict.update(bound_imp_desc.struct.dump_dict())
  6023. bound_imp_desc_dict["DLL"] = bound_imp_desc.name
  6024. for bound_imp_ref in bound_imp_desc.entries:
  6025. bound_imp_ref_dict = {}
  6026. bound_imp_ref_dict.update(bound_imp_ref.struct.dump_dict())
  6027. bound_imp_ref_dict["DLL"] = bound_imp_ref.name
  6028. if hasattr(self, "DIRECTORY_ENTRY_DELAY_IMPORT"):
  6029. dump_dict["Delay Imported symbols"] = []
  6030. for module in self.DIRECTORY_ENTRY_DELAY_IMPORT:
  6031. module_list = []
  6032. dump_dict["Delay Imported symbols"].append(module_list)
  6033. module_list.append(module.struct.dump_dict())
  6034. for symbol in module.imports:
  6035. symbol_dict = {}
  6036. if symbol.import_by_ordinal is True:
  6037. symbol_dict["DLL"] = module.dll
  6038. symbol_dict["Ordinal"] = symbol.ordinal
  6039. else:
  6040. symbol_dict["DLL"] = module.dll
  6041. symbol_dict["Name"] = symbol.name
  6042. symbol_dict["Hint"] = symbol.hint
  6043. if symbol.bound:
  6044. symbol_dict["Bound"] = symbol.bound
  6045. module_list.append(symbol_dict)
  6046. if hasattr(self, "DIRECTORY_ENTRY_RESOURCE"):
  6047. dump_dict["Resource directory"] = []
  6048. dump_dict["Resource directory"].append(
  6049. self.DIRECTORY_ENTRY_RESOURCE.struct.dump_dict()
  6050. )
  6051. for res_type in self.DIRECTORY_ENTRY_RESOURCE.entries:
  6052. resource_type_dict = {}
  6053. if res_type.name is not None:
  6054. resource_type_dict["Name"] = res_type.name
  6055. else:
  6056. resource_type_dict["Id"] = (
  6057. res_type.struct.Id,
  6058. RESOURCE_TYPE.get(res_type.struct.Id, "-"),
  6059. )
  6060. resource_type_dict.update(res_type.struct.dump_dict())
  6061. dump_dict["Resource directory"].append(resource_type_dict)
  6062. if hasattr(res_type, "directory"):
  6063. directory_list = []
  6064. directory_list.append(res_type.directory.struct.dump_dict())
  6065. dump_dict["Resource directory"].append(directory_list)
  6066. for resource_id in res_type.directory.entries:
  6067. resource_id_dict = {}
  6068. if resource_id.name is not None:
  6069. resource_id_dict["Name"] = resource_id.name
  6070. else:
  6071. resource_id_dict["Id"] = resource_id.struct.Id
  6072. resource_id_dict.update(resource_id.struct.dump_dict())
  6073. directory_list.append(resource_id_dict)
  6074. if hasattr(resource_id, "directory"):
  6075. resource_id_list = []
  6076. resource_id_list.append(
  6077. resource_id.directory.struct.dump_dict()
  6078. )
  6079. directory_list.append(resource_id_list)
  6080. for resource_lang in resource_id.directory.entries:
  6081. if hasattr(resource_lang, "data"):
  6082. resource_lang_dict = {}
  6083. resource_lang_dict["LANG"] = resource_lang.data.lang
  6084. resource_lang_dict[
  6085. "SUBLANG"
  6086. ] = resource_lang.data.sublang
  6087. resource_lang_dict["LANG_NAME"] = LANG.get(
  6088. resource_lang.data.lang, "*unknown*"
  6089. )
  6090. resource_lang_dict[
  6091. "SUBLANG_NAME"
  6092. ] = get_sublang_name_for_lang(
  6093. resource_lang.data.lang,
  6094. resource_lang.data.sublang,
  6095. )
  6096. resource_lang_dict.update(
  6097. resource_lang.struct.dump_dict()
  6098. )
  6099. resource_lang_dict.update(
  6100. resource_lang.data.struct.dump_dict()
  6101. )
  6102. resource_id_list.append(resource_lang_dict)
  6103. if (
  6104. hasattr(resource_id.directory, "strings")
  6105. and resource_id.directory.strings
  6106. ):
  6107. for idx, res_string in list(
  6108. resource_id.directory.strings.items()
  6109. ):
  6110. resource_id_list.append(
  6111. res_string.encode(
  6112. "unicode-escape", "backslashreplace"
  6113. ).decode("ascii")
  6114. )
  6115. if (
  6116. hasattr(self, "DIRECTORY_ENTRY_TLS")
  6117. and self.DIRECTORY_ENTRY_TLS
  6118. and self.DIRECTORY_ENTRY_TLS.struct
  6119. ):
  6120. dump_dict["TLS"] = self.DIRECTORY_ENTRY_TLS.struct.dump_dict()
  6121. if (
  6122. hasattr(self, "DIRECTORY_ENTRY_LOAD_CONFIG")
  6123. and self.DIRECTORY_ENTRY_LOAD_CONFIG
  6124. and self.DIRECTORY_ENTRY_LOAD_CONFIG.struct
  6125. ):
  6126. dump_dict[
  6127. "LOAD_CONFIG"
  6128. ] = self.DIRECTORY_ENTRY_LOAD_CONFIG.struct.dump_dict()
  6129. if hasattr(self, "DIRECTORY_ENTRY_DEBUG"):
  6130. dump_dict["Debug information"] = []
  6131. for dbg in self.DIRECTORY_ENTRY_DEBUG:
  6132. dbg_dict = {}
  6133. dump_dict["Debug information"].append(dbg_dict)
  6134. dbg_dict.update(dbg.struct.dump_dict())
  6135. dbg_dict["Type"] = DEBUG_TYPE.get(dbg.struct.Type, dbg.struct.Type)
  6136. if self.has_relocs():
  6137. dump_dict["Base relocations"] = []
  6138. for base_reloc in self.DIRECTORY_ENTRY_BASERELOC:
  6139. base_reloc_list = []
  6140. dump_dict["Base relocations"].append(base_reloc_list)
  6141. base_reloc_list.append(base_reloc.struct.dump_dict())
  6142. for reloc in base_reloc.entries:
  6143. reloc_dict = {}
  6144. base_reloc_list.append(reloc_dict)
  6145. reloc_dict["RVA"] = reloc.rva
  6146. try:
  6147. reloc_dict["Type"] = RELOCATION_TYPE[reloc.type][16:]
  6148. except KeyError:
  6149. reloc_dict["Type"] = reloc.type
  6150. return dump_dict
  6151. def get_physical_by_rva(self, rva):
  6152. """Gets the physical address in the PE file from an RVA value."""
  6153. try:
  6154. return self.get_offset_from_rva(rva)
  6155. except Exception:
  6156. return None
  6157. ##
  6158. # Double-Word get / set
  6159. ##
  6160. def get_data_from_dword(self, dword):
  6161. """Return a four byte string representing the double word value (little endian)."""
  6162. return struct.pack("<L", dword & 0xFFFFFFFF)
  6163. def get_dword_from_data(self, data, offset):
  6164. """Convert four bytes of data to a double word (little endian)
  6165. 'offset' is assumed to index into a dword array. So setting it to
  6166. N will return a dword out of the data starting at offset N*4.
  6167. Returns None if the data can't be turned into a double word.
  6168. """
  6169. if (offset + 1) * 4 > len(data):
  6170. return None
  6171. return struct.unpack("<I", data[offset * 4 : (offset + 1) * 4])[0]
  6172. def get_dword_at_rva(self, rva):
  6173. """Return the double word value at the given RVA.
  6174. Returns None if the value can't be read, i.e. the RVA can't be mapped
  6175. to a file offset.
  6176. """
  6177. try:
  6178. return self.get_dword_from_data(self.get_data(rva, 4), 0)
  6179. except PEFormatError:
  6180. return None
  6181. def get_dword_from_offset(self, offset):
  6182. """Return the double word value at the given file offset. (little endian)"""
  6183. if offset + 4 > len(self.__data__):
  6184. return None
  6185. return self.get_dword_from_data(self.__data__[offset : offset + 4], 0)
  6186. def set_dword_at_rva(self, rva, dword):
  6187. """Set the double word value at the file offset corresponding to the given RVA."""
  6188. return self.set_bytes_at_rva(rva, self.get_data_from_dword(dword))
  6189. def set_dword_at_offset(self, offset, dword):
  6190. """Set the double word value at the given file offset."""
  6191. return self.set_bytes_at_offset(offset, self.get_data_from_dword(dword))
  6192. ##
  6193. # Word get / set
  6194. ##
  6195. def get_data_from_word(self, word):
  6196. """Return a two byte string representing the word value. (little endian)."""
  6197. return struct.pack("<H", word)
  6198. def get_word_from_data(self, data, offset):
  6199. """Convert two bytes of data to a word (little endian)
  6200. 'offset' is assumed to index into a word array. So setting it to
  6201. N will return a dword out of the data starting at offset N*2.
  6202. Returns None if the data can't be turned into a word.
  6203. """
  6204. if (offset + 1) * 2 > len(data):
  6205. return None
  6206. return struct.unpack("<H", data[offset * 2 : (offset + 1) * 2])[0]
  6207. def get_word_at_rva(self, rva):
  6208. """Return the word value at the given RVA.
  6209. Returns None if the value can't be read, i.e. the RVA can't be mapped
  6210. to a file offset.
  6211. """
  6212. try:
  6213. return self.get_word_from_data(self.get_data(rva)[:2], 0)
  6214. except PEFormatError:
  6215. return None
  6216. def get_word_from_offset(self, offset):
  6217. """Return the word value at the given file offset. (little endian)"""
  6218. if offset + 2 > len(self.__data__):
  6219. return None
  6220. return self.get_word_from_data(self.__data__[offset : offset + 2], 0)
  6221. def set_word_at_rva(self, rva, word):
  6222. """Set the word value at the file offset corresponding to the given RVA."""
  6223. return self.set_bytes_at_rva(rva, self.get_data_from_word(word))
  6224. def set_word_at_offset(self, offset, word):
  6225. """Set the word value at the given file offset."""
  6226. return self.set_bytes_at_offset(offset, self.get_data_from_word(word))
  6227. ##
  6228. # Quad-Word get / set
  6229. ##
  6230. def get_data_from_qword(self, word):
  6231. """Return an eight byte string representing the quad-word value (little endian)."""
  6232. return struct.pack("<Q", word)
  6233. def get_qword_from_data(self, data, offset):
  6234. """Convert eight bytes of data to a word (little endian)
  6235. 'offset' is assumed to index into a word array. So setting it to
  6236. N will return a dword out of the data starting at offset N*8.
  6237. Returns None if the data can't be turned into a quad word.
  6238. """
  6239. if (offset + 1) * 8 > len(data):
  6240. return None
  6241. return struct.unpack("<Q", data[offset * 8 : (offset + 1) * 8])[0]
  6242. def get_qword_at_rva(self, rva):
  6243. """Return the quad-word value at the given RVA.
  6244. Returns None if the value can't be read, i.e. the RVA can't be mapped
  6245. to a file offset.
  6246. """
  6247. try:
  6248. return self.get_qword_from_data(self.get_data(rva)[:8], 0)
  6249. except PEFormatError:
  6250. return None
  6251. def get_qword_from_offset(self, offset):
  6252. """Return the quad-word value at the given file offset. (little endian)"""
  6253. if offset + 8 > len(self.__data__):
  6254. return None
  6255. return self.get_qword_from_data(self.__data__[offset : offset + 8], 0)
  6256. def set_qword_at_rva(self, rva, qword):
  6257. """Set the quad-word value at the file offset corresponding to the given RVA."""
  6258. return self.set_bytes_at_rva(rva, self.get_data_from_qword(qword))
  6259. def set_qword_at_offset(self, offset, qword):
  6260. """Set the quad-word value at the given file offset."""
  6261. return self.set_bytes_at_offset(offset, self.get_data_from_qword(qword))
  6262. ##
  6263. # Set bytes
  6264. ##
  6265. def set_bytes_at_rva(self, rva, data):
  6266. """Overwrite, with the given string, the bytes at the file offset corresponding
  6267. to the given RVA.
  6268. Return True if successful, False otherwise. It can fail if the
  6269. offset is outside the file's boundaries.
  6270. """
  6271. if not isinstance(data, bytes):
  6272. raise TypeError("data should be of type: bytes")
  6273. offset = self.get_physical_by_rva(rva)
  6274. if not offset:
  6275. return False
  6276. return self.set_bytes_at_offset(offset, data)
  6277. def set_bytes_at_offset(self, offset, data):
  6278. """Overwrite the bytes at the given file offset with the given string.
  6279. Return True if successful, False otherwise. It can fail if the
  6280. offset is outside the file's boundaries.
  6281. """
  6282. if not isinstance(data, bytes):
  6283. raise TypeError("data should be of type: bytes")
  6284. if 0 <= offset < len(self.__data__):
  6285. self.set_data_bytes(offset, data)
  6286. else:
  6287. return False
  6288. return True
  6289. def set_data_bytes(self, offset: int, data: bytes):
  6290. if not isinstance(self.__data__, bytearray):
  6291. new_data = bytearray(self.__data__)
  6292. self._close_data()
  6293. self.__data__ = new_data
  6294. self.__data__[offset : offset + len(data)] = data
  6295. def merge_modified_section_data(self):
  6296. """Update the PE image content with any individual section data that has been
  6297. modified.
  6298. """
  6299. for section in self.sections:
  6300. section_data_start = self.adjust_PointerToRawData(section.PointerToRawData)
  6301. section_data_end = section_data_start + section.SizeOfRawData
  6302. if section_data_start < len(self.__data__) and section_data_end < len(
  6303. self.__data__
  6304. ):
  6305. self.set_data_bytes(section_data_start, section.get_data())
  6306. def relocate_image(self, new_ImageBase):
  6307. """Apply the relocation information to the image using the provided image base.
  6308. This method will apply the relocation information to the image. Given the new
  6309. base, all the relocations will be processed and both the raw data and the
  6310. section's data will be fixed accordingly.
  6311. The resulting image can be retrieved as well through the method:
  6312. get_memory_mapped_image()
  6313. In order to get something that would more closely match what could be found in
  6314. memory once the Windows loader finished its work.
  6315. """
  6316. relocation_difference = new_ImageBase - self.OPTIONAL_HEADER.ImageBase
  6317. if (
  6318. len(self.OPTIONAL_HEADER.DATA_DIRECTORY) >= 6
  6319. and self.OPTIONAL_HEADER.DATA_DIRECTORY[5].Size
  6320. ):
  6321. if not hasattr(self, "DIRECTORY_ENTRY_BASERELOC"):
  6322. self.parse_data_directories(
  6323. directories=[DIRECTORY_ENTRY["IMAGE_DIRECTORY_ENTRY_BASERELOC"]]
  6324. )
  6325. if not hasattr(self, "DIRECTORY_ENTRY_BASERELOC"):
  6326. self.__warnings.append(
  6327. "Relocating image but PE does not have (or pefile cannot "
  6328. "parse) a DIRECTORY_ENTRY_BASERELOC"
  6329. )
  6330. else:
  6331. for reloc in self.DIRECTORY_ENTRY_BASERELOC:
  6332. # We iterate with an index because if the relocation is of type
  6333. # IMAGE_REL_BASED_HIGHADJ we need to also process the next entry
  6334. # at once and skip it for the next iteration
  6335. #
  6336. entry_idx = 0
  6337. while entry_idx < len(reloc.entries):
  6338. entry = reloc.entries[entry_idx]
  6339. entry_idx += 1
  6340. if entry.type == RELOCATION_TYPE["IMAGE_REL_BASED_ABSOLUTE"]:
  6341. # Nothing to do for this type of relocation
  6342. pass
  6343. elif entry.type == RELOCATION_TYPE["IMAGE_REL_BASED_HIGH"]:
  6344. # Fix the high 16-bits of a relocation
  6345. #
  6346. # Add high 16-bits of relocation_difference to the
  6347. # 16-bit value at RVA=entry.rva
  6348. self.set_word_at_rva(
  6349. entry.rva,
  6350. (
  6351. self.get_word_at_rva(entry.rva)
  6352. + relocation_difference
  6353. >> 16
  6354. )
  6355. & 0xFFFF,
  6356. )
  6357. elif entry.type == RELOCATION_TYPE["IMAGE_REL_BASED_LOW"]:
  6358. # Fix the low 16-bits of a relocation
  6359. #
  6360. # Add low 16 bits of relocation_difference to the 16-bit
  6361. # value at RVA=entry.rva
  6362. self.set_word_at_rva(
  6363. entry.rva,
  6364. (
  6365. self.get_word_at_rva(entry.rva)
  6366. + relocation_difference
  6367. )
  6368. & 0xFFFF,
  6369. )
  6370. elif entry.type == RELOCATION_TYPE["IMAGE_REL_BASED_HIGHLOW"]:
  6371. # Handle all high and low parts of a 32-bit relocation
  6372. #
  6373. # Add relocation_difference to the value at RVA=entry.rva
  6374. self.set_dword_at_rva(
  6375. entry.rva,
  6376. self.get_dword_at_rva(entry.rva)
  6377. + relocation_difference,
  6378. )
  6379. elif entry.type == RELOCATION_TYPE["IMAGE_REL_BASED_HIGHADJ"]:
  6380. # Fix the high 16-bits of a relocation and adjust
  6381. #
  6382. # Add high 16-bits of relocation_difference to the 32-bit
  6383. # value composed from the (16-bit value at
  6384. # RVA=entry.rva)<<16 plus the 16-bit value at the next
  6385. # relocation entry.
  6386. # If the next entry is beyond the array's limits,
  6387. # abort... the table is corrupt
  6388. if entry_idx == len(reloc.entries):
  6389. break
  6390. next_entry = reloc.entries[entry_idx]
  6391. entry_idx += 1
  6392. self.set_word_at_rva(
  6393. entry.rva,
  6394. (
  6395. (self.get_word_at_rva(entry.rva) << 16)
  6396. + next_entry.rva
  6397. + relocation_difference
  6398. & 0xFFFF0000
  6399. )
  6400. >> 16,
  6401. )
  6402. elif entry.type == RELOCATION_TYPE["IMAGE_REL_BASED_DIR64"]:
  6403. # Apply the difference to the 64-bit value at the offset
  6404. # RVA=entry.rva
  6405. self.set_qword_at_rva(
  6406. entry.rva,
  6407. self.get_qword_at_rva(entry.rva)
  6408. + relocation_difference,
  6409. )
  6410. self.OPTIONAL_HEADER.ImageBase = new_ImageBase
  6411. # correct VAs(virtual addresses) occurrences in directory information
  6412. if hasattr(self, "DIRECTORY_ENTRY_IMPORT"):
  6413. for dll in self.DIRECTORY_ENTRY_IMPORT:
  6414. for func in dll.imports:
  6415. func.address += relocation_difference
  6416. if hasattr(self, "DIRECTORY_ENTRY_TLS"):
  6417. self.DIRECTORY_ENTRY_TLS.struct.StartAddressOfRawData += (
  6418. relocation_difference
  6419. )
  6420. self.DIRECTORY_ENTRY_TLS.struct.EndAddressOfRawData += (
  6421. relocation_difference
  6422. )
  6423. self.DIRECTORY_ENTRY_TLS.struct.AddressOfIndex += relocation_difference
  6424. self.DIRECTORY_ENTRY_TLS.struct.AddressOfCallBacks += (
  6425. relocation_difference
  6426. )
  6427. if hasattr(self, "DIRECTORY_ENTRY_LOAD_CONFIG"):
  6428. load_config = self.DIRECTORY_ENTRY_LOAD_CONFIG.struct
  6429. if (
  6430. hasattr(load_config, "LockPrefixTable")
  6431. and load_config.LockPrefixTable
  6432. ):
  6433. load_config.LockPrefixTable += relocation_difference
  6434. if hasattr(load_config, "EditList") and load_config.EditList:
  6435. load_config.EditList += relocation_difference
  6436. if (
  6437. hasattr(load_config, "SecurityCookie")
  6438. and load_config.SecurityCookie
  6439. ):
  6440. load_config.SecurityCookie += relocation_difference
  6441. if (
  6442. hasattr(load_config, "SEHandlerTable")
  6443. and load_config.SEHandlerTable
  6444. ):
  6445. load_config.SEHandlerTable += relocation_difference
  6446. if (
  6447. hasattr(load_config, "GuardCFCheckFunctionPointer")
  6448. and load_config.GuardCFCheckFunctionPointer
  6449. ):
  6450. load_config.GuardCFCheckFunctionPointer += relocation_difference
  6451. if (
  6452. hasattr(load_config, "GuardCFDispatchFunctionPointer")
  6453. and load_config.GuardCFDispatchFunctionPointer
  6454. ):
  6455. load_config.GuardCFDispatchFunctionPointer += relocation_difference
  6456. if (
  6457. hasattr(load_config, "GuardCFFunctionTable")
  6458. and load_config.GuardCFFunctionTable
  6459. ):
  6460. load_config.GuardCFFunctionTable += relocation_difference
  6461. if (
  6462. hasattr(load_config, "GuardAddressTakenIatEntryTable")
  6463. and load_config.GuardAddressTakenIatEntryTable
  6464. ):
  6465. load_config.GuardAddressTakenIatEntryTable += relocation_difference
  6466. if (
  6467. hasattr(load_config, "GuardLongJumpTargetTable")
  6468. and load_config.GuardLongJumpTargetTable
  6469. ):
  6470. load_config.GuardLongJumpTargetTable += relocation_difference
  6471. if (
  6472. hasattr(load_config, "DynamicValueRelocTable")
  6473. and load_config.DynamicValueRelocTable
  6474. ):
  6475. load_config.DynamicValueRelocTable += relocation_difference
  6476. if (
  6477. self.PE_TYPE == OPTIONAL_HEADER_MAGIC_PE_PLUS
  6478. and hasattr(load_config, "CHPEMetadataPointer")
  6479. and load_config.CHPEMetadataPointer
  6480. ):
  6481. load_config.CHPEMetadataPointer += relocation_difference
  6482. if (
  6483. hasattr(load_config, "GuardRFFailureRoutine")
  6484. and load_config.GuardRFFailureRoutine
  6485. ):
  6486. load_config.GuardRFFailureRoutine += relocation_difference
  6487. if (
  6488. hasattr(load_config, "GuardRFFailureRoutineFunctionPointer")
  6489. and load_config.GuardRFFailureRoutineFunctionPointer
  6490. ):
  6491. load_config.GuardRFVerifyStackPointerFunctionPointer += (
  6492. relocation_difference
  6493. )
  6494. if (
  6495. hasattr(load_config, "GuardRFVerifyStackPointerFunctionPointer")
  6496. and load_config.GuardRFVerifyStackPointerFunctionPointer
  6497. ):
  6498. load_config.GuardRFVerifyStackPointerFunctionPointer += (
  6499. relocation_difference
  6500. )
  6501. if (
  6502. hasattr(load_config, "EnclaveConfigurationPointer")
  6503. and load_config.EnclaveConfigurationPointer
  6504. ):
  6505. load_config.EnclaveConfigurationPointer += relocation_difference
  6506. if (
  6507. hasattr(load_config, "VolatileMetadataPointer")
  6508. and load_config.VolatileMetadataPointer
  6509. ):
  6510. load_config.VolatileMetadataPointer += relocation_difference
  6511. if (
  6512. hasattr(load_config, "GuardEHContinuationTable")
  6513. and load_config.GuardEHContinuationTable
  6514. ):
  6515. load_config.GuardEHContinuationTable += relocation_difference
  6516. if (
  6517. hasattr(load_config, "GuardXFGCheckFunctionPointer")
  6518. and load_config.GuardXFGCheckFunctionPointer
  6519. ):
  6520. load_config.GuardXFGCheckFunctionPointer += relocation_difference
  6521. if (
  6522. hasattr(load_config, "GuardXFGDispatchFunctionPointer")
  6523. and load_config.GuardXFGDispatchFunctionPointer
  6524. ):
  6525. load_config.GuardXFGDispatchFunctionPointer += relocation_difference
  6526. if (
  6527. hasattr(load_config, "GuardXFGTableDispatchFunctionPointer")
  6528. and load_config.GuardXFGTableDispatchFunctionPointer
  6529. ):
  6530. load_config.GuardXFGTableDispatchFunctionPointer += (
  6531. relocation_difference
  6532. )
  6533. if (
  6534. hasattr(load_config, "CastGuardOsDeterminedFailureMode")
  6535. and load_config.CastGuardOsDeterminedFailureMode
  6536. ):
  6537. load_config.CastGuardOsDeterminedFailureMode += (
  6538. relocation_difference
  6539. )
  6540. if (
  6541. hasattr(load_config, "GuardMemcpyFunctionPointer")
  6542. and load_config.GuardMemcpyFunctionPointer
  6543. ):
  6544. load_config.GuardMemcpyFunctionPointer += relocation_difference
  6545. def verify_checksum(self):
  6546. return self.OPTIONAL_HEADER.CheckSum == self.generate_checksum()
  6547. def generate_checksum(self):
  6548. # This will make sure that the data representing the PE image
  6549. # is updated with any changes that might have been made by
  6550. # assigning values to header fields as those are not automatically
  6551. # updated upon assignment.
  6552. #
  6553. # data = self.write()
  6554. # print('{0}'.format(len(data)))
  6555. # for idx, b in enumerate(data):
  6556. # if b != ord(self.__data__[idx]) or (idx > 1244440 and idx < 1244460):
  6557. # print('Idx: {0} G {1:02x} {3} B {2:02x}'.format(
  6558. # idx, ord(self.__data__[idx]), b,
  6559. # self.__data__[idx], chr(b)))
  6560. new_data = self.write()
  6561. self._close_data()
  6562. self.__data__ = new_data
  6563. # Get the offset to the CheckSum field in the OptionalHeader
  6564. # (The offset is the same in PE32 and PE32+)
  6565. checksum_offset = self.OPTIONAL_HEADER.get_file_offset() + 0x40 # 64
  6566. checksum = 0
  6567. # Verify the data is dword-aligned. Add padding if needed
  6568. #
  6569. remainder = len(self.__data__) % 4
  6570. data_len = len(self.__data__) + ((4 - remainder) * (remainder != 0))
  6571. for i in range(data_len // 4):
  6572. # Skip the checksum field
  6573. if i == checksum_offset // 4:
  6574. continue
  6575. if i + 1 == (data_len // 4) and remainder:
  6576. dword = struct.unpack(
  6577. "I", self.__data__[i * 4 :] + (b"\0" * (4 - remainder))
  6578. )[0]
  6579. else:
  6580. dword = struct.unpack("I", self.__data__[i * 4 : i * 4 + 4])[0]
  6581. # Optimized the calculation (thanks to Emmanuel Bourg for pointing it out!)
  6582. checksum += dword
  6583. if checksum >= 2**32:
  6584. checksum = (checksum & 0xFFFFFFFF) + (checksum >> 32)
  6585. checksum = (checksum & 0xFFFF) + (checksum >> 16)
  6586. checksum = (checksum) + (checksum >> 16)
  6587. checksum = checksum & 0xFFFF
  6588. # The length is the one of the original data, not the padded one
  6589. #
  6590. return checksum + len(self.__data__)
  6591. def is_exe(self):
  6592. """Check whether the file is a standard executable.
  6593. This will return true only if the file has the IMAGE_FILE_EXECUTABLE_IMAGE flag
  6594. set and the IMAGE_FILE_DLL not set and the file does not appear to be a driver
  6595. either.
  6596. """
  6597. EXE_flag = IMAGE_CHARACTERISTICS["IMAGE_FILE_EXECUTABLE_IMAGE"]
  6598. if (
  6599. (not self.is_dll())
  6600. and (not self.is_driver())
  6601. and (EXE_flag & self.FILE_HEADER.Characteristics) == EXE_flag
  6602. ):
  6603. return True
  6604. return False
  6605. def is_dll(self):
  6606. """Check whether the file is a standard DLL.
  6607. This will return true only if the image has the IMAGE_FILE_DLL flag set.
  6608. """
  6609. DLL_flag = IMAGE_CHARACTERISTICS["IMAGE_FILE_DLL"]
  6610. if (DLL_flag & self.FILE_HEADER.Characteristics) == DLL_flag:
  6611. return True
  6612. return False
  6613. def is_driver(self):
  6614. """Check whether the file is a Windows driver.
  6615. This will return true only if there are reliable indicators of the image
  6616. being a driver.
  6617. """
  6618. # Checking that the ImageBase field of the OptionalHeader is above or
  6619. # equal to 0x80000000 (that is, whether it lies in the upper 2GB of
  6620. # the address space, normally belonging to the kernel) is not a
  6621. # reliable enough indicator. For instance, PEs that play the invalid
  6622. # ImageBase trick to get relocated could be incorrectly assumed to be
  6623. # drivers.
  6624. # This is not reliable either...
  6625. #
  6626. # if any((section.Characteristics &
  6627. # SECTION_CHARACTERISTICS['IMAGE_SCN_MEM_NOT_PAGED']) for
  6628. # section in self.sections ):
  6629. # return True
  6630. # If the import directory was not parsed (fast_load = True); do it now.
  6631. if not hasattr(self, "DIRECTORY_ENTRY_IMPORT"):
  6632. self.parse_data_directories(
  6633. directories=[DIRECTORY_ENTRY["IMAGE_DIRECTORY_ENTRY_IMPORT"]]
  6634. )
  6635. # If there's still no import directory (the PE doesn't have one or it's
  6636. # malformed), give up.
  6637. if not hasattr(self, "DIRECTORY_ENTRY_IMPORT"):
  6638. return False
  6639. # self.DIRECTORY_ENTRY_IMPORT will now exist, although it may be empty.
  6640. # If it imports from "ntoskrnl.exe" or other kernel components it should
  6641. # be a driver
  6642. system_DLLs = {
  6643. b"ntoskrnl.exe",
  6644. b"hal.dll",
  6645. b"ndis.sys",
  6646. b"bootvid.dll",
  6647. b"kdcom.dll",
  6648. }
  6649. if system_DLLs.intersection(
  6650. {imp.dll.lower() for imp in self.DIRECTORY_ENTRY_IMPORT}
  6651. ):
  6652. return True
  6653. driver_like_section_names = {b"page", b"paged"}
  6654. if driver_like_section_names.intersection(
  6655. {section.Name.lower().rstrip(b"\x00") for section in self.sections}
  6656. ) and (
  6657. self.OPTIONAL_HEADER.Subsystem
  6658. in (
  6659. SUBSYSTEM_TYPE["IMAGE_SUBSYSTEM_NATIVE"],
  6660. SUBSYSTEM_TYPE["IMAGE_SUBSYSTEM_NATIVE_WINDOWS"],
  6661. )
  6662. ):
  6663. return True
  6664. return False
  6665. def get_overlay_data_start_offset(self):
  6666. """Get the offset of data appended to the file and not contained within
  6667. the area described in the headers."""
  6668. largest_offset_and_size = (0, 0)
  6669. def update_if_sum_is_larger_and_within_file(
  6670. offset_and_size, file_size=len(self.__data__)
  6671. ):
  6672. if sum(largest_offset_and_size) < sum(offset_and_size) <= file_size:
  6673. return offset_and_size
  6674. return largest_offset_and_size
  6675. if hasattr(self, "OPTIONAL_HEADER"):
  6676. largest_offset_and_size = update_if_sum_is_larger_and_within_file(
  6677. (
  6678. self.OPTIONAL_HEADER.get_file_offset(),
  6679. self.FILE_HEADER.SizeOfOptionalHeader,
  6680. )
  6681. )
  6682. for section in self.sections:
  6683. largest_offset_and_size = update_if_sum_is_larger_and_within_file(
  6684. (section.PointerToRawData, section.SizeOfRawData)
  6685. )
  6686. skip_directories = [DIRECTORY_ENTRY["IMAGE_DIRECTORY_ENTRY_SECURITY"]]
  6687. for idx, directory in enumerate(self.OPTIONAL_HEADER.DATA_DIRECTORY):
  6688. if idx in skip_directories:
  6689. continue
  6690. try:
  6691. largest_offset_and_size = update_if_sum_is_larger_and_within_file(
  6692. (self.get_offset_from_rva(directory.VirtualAddress), directory.Size)
  6693. )
  6694. # Ignore directories with RVA out of file
  6695. except PEFormatError:
  6696. continue
  6697. if len(self.__data__) > sum(largest_offset_and_size):
  6698. return sum(largest_offset_and_size)
  6699. return None
  6700. def get_overlay(self):
  6701. """Get the data appended to the file and not contained within the area described
  6702. in the headers."""
  6703. overlay_data_offset = self.get_overlay_data_start_offset()
  6704. if overlay_data_offset is not None:
  6705. return self.__data__[overlay_data_offset:]
  6706. return None
  6707. def trim(self):
  6708. """Return the just data defined by the PE headers, removing any overlaid data."""
  6709. overlay_data_offset = self.get_overlay_data_start_offset()
  6710. if overlay_data_offset is not None:
  6711. return self.__data__[:overlay_data_offset]
  6712. return self.__data__[:]
  6713. # According to the document:
  6714. # [ Microsoft Portable Executable and Common Object File Format Specification ]
  6715. # "The alignment factor (in bytes) that is used to align the raw data of sections in
  6716. # the image file. The value should be a power of 2 between 512 and 64 K, inclusive.
  6717. # The default is 512. If the SectionAlignment is less than the architecture's page
  6718. # size, then FileAlignment must match SectionAlignment."
  6719. def adjust_PointerToRawData(self, val):
  6720. if self.OPTIONAL_HEADER.FileAlignment >= MIN_VALID_FILE_ALIGNMENT:
  6721. # If it's not a power of two, report it:
  6722. if self.FileAlignment_Warning is False and not power_of_two(
  6723. self.OPTIONAL_HEADER.FileAlignment
  6724. ):
  6725. self.__warnings.append(
  6726. "If FileAlignment > 0x200 it should be a power of 2. Value: %x"
  6727. % (self.OPTIONAL_HEADER.FileAlignment)
  6728. )
  6729. self.FileAlignment_Warning = True
  6730. # (val / SECTOR_SIZE) * SECTOR_SIZE
  6731. return val & ~0x1FF
  6732. # According to the document:
  6733. # [ Microsoft Portable Executable and Common Object File Format Specification ]
  6734. # "The alignment (in bytes) of sections when they are loaded into memory. It must be
  6735. # greater than or equal to FileAlignment. The default is the page size for the
  6736. # architecture."
  6737. #
  6738. def adjust_SectionAlignment(self, val, section_alignment, file_alignment):
  6739. # If the SectionAlignment is less than the architecture's page size, then
  6740. # FileAlignment must match SectionAlignment.
  6741. if section_alignment < 0x1000:
  6742. if (
  6743. file_alignment != section_alignment
  6744. and self.SectionAlignment_Warning is False
  6745. ):
  6746. self.__warnings.append(
  6747. f"If SectionAlignment(0x{section_alignment:x}) < 0x1000 it should "
  6748. f"equal FileAlignment(0x{file_alignment:x})"
  6749. )
  6750. self.SectionAlignment_Warning = True
  6751. return cache_adjust_SectionAlignment(val, section_alignment, file_alignment)
  6752. def main():
  6753. import sys
  6754. usage = """\
  6755. pefile.py <filename>
  6756. pefile.py exports <filename>"""
  6757. if not sys.argv[1:]:
  6758. print(usage)
  6759. elif sys.argv[1] == "exports":
  6760. if not sys.argv[2:]:
  6761. sys.exit("error: <filename> required")
  6762. pe = PE(sys.argv[2])
  6763. for exp in pe.DIRECTORY_ENTRY_EXPORT.symbols:
  6764. print(
  6765. hex(pe.OPTIONAL_HEADER.ImageBase + exp.address), exp.name, exp.ordinal
  6766. )
  6767. else:
  6768. print(PE(sys.argv[1]).dump_info())
  6769. if __name__ == "__main__":
  6770. main()