Http://www.vckbase.com/document/viewdoc? Id = 1335
Detailed description of PE File Format (II)
Author: msdn
Author: Li Ma (http://home.nuc.edu.cn /~ Titilima)
Pre-defined section
A Windows NT ApplicationProgramTypically, nine predefined segments are available:. Text,. BSS,. RDATA,. Data,. rsrc,. edata,. idata,. pdata, And. debug. Some applications do not need all these segments, and some applications define more segments for their special needs. This approach works with MS-DOS and Windows 3.1CodeThe CIDR block is similar to the data segment. In fact, the method by which an application defines a unique segment is to use a standard compiler to indicate the name of the code segment and data segment, alternatively, use the namefield compiler option-NT -- which is the same as the unique code segment and data segment defined by the application in Windows 3.1.
The following is a discussion about some interesting public sections in the Windows nt pe file.
Executable code segment,. Text
One difference between Windows 3.1 and Windows NT is that the default practice of Windows NT is to combine all the code segments (as they are mentioned in Windows 3.1) into a separate segment, the name is ". text ". Since Windows NT uses a page-based virtual memory management system, it is unwise to put separate code into different segments. Therefore, having a large code segment is very convenient for operating system and application developers.
The. text section also contains the entry points mentioned earlier. IAT also exists before the module entry point in the. Text Segment. (The existence of iat in the. Text Segment is very meaningful, because this table is actually a series of Jump commands and their jump targets are fixed addresses .) When the executable image of Windows NT is loaded into the address space of the process, IAt is determined together with the physical address of each imported function. To search for iat in the. Text Segment, the loader only locates the module entry point, while the IAT occurs before the entry point. Since each entry has the same size, it is easy to look back at the starting position of the table.
Data Segment,. BSS,. RDATA,. Data
The. BSS segment indicates the uninitialized data of the application, including all the variables declared static in the function or source module.
The. RDATA segment indicates read-only data, such as the string text volume, constants, and debugging directory information.
All other variables (except the automatic variables that appear on the stack) are stored in the. Data Segment. Basically, these are the global variables of the application or module.
Resource segment,. rsrc
The. rsrc section contains the module resource information. It starts with a resource directory structure, which is like most other structures, but its data is further organized in a Resource Tree. The following image_resource_directory structure forms the root and nodes of the tree.
// Winnt. htypedef struct _ image_resource_directory {ulong characteristics; ulong timedatestamp; ushort majorversion; ushort minorversion; ushort versions;} image_resource_directory, * optional;
Check the directory structure and you will find that there is no pointer to the next node. However, in this structure, two domains numberofnamedentries and numberofidentries replace pointers, which are used to indicate the number of entries attached to the directory. Here, I mean that the directory entry is behind the directory in the segment data. Entries with names appear in ascending alphabetical order, followed by ID entries in ascending order of values.
A directory entry consists of two fields, as described in the image_resource_directory_entry structure below:
// Winnt. htypedef struct _ image_resource_directory_entry {ulong name; ulong offsettodata;} image_resource_directory_entry, * pimage_resource_directory_entry;
The two domains have different purposes based on different tree levels. The name field is used to identify a resource type, a resource name, or a resource language ID. Offsettodata and is often used to point to a sibling node in the tree-that is, a directory node or a leaf node.
Leaf nodes are the bottom-layer nodes in the Resource Tree. They define the size and position of the current resource data. The image_resource_data_entry structure is used to describe each leaf node:
// Winnt. htypedef struct _ image_resource_data_entry {ulong offsettodata; ulong size; ulong codePage; ulong reserved;} image_resource_data_entry, * pimage_resource_data_entry;
The offsettodata and size fields indicate the location and size of the current resource data. Since this information is mainly used by functions after the application is loaded, it makes more sense to use offsettodata as a relatively virtual address. -- Fortunately, that's right. Interestingly, all other offsets, such as pointers from the directory entry to other directories, are offset relative to the root node location.
For more information, see Figure 2.
Figure 2. A simple Resource Tree Structure
Figure 2 describes a very simple Resource Tree that contains only two resource objects: A menu and a string table. At a deeper level, each of them has a subitem. However, you can still see how complicated the Resource Tree is-even if it only has a little resource like this.
At the root of the tree, the first directory contains an entry for all resource types contained in the file, regardless of the resource types. In Figure 2, there are two entrances marked by the root of a tree, one of which is a menu and the other is a string table. If the file has one or more dialog box resources, the root node will have another entry, so there is another branch of the dialog box resources.
Winuser. h identifies the basic resource types, which are listed below:
// Winuser. h/** predefined resource types */# define rt_cursor makeintresource (1) # define rt_bitmap makeintresource (2) # define rt_icon makeintresource (3) # define rt_menu makeintresource (4) # define rt_dialog makeintresource (5) # define rt_string makeintresource (6) # define rt_fontdir makeintresource (7) # define rt_font makeintresource (8) # define prepare makeintresource (9) # define rt_rcdata makeintresource (10) # define rt_messagetable makeintresource (11)
At the first level of the tree, the makeintresource values listed above are placed at the name of each type entry, which identifies different resource types.
Each root directory entry points to a sibling node at the second level in the tree. These nodes are also directories, and each has its own entry. At this level, a directory is used to identify each resource type with a given type. If your application has multiple menus, the second level in the tree will prepare an entry for each menu.
You may realize that resources can be identified by names or integers. At this level, they are identified by the name field of the directory structure. If the most important bit in the Name field is set, the other 31 digits will be used as an offset to the image_resource_dir_string_u structure.
// Winnt. htypedef struct _ image_resource_dir_string_u {ushort length; wchar namestring [1];} image_resource_dir_string_u, * pimage_resource_dir_string_u;
This structure only consists of a 2-byte length field and a Unicode Character length.
On the other hand, if the most important bit of the Name field is cleared, its 31-bit low is used to represent the integer ID of the resource. Figure 2 demonstrates that a menu resource is a named resource and a string table is an ID resource.
if there are two menu resources, one identified by the name and the other identified by the resource, they will have two entries after the menu resource directory. A resource entry with a name is the first, followed by a resource identified by an integer. The directory domains numberofnamedentries and numberofidentries will each contain a value of 1, indicating the current entry.
at the second level, the Resource Tree will no longer expand branches further. Level 1 branch to the directory indicating each resource type, level 2 branch to the directory of each resource represented by the identifier, the third layer is a one-to-one ing between individually identified resources and their language IDs. To represent the language ID of a resource, the name field of the Directory Entry structure is used to represent the main language ID and sub-language ID of the resource. Windows NT Win32 SDK lists the default value resources. For example, for 0x0409, 0x09 indicates the main language lang_english, and 0x04 indicates the sub-language sublang_english_can. All language ID values are defined in Windows NT Win32 SDK file winnt. h.
since the language ID node is the last directory node in the tree, the offsettodata field of the entry structure is the offset to a leaf node (that is, the image_resource_data_entry structure mentioned above.
let's look back at Figure 2. You will find that each language directory entry corresponds to a data entry. This node only represents the size of the resource data and the relative virtual address of the resource data.
one advantage of having so many structures in the resource data segment (. rsrc) is that you can directly collect a lot of information from this segment without accessing the resource itself. For example, you can obtain how many types of resources are available, which resources (if any) use special language IDs, whether specific resources exist, and the size of individual types of resources. To demonstrate how to use this information, the following functions demonstrate how to determine different types of resources contained in a file:
// pefile. CINT winapi getlistofresourcetypes (lpvoid lpfile, handle hheap, char ** pszrestypes) {pimage_resource_directory prdroot; pimage_resource_directory_entry prde; char * pmem; int ncnt, I; /* obtain the root directory of the Resource Tree */If (prdroot = (pimage_resource_directory) imagedirectoryoffset (lpfile, image_directory_entry_resource) = NULL) return 0; /* allocate enough space on the heap to cover all types */ncnt = prdroot-> numberofidentries * (maxresourcename + 1); * pszrestypes = (char *) heapalloc (hheap, heap_zero_memory, ncnt); If (pmem = * pszrestypes) = NULL) return 0;/* point the pointer to the entry of the first resource type */prde = (pimage_resource_directory_entry) (DWORD) prdroot + sizeof (image_resource_directory);/* loop through all resource directory entry types */for (I = 0; I
numberofidentries; I ++) {If (loadstring (hdll, prde-> name, pmem, maxresourcename) pmem + = strlen (pmem) + 1; prde ++;} return ncnt ;}
This function writes a list of resource type names to variables identified by pszrestypes. Note that in the core part of this function, loadstring uses the name field of the Directory Entry of each resource type as the string ID. If you view pefile. RC, you will find that I have defined a series of resource types and their IDs are exactly the same as those defined in the directory entry. Pefile. dll also has a function that returns the total number of resource objects in the. rsrc segment. In this way, it is much easier to extract other information from this section, and use these functions or write other functions.
Export data segment,. edata
The. edata segment contains the exported data of the application or DLL. When this segment appears, it will contain a directory for exporting information.
// Winnt. htypedef struct _ construct {ulong characteristics; ulong timedatestamp; ushort majorversion; ushort minorversion; ulong name; ulong base; ulong numberoffunctions; ulong worker; Pulong * worker; pushort * addressofnameordinals;} image_export_directory, * pimage_export_directory;
The Name field in the export directory identifies the name of the executable module. The numberoffunctions field and numberofnames field indicate the number of exported functions in the module and the names of these functions.
the addressoffunctions field is the offset from the list of exported function entries. The addressofnames field is the offset from the start of an export function name list, which is separated by null. Addressofnameordinals is the offset from the list of the same export Function Sequence values (2 bytes in length for each value.
the three addressof... domains are relative virtual addresses in the process address space when the module is loaded. Once the module is loaded, the base address of the module should be added to the virtual address to obtain the exact address in the process geological space. However, this address can still be determined before the file is loaded: as long as the virtual address (virtualaddress) of the segment header is subtracted from the given domain address ), add the offset of the previous object (pointertorawdata). The result is the offset in the image file. The following example illustrates this technology:
// pefile. CINT winapi getexportfunctionnames (lpvoid lpfile, handle hheap, char ** pszfunctions) {image_section_header sh; pimage_export_directory ped; char * pnames, * PCNT; int I, ncnt;/* Get. segment header in the edata field and pointer to the data directory */If (PED = (pimage_export_directory) imagedirectoryoffset (lpfile, image_directory_entry_export) = NULL) return 0; getsectionhdrbyname (lpfile, & SH ,". edata ");/* determines the offset of the exported function name */pnames = (char *) (* (int *) (INT) PED-> addressofnames-(INT) Sh. virtualaddress + (INT) Sh. pointertorawdata + (INT) lpfile)-(INT) Sh. virtualaddress + (INT) Sh. pointertorawdata + (INT) lpfile);/* calculate the memory allocated to all strings */PCNT = pnames; for (I = 0; I <(INT) PED-> numberofnames; I ++) while (* PCNT ++); ncnt = (INT) (PCNT. pnames);/* allocate memory for function names on the heap */* pszfunctions = heapalloc (hheap, heap_zero_memory, ncnt);/* Copy all strings to the buffer */copymemory (lpvoid) * pszfunctions, (lpvoid) pnames, ncnt); Return ncnt ;}
Note that in this function, the variable pnames is assigned by the method that determines the offset address and the current offset location. The offset address and offset are both relative virtual addresses. Therefore, you must convert them before use. This is reflected in the function. Although you can write a similar function to determine the sequence value or function entry point, why am I not doing well for you? -- Getnumberofexportedfunctions, getexportfunctionentrypoints, and getexportfunctionordinals already exist in pefile. dll.
Import Data Segment,. idata
The. idata segment is the imported data, including the import and export address name table. Although image_directory_entry_import is defined, there is no corresponding import directory structure in winnt. h. There are several other structures named image_import_by_name, image_thunk_data, and image_import_descriptor. In my opinion, I really don't know how these structures match. idata segments are associated, so I spent several hours deciphering. idata segment object and get a simpler structure. I name it image_import_module_directory.
// Pefile. htypedef struct tagimportdirectory {DWORD dwrvafunctionnamelist; DWORD dwuseless1; DWORD dwuseless2; DWORD dwrvamodulename; DWORD dwrvafunctionaddresslist;} region, * region;
unlike the Data Directories of other segments, this is repeated as each import module in the file. You can regard it as an entry in the module data directory list, rather than a data directory of the entire data segment. Each entry is a directory pointing to the imported information of a specific module.
In the image_import_module_directory structure, a domain dwrvamodulename is a relative virtual address pointing to the module name. There are two dwuseless parameters in the structure to keep the segments aligned. The PE file format specification mentions some things about the import mark, time/date mark, and Master/times version. However, in my experiment, these two fields are all empty at the beginning and end, so I still think they are useless.
Based on the definition of this structure, you can obtain the names of all the modules and functions imported in the executable file. The following functions demonstrate how to obtain all the imported function names in a specific PE file:
// pefile. CINT winapi getimportmodulenames (lpvoid lpfile, handle hheap, char ** pszmodules) {shortpid; image_section_header idsh; byte * pdata; int ncnt = 0, nsize = 0, I; char * pmodule [1024]; char * psz; pid = (pimage_import_module_directory) imagedirectoryoffset (lpfile, image_directory_entry_import); pdata = (byte *) PID;/* location. idata segment header */If (! Getsectionhdrbyname (lpfile, & idsh ,". idata ") return 0;/* extract all import modules */while (PID-> dwrvamodulename) {/* allocate a buffer for the absolute string offset */pmodule [ncnt] = (char *) (pdata + (PID-> dwRVAModuleName-idsh.VirtualAddress )); nsize + = strlen (pmodule [ncnt]) + 1;/* added to the next import directory entry */PID ++; ncnt ++ ;} /* assign all strings to a large heap memory */* pszmodules = heapalloc (hheap, heap_zero_memory, nsize); psz = * pszmodules; for (I = 0; I
this function is very understandable, but it is worth noting that the while loop is worth noting. This loop ends when PID-> dwrvamodulename is 0, which implies an empty structure at the end of the image_import_module_directory structure list, which has a value of 0, at least the dwrvamodulename field is 0. This is what I studied in my experiments on the file and later in the PE file format.
the first domain in this structure, dwrvafunctionnamelist, is a relative virtual address. This address points to a list of relative virtual addresses, which are some file names in the file. As shown in the following data, the module and function names of all import modules are listed in. idata segment data:
e6a7 0000 f6a7 0000 08a8 0000 1aa8 0000 ................ 28a8 0000 3ca8 0000 4ca8 0000 0000 0000 (... <... l ....... 0000 4765 744f 7065 6e46 696c 654e 616d .. getopenfilenam6541 0000 636f 6d64 6c67 3332 2e64 6c6c ea .. comdlg32.dll0000 2500 4372 6561 7465 466f 6e74 496e .. %. createfontin6469 7265 6374 4100 4744 4933 322e 646c directa. gdi32.dl6c00 a000 4765 7444 6576 6963 6543 l... getdevicecap7300 c600 4765 7453 746f 636b 4f62 6a65 s... getstockobje6374 0000 d500 4765 7454 6578 744d 6574 CT .... gettextmet00009 6373 4100 1001 5365 6c65 6374 4f62 ricsa... selectob6a65 6374 0000 1601 5365 7442 6b43 6f6c ject .... setbkcol6f72 0000 3501 5365 7454 6578 7443 6f6c or .. 5. settextcol6f72 0000 4501 5465 7874 4f75 7441 0000 or .. e. textouta ..
the preceding data is part of the exeview. EXE sample program. idata segment. This special section indicates the start point of the List of Import modules and function names. If you start to check the data segment, you should recognize some familiar Win32 API functions and module names. If you read from the top down, you can find getopenfilenamea, followed by comdlg32.dll. Then you can find createfontindirecta, followed by gdi32.dll, getdevicecaps, getstockobject, and gettextmetrics.
this pattern is repeated in the. idata segment. The first module is comdlg32.dll, and the second is gdi32.dll. Note that the first module exports only one function, while the second module exports many functions. In both cases, a function name is first displayed, followed by the module name, and then other function names (if any ).
the following functions demonstrate how to obtain all function names of a specified module.
// Pefile. CINT winapi plugin (lpvoid lpfile, handle hheap, char * pszmodule, char ** pszfunctions) {pimage_import_module_directory PID; image_section_header idsh; DWORD dwbase; int ncnt = 0, nsize = 0; DWORD dwfunction; char * psz;/* positioning. idata segment header */If (! Getsectionhdrbyname (lpfile, & idsh ,". idata ") return 0; pid = (pimage_import_module_directory) imagedirectoryoffset (lpfile, image_directory_entry_import); dwbase = (DWORD) PID. idsh. virtualaddress);/* query module PID */while (PID-> dwrvamodulename & strcmp (pszmodule, (char *) (PID-> dwrvamodulename + dwbase ))) PID ++;/* If the module is not found, exit */If (! PID-> dwrvamodulename) return 0;/* Total number of functions and string length */dwfunction = PID-> dwrvafunctionnamelist; while (dwfunction & * (DWORD *) (dwfunction + dwbase) & * (char *) (* (DWORD *) (dwfunction + dwbase) + dwbase + 2) {nsize + = strlen (char *) (* (DWORD *) (dwfunction + dwbase) + dwbase + 2) + 1; dwfunction + = 4; ncnt ++ ;} /* allocate the function name space on the heap */* pszfunctions = heapalloc (hheap, heap_zero_memory, nsize); psz = * pszfunctions; /* copy the function name to the memory pointer */dwfunction = PID-> dwrvafunctionnamelist; while (dwfunction & * (DWORD *) (dwfunction + dwbase) & * (char *) (* (DWORD *) (dwfunction + dwbase) + dwbase + 2) {strcpy (psz, (char *) (* (DWORD *) (dwfunction + dwbase) + dwbase + 2); psz + = strlen (char *) (* (DWORD *) (dwfunction + dwbase )) + dwbase + 2) + 1; dwfunction + = 4;} return ncnt ;}
Just like the getimpmodmodulenames function, this function relies on the end of each information list to obtain a zero entry. In this case, the function name list ends with zero.
The last domain dwrvafunctionaddresslist is a relatively virtual address that points to a virtual address table. During file loading, this virtual address table will be placed in segment data by the loader. However, before file loading, these virtual addresses will be replaced by some virtual addresses that closely match the function name list. Therefore, before file loading, there are two identical virtual address lists pointing to the list of imported functions.
Debugging Information Section,. Debug
Debugging information is located in the. debug section, and the PE file format also supports separate debugging files (usually identified by the. dbg extension) as a way to centralize debugging information. The debugging section contains debugging information, but the debugging directory is located in the. RDATA section mentioned earlier. Each directory contains debugging information in the. debug section. The structure image_debug_directory of the DEBUG directory is defined:
// Winnt. htypedef struct _ construct {ulong listener; ulong timedatestamp; ushort majorversion; ushort minorversion; ulong type; ulong sizeofdata; ulong listener; ulong pointertorawdata;} Handle, * handle;
This segment is divided into separate parts, each of which contains debugging information data of different types. Each part is a debugging directory as above. Different debugging information types are as follows:
// Winnt. h # define defaults 0 # define image_debug_type_coff 1 # define image_debug_type_codeview 2 # define image_debug_type_fpo 3 # define image_debug_type_misc 4
The Type field in each directory indicates the debugging information type of this directory. As you can see, in the above table, the PE File Format supports many different debugging information types and some other information fields. For those information, image_debug_type_misc is unique. This information is added to the mixed information describing the executable image, which cannot be added to any structured data segment in the PE file format. This is the most suitable location for the image file, and the image name will definitely appear here. If the image is exported, the exported data segment also contains the image name.
Each debugging information has its own header structure, which defines its own data. These structures are listed in winnt. h. One interesting thing about image_debug_directory is that it contains two fields that identify debugging information. The first is addressofrawdata, which is the virtual address of the data loaded to the relative file; the other is pointertorawdata, which is the actual offset in the PE file where the data is located. This makes it easy to locate the specified debugging information.
For the final example, consider the following function code, which extracts the image name from the image_debug_misc structure.
// pefile. CINT winapi retrievemodulename (lpvoid lpfile, handle hheap, char ** pszmodule) {pimage_debug_directory PDD; pimage_debug_misc PDM = NULL; int ncnt; If (! (PDD = (pimage_debug_directory) imagedirectoryoffset (lpfile, image_directory_entry_debug) return 0; while (PDD-> sizeofdata) {If (PDD-> type = image_debug_type_misc) {PDM = (pimage_debug_misc) (DWORD) PDD-> pointertorawdata + (DWORD) lpfile); ncnt = lstrlen (PDM-> data) * (PDM-> Unicode? 2: 1); * pszmodule = (char *) heapalloc (hheap, heap_zero_memory, ncnt + 1); copymemory (* pszmodule, PDM-> data, ncnt); break ;} PDD ++;} If (PDM! = NULL) return ncnt; else return 0 ;}
As you can see, the DEBUG directory structure makes it easier to locate a specific type of debugging information. As long as the image_debug_misc structure is located, extracting the image name is as simple as calling the copymemory function.
As described above, debugging information can be stripped to a separate. dbg file. The Windows nt sdk contains a program named rebase. EXE. For example, the following statement can strip the debugging information named test. EXE:
Rebase-B 40000-X c: \ samples \ testdir test.exe
The debugging information is placed in a new file named test. dbg, which is located in c: \ samples \ testdir. This file starts with a separate image_separate_debug_header structure, followed by a copy of the segment header in the original executable image. After the segment header, It is the data of the. debug segment. That is to say, after the segment header, it is a series of image_debug_directory structures and related data. The debugging information retains the debugging information of the general image file described above.
PE File Format Summary
The PE file format for Windows NT introduces a new structure to developers familiar with windows and MS-DOS environments. However, developers familiar with UNIX environments will find that the PE file format is very similar to the coff specification (if it is not based on coff ).
Composition of the entire format: A MS-DOS of the mz header, followed by a real-mode residual program, PE file flag, PE File Header, PE Optional header, all segment headers, finally, it is all the segment entities.
The end of the optional Header is an array of data directory entries. These virtual addresses point to the data directory in the object segment. Each data directory represents how object data is organized in a specific segment.
The PE file format has 11 predefined segments, which are common to Windows NT applications, but each application can define its own unique segments for its own code and data.
. Debug pre-defined segments can also be separated into a separate debugging file. In this case, a specific debugging header is used to parse the debugging file. The PE file also has a flag to indicate that the debugging data is separated.
Pefile. dll Function Description
Pefile. dll is mainly composed of some functions. These functions can be used to obtain the offset in a given PE file, or to copy some data in the file to a specific structure. Each function has a requirement-the first parameter is a pointer pointing to the starting position of the PE file. That is to say, this file must be first mapped to the address space of your process, and then the location of the ing file can be passed in as the lpfile value of the first parameter of each function.
I want to make the name of a function so that you can see and understand it, and each function is listed with a comment describing its purpose in detail. If you still don't understand the functions of a function after reading the function list, refer to the exeview. EXE example to find out how the function is used. The following function prototype list can be found in pefile. h:
// Pefile. h/* Get the pointer to the MS-DOS MZ header */bool winapi getdosheader (lpvoid, pimage_dos_header);/* decide. EXE file type */DWORD winapi imagefiletype (lpvoid);/* Get pointer to PE File Header */bool winapi getpefileheader (lpvoid, pimage_file_header ); /* obtain the pointer to the PE optional Header */bool winapi getpeoptionalheader (lpvoid, pimage_optional_header);/* return the module entry point address */lpvoid winapi getmoduleentrypoint (lpvoid ); /* Total Number of middle sections of the returned file */INT winapi numofsections (lpvoid ); /* returns the preferred base address when the executable file is loaded into the process address space */lpvoid winapi getimagebase (lpvoid ); /* determine the location of a specific image data directory in the file */lpvoid winapi imagedirectoryoffset (lpvoid, DWORD ); /* get the names of all segments in the file */INT winapi getsectionnames (lpvoid, handle, char **); /* copy the header information of a specific segment */bool winapi getsectionhdrbyname (lpvoid, pimage_section_header, char *); /* obtain the list of import module names separated by null characters */INT winapi getimportmodulenames (lpvoid, handle, char **); /* obtain a list of import functions separated by null characters */INT winapi getimportfunctionnamesbymodule (lpvoid, handle, char *, char **); /* obtain the list of export functions separated by null characters */INT winapi getexportfunctionnames (lpvoid, handle, char **); /* obtain the total number of export functions */INT winapi getnumberofexportedfunoid (lpvoid);/* obtain the virtual address entry list of the export function */lpvoid winapi getexportfunctionentrypoints (lpvoid ); /* obtain the list of export Function Sequence values */lpvoid winapi getexportfunctionordinals (lpvoid);/* determine the resource object type */INT winapi getnumberofresources (lpvoid ); /* types of all resource objects used in the returned file */INT winapi getlistofresourcetypes (lpvoid, handle, char **); /* determine whether the debugging information has been separated from the file */bool winapi isdebuginfostripped (lpvoid);/* obtain the image file name */INT winapi retrievemodulename (lpvoid, handle, char **);/* determines whether the file is a valid debugging file */bool winapi isdebugfile (lpvoid ); /* return the debugging header from the debugging file */bool winapi getseparatedebugheader (lpvoid, pimage_separate_debug_header); in addition to the functions listed above, the macro mentioned earlier in this article is also defined in pefile. h, the complete list is as follows:/* offset of the PE file flag */# define ntsignature (A) (lpvoid) (byte *) A + \ (pimage_dos_header) a)-> e_lfanew)/* the OS header identifies the dual-character nt PE file mark; the PE file header is followed by this double character */# define pefhdroffset (A) (lpvoid) (byte *) A + \ (pimage_dos_header)) -> e_lfanew + \ size_of_nt_signature)/* The Optional PE Header follows the PE File Header */# define opthdroffset (A) (lpvoid) (byte *) A + \ (pimage_dos_header) A)-> e_lfanew + \ size_of_nt_signature + \ sizeof (image_file_header ))) /* the segment header follows the Optional PE Header */# define sechdroffset (A) (lpvoid) (byte *) A ++ \ (pimage_dos_header)) -> e_lfanew + \ size_of_nt_signature + \ sizeof (image_file_header) + \ sizeof (image_optional_header )))
to use pefile. dll, you only need to include the pefile. h file and link it to this DLL in the application. All these functions are mutually exclusive functions, but some functions can support each other to obtain file information. For example, getsectionnames can be used to obtain the names of all segments. In this way, to obtain a segment header with a unique segment name (defined by the application developer during compilation, you need to first obtain the list of all names, and then call the getsectionheaderbyname function for the exact segment name. Now, you can enjoy what I bring you!