Peering Inside the pe: a tour of the win32 portable executable file format
Matt Pietrek March 1994 This article comes from Microsoft System Journal, March 1994. Copyright,? 1994 Miller Freeman, Inc. All rights reserved! Any part of this article is not plagiarized in any form without Miller Freeman, except for a summary reference in papers or comments). The executable file format of an operating system is a mirror of this system in many ways. Although learning an executable format is usually not a programmer's primary task, you can learn a lot of knowledge from this. In this article, I will give a detailed description of all Win32 systems (such as WinNT, Win9x) of Microsoft (such as WINNT, WIN9X). In a presented future, including Windows2000, PE file format plays an important role in Microsoft operating systems. If you are using Win32 or Winnt, then you are already using a PE file. Even you just use Visual C programming under Windows3.1, which is still the PE file (the 32-bit MS-DOS extension component of Visual C ). In short, the PE format has been applied in general and is still inevitable in the short future. It is time to find out what this new executable format is brought about by the operating system. I won't let you stare at the endless sixteen-entered DUMP, and I will not discuss the importance of every individual bit of the page. Instead, I will introduce you to the concepts contained in the PE file and link them with what you encounter every day. For example, the concept of thread local variables, as described below: Declspec (Thread) INT i; I am going crazy until I found that it is so simple and elegant in executable. Since many of you have a background of 16 windows, I will trace the construction of Win32 PE files back to the 16-bit NE file in its equivalent. In addition to a different executable file format, Microsoft also introduces a new target module format generated with its compiler and assembler. This new OBJ file format has many Dongdong in the PE file. I have made many documents that useless to find this new OBJ file format. So I analyze it with my understanding, and, here, in addition to the PE file, I will describe part of it. Everyone knows that Windows NT inherits the tradition of VAX? VMS? And UNIX? Many founders of Windows NT are designed and encoded on these platforms before entering Microsoft. When they start designing Windows NT, it is natural, in order to minimize project startup time, they will use the previously written and tested tools. Use these tools to generate and operate the executable and OBJ file formats called COFF (Common Object File Format's first letters). The relative age of COFF can be specified in an octave domain. Coff itself is a good starting point, but it needs to be extended to a modern operating system such as Windows 95 and Windows NT needs. The result of this update is (PE format) portable executable file format. It is called "portable" because WindowsNT implemented on all platforms (such as X86, Alpha, MIPS, etc.) uses the same executable file format. Of course, there are many different things such as binary code CPU instructions. Important is that the operating system's loaders and programming tools do not need to be able to achieve full rewriting for any CPU.
Microsoft discards existing 32-bit tools and executable formats confirmed that they want WindowsNT upgrades and run faster determination. The virtual device driver written for 16-bit Windows uses a different 32-bit file layout --le file format - WindowsNT appears very early. More important than this is the replacement of the OBJ file! Before Windows C compiler, all Microsoft compilers use Intel's Omf (Object Module Format) specification. Just as mentioned earlier, Microsoft's Win32 compiler generates the OBJ file in the COFF format. Some Microsoft competitors, such as Borland and Symentec, choose to abandon the COFF format and stick to Intel's Omf file format. Such a result is to make OBJ and LIB companies have to distribute different versions of these libraries for each different compiler (if they don't do this) in order to use multiple different compilers. The PE file format is filed in Winnt.h header files (with the least accurate language)! Circular in the middle part of Winnt.h title is a fast in "Image Format". This block begins on a small column before moving the MS-DOS MZ file head and the Ne file header into the new PE file. Winnt.h provides definitions of fresh data structures for PE files, but only rarely contributes to understanding of these data structures and logo variables. No matter who wrote such a header file for the PE file format, it is definitely a believer (suddenly the name of Michael J. O'Leary) is suddenly continuous. Describe the name, along with the embedded structure and macro. When you are encoded with Winnt.h, similar to the following expressions are not fresh: pntheader-> optionalHeader.dataDirectory [image_directory_entry_debug] .VirtualAddress; in order to help logic understand the information in these Winnt.h, read Migration of executable and public object file formats, these are available in MSDNs, which are available in CD, which has been included in August 2001. Now let us switch to the main body of the OBJ file in the COFF format, Winnt.h includes Coff Obj and LIB structure definitions and type definitions. Unfortunately, I haven't found similar documents in the executable format mentioned above. Since PE files and Coff obj files are like this, I decided to bring these files to the focus and put them also document. Just read about the composition of the PE file, you also want Dump some PE files to see these concepts. If you use Microsoft based on 32-bit Windows development tools, the Dumpbin program can convert PE files and Coff obj / lib files to readable forms. In all PEDUMP, Dumpbin is the most easily understood. It happens to have some good options to disassemble the code blocks that are parsing files. Borland users can use TDUMP to browse PE files, but TDUMP cannot resolve the Coff Obj / lib file. This is not an important thing because the Borland's compiler first does not generate an OBJ file in the COFF format. I wrote a DUMP program for PE and Coff obj files --pedump (see Table 1), I want to provide some output than Dumpbin more understandable. Although it does not have an inverse editor and works with the lib library file, it is the same in other aspects and Dumpbin, and adds some new features to make it worthy of recognition. Its source code can be found on any MSJ Electronics Expo, all I don't plan to list all him here. Instead, I showcase some samples from PEDUMP to clarify the concept I have described for them.
Dismissue: Table 1 PEDUMP.CFILE: / / -------------------- // Program: PEDUMP / / FILE: PEDUMP.C / / Author: Matt Pietrek - 1993File: / / -------------------- # Include
Char helptext [] = "PEDUMP - WIN32 / Coff .exe / .obj File Dumper - 1993 Matt Pietrek / N / N" "Syntax: PEDUMP [Switches] filename / n / n" "/ a include everything in dump / n" "/ H include hex dump of sections / n" "/ L include line number information / n" "/ r show base relocations / n" "/ s show symbol table / n";
// Open up a file, memory map it, and call the appropriate dumping routinevoid DumpFile (LPSTR filename) {HANDLE hFile; HANDLE hFileMapping; LPVOID lpFileBase; PIMAGE_DOS_HEADER dosHeader; hFile = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL , 0); if (hfile = = invalid_handle_value) {Printf ("COULDN '}); return;} hfilemapping = createfilemapping (HFileMapping (HFile, NULL, Page_ReadOnly, 0, 0, null); IF (HFILEMAPPING = 0) {CloseHandle (HFILE); Printf ("COULDN ''} () / n"); Return;} lpfilebase = mapViewoffile (HFileMapping, File_Map_read, 0, 0, 0); IF LPFILEBASE = 0) {CloseHandle (HFileMapping); CloseHandle (HFILE); Printf ("" COULDN't Map View of File with MapViewOffile () / N "); Return;}
printf ( "Dump of file% s / n / n", filename); dosHeader = (PIMAGE_DOS_HEADER) lpFileBase; if (dosHeader-> e_magic = = IMAGE_DOS_SIGNATURE) {DumpExeFile (dosHeader);} else if ((dosHeader-> e_magic = = 0x014c) // does it look like a i386 && (dosheader-> e_sp == 0) // Coff obj file ??? {// The Two Tests Above Aren't What They Look Like. TheY're // really checking for IMAGE_FILE_HEADER.Machine = = i386 (0x14C) // and IMAGE_FILE_HEADER.SizeOfOptionalHeader = = 0; DumpObjFile ((PIMAGE_FILE_HEADER) lpFileBase);} else printf ( "unrecognized file format / n"); UnmapViewOfFile (lpFileBase); CloseHandle ( Hfilemapping; closehandle (hfile);
// process all the command line arguments and return a pointer to // the filename argument.pstr processcommandline (int Argc, char * argv []) {INT i; for (i = 1; i 1 Win32 and PE Basic Concept Lets We review Several Basic Concepts (see Figure 1) through the design of the PE file (see Figure 1). I use the term "module" to represent an executable or a DLL that loads the memory code (data), resources, in addition to code and data are directly used by your program, one module can also The support data structure consisting of Windows used to determine the location of the data and the code load. In 16-bit Windows, these support data structures are in the module database (segment with an HMODULE). In Win32, these data structures are in the PE file head, which I will explain it. Figure 1 PE file 图 图 About the PE file is the most important thing that the executable file on the disk is very similar after the Windows is transferred to memory. The Windows Loader does not have to create a process for a work from the disk to load a file from the disk. The loader uses the memory map file mechanism to map similar blocks in the file to the virtual space. With a structural analysis model, a PE file is similar to a prefabricated room. It starts in nature in such space, and there are several parts that connect it to the remaining space (that is, let it contact it on its DLL, etc.). This is as easy to apply on the DLL of the PE format. Once this module is loaded, Windows can effectively treat it and other memory map files. Unlike 16 windows. The 16-bit NE file is used to read part of the file and create a completely different data structure representing the module in memory. When the data segment or code segment needs to be loaded, the loader must apply for a segment from the global heap, find out fresh data from the executable, go to this position, read these fresh data, and Appropriate correction. In addition, each 16-bit module is responsible for remembering all segment selectors current it used, regardless of whether this segment is discarded, so. For Win32, all code, data, resources, import tables, and other required module data structures used in modules are in a consecutive memory block. In this situation, you just need to know where the loader maps the executable file. By a pointer as part of the image, you can easily find all the different blocks of this module. Another concept you need to know is relative virtual address (RVA). Many domains in the PE file are specified by the term RVA. A RVA is just some projects to be mapped to the mortems of the file. For example, the loader maps a file to the memory block started by the virtual address 0x10000. If the actual table in an image is 0x10464, then its RVA is 0x464. (Virtual Address 0x10464) - (base 0x10000) = RVA 0x00464 In order to convert a RVA into a useful pointer, you only need to add the RVA value to the base address of the module. The base address is the first place in memory map EXE and DLL files, which is a very important concept in Win32. For convenience, WindowsNT and Windows9x's base address as an instance handle (Hinstance) of this module. In WIN32, the base address of the module is called Hinstance may result in confusion because the term "example handle" is from 16-bit Windows. A program gives each copy of the 16-bit Windows to get its own subsequent data segments (and a global handle) to separate it and the other copies of this program, the term "example handle". In WIN32, each program does not have to distinguish other programs because they do not share the same address space. The term Instance still maintains continuity between 16 Windows and 32-bit Windows. It is important in Win32 that you can call getModuleHandle () to get a pointer to access its components (translation) for any DLL call. Translation: If the dllname is NULL, you get your own module handle. This is very useful. If the startup code generated by the usual compiler will get this handle and pass it as a parameter hinstance to WinMain! The concept of the PE file you ultimately needs to be "section". One block or resource equivalent in a block in the PE file. The block can contain code or data. Different from the paragraph, the block is a continuous space in memory without size limits. These blocks contain code or data that are directly declared and used when your connector and library are created for you and contain other data blocks that are important to the operating system. In some descriptions in some PE format, blocks are also called objects. The term object has so much meaning so that only the code and data are called "block". 2 PE headers and other executable File formats, the PE file has some domains that define the remainder of the file in well-known places. The header contains the location and size of this like code and data, the operating system is intervened, such as the initial stack size, and other important block information, I will introduce it. Compared with Microsoft's other executable formats, the main first part is not the beginning of the file. Typical PE files start hundreds of bytes occupied by DOS residual part. This residual part is a small program that can be printed as "this program cannot run under DOS!". So, you can get such an error message in a system that doesn't support Win32. When the loader maps a Win32 program to memory, the first byte of this mapping file corresponds to the first byte of the DOS residual portion. It is undoubtedly. With any Win32-based program that you start, there is a DOS-based program partition that is loaded. Like other Other executable formats of Microsoft, you can get the real head by finding its start offset, this offset is placed in the DOS residual head. The Winnt.h header contains the data structure definition of the DOS residual program, making it easy to find the starting position of the PE header. The E_LFANEW domain is the offset of the real head of PE. In order to obtain a pointer in the memory, only the value is added to the base address of the image. File: // ignore the type transformation and pointer transformation ... Pntheader = dosheader dosheader-> e_lfanew; once you have a pointer to the top of the PE, the game can start! The PE The header is the structure of an image_nt_headers, defined in Winnt.h. This structure consists of a double word (DWORD) and two sub-structures, the layout is as follows: DWORD SIGNATURE; image_file_header filehead; image_optional_header optionalheader; Sign domain with ASCII means "PE / 0/0". If you use the E_LFANEW domain in the DOS header, you get a NE flag instead of PE, then this is a 16-bit NE file. Similarly, LE in the flag domain represents this is a Windows3.x virtual device driver (VXD). Lx means that this file is an OS / 2 2.0 file. The PE DWORD flag is the structure image_file_header. This domain only contains the most basic information of this file. This structure is not changed from its original COFF implementation. In addition to part of the PE head, it also manifests the first part of the COFF OBJ file generated by the Microsoft Win32 compiler. IMAGE_FILE_HEADER this field is shown below: Table 2 IMAGE_FILE_HEADER Fields WORD Machine indicates the type of CPU, below defines some of the CPU ID0x14d Intel i8600x14c Intel I386 (same ID used for 486 and 586) 0x162 MIPS R30000x166 MIPS R40000x183 DEC Alpha AXPWORD NumberOfSections this document The number of blocks. DWORD TIMEDASTAMP connector generates this file (the OBJ file is a compiler), the number saved by this domain is from 4:00 pm in December 1969 to the current number of seconds. DWORD POINTOSYMBOLTABLE COFF symbol table file offset. This domain is only used for OBJ files and PE files with COFF debugging information. The PE file supports multiple debug information formats, so the debugger should point to the image_directory_entry_debug entry of the data directory. DWORD NUMBEROFSYMBOLS COFF number of symbols. See above. Word SizeOfoptionalHeader The size of the optional header behind this structure. In the OBJ file, this domain is 0. In the executable, this is the size of the image_optional_header structure after this structure. Word Characteristics About this file message. Some important domains are as follows: 0x0001 This file does not have a relocation information 0x0002 executable file image (not OBJ or LIB file) 0x2000 file is a dynamic connection library, not program Other domains are defined in WinNT.H. The third component of the PE head is an image_optional_header structure. This part is of course not "optional" for PE files. The COFF format allows separate implementations to define a structure that exceeds the standard image_file_header additional information. Image_optional_header The domain in the PE is a very critical information outside the basic information of Image_File_Header. Not all domains of Image_Optional_Header are important (see Figure 4). More important, you need to know the imageBase and Subsystem domain. You can ignore the description of other domains. Table 3 Image_File_Header's domain: Word Magic is characterized by some categories of flag words, usually 0x010b. Byte Majorlinkerversion Byte MinorlinkerVersion generates the version of the connector of this file. This number is made in decimalization than hexadecimal. A typical connector version is 2.23. DWORD SIZEOFCODE The carry size of all code blocks. Usually most files have only one code block, so this domain and .Text block match. DWORD SizeOfinitializedData The size of the block that is initialized (excluding code segments). However, it is inconsistent with its expression in the document. DWORD SIZEOFUNITIALIZEDDATA The loader is applied in virtual memory, but the size of the block is not occupied in the file on the disk. These blocks do not need to specify initial values when the program is started, so the term is "unmected data". Uninjected data is usually in a block called .bss. The DWORD AddressOfEnTryPoint loader starts executing the address of this program, that is, the entry address of this PE file. This is a RVA, usually in the .Text block. DWORD baseofcode code block start address RVA. In memory, the code block is usually before the PE header, the data block. This value is usually 0x1000 in the EXE file generated by Microsoft connector. Like the Borland connector TLINK32, the RVA and image bases of the image first code block are added to the image. Translation: This domain seems to have not used it. DWORD baseofData data block start address RVA. In memory, data blocks are often last, after the PE header and the code block. Translation: This domain seems to have not used anything. When the DWORD ImageBase connector creates an executable file, it assumes that this file is mapped to a specified place in the memory, and this address exists in this domain, assumes that one load address can optimize the connector to save space. If the loader really maps this file to this place, the code does not require any changes before running. In the executable created for WindowsNT, the default imagebase is 0x10000. For DLL, the default is 0x40000. In Window95, address 0x10000 cannot be used to load 32-bit EXE files because this area is in a linear address space shared by all processes. Therefore, Microsoft changes the default base address of WIN32 executable to 0x40000, assuming that the base address is 0x10000 sitting in Windows 95 requires a longer load time because the loader needs to be relocated the base address. Translation: This domain is "prefered load address", if there is nothing, this is the address after the PE file loads. DWord SectionAlignment When each block must ensure an integer multiple of this value. For the purpose of paging, the default sectionalignment is 0x1000. DWORD FileAlignment In the PE file, the fresh data that makes up each block must ensure that the integer multiple of this value is started. The default is 0x200 bytes, perhaps to ensure that the block begins in a disk sector (a sector is usually 512 bytes). The segment / resource alignment size in this domain and the Ne file is equivalent. Unlike the NE file, the PE file usually does not have hundreds of blocks, so it is very small for alignment. Word MajoroPeratingSystemVersion Word MinorOperatingSystemVersion This program runs the minimum version number of the operating system. This domain is a bit embossed because the subsystem domain (later) can provide similar functions. This field is 1.0 in the WIN32 that is currently available. Word MajorImageVersion Word MinorImageVersion A domain that can be defined by a user. This allows you to have different EXE and DLL versions. You can set the value of this domain through the linker / version option. For example: "Link /Version:2.0 Myobj.obj". Word MajorsubsystemVersion Word MinorsubsystemVersion This program runs the minimum subsystem version number. A typical value of this domain is 3.10 (representing WindowsNT 3.1). DWORD reserved1 is usually 0. The DWORD SIZEOFIMAGE loader must care about the size of all parts of all parts. It is the size of the area from the beginning of the image to the last block. The last block is in the end of SectionAlignment. Translation: This is very important, you can be big, but not small! DWORD SIZEOFHEADERS PE header and block size. The actual data of the block is followed by all the first components. DWORD Checksum This file's CRC checksum. In Microsoft executable format, this domain is ignored and set to 0. An exception to this rule is trust service, which must have a legal checkup. The subsystem type used by the user interface of the Word Subsystem executable file. Winnt.h defines these values: Native 1 does not require subsystems (such as device driver) Windows_gUI 2 running Windows_cui 3 under Windows graphics user interface system running under Windows Character subsystem (console program) OS2_CUI 5 in OS / 2 Under the 1-character subsystem (OS / 2 1.x only on the OS / 2 1.x) POSIX_CUI 7 Run Word DllCharacteristics under the POSIX Character subsystem Specifies the label variable of the initialization function (such as dllmain) that the next DLL will be called. This value is often set to 0. However, the operating system still invokes the initialization function of the DLL in the following four cases. The following value is defined as: 1 DLL When the first load is loaded into the process space to call 2 a thread at the end of call 4 a thread start calling 8 When exiting the DLL DWORD SIZEOFSTACKRESERVE is the total number of virtual memory that is reserved for the initial thread. However, not all of these memory is submitted (see next domain). The default value of this domain is 0x100000 (1 MBYTES). If you specify the stack size as 0 in CreateThread, the result will be the same value (0x10000). DWORD SIZEOFSTACKCOMMIT starts the total number of initial thread stacks. For Microsoft connectors, this domain is the 0x1000 byte (one page), and TLINK32 is two pages. DWORD SIZEOFHEAPRESERVE is the total number of virtual memory for the initial process. The handle of this pile can be obtained with getPocessHeap. Not all of these memory is submitted (see next domain). DWORD SIZEOFHEAPCommit starts the total amount of memory submitted for the process heap. The default is a page. DWORD loaderflags can be seen from Winnt.H, which is linked to debugging support. I have never seen which executable files are set, clear it allows the connector to set it. The following value is defined as: 1. Call a endpoint instruction before starting the process 2. Call a debugger when the process is loaded DWord NumberofrvaAndsizes Number of entries in the data directory array (see below). Current tools typically set this value to 16. Image_data_directory dataDirectory [image_numberof_directory_entries] A Image_Data_directory structure array. The initial array element contains the starting RVA and size of the important part of the executable. Some of this array is not used now. The first element of this array often exports the address and size of the function table. The second array entry is the address and size of the import function table, and so on. For a complete, defined array entry, see image_directory_entry_xxx definition in Winnt.h. This array allows the loader to quickly find a specified block (eg, import function table) of this image, without the need to traverse the image of the image, determine by the comparison name. Most array entries describe a whole piece of data. However, the image_directory_entry_debug item includes only a small number of bytes of the .rdata block. 3 blocks between the PE header and the image block is a block table. The block table is essentially a telephone number containing each block information in the image. The blocks in the image are arranged in their starting address (RVA) instead of alphabetic. Now, I will further clarify what is a block. In the NE file, your program code and data are stored in a segment of each other. Part of the Ne head is a structure array, each of which is used by your program. Each structure in the array contains information of a segment. This information stores the type of segment (code or data), size, and location it in the file. In the PE file, the blocks in the block and the NE file are similar. Different from the Dial of the ne file, the PE block entry does not store a code and data block selection. Instead, the fresh data of each block table item storage file is mapped to the memory in memory. So the block is similar to the 32-bit segment, but they are actually not separate segments. They are actually a memory range of the process virtual space. Another PE file and the NE file are how it managed to manage your program, but the support data to be used by the operating system; for example, the DLL list or correction table of the executable file is used. In the NE file, the resource is not treated as a segment. Even the selection of the selection is even assigned, the information about the resource is not stored in the table table of the NE file header. Instead, the resource submitted to a separate table is tailored to the head of the PE. Information about importing and exporting functions is not authorized to give it its own paragraph; it is interleaving in the Ne head. The story of the PE file is different. Any code or data that may be considered to be critical is exist in a complete block. Thus, the information importing the function table exists in its own block, the export table is the same. The same is true for repositioning data. Any code or data that can be required for programs or operating systems can get their own blocks. Before I discuss a particular block, I need to describe the data of the operating system to manage these blocks. It is an Image_SECTION_HEADER array in the memory in the memory. The number of elements of the array is given in the PE header (Image_nt_Header.FileHeader.NumberofSections "field). I use PEDUMP to output all the domains and their properties of block tables and blocks. Table 5 describes a block table for a typical EXE file output with PEDUMP, and Table 6 gives a block table of the OBJ file. Block table 01 .text VirtSize Table 4 a typical EXE file: 00005AFA VirtAddr: 00001000 raw data offs: 00000400 raw data size: 00005C00 relocation offs: 00000000 relocations: 00000000 line # offs: 00009220 line # 's: 0000020C characteristics: 60000020 CODE MEM_EXECUTE MEM_READ02 .bss VirtSize: 00001438 VirtAddr: 00007000 raw data offs: 00000000 raw data size: 00001600 relocation offs: 00000000 relocations: 00000000 line # offs: 00000000 line # 's: 00000000 characteristics: C0000080 UNINITIALIZED_DATA MEM_READ MEM_WRITE 03 .rdata VirtSize: 0000015C VirtAddr: 00009000 raw data offs: 00006000 raw data size: 00000200 relocation offs: 00000000 relocations: 00000000 line # offs: 00000000 line # 's: 00000000 characteristics: 40000040 INITIALIZED_DATA MEM_READ04 .data VirtSize: 0000239C VirtAddr: 0000A000 Raw Data Offs: 00006200 Raw Data Size: 00000000 RAW DATA Size: 00000000 Relocations: 00000000 line # offs: 00000000 line # 's: 00000000 Characteristics: c0000040 Initialized_data mem_read mem_write 05 .idata VirtSize: 0000033E VirtAddr: 0000D000 raw data offs: 00008600 raw data size: 00000400 relocation offs: 00000000 relocations: 00000000 line # offs: 00000000 line # 's: 00000000 characteristics: C0000040 INITIALIZED_DATA MEM_READ MEM_WRITE 06 .reloc VirtSize: 000006CE VirtAddr: 0000E000 raw data offs: 00008A00 raw data size: 00000800 relocation offs: 00000000 relocations: 00000000 line # offs: 00000000 line # 's: 00000000 characteristics: 42000040 INITIALIZED_DATA MEM_DISCARDABLE MEM_READ Table 5 a typical file OBJ block table 01 .drectve PhysAddr: 00000000 VirtAddr: 00000000 raw data offs: 000000DC raw data size: 00000026 relocation offs: 00000000 relocations: 00000000 line # offs: 00000000 line # 's: 00000000 characteristics: 00100A00 LNK_INFO LNK_REMOVE 02 .debug $ S PhysAddr: 00000026 VirtAddr: 00000000 raw data offs: 00000102 raw data size: 000016D0 relocation offs: 000017D2 relocations: 00000032 line # offs: 00000000 line # 's: 00000000 characteristics: 42100048 INITIALIZED_DATA MEM_DISCARDABLE MEM_READ 03 .data PhysAddr: 000016F6 VirtAddr: 00000000 raw data offs: 000019C6 raw data size: 00000D87 relocation offs: 0000274D relocations: 00000045 line # offs: 00000000 line # 's: 00000000 characteristics: C0400040 INITIALIZED_DATA MEM_READ MEM_WRITE04 .text PhysAddr: 0000247D VirtAddr: 00000000 Raw Data Offs: 000029FF Raw Data Size: 000010DA RELOCATION OFFS: 00003AD9 RELOCATIONS: 000000E9 LINE # offs: 000043f3 line # 's: 000000d9 characteristics: 60500020 code mem_execute mem_read 05 .debug $ T PhysAddr: 00003557 VirtAddr: 00000000 raw data offs: 00004909 raw data size: 00000030 relocation offs: 00000000 relocations: 00000000 line # offs: 00000000 line # 's: 00000000 characteristics: 42100048 INITIALIZED_DATA MEM_DISCARDABLE MEM_READ each has IAMGE_SECTION_HEADER A format as described in Figure 7. Note that the information stored in each block is missing what is very interesting. First, pay attention not to indicate any preloaded properties. The NE file format allows you to specify the properties of the preloaded segment that should be loaded with the module. OS / 2? 2.0 lx format is a bit similar, allowing you to specify the preloaded eight page (memory page: translation, the same below). There is no similar thing in PE format. Microsoft must ensure that the load performance of the Win32 requirement page. Table 6 Image_section_Header Format Byte Name [Image_SizeOf_short_name] This is a 8-byte ANSI name named (not Unicode) named. Most of the block names begin to "." (Such as ".text"), but this is not necessary, just like some PE documents you may believe. You can use any segment in the assembly language to indicate your own block. Or instruct it to "#pragma Data_Seg" in the Microsoft C / C compiler. It should be noted that if the block name is full of 8 bytes, there is no NULL end byte. If you are passionate about Printf, you can use% 8s to avoid copying this name into a buffer, and then add a null byte at the end. Union {dWord PhysicalAddress DWORD Virtualsize} MISC; in EXE and OBJ, this domain is different. In EXE, it saves the actual size of code or data. This size is to be aligned and carrying the calibration file. The SIZEOFRAWDATA domain of this structure (this word is not exactly) saves the size of the calibration file to the size and carrying the size. Borland's connector has changed the meaning of these two domains, so it is correct. For the OBJ file, this domain indicates the physical dimensions of the block. The first block begins at address 0. In order to find the next block in the OBJ file, add SIZEOFRAWDATA to the current block base address. DWORD VirtualAddress In EXE, this domain saves the loader to map the block to the RVA in the memory. To calculate a given block in the actual start address, the base address of this image is stored in VirtualAddress in this domain. With Microsoft tools, the default RVA of the first block is 0x1000. In the OBJ file, this domain does not make sense and is set to 0. DWORD SIZEOFRAWDATA In EXE, this domain contains this block to align the size of the file. For example, assume that the alignment of a file is 0x200. If this block's VirtualAddress field (front domain) is 0x35a, then this domain is 0x400. In the OBJ file, this domain contains exact sizes of blocks provided by the compiler or assembler. In other words, for Obj, it is equivalent to the VirtualSize domain in EXE. DWORD POINTERTORAWDATA This is a file-based offset that can be found by the compiler or assembler by this offset. If your program is mapped by a PE or COFF file to memory (instead of allowing the operating system to load), then this domain is more important than VirtualAddress. In this case you have a full linear file mapping, you will find the data of the block at this offset, not the RVA specified by the VirtualAddress domain. DWORD POINTERTORELOCATION In Obj, this is a file-based offset value of the relocation information of the block. The relocation information of each OBJ block is followed by the fresh data of this block. In EXE, this domain (and later) is meaningless, it is set to 0. When the connector generates EXE, it solves most of this correction value, only the base number of relocation and import functions will be resolved during load. Regarding the basic weight positioning information and import functions remain in their own block, for an EXE, there is no need to keep up with its relocation information after fresh data of each block. DWORD POINTERTOLINENUMBERS This is a line-based offset. The line number table connects the source file and (the compiler) to the first site of the (machine) code generated by this line. In the modern debug format such as the CodeView format, the line number information is stored as part of the debug information. However, in the COFF debug format, the storage of the line number information and the symbolic name / type information is separated. Usually only code blocks (such as .text) have line number information. In the EXE file, the line number information is collected in the end of the file. In the OBJ file, the line number information of a block follows the fresh block data and the relocation table of this block. The number of repositioning items in the relocation table of the Word NumberOfrelocations block (refer to the PointertoreLocations field above). This domain seems to be only related to the OBJ file. The number of line number items in the line number table of the Word Numberoflinenumbers (refer to the PointertolinenumBers field above). DWORD Characteristics Most programmers are called flag, and the Coff / PE format is called character. This domain is a logo set indicating block properties (such as code / data, readable, writable). A complete list of all possible block properties, see the definition of image_scn_xxx_xxx in Winnt.h. The following is some important signs: 0x00000020 This block contains code. Typically and the executable flag (0x80000000) is set together. 0x00000040 This block contains initialized data. This flag is set to almost all blocks of the block and .BSS blocks. 0x00000080 This block contains uninitialized data (such as .bss block) 0x00000200 This block contains annotations or other information. A typical usage of this block is the compiler generated. DRECTVE block, contains the linker command. 0x00000800 The content of this block should not be put into the final EXE file. These blocks are compiler or assembler to deliver information to the connector. 0x02000000 This block can be discarded, because once it is loaded, its process does not need it. The most commonly discarded block is the basic weight position block (.reloc). 0x10000000 This block is shared. When used with DLL, the data of this block can be shared between the process of using this DLL. The data block is non-shared by default, which means that all processes of this DLL have their own copy of this block. In a more professional term, the shared block tells the memory manager to map the page of this block using this DLL to the same physical page in memory. To make a block can be shared, use the Share attribute when connecting. Such as: LINK / Section: MyData, RWS ... Tell the connector called "MyData" block is readable, writable, shared. 0x20000000 This block is executable. This flag is usually set when the "included code" flag (0x00000020) is set. 0x40000000 This block is readable. In the EXE file, this domain is almost always set. 0x80000000 This block is writable. If this block is not set in an EXE block, the loader will use the memory map page to read only or "only". Typical blocks with this property are .data and .bss. Interestingly, .idata block also has this attribute. PE format is still lacking the concept of "page table". In the LX format, the image_section_table equivalent of OS / 2 does not direct the code or data block in the file. Instead, it points to a page lookup table that indicates a specific range of properties and locations in blocks. PE format allocates all, and make sure that the data in all blocks will be continuously stored in the file. Compare these two formats: LX can allow greater flexibility, but PE style is simpler and easier to work together. I have written Dumper of these two files. Another welcome change in the PE format is that all items are stored as simple double word offset. In the NE format, almost all of all things are stored as their sector values. In order to get the actual offset, you first need to find the NE header size and convert it into a sector size (typically 16 and 512 bytes). Then you need to multiply the sector size by the specified sector offset to get the actual file offset. If some things in the ne file are accidentally stored as a sector offset, this may be relative to the neighborhood. Because the NE head is not in the beginning of the file, you need to adjust the NE head of this file in your own code. In short, PE format is easier to work together than NE, LX, or Le format (assuming you can use memory image files). 4 Universal blocks have seen a general block and what they are in, let's take a look at the general block you will be found in the exe and obj files. This column vote is not complete, but contains the blocks you have encountered every day (even you don't realize). .Text block is a universal code block generated at the end of the compiler or the assembler. Because the PE file runs in 32-bit mode, there is no 16-bit restriction, there is no reason to divide the code into separate blocks according to the separate source file. Instead, the connector connects the .Text block from different OBJ files to a large .Text block in the exe file. If you use Borland C , the compiler puts the resulting code in a block named Code. The Borland C generated PE file has a block called Code instead of .Text. I will explain short. Figure 2. Calling a Function In another module In addition to the code created by the compiler or from the code obtained in the runtime library, it is more interesting to find additional code in the .Text block. In a PE file, when you call a function in another module (such as getMessage in user32.dll), the CALL instruction generated by the compiler does not transfer the control directly to this function in the DLL (see Figure 8). Instead, the CALL instruction shifts the control to a JMP DWORD PTR [xxxxxxxx] instruction in .text. This JMP instruction (translation 1) passes indirect transfer control over DWORD variables in .idata. The DWORD of the .idata block contains the actual address of the operating system function entry. After a while, I started to understand why DLL calls are implemented in this way. Transfer all the calls to a given DLL function via a location, the loader does not need to change the instructions of each calling DLL. All PE loaders must do to put the correct address of the target function in a DWORD of .idata. There is no need to change any CALL instructions. Different in the NE file, each segment contains a correction table that needs to be applied to this segment. If this segment calls a given DLL function 20 times, the loader must write the address of this function into each call instruction of this segment. The disadvantage of the PE method is that you cannot initialize a variable with a true address of a DLL function. For example, you have to consider this situation: FarProc PfNgetMessage = getMessage; will save the getMessage address to the variable PfNgetMessage. In the 16-bit Windows, this can work, but cannot be in Win32. In WIN32, the variable PFNGetMessage finally stores the JMP DWORD PTR [xxxxxxxx] replacement indication (translation 2) mentioned earlier. If you want to call a function through a function pointer, things will be as expected. However, if you want to read the bytes starting with GetMessage, you will not be as wish (unless you do your own work after you follow .idata pointer). Behind I will return to this topic - in the discussion of the import table. Translation 1: English Thunk, orthodox Computer Special Terminology for "Form Converter", similar macro (Macro), so I translated it into an "replacement instruction", refers to the replacement indication of XXXXXXX in the specific instruction. with. Translation 2: The current compiler such as VC6 or more, etc., the resulting import function call code is no longer a first relative call instruction to JMP [xxxx], then go to XXXX (the real import function entry), but Use a higher efficiency, more easily understood: Call [xxxx]. It used to use the indirect way to compatibility with compilers. But now there are still some compilers, such as MASM, until version 7.0, or in front of the indirect manner, from here, Microsoft's attitude is attitude towards ASM. Although Borland can make the code block of the compiler output to .Text, but it is selected as the default segment name. In order to determine the block name in the PE file, the Borland's connector (TLINK32.exe) removes the segment name from the OBJ file and truncates it as 8 characters (if necessary). When the block name is just a small problem, how the Borland PE file links to other modules is an important difference. Just as I mentioned in the .Text description, all the call to the OBJ is replaced by a JMP DWORD PTR [xxxxxxxx]. Under Microsoft System, this instruction arrives through a import library .Text block. Because the library manager (LIB32) creates a warehouse (and this replacement indication) when you link the external DLL, the connector does not need to "know" how to generate this replacement instruction. The import library is actually only some of the more code and data that links to this PE file. The system of the Borland processing import function is just a simple 16-bit NE file mode extension. The import library used by the Borland connector is actually just a list of function names along with its DLL name. So TLINK32 is responsible to determine the correction of the external DLL, and generates an instruction indication for it into an appropriate JMP DWORD PTR [xxxxxxxx]. TLINK32 stores this replacement indicator in its created name .icode block. Just like .Text is the default code block ,.DATA block is the home of the initialized data. These data contain global and static partial variables initialized at compile time. It also includes a text string. The connector combines all the .DATA blocks from the OBJ / LIB file to an EXE file. Data block. The local variable is loaded into a stack of threads, not occupying space in .data or .bss. The .BSS block is where the global and static partial variables that are not initialized are stored. The connector connects all the .bss blocks in the OBJ / lib file to a .bss block of the EXE file. In the block table, the RawDataOffset domain of the .bss block is set to 0, indicating that this block does not take any space in the file. TLINK does not produce this block. Instead, it expands the virtual size of the DATA block. The .CRT block is a block (from the name) of another initialized data used by the Microsoft C / C runtime library. I can't understand why this data is not placed in .data. (Translation) Translation: From the literal meaning of CRT, it should be "C Run Time", the C Runtime Library. .RSRC block All resources of this module. In the early days of Windows NT, the 16-bit rc.exe output RES file is a format that Microsoft's PE connector cannot recognize. The CVTRES program converts this RES file into the OBJ file in the COFF format, putting the resource data in the OBJ.RSRC block. The connector can link this resource OBJ as another OBJ, allowing the connector to "know" about the special thing about resources. Microsoft's recently released more connector can directly process the Res file. The .idata block contains information about the function (and data) of this module from other DLL imports (translation). The module reference table of this block and ne file is equivalent. A key difference is that each function imported by the PE file is specifically in this block. To find equivalents in the Ne file, you have to explore the relocation information of the end of this paragraph fresh data. Translation: There is no this block in many compilers now, but IMPORTTABLE is not no, instead, IMPORTTABLE is only indicated by DataDirectory [1], generally pointing in .Text block or .data block. The .edata block is a list of functions and data from this PE file to other modules. Its NE file equivalent is the joint, resident watchmake, and non-resident list, and 16 Windows, few reason to export some things from an EXE file, so you usually only see it in the DLL. EDATA block. When using Microsoft tools, the data in the .edata block is coming to the PE file via an Exp file. For a way, the connector does not generate this information for it. Instead, it rely on the library manager (lib32) to scan the OBJ file, and create an Exp file, the connector is to join the list of modules to link. Yes, good! These troublesome EXP files are actually only different OBJ files. .reloc block holds a basic redistribution table. Basic weight positioning is an adjustment to a directive or initialized variable value, if the loader cannot load this file into the connector assumption, this is important. If the loader can load this image into the base address of the PREFER, the loader completely ignores the relocation information of this block. If you are willing to adventure, and hope that the loader can always load this image to the assumed base address, you can tell the linker to remove this information via the / fixed option. This can save space in the executable file, but will cause this executable to work in other Win32 implementations. For example, assume that you have established an EXE file for Windows NT and set the base address to 0x10000. If you let the connector remove the positioning information, this EXE file will not be able to run under Windows 95, because the address 0x10000 is already used by the system here. Note that the JMP and CALL instructions generated by the compiler are important, and preferred it uses the relative offset version, not the true offset version in the 32-bit segment. If the image needs to be loaded into the base address assumed by the non-connectizer, these instructions do not need to be changed because it is relatively addressed. The result is that there is no need to relocate so much. Relocation usually only needs to use a 32-bit offset to some data. For example, let's take a look, you have the following global variable declaration: int i; int * ptr = & i; if the connector assumes an image base address of 0x10000, the address of the variable i will eventually be a specific value such as 0x12004. In the memory used to store the pointer "PTR", the connector will write into 0x12004 because this is the address of the variable I. If the loader decides to load this file into the base address 0x70000 for some reason, the address of the variable I will be 0x72004. .reloc block is a list of some memory locations in the image, which is different when the connector assumes the load address and the actual load address of the connector and the actual load address. This factor needs to be considered. When you use compiler command__declspec (thread), the data you define is not .DATA and .BSS block. It eventually in the .tls block, this block indicates "thread local storage" and contacts the Win32 TLSalloc function group. Handling .TLS block When the Memory Manager sets the page table for the process to switch the thread at any time, there is a new physical memory page set map to the address space of the .tls block. This allows global variables within the thread. In most cases, this mechanism is utilized, which is easier than a "TLSalloc" (Note: Original TLSalloc'ed) slot (Note: Original TLSalloc'ed) slot (Note: Original slot) than the thread allocation memory. Unfortunately, it is necessary to pay attention - must in-depth research. TLS block and __declspec (thread) variables. In WindowsNT and Windows 95, if the DLL is dynamically loaded by the library, this thread local storage mechanism will not work in this DLL. However, in an exe or an implicit loaded DLL, everything works fine. If you don't hibernate the chain to receive this DLL, you need to press the thread, you have to set the thread dynamic memory allocation in the past and use TLSalloc and TLSGetValue. Although .RDATA blocks are typically between .Data and .BSS blocks, your procedures generally do not see and use data in these blocks. . RDATA blocks are used in at least two things. First, in the EXE generated by the Microsoft Connector, the .rdata block stores the debug directory, which occurs in the EXE file. (In the exe of TLINK32, the debug directory is in a block named ".debug"). The debug directory is an image_debug_directory structure array. These structures maintain the type, size, and position of the variable stored in the file. The three main debug information types are shown below: CodeView?, Coff, and FPO, Table 9 shows a typical debug directory of PEDUMP output. Table 7 A typical debug directory Type Size Address FilePtr Charactr TimeDate VersionCOFF 000065C5 00000000 00009200 00000000 2CF8CF3D 0.00 ??? 00000114 00000000 0000F7C8 00000000 2CF8CF3D 0.00FPO 000004B0 00000000 0000F8DC 00000000 2CF8CF3D 0.00CODEVIEW 0000B0B4 00000000 0000FD8C 00000000 2CF8CF3D 0.00 The debug directory does not have to be found in the Start of the .rdata block. To find the beginning of the debug directory, use the seventh entry of the data directory (image_directory_entry_debug) of the RVA. The data directory is at the end of the PE header of the file. To determine the number of entries for the debug directory generated by the Microsoft Connector, the size of the debug directory (in the size domain of the data directory entry) is divided by the size of an image_debug_directory structure. TLINK32 produces a simple number, usually 1. The PEDUMP sample program describes this. Another useful part of the .rdata domain is "description string". If you specify a DESCRIPTION entry in the program's DEF file, this specified description string appears in the .rdata block. In the NE format, the first entry describing the string is always a non-resident list. Description Series is used to maintain a useful text string that describes this file. Unfortunately, I haven't found a convenient way to get it. I saw some descriptions of string before the debug directory of the PE file, it is after the debug directory. I can't find the consistent method of this description string (or even this method does not exist at all). .debug $ s and .debug $ t block only in OBJ. They save the CodeView debug symbols and type information. These blocks are inherited from the previous 16-bit compiler ($$ Symbols and $$ Type). The only use of the .debug $ T is to keep the path to the PDB file containing all OBJ information in the project. The connector is read from the PDB and uses it to create components of the CodeView information, which are placed at the end of the PE file. The .dRectVe block only appears in the OBJ file. It contains connector commands represented by text. For example, in either OBJ in which I use Microsoft Compiler, the following strings appear in .DRectVe block: -defaultlib: libc -defaultlib: OldNames When you use __Declspec (export) in the program, compiler Simply output the equivalent command line into the .dRectVe block (for example: "- exprot: myfunction). In the process of playing with PEDUMP, I encountered other blocks from time to time. For example, in the WINDOW95 Kernel32.dll, there are Lockcode and LockData blocks. Probably this is a special page handling method, to avoid the shortage (translation). Demolition: Location, in page-memory management, a virtual memory accessible virtual memory is not mapped into physical memory, and the shortage interrupt will occur. For the shortage interrupt, see the operating system related books. I learned two lessons from here. First: Do not use only a constraint to use the standard blocks provided by the compiler or assembler. If you need a separate block due to some reason, don't hesitate to create yourself! In the C / C compiler, use #pragma code_seg and #pragma data_seg. In assembly language, just create a 32-bit segment different from the standard block (will be a block). If you use TLINK32, you have to use a different class or turn it off the code segment packaging (Packing). Other things that you want to remember are using non-standard blocks. You will understand the intentions and implementations of special PE files. 5 PE files in front, I describe how the function call is in an external DLL and does not call this DLL directly. Instead, in the .Text block in the actuator (if you use Borland C is the .icode block), the CALL directive reaches a JMP DWORD PTR [xxxxxxxx] instruction. The address of the JMP instruction looks forward to the control to the actual target address. The PE file .idata will contain some necessary information, which is the address of the loader to determine the address of the target function and correct them in the active image. .idata block (or import table, I prefer this call) began in an image_import_descriptor array. Each DLL has a PE file implies image_import_descriptor on the link. There is no domain that specifies the number of structures in this array. Instead, the last element of this array is a full NULL image_import_descriptor. Image_import_descriptor's format is shown in Table 8. Table 8 image_import_descriptor Format DWORD Characteristics at a time, this may be a flag set. However, Microsoft has changed its meaning and is no more confused to upgrade Winnt.h. This month is actually a shift (RVA) that pointing the pointer array. Each pointer points to an image_import_by_name structure. DWORD TIMEDASTAMP indicates the creation time of this file. DWORD ForwarderChain This domain contacts the forward chain. The forward chain includes a DLL function to transfer a reference to another DLL. For example, in WindowsNT, NTDLL.DLL appears some of its forwards to the function to kernel32.dll. The application may think that it is called a function in NTDLL.DLL, but it finally calls the function in kernel32.dll. This domain also includes an index of a firstthunk array (instant). Use this domain index to extract the function forward to another DLL. Unfortunately, how the function does not have a document forwarded to the format, and the example of the forward function is difficult to find. DWORD NAME This is the name of importing the DLL, pointing to the ASCII string ending with NULL. The common example is Kernel32.dll and user32.dll. PIMAGE_THUNK_DATA FIRSTTTHUNK This domain is an offset (RVA) integrated to Image_thunk_Data. Almost any case, this domain is interpreted as a pointer to a pointer to the image_import_by_name structure. If this domain is not one of these pointers, it is regarded as an export number value that will be imported from this imported DLL. If you can actually import a function from the order instead of import from the name, it is unclear from the document. An important part of image_import_descriptor is the imported DLL name and two image_import_by_name pointer arrays. In the exe file, these two arrays (by the CHARACTERISTICS field and the firstthunk domain) are parallel to each other, all as the last element of the array with the NULL pointer. The pointers in the two arrays point to the image_import_by_name structure. Table 3 shows this layout in graphics. Table 12 shows the output of PEDUMP to an introduction table. FIG import table 3. Two parallel array of pointers to a table 9. EXE file GDI32.dll Hint / Name Table: 00013064 TimeDateStamp: 2C51B75B ForwarderChain: FFFFFFFF First thunk RVA: 00013214 Ordn Name 48 CreatePen 57 CreateSolidBrush 62 DeleteObject 160 GetDeviceCaps // Rest of table omitted ... KERNEL32.dll Hint / Name Table: 0001309C TimeDateStamp: 2C4865A0 ForwarderChain: 00000014 First thunk RVA: 0001324C Ordn Name 83 ExitProcess 137 GetCommandLineA 179 GetEnvironmentStrings 202 GetModuleHandleA // Rest of table omitted ... SHELL32.dll Hint / Name Table: 00013138 TimeDateStamp: 2C41A383 ForwarderChain: FFFFFFFF First thunk RVA: 000132E8 Ordn Name 46 ShellAboutA USER32.dll Hint / Name Table: 00013140 TimeDateStamp: 2C474EDF ForwarderChain: FFFFFFFF First thunk RVA: 000132F0 Ordn Name 10 BeginPaint 35 CharUpperA 39 CheckDlgButton 40 CheckMenuItem // Rest of import table of each function table omitted ... PE file has a Image_import_by_name structure. Image_import_by_name The structure is very simple, it looks like this: Word Hint; Byte Name [?]; The first domain is the best guess for the export number of import functions. Unlike the NE file, this value is not necessary. Thus, the loader instructs it as a suggested start value for one-point lookup. The next is an ASCIIZ string that imports the name of the function. Why is there two parallel pointer arguments pointing to structural image_import_by_name? The first array (by the Characteristics field pointing) is left separately and is not modified. It is often referred to as nomination table. The second array (pointing by the firstthunk domain) will be overwritten by the PE loader. The loader is introduced each pointer in this array and finds the address of the function points to each image_import_by_name structure. The loader then covers this pointer to the image_import_by_name structure with the found function address. JMP DWORD PTR [xxxxxxx] Replace [xxxxxxxx] in the instruction indicates an entry of the firstthunk array. Because this pointer array covered by the loader actually maintains the address of all import functions, called the Import Address Table. For Borland users, the above description is a bit awkward. The PE file generated by TLINK32 is missing one of the arrays. In such an actant, the characteristics field in the image_import_descriptor (Nomination Array) is 0. Thus, only the array pointed to by the Firstthunk domain (import address table) is necessary in the PE file. The story is here to end unless I am going deep into an interesting question when I write PEDUMP. In the optimized exploration, Microsoft "Optimized" The system DLL (kernel32.dll, etc.) in WindowsNT. In this optimization, the pointer in this array no longer points to the image_import_by_name structure, which already contains the address of the import function. In other words, the loader does not need to find the address of the function and override the Thunk array (translation) with the address of the import function. For the PEDUMP program that wants this array contains pointers to the image_import_by_name structure, this has led to a problem. You may be thinking, "But Matt, why is it not in the way using nomination form arrays?" This may be a perfect solution unless the nomination array does not exist in the Borland file. PEDUMP processes all of these situations, but the code is grateful. Annotation: This is Bound Import, about Bound Import, please see: Matt Pietrek "Inside Windows An In-Depth Look into the Win32 Portable Executable File Format, Part 2" From MSDN Magazine March 2002 on InternetURL: http: //msdn.microsoft .com / msdnmag / Issues / 02/03 / PE2 / PE2.ASP Because the import address table is relatively easy to intercept an EXE or DLL of another DLL in a writable block. Just modify the appropriate import address entry to point to the function you want to intercept. No code to modify the caller or the adjuster. Note that Microsoft's PE file import table is not fully being synchronized by the connector, this is interesting. The instructions of all the modes of the function in another DLL are in an import library. When you connect a DLL, the library manager (lib32.exe or lib.exe) scans the OBJ file that will be connected and creates an import library. This import library is completely different from the import library used by the 16-bit NE file connector. The 32-bit library manager generated import library has a .Text block and a few .idata $ block. The .TEXT block contains an alternate indication of JMP [xxxx], which has a name in the symbol table of the OBJ file to store it. This symbolic name is unique to all the functions that will be exported from the DLL (for example: _dispatch_message @ 4). One of the import library. IData $ block contains an alternative indication reference (translation: ie XXXX in JMP [xxxx]). Another. IData $ block has a space for the prompt serial number (Hint Ordinal) before the function name. These two domains constitute an image_import_by_name structure. When you connect a PE file using the import library, the block of the import library is added to the connector to process the list of your blocks in the OBJ file. Once the name of this replacement instruction in the library is the same as the function name to be imported, the connector assumes that this replacement indication is this import function and fixes the import function to point to this replacement. This replacement indication in the import library is essentially as this import function itself. In addition to providing a code part of an import function replacing the indication, the import library also provides a piece of PE file. IDATA block (or import table). These pieces come from the different .idata $ blocks in the library manager in the import library. Briefly, the connector actually does not know the difference between the import function and the normal function that appears in different OBJ files. The connector is only established and combined according to its border adjustment rules, so all things are naturally enlarged. 6 Terms Fresh Data: Original "Rawdata" means that the raw data is originally originally used to read the data from the disk without any changes. Replacement indication: Original "THUNK", essentially a directive, this instruction has floating address domain. As jMP [xxxx] in the article, XXXX is a floating address, or a resettable address (Relocatable address). Obj file: Object file, that is, the built-in target file is generated. This file can only form an executable file only after the (and LIB) connection.