Detailed Overview and Internals of PE File

A win32 portable executable(PE) file consists of: DOS Header, PE Header, Section Table, Sections. Analyzing a PE file gives us a lot of information like the address in memory where the file will be located (ImageBase), address of entry point, imported and exported functions, packed or unpacked etc. Thus this static analysis can indicate whether it’s a malware or not etc.


PE File Architecture

DOS MZ header is 64 bytes in size. Its structure, IMAGE_DOS_HEADER, is defined in winnt.h. The important fields present in this are:
e_magic-> contains the value 4Dh, 5Ah (or letters “MZ” for Mark Zbikowsky who designed the MS-DOS);
e_lfanew-> contains the offset of the PE header.


The PE header structure IMAGE_NT_HEADERS contains 3 fields: Signature, FileHeader and OptionalHeader.
Signature is a DWORD(4 bytes) containing the value 50h, 45h, 00h, 00h (“PE” followed by two terminating zeroes).
FileHeader (IMAGE_FILE_HEADER) is the next 20 bytes of the PE file and contains info about the physical layout & properties of the file e.g. number of sections.
OptionalHeader (IMAGE_OPTIONAL_HEADERS) is always present and forms the next 224 bytes. It contains info about the logical layout inside the PE file with fields like AddressOfEntryPoint, ImageBase, FileAlignment, SectionAlignment etc. The last 128 bytes contains the Data Directory which is an array of 16 IMAGE_DATA_DIRECTORY structures.

The Section Table contains information about each section present in the pefile. Some of the sections are:
Code section: .text contains the executable instructions
Data Section: .bss –>uninitialized variables; .rdata->read only variables; .data->initialized variables
Export data: .edata->names and addresses of exported functions as well as Export Directory
Import data: .idata-> names and variables of imported functions as well as Import Descriptor and IAT.


Swiss Army knife tools

Some of the important and useful tools you may need to explore more details are: PEid, PEview, PE Explorer, PE browse, dependency walker, Resource hacker, Lord PE and Import Reconstructor.

(PE Viewer to view internal details in organized format)


Though lots of GUI tools are available for studying a pe-file, you can program your own custom tool in python using the “pefile” module. Let’s study the usage of this module.

To open an executable using pefile:

>>> pe=pefile.PE(‘hello.exe’)  

>>> pe

<pefile.PE instance at 0x0000000002B06248>


We can search for individual fields like:

>>> pe.OPTIONAL_HEADER.AddressOfEntryPoint  


>>> pe.OPTIONAL_HEADER.ImageBase


>>> pe.FILE_HEADER.NumberOfSections



Let’s check each section in detail:

>>> for section in pe.sections:  

print (section.Name, hex(section.VirtualAddress), hex(section.Misc_VirtualSize), section.SizeOfRawData )

(‘.text\x00\x00\x00’, ‘0x1000’, ‘0x26’, 512)
(‘.rdata\x00\x00’, ‘0x2000’, ‘0x92’, 512)
(‘.data\x00\x00\x00’, ‘0x3000’, ‘0xd’, 512)


To dump all the fields of the file, just a single simple command:

>>> print pe.dump_info()


0x0        0x0   e_magic:                       0x5A4D
0x2        0x2   e_cblp:                        0x90
0x4        0x4   e_cp:                          0x3
0x6        0x6   e_crlc:                        0x0
0x8        0x8   e_cparhdr:                     0x4
0xA        0xA   e_minalloc:                    0x0
0xC        0xC   e_maxalloc:                    0xFFFF
0xE        0xE   e_ss:                          0x0
0x10       0x10  e_sp:                          0xB8
0x12       0x12  e_csum:                        0x0
0x14       0x14  e_ip:                          0x0
0x16       0x16  e_cs:                          0x0
0x18       0x18  e_lfarlc:                      0x40
0x1A       0x1A  e_ovno:                        0x0
0x1C       0x1C  e_res:
0x24       0x24  e_oemid:                       0x0
0x26       0x26  e_oeminfo:                     0x0
0x28       0x28  e_res2:
0x3C       0x3C  e_lfanew:                      0xB0


0xB0       0x0   Signature:                     0x4550


0xB4       0x0   Machine:                       0x14C
0xB6       0x2   NumberOfSections:              0x3
0xB8       0x4   TimeDateStamp:                 0x52123CCD [Mon Aug 19 15:42:05 2013 UTC]
0xBC       0x8   PointerToSymbolTable:          0x0
0xC0       0xC   NumberOfSymbols:               0x0
0xC4       0x10  SizeOfOptionalHeader:          0xE0
0xC6       0x12  Characteristics:               0x10F




0xC8       0x0   Magic:                         0x10B
0xCA       0x2   MajorLinkerVersion:            0x5
0xCB       0x3   MinorLinkerVersion:            0xC
0xCC       0x4   SizeOfCode:                    0x200
0xD0       0x8   SizeOfInitializedData:         0x400
0xD4       0xC   SizeOfUninitializedData:       0x0
0xD8       0x10  AddressOfEntryPoint:           0xFFE
0xDC       0x14  BaseOfCode:                    0x1000
0xE0       0x18  BaseOfData:                    0x2000
0xE4       0x1C  ImageBase:                     0x400000
0xE8       0x20  SectionAlignment:              0x1000
0xEC       0x24  FileAlignment:                 0x200
0xF0       0x28  MajorOperatingSystemVersion:   0x4
0xF2       0x2A  MinorOperatingSystemVersion:   0x0
0xF4       0x2C  MajorImageVersion:             0x0
0xF6       0x2E  MinorImageVersion:             0x0
0xF8       0x30  MajorSubsystemVersion:         0x4
0xFA       0x32  MinorSubsystemVersion:         0x0
0xFC       0x34  Reserved1:                     0x0
0x100      0x38  SizeOfImage:                   0x4000
0x104      0x3C  SizeOfHeaders:                 0x400
0x108      0x40  CheckSum:                      0x0
0x10C      0x44  Subsystem:                     0x2
0x10E      0x46  DllCharacteristics:            0x0
0x110      0x48  SizeOfStackReserve:            0x100000
0x114      0x4C  SizeOfStackCommit:             0x1000
0x118      0x50  SizeOfHeapReserve:             0x100000
0x11C      0x54  SizeOfHeapCommit:              0x1000
0x120      0x58  LoaderFlags:                   0x0
0x124      0x5C  NumberOfRvaAndSizes:           0x10


———-PE Sections———-

0x1A8      0x0   Name:                          .text
0x1B0      0x8   Misc:                          0x26
0x1B0      0x8   Misc_PhysicalAddress:          0x26
0x1B0      0x8   Misc_VirtualSize:              0x26
0x1B4       0xC   VirtualAddress:                0x1000
0x1B8      0x10  SizeOfRawData:                 0x200
0x1BC      0x14  PointerToRawData:              0x400
0x1C0      0x18  PointerToRelocations:          0x0
0x1C4      0x1C  PointerToLinenumbers:          0x0
0x1C8      0x20  NumberOfRelocations:           0x0
0x1CA      0x22  NumberOfLinenumbers:           0x0
0x1CC      0x24  Characteristics:               0x60000020


Entropy: 0.378734 (Min=0.0, Max=8.0)
MD5     hash: 4c606281effe29223bc83ca9145b4f76
SHA-1   hash: bb75af7ac66e4d653896f43a9dff5bad7c14b792
SHA-256 hash: fa0adde4c12513dde5497209d960d3f29c1e47bab9d29025641425ec67c1e286
SHA-512 hash: 8a43e9314e29c38f884f7668ca8c440f37d488d0ef8672db313d643e8e44db871042f7110e72f47fe289c7b2a8831b8b92f093515edb2eae4b5ada7692b118a9

0x1D0      0x0   Name:                          .rdata
0x1D8      0x8   Misc:                          0x92
0x1D8      0x8   Misc_PhysicalAddress:          0x92
0x1D8      0x8   Misc_VirtualSize:              0x92
0x1DC      0xC   VirtualAddress:                0x2000
0x1E0      0x10  SizeOfRawData:                 0x200
0x1E4      0x14  PointerToRawData:              0x600
0x1E8      0x18  PointerToRelocations:          0x0
0x1EC      0x1C  PointerToLinenumbers:          0x0
0x1F0      0x20  NumberOfRelocations:           0x0
0x1F2      0x22  NumberOfLinenumbers:           0x0
0x1F4      0x24  Characteristics:               0x40000040

Entropy: 1.144718 (Min=0.0, Max=8.0)
MD5     hash: 768cb8b25733abbd2e7ac650764e7f54
SHA-1   hash: 52710da9fc596d89ac7f0dcb6d038b7ec85f39ec
SHA-256 hash: 6a94c94b9d89b59d3d7e3c3c1829ef55e7bf4dd17ddedbe9ec41a1cdbc9f1728
SHA-512 hash: 5991c9514b027fc5122ec34d3a91e2aa60412953baca52fc537aff2f432c516ea736abb7d302760c6583cacfa8ad667814216aabcb09d8a278ded872efe18933

0x1F8      0x0   Name:                          .data
0x200      0x8   Misc:                          0xD
0x200      0x8   Misc_PhysicalAddress:          0xD
0x200      0x8   Misc_VirtualSize:              0xD
0x204      0xC   VirtualAddress:                0x3000
0x208      0x10  SizeOfRawData:                 0x200
0x20C      0x14  PointerToRawData:              0x800
0x210      0x18  PointerToRelocations:          0x0
0x214      0x1C  PointerToLinenumbers:          0x0
0x218      0x20  NumberOfRelocations:           0x0
0x21A      0x22  NumberOfLinenumbers:           0x0
0x21C      0x24  Characteristics:               0xC0000040


Entropy: 0.231158 (Min=0.0, Max=8.0)
MD5     hash: e48ff2dac59702844e32e3b3275d376e
SHA-1   hash: 39b2d4a38baad338a99e271015b9aa7e5b5c5963
SHA-256 hash: 41f9998ac78561a82e66c9a34ae742e36a0bc4b4b58b44e46743406dfc25fa72
SHA-512 hash: 5b21dc86b36c27180322ebb1b8f98a0d24c9ba34baec013a9aaccd1d349afad678327ed5be2550ed560aa64a09fe606f49a68f4608751647f0a638019a81a756



0x128      0x0   VirtualAddress:                0x0
0x12C      0x4   Size:                          0x0

0x130      0x0   VirtualAddress:                0x2010
0x134      0x4   Size:                          0x3C

0x138      0x0   VirtualAddress:                0x0
0x13C      0x4   Size:                          0x0

0x140      0x0   VirtualAddress:                0x0
0x144      0x4   Size:                          0x0

0x148      0x0   VirtualAddress:                0x0
0x14C      0x4   Size:                          0x0

0x150      0x0   VirtualAddress:                0x0
0x154      0x4   Size:                          0x0

0x158      0x0   VirtualAddress:                0x0
0x15C      0x4   Size:                          0x0

0x160      0x0   VirtualAddress:                0x0
0x164      0x4   Size:                          0x0

0x168      0x0   VirtualAddress:                0x0
0x16C      0x4   Size:                          0x0

0x170      0x0   VirtualAddress:                0x0
0x174      0x4   Size:                          0x0

0x178      0x0   VirtualAddress:                0x0
0x17C      0x4   Size:                          0x0

0x180      0x0   VirtualAddress:                0x0
0x184      0x4   Size:                          0x0

0x188      0x0   VirtualAddress:                0x2000
0x18C      0x4   Size:                          0x10

0x190      0x0   VirtualAddress:                0x0
0x194      0x4   Size:                          0x0

0x198      0x0   VirtualAddress:                0x0
0x19C      0x4   Size:                          0x0

0x1A0      0x0   VirtualAddress:                0x0
0x1A4      0x4   Size:                          0x0


———-Imported symbols———-

0x610      0x0   OriginalFirstThunk:            0x204C
0x610      0x0   Characteristics:               0x204C
0x614      0x4   TimeDateStamp:                 0x0        [Thu Jan 01 00:00:00 1970 UTC]
0x618      0x8   ForwarderChain:                0x0
0x61C      0xC   Name:                          0x206A
0x620      0x10  FirstThunk:                    0x2000

kernel32.dll.ExitProcess Hint[155]


0x624      0x0   OriginalFirstThunk:            0x2054
0x624      0x0   Characteristics:               0x2054
0x628      0x4   TimeDateStamp:                 0x0        [Thu Jan 01 00:00:00 1970 UTC]
0x62C      0x8   ForwarderChain:                0x0
0x630      0xC   Name:                          0x2086
0x634      0x10  FirstThunk:                    0x2008

user32.dll.MessageBoxA Hint[433]



Sometimes while manually unpacking a packed file, we need to change the Address of Entry Point to OEP.

>>> pe.OPTIONAL_HEADER.AddressOfEntryPoint = 5000 

>>> pe.write(filename=” hello1.exe”)


Packed PE files

A file is packed mainly for 3 purposes:
• To hide the behaviour of the malware from AV
• To reduce the size of the exe
• To prevent crackers from cracking the serial key of the software.


PEiD is the best tool available to find whether the executable is packed or not. It can detect signatures of almost all packers available.

(A deep scan in PEiD shows that UPX packer was used to pack explorer.exe)


Though packing an executable makes it difficult to perform static analysis of a malware, dynamic analysis has no effects because the file ultimately has to be unpacked before executing. Hence, it can only be used to fool a novice user!


Above diagram shows comparison of packed and unpacked “putty.exe” files through PE Browse. Packing a file with UPX will compress the various sections present, because these consume the maximum size. Compressed sections are renamed as UPX0, UPX1 and so on. We also see that many imported functions from the various DLLs are missing.


The execution of a packed exe starts with the new Address of Entry point. Initially, the register status is saved using PUSHAD instruction. Then unpacking of the packed sections is done followed by resolving the import table of the original exe. Restore the original register status using POPAD instruction and finally jump to the OEP.


Once we understand how a packed exe works, it is easy to unpack it manually. Following steps can be followed while unpacking using OllyDbg as mentioned in one of the trainings by SecurityXploded:

  1. Start tracing until you encounter a PUSHAD instruction(generally its the 1st or 2nd instruction).
  2. Put a hardware breakpoint at address stored by ESP in the immediate next instruction.
  3. Press F9 to continue execution. Execution stops at the breakpoint address.
  4. Continue tracing until you encounter a JMP instruction which will jump to OEP.
  5. At OEP, dump the whole program using Ollydump plugin
  6. Fix Address of Entry point of the new executable and resolve the imports using Import Reconstructor (ImpRec).



1] Bin Portable Executable File Format-A Reverse Engineer View

2] Manual Unpacking of UPX using OllyDbg

3] pefile module in python


No Comments Yet

Leave a Reply

Just Launched – MalwareNet

Follow us on Facebook

Join Mailing List

Get direct access to our expert trainers or mingle with like minded security folks in our mailing list