
This article is a story about how the executable files are arranged (to the point! These are the very things that are obtained after compiling applications with the .exe extension). After the code is written, the libraries are connected, resources are loaded to the project (icons for windows, any text files, pictures, etc.), all of this is put together in one single executable file, mainly with the .exe extension. That's it in this pool we dive.
* The article is under the auspices of "for beginners" and therefore will be replete with diagrams and descriptions of important elements of the load.Introduction

PE format is the format of executable files of all 32-bit and 64-bit Windows systems. Currently there are two PE file formats: PE32 and PE32 +. PE32 format for x86 systems, and PE32 + for x64. The structures described can be seen in the
WINNT.h header file that comes with the SDK. A description of this format from microsoft can be downloaded
here , but for now I’ll leave a little schematic representation here. Just run over your eyes, in the process of the article you will begin to grasp and everything will decompose on the shelves.
')

Any file, it is only a sequence of bytes. And the format is like a special map (treasure) for it. That is, it shows what is where, where are the islands with coconuts, where with bananas, where the sandy shores are, and where are the Somali ones, where it’s better not to go. So let's explore the wide open spaces of this ocean. Give off the moorings!
“Now you will hear a sad story. about boy bobby
(Treasure Island)
Dos-Header (IMAGE_DOS_HEADER) and Dos-stub

Dos title. This is the very first structure (the very first island we met on the way) in the file and it is 64 bytes in size. In this structure, the most important fields are
e_magic and
e_lfnew . Let's see how the structure looks like:

To study all the fields at this stage is useless. they don’t carry much meaning. Consider only those that are necessary for download and are of particular interest. (Further and below in the text, the format of the field description will be of the form
name : TYPE - description).
e_magic : WORD is the signature located at offset 0 from the beginning of the file and equal to “MZ”. Rumor has it that MZ's abbreviation for Mark Zbinovski is the
most vicious pirate in the entire water space, the leading developer of MS DOS and EXE format. If this signature is not equal to MZ, then the file will not load.
e_lfnew : DWORD - offset of the PE header relative to the beginning of the file. A PE header should start with a signature (signature / signature) PE \ x0 \ x0. PE header can be located anywhere in the file. If you look at the structure, you can see that
e_lfnew is at offset 0x3C (60 in decimal). That is, in order to read this value, we have to “add” 60 bytes from the pointer to the beginning of the file (we introduce the notation -
ptrFile ) and then we face face to face before e_lfnew. We read this value (let it be
peStep ) and add the value
peStep to
ptrFile . Mission completed - we are the boss, this should be a PE header. And we can certainly find out by checking the first four bytes of this header. As mentioned above, they should be equal to PE \ x0 \ x0.
After the first 64 bytes of the file, dos-stub starts (pirates also call it dos stub). This area in memory is mostly filled with zeros. (Take another look at the structure - the stub lies after the dos-header (a) and before the PE header) It serves only for backward compatibility; it does not need anything for current systems. It can contain a mini version of the dos program limited to 192 bytes (256 is the end of the stub, 64 is the size of the dos header). But it is easier to find an Access Point in Zimbabwe than such a program. Standard behavior, if you run a program on dos, it will display messages like “This program cannot be run in DOS mode.” Or “This program must be run under win32”. If you see these lines, it means that you have fallen ... to the distant 85th.

“To hell with money, I'm talking about Flint's papers!”
(Treasure Island)
PE-Header (IMAGE_NT_HEADER)

Read
e_lfnew , retreated from the beginning of the file to
peStep bytes. Now we can begin to analyze the PE header. This is a new island for us and it should be located in the following 0x18 bytes.
The structure is presented below:
typedef struct _IMAGE_NT_HEADERS { DWORD Signature; IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER OptionalHeader; } IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS;
This is an interesting structure, because it contains substructures. If you represent a PE file as an ocean, each structure is a mainland (or island). On the continents there are states that can tell about their territory. And the story is made up of the history of individual cities (fields) in this state. So - NT Header - this is the mainland, which contains countries such as Signature (city-state), FileHeader, OptionalHeader. As already mentioned,
Signature : DWORD contains a 4-byte signature that characterizes the file format. Consider what else this continent can tell us.
File Header (IMAGE_FILE_HEADER)
This is a country
where they always shoot, trade in drugs and engage in prostitution where each city tells in which ideal state it is located. This is with regard to the informal description, and the formal is as follows - a set of fields that describes the basic characteristics of the file. Let's consider this
power structure :
typedef struct _IMAGE_FILE_HEADER { WORD Machine; WORD NumberOfSections; DWORD TimeDateStamp; DWORD PointerToSymbolTable; DWORD NumberOfSymbols; WORD SizeOfOptionalHeader; WORD Characteristics; } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
I just dryly describe these fields, because The names are intuitive and are immediate meanings, and not VA, RVA, RAW, and other
terrible intriguing things that we have only heard about from old pirates. Although we have already encountered RAW, these are just offsets relative to the beginning of the file (they are also called raw pointers or file offset). That is, if we have a RAW address, this means that we need to step from the beginning of the file to the RAW positions (
ptrFile + RAW). Then you can start reading the values. A prime example of this type is
e_lfnew - which we covered above in the Dos header.
*
Machine : WORD is a number (2 bytes) specifies the processor architecture on which this application can run.
NumberOfSections : DWORD - the number of sections in the file. Sections (further we will call the section table) follow immediately after the header (PE-Header). The documentation says that the number of sections is limited to 96.
TimeDateStamp : WORD - the number storing the date and time of file creation.
PointerToSymbolTable : DWORD is the offset (RAW) to the symbol table, and SizeOfOptionalHeader is the size of this table. This table is designed to serve to store debug information, but the squad did not notice the loss of a fighter from the very beginning of the service. Most often, this field is cleared with zeros.
SIzeOfOptionHeader : WORD - the size of the optional header (which immediately follows the current one) The documentation indicates that it is set to 0 for the object file ...
*
Characteristics : WORD - file characteristics.
* - fields that are defined by a range of values. Tables of possible values ​​are presented in the description of the structure at the office. site and will not be listed here, because nothing particularly important for understanding the format they do not carry.
Leave this island! We need to move on. Landmark - a country called Optional-Header.
“- Where is the card, Billy? I need a map. ”
(Treasure Island)
Optional-Header (IMAGE_OPTIONAL_HEADER)

The name of
this continent title is not very successful. This header is mandatory and has 2 formats PE32 and PE32 + (IMAGE_OPTIONAL_HEADER32 and IMAGE_OPTIONAL_HEADER64, respectively). The format is stored in the
Magic : WORD field. The header contains the necessary information to download the file.
As always :
IMAGE_OPTIONAL_HEADER typedef struct _IMAGE_OPTIONAL_HEADER { WORD Magic; BYTE MajorLinkerVersion; BYTE MinorLinkerVersion; DWORD SizeOfCode; DWORD SizeOfInitializedData; DWORD SizeOfUninitializedData; DWORD AddressOfEntryPoint; DWORD BaseOfCode; DWORD BaseOfData; DWORD ImageBase; DWORD SectionAlignment; DWORD FileAlignment; WORD MajorOperatingSystemVersion; WORD MinorOperatingSystemVersion; WORD MajorImageVersion; WORD MinorImageVersion; WORD MajorSubsystemVersion; WORD MinorSubsystemVersion; DWORD Win32VersionValue; DWORD SizeOfImage; DWORD SizeOfHeaders; DWORD CheckSum; WORD Subsystem; WORD DllCharacteristics; DWORD SizeOfStackReserve; DWORD SizeOfStackCommit; DWORD SizeOfHeapReserve; DWORD SizeOfHeapCommit; DWORD LoaderFlags; DWORD NumberOfRvaAndSizes; IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; } IMAGE_OPTIONAL_HEADE R, *PIMAGE_OPTIONAL_HEADER;
* As always, we will study only the main fields that have the greatest impact on the idea of ​​loading and how to move further along the file. Let's agree - in the fields of this structure, contain values ​​with VA (Virtual address) and RVA (Relative virtual address) addresses. These are already addresses not similar to RAW, and they need to be able to read (more precisely, to read). We will certainly learn how to do it, but first we will analyze the structures that follow each other in order not to get confused. For now, just remember - these are addresses that, after calculations, indicate a specific place in the file. Also meet a new concept - alignment. We will consider it in a compartment with RVA addresses, since these are rather closely related.
AddressOfEntryPoint : DWORD - RVA address of the entry point. Can point to any point in the address space. For .exe files, the entry point corresponds to the address from which the program starts to run and cannot be zero!
BaseOfCode : DWORD - RVA of the beginning of the program code (code section).
BaseOfData : DWORD - RVA of the beginning of the program code (data section).
ImageBase : DWORD is the preferred base address for loading the program. Must be a multiple of 64kb. In most cases, equal to 0x00400000.
SectionAligment : DWORD - the size of the alignment (bytes) of the section when unloaded into virtual memory.
FileAligment : DWORD - the size of the alignment (bytes) of the section within the file.
SizeOfImage : DWORD - the size of the file (in bytes) in memory, including all headers. Must be a multiple SectionAligment.
SizeOfHeaders : DWORD - the size of all headers (DOS, DOS-Stub, PE, Section) aligned with FileAligment.
NumberOfRvaAndSizes : DWORD - the number of directories in the directory table (below the table itself). At the moment, this field is always equal to the symbolic IMAGE_NUMBEROF_DIRECTORY_ENTRIES constant, which is 16.
DataDirectory [NumberOfRvaAndSizes]: IMAGE_DATA_DIRECTORY - data directory. Simply put, this is an array (size 16), each element of which contains a structure of two DWORD values.
Consider what the
IMAGE_DATA_DIRECTORY structure is :
typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; DWORD Size; } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
What we have? We have an array of 16 elements, each element of which contains the address and size (what? How? Why? All in a minute). The question is what exactly these characteristics are. For this, microsoft has special constants to match. They can be seen at the very end of the structure description. For now:
Aha We see that each element of the array is responsible for the table attached to it. But alas, while these shores are beyond our reach, because We do not know how to work with VA and RVA addresses. And in order to learn, we need to learn what sections are. They will tell about their structure and work, after which it will become clear why VA, RVA and alignments are needed. In this article, we will only cover export and import. The purpose of the remaining fields can be found in the office. documentation, or in books. So here. Actually fields:
VirtualAddress : DWORD - RVA per table, which corresponds to the array element.
Size : DWORD - the size of the table in bytes.
So! To get to such exotic beaches as tables of imports, exports, resources and others, we need to complete a quest with sections. Well, young boy, let's take a look at the general map, determine where we are and move on:

And we are not directly in front of the wide spaces of the sections. We need to certainly elicit what they are doing and finally deal with a different kind of addressing. We want real adventures! We want to go to such republics as import and export tables as soon as possible. The old pirates say that not everyone could get to them, and the one who got
back came with gold and women with sacred knowledge about the ocean. We leave and keep the path to the Section header.
“- You are deposed, Silver! Get down from the barrel! ”
(Treasure Island)
Section-header (IMAGE_SECTION_HEADER)

Right after the
DataDirectory array,
sections follow one another. The table of sections is a sovereign state, which is divided into the
NumberOfSections of cities. Each city has its own craft, its own rights, as well as the size of 0x28 bytes. The number of sections is indicated in the
NumberOfSections field, which is stored in the File-header-e. So, consider the
structure :
typedef struct _IMAGE_SECTION_HEADER { BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; union { DWORD PhysicalAddress; DWORD VirtualSize; } Misc; DWORD VirtualAddress; DWORD SizeOfRawData; DWORD PointerToRawData; DWORD PointerToRelocations; DWORD PointerToLinenumbers; WORD NumberOfRelocations; WORD NumberOfLinenumbers; DWORD Characteristics; } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
Name : BYTE [IMAGE_SIZEOF_SHORT_NAME] - section name. Currently has a length of 8 characters.
VirtualSize : DWORD - section size in virtual memory.
SizeOfRawData : DWORD - the size of the section in the file.
VirtualAddress : DWORD - RVA section address.
SizeOfRawData : DWORD - the size of the section in the file. Must be a multiple
FileAligment .
PointerToRawData : DWORD - RAW offset to the beginning of the section. It must also be a multiple of
FileAligment ...
Characteristics : DWORD - section access attributes and rules for its loading into virtual. memory. For example, an attribute to determine the contents of a section (initial. Data, not initial. Data, code). Or access attributes - read, write, execute. This is not their full range. Characteristics are given by constants from the same WINNT.h, which begin with IMAGE_SCN_. More details about the attributes of sections can be
here . Attributes are also well described in the books of Chris Kaspersky - the list of references at the end of the article.
Regarding the name, you should remember the following - the section with resources should always be named .rsrc. Otherwise, the resources will not be loaded. As for the rest of the sections - that name can be anything. Usually there are meaningful names, such as .data, .src, etc ... But this also happens:

Sections, this is an area that is unloaded into virtual memory and all work happens directly with this data. The address in virtual memory without any offsets is called Virtual address, abbreviated VA. The preferred address for downloading the application is set in the
ImageBase field. This is like the point from which the application area begins in virtual memory. And relative to this point, RVA (Relative virtual address) offsets are counted. That is, VA =
ImageBase + RVA;
ImageBase is always known to us and having received VA or RVA at our disposal, we can express one through the other.
It seems to have settled down. But this is virtual memory! And then we are in the physical. Virtual memory for us now is like traveling to other galaxies, which we can only imagine. So we don’t get into virtual memory at the moment, but we can find out what will be there, because it’s taken from our file.
Alignment

In order to properly represent the discharge in Wirth. memory, you need to deal with such a mechanism as alignment. First, let's take a look at the diagram of how sections are unloaded into memory.

As you can see, the section is not being unloaded into memory in size. Alignments are used here. This is a value that must be a multiple of the size of the section in memory. If you look at the scheme, we will see that the size of the section is 0x28, and it is unloaded in the amount of 0x50. This is due to the size of the alignment. 0x28 “does not reach” up to 0x50 and as a result, the section will be unloaded, and the rest of the space in the amount of 0x50-0x28 will vanish. And if the section size would be larger than the alignment size, then what? For example,
sectionSize = 0x78, and
sectionAligment = 0x50, i.e. remained unchanged. In this case, the section would occupy 0xA0 (0xA0 = 0x28 * 0x04) bytes in memory. That is, a value that is a multiple of
sectionAligment and fully covers
sectionSize . It should be noted that sections in the file are aligned in a similar way, only by the size of
FileAligment . Having obtained the necessary base, we can figure out how to convert from RVA to RAW.
“This is not a plain here, the climate is different here.”
(V.S. Vysotsky)
A little lesson in arithmetic

Before starting execution, some part of the program must be sent to the address space of the processor. The address space is the amount of RAM physically addressed by the processor. The “piece” in the address space where the program is unloaded is called a virtual image (virtual image). The image is characterized by the address of the base load (Image base) and size (Image size). So, VA (Virtual address) is an address relative to the beginning of virtual memory, and RVA (Relative Virtual Address) is relative to the place where the program was downloaded. How to find out the base download address of the application? To do this, there is a separate field in the optional header called
ImageBase . It was a small prelude to brush up on. Now consider a schematic representation of different addresses:

Duck, how does one read the information from a file without unloading it into virtual memory? To do this, you need to convert addresses to RAW format. Then we can step inside the file to the area we need and read the necessary data. Since RVA is an address in virtual memory, the data for which were projected from a file, we can reverse the process. For this we need the
key nine for sixteen simple arithmetic. Here are a few formulas:
VA = ImageBase + RVA; RAW = RVA - sectionRVA + rawSection;
As you can see, in order to calculate RAW, we need to determine the section that owns the RVA. To do this, go through all sections and check the following conditions:
RVA >= sectionVitualAddress && RVA < ALIGN_UP(sectionVirtualSize, sectionAligment)
Putting all the puzzles together, we’ll get this listing:
typedef uint32_t DWORD; typedef uint16_t WORD; typedef uint8_t BYTE; #define ALIGN_DOWN(x, align) (x & ~(align-1)) #define ALIGN_UP(x, align) ((x & (align-1))?ALIGN_DOWN(x,align)+align:x)
* I did not include in the code type declaration, and array initialization, but only provided functions that will help in calculating addresses. As you can see, the code is not very complicated. Is that a little confusing. This passes ... if you take a little more time to pop up into the .exe through the disassembler.
HOORAY! Understood. Now we can go to the edges of resources, libraries of import and export, and in general where the soul wants. We just learned to work with a new type of addressing. Let's hit the road!
"-Not bad, not bad! Yet they got their ration today! ”
(Treasure Island)
Export table

In the very first element of the
DataDirectory array, the
RVA is stored on the export table, which is represented by the IMAGE_EXPORT_DIRECTORY structure. This table is characteristic of dynamic library files (.dll). The main task of the table is to link the exported functions with their RVA. Description presented in the
office. Specifications :
typedef struct _IMAGE_EXPORT_DIRECTORY { DWORD Characteristics; DWORD TimeDateStamp; WORD MajorVersion; WORD MinorVersion; DWORD Name; DWORD Base; DWORD NumberOfFunctions; DWORD NumberOfNames; DWORD AddressOfFunctions; DWORD AddressOfNames; DWORD AddressOfNameOrdinals; } IMAGE_EXPORT_DIRECTORY,*PIMAGE_EXPORT_DIRECTORY;
This structure contains three pointers to three different tables. This is a table of names (functions) (
AddressOfNames ), ordinals (
AddressOfNamesOrdinals ), addresses (
AddressOfFunctions ). The Name field stores the RVA of the dynamic library name. The ordinal is like an intermediary, between the name table and the address table, and is an array of indices (the size of the index is 2 bytes). For greater clarity, consider the scheme:

Consider an example. Suppose the i-th element of the array of names indicates the name of the function. Then the address of this function can be obtained by referring to the i-th element in the array of addresses. Those. i is the ordinal.
Attention! If you took for example the 2nd element in the table of ordinals, this does not mean 2 - this is the ordinal for the tables of names and addresses. The index is the value stored in the second element of the array of ordinals.
The number of values ​​in the name tables (
NumberOfNames ) and the ordinals are equal and do not always coincide with the number of elements in the address table (
NumberOfFunctions ).
“They came for me. Thanks for attention. They must be killing now! ”
(Treasure Island)
Import table

An import table is an integral part of any application that uses dynamic libraries. This table helps to associate calls of functions of dynamic libraries with the corresponding addresses. Import can occur in three different modes: standard, binding (bound import) and delayed (delay import). Since The topic of importing is quite multifaceted and draws on a separate article, I will describe only the standard mechanism, and I will describe the rest only with a “skeleton”.
Standard import - in
DataDirectory under the index IMAGE_DIRECTORY_ENTRY_IMPORT (= 1) the import table is stored. It is an array of IMAGE_IMPORT_DESCRIPTOR elements. The import table stores (by array) the names of the functions / ordinals and to what place the loader must write the effective address of these functions. This mechanism is not very effective, because frankly, it all comes down to going through the entire export table for each necessary function.
Bound import - with this work scheme, TimeDateStamp and ForwardChain are entered in the fields (in the first element of the standard import table) -1 and the binding information is stored in the
DataDirectory cell with the index IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (= 11). That is, it is a kind of flag for the loader that you need to use bound import. Also for the "chain of bound imports" feature their structures. The operation algorithm consists in the following - the necessary library is unloaded into the virtual memory of the application and all the necessary addresses are "bind" at the compilation stage. One of the drawbacks is that when recompiling the dll, the application itself will need to be recompiled, since function addresses will be changed.
Delay import - with this method it is assumed that the .dll file is attached to the executable, but it is not immediately unloaded into memory (as in the previous two methods), but only when the application first accesses the symbol (this is how the unloading elements from dynamic libraries are called). That is, the program runs in memory and as soon as the process reaches the function call from the dynamic library, a special handler is called that loads the dll and spreads the effective addresses of its functions. For deferred import, the loader accesses DataDirectory [IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT] (item number 15).Having a little illuminated the import methods, we proceed directly to the import table.“-This is a sailor! His clothes were sea. - Yah? Have you ever thought of finding a bishop here? ”
(Treasure Island — John Silver)
Import-descriptor (IMAGE_IMPORT_DESCRIPTOR)
In order to find out the coordinates of the import table, we need to access the DataDirectory array . Namely, to the element IMAGE_DIRECTORY_ENTRY_IMPORT (= 1). And read the RVA table address. Here is a general outline of the path that needs to be done:
Then, from RVA, we get RAW, in accordance with the formulas given above, and then “step along” the file. Now we are right next to an array of structures called IMAGE_IMPORT_DESCRIPTOR. A sign of the end of the array is the “zero” structure. typedef struct _IMAGE_IMPORT_DESCRIPTOR { union { DWORD Characteristics; DWORD OriginalFirstThunk; } DUMMYUNIONNAME; DWORD TimeDateStamp; DWORD ForwarderChain; DWORD Name; DWORD FirstThunk; } IMAGE_IMPORT_DESCRIPTOR,*PIMAGE_IMPORT_DESCRIPTOR;
I could not get a link to the structure description on msdn, but you can see it in the WINNT.h file. Let's start to understand.OriginalFirstThunk : DWORD - RVA import name table (INT).TimeDateStamp : DWORD - date and time.ForwarderChain : DWORD - the index of the first character forwarded.Name : DWORD - RVA strings with the name of the library.FirstThunk : DWORD - RVA Import Address Table (IAT).It's all a bit like export. Also the name table (INT) and also the rags on itaddresses (IAT). Also RVA library name. Only here INT and IAT refer to an array of IMAGE_THUNK_DATA structures. It is presented in two forms - for the 64th and for the 32nd systems and differ only in the size of the fields. Consider the example of x86: typedef struct _IMAGE_THUNK_DATA32 { union { DWORD ForwarderString; DWORD Function; DWORD Ordinal; DWORD AddressOfData; } u1; } IMAGE_THUNK_DATA32,*PIMAGE_THUNK_DATA32;
It is important to answer that further actions depend on the most significant bit of the structure. If it is set, the remaining bits are the number of the character being imported (import by number). Otherwise (high bit cleared), the remaining bits specify the RVA of the imported character (import by name). If we have an import by name, then the pointer stores the address to the following structure: typedef struct _IMAGE_IMPORT_BY_NAME { WORD Hint; BYTE Name[1]; } IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
Here Hint is the number of the function, and Name is the name.What is all this for?
All these arrays, structures ... Consider for clarity, a wonderful scheme with exelab :
What is happening here ... The OriginalFirstThunk field refers to an array where information on imported functions is stored. The FirstThunk field refers to a similar array of the same dimension, except that it is filled during loading with effective addresses of functions. Those.
the loader analyzes the OriginalFirstThunk , determines the real address of the function for each of its elements, and enters this address in FirstThunk . In other words, the binding of imported characters.“I don't like this expedition! I do not like these sailors! And anyway ... what? !!! Oh yes! Not!I don't like anything at all, sir! ”
(Treasure Island - Captain Smollett)
Overboard
The article presented only the base for executable files. Other types of imports are not affected, the behavior of conflicting (for example, physical section size, more virtual) or ambiguous (in the same import - the question of which method to use) situations. But this is all for more detailed study and depends on the specific loaders in the OS and the compilers that compiled the program. Also not affected directories of resources, debugging and other. For those who are interested, you can read more detailed guidance presented in the list of references at the end of the article.“Say, Ham, will we wag for a long time like a bowling boat? I'm sick of the captain to death. Stop him commanding! I want to live in his cabin. ”
(Treasure Island)
Conclusion
After we returned from the trip, I’ll summarize what we saw and what they carried out. Today we have understood a lot . Namely, I will describe the process of downloading an application in general terms.- First, the headers are read and checked that the file is executable. Otherwise, the work stops before it starts.
- The loader allocates the required amount of virtual memory for the application. If possible, the application will be downloaded to the preferred address. If not, then another part of the memory will be allocated for the application and loaded from this address.
- ( ) . .
- , .
- dll. .
This article ends. I think this information is quite enough to have a basic understanding of executable files. The most curious way on exelab , wasm , msdn , assembler and disassembler.For a clearer understanding, you can recommend to study the charts. It really gives a more complete picture of what is happening inside. As an example, I can offer this article by alizar user or more general schemes in Google . Hope you enjoyed our trip.Bibliography
ru.wikipedia.org/wiki/Portable_Executablemsdn.microsoft.com/en-us/library/ms809762.aspxacmvm2.srv.mst.edu/wp-content/uploads/2014/07/PE-Header-Bible.pdfcs.usu.edu.ru/docs/pe—exelab.ru/faq