================================= Structure of Import Symbols table ================================= A portable executable (PE) file contains IMAGE_NT_HEADERS This struct's members are: DWORD Signature IMAGE_FILE_HEADER FileHeader IMAGE_OPTIONAL_HEADER OptionalHeader // not really optional, fyi Then the IMAGE_OPTIONAL_HEADER struct contains: // // Standard fields. // WORD Magic; BYTE MajorLinkerVersion; BYTE MinorLinkerVersion; DWORD SizeOfCode; DWORD SizeOfInitializedData; DWORD SizeOfUninitializedData; DWORD AddressOfEntryPoint; DWORD BaseOfCode; DWORD BaseOfData; // // NT additional fields. // DWORD ImageBase; DWORD SectionAlignment; DWORD FileAlignment; WORD MajorOperatingSystemVersion; WORD MinorOperatingSystemVersion; WORD MajorImageVersion; WORD MinorImageVersion; WORD MajorSubsystemVersion; WORD MinorSubsystemVersion; DWORD Reserved1; DWORD SizeOfImage; DWORD SizeOfHeaders; DWORD CheckSum; WORD Subsystem; WORD DllCharacteristics; DWORD SizeOfStackReserve; DWORD SizeOfStackCommit; DWORD SizeOfHeapReserve; DWORD SizeOfHeapCommit; DWORD LoaderFlags; DWORD NumberOfRvaAndSizes; IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; Then the DataDirectory array : 0'th member: Export Symbols 1'st member: Import Symbols 2'nd member: Resources ... 11'th member: Bound Import 12'th member: Import Address Table ... The IMAGE_DATA_DIRECTORY struct consists of two fields: DWORD VirtualAddress; // the RVA of the data structure DWORD Size; // the size of the data structure Then if we have a PE that imports two modules, we'll have: (1) one IMAGE_DATA_DIRECTORY structure for Import Symbols (2) let's say that struct's VirtualAddress is 0xc7d8 -- recall this is an RVA (3) then 0xc7d8 is where the first IMAGE_IMPORT_DESCRIPTOR lives (4) since we are importing two modules, there will be 3 IMAGE_IMPORT_DESCRIPTOR structs in our array (which starts at 0xc7d8, recall) --> the 3rd IMAGE_IMPORT_DESCRIPTOR is all zeroes (5) then the Size field above (for this IMAGE_DATA_DIRECTORY) will be 3 * sizeof( IMAGE_IMPORT_DESCRIPTOR ) The IMAGE_IMPORT_DESCRIPTOR struct consists of five fields : Union { DWORD Characteristics; PIMAGE_THUNK_DATA OriginalFirstThunk; }; DWORD TimeDateStamp; DWORD ForwarderChain; DWORD Name; PIMAGE_THUNK_DATA FirstThunk; --> Each of these fields is 4 bytes long so sizeof( IMAGE_IMPORT_DESCRIPTOR ) is 20 bytes == 0x1C --> So the Size field will be 60 bytes == 0x3C Now let's examine the contents of the Import Symbols table; recall that this table is -- in this case -- an array of 3 IMAGE_IMPORT_DESCRIPTOR structs. Now here are some potential values for the fields: ------------------------------------- // 3rd IMAGE_IMPORT_DESCRIPTOR struct ------------------------------------- 0xc810 FirstThunk : 0 0xc80c Name : 0 0xc808 ForwarderChain : 0 0xc804 TimeDateStamp : 0 0xc800 OriginalFirstThunk : 0 ------------------------------------- // 2nd IMAGE_IMPORT_DESCRIPTOR struct ------------------------------------- 0xc7fc FirstThunk : 0x8000 0xc7f8 Name : 0xc398 (the RVA of the name of this module) 0xc7f4 ForwarderChain : 0 0xc7f0 TimeDateStamp : 0 0xc7ec OriginalFirstThunk : 0xc084 ------------------------------------- // 1st IMAGE_IMPORT_DESCRIPTOR struct ------------------------------------- 0xc7e8 FirstThunk : 0xc178 0xc7e4 Name : 0xc2c0 (the RVA of the name of this module) 0xc7e0 ForwarderChain : 0 0xc7dc TimeDateStamp : 0 0xc7d8 OriginalFirstThunk : 0xc030 All addresses are RVAs. ------------------------------ Now at locations (from above): ------------------------------ 0xc2c0 contains "MSVCR80.DLL" 0xc398 contains "KERNEL32.DLL" Now OriginalFirstThunk points to the Import Name Table for the module and FirstThunk points to the Import Address Table for the module. Each of these two tables (Import Name and Import Address) consist of an array of IMAGE_THUNK_DATA structs. An IMAGE_THUNK_DATA struct consists of: Union { PBYTE ForwarderString; PDWORD Function; DWORD Ordinal; PIMAGE_IMPORT_BY_NAME AddressOfData; }; In our case the Union member "AddressOfData" will be the member used for each struct in these two tables. This value (AddressOfData) contains the RVA for an IMAGE_IMPORT_BY_NAME struct. The contents of an IMAGE_IMPORT_NAME struct are : WORD Hint; // recall that WORDs are two bytes each BYTE Name[1]; // this is NOT really 1 byte long; This is a variable-sized field For the PE -- before it's loaded -- the Import Name Table and the Import Address Table are merely two copies of the same table. For example let's say we're importing twenty or so symbols from MSVCR80.dll then our Import Name Table starts at 0xc030 (OriginalFirstThunk address above) and our Import Address Table starts at 0xc178 (FirstThunk address above). ------------------------------------- So we might have something like this: ------------------------------------- ------------------------------------ // Import Name Table for MSVCR80.dll ------------------------------------ 0xc080 AddressOfData : 0 // null terminate table ... 0xc044 AddressOfData : 0xc300 0xc040 AddressOfData : 0xc2f8 0xc03c AddressOfData : 0xc2ea 0xc038 AddressOfData : 0xc2e0 0xc034 AddressOfData : 0xc2d8 0xc030 AddressOfData : 0xc2cc --------------------------------------- // Import Address Table for MSVCR80.dll --------------------------------------- 0xc1c8 AddressOfData : 0 // null terminate table ... 0xc18c AddressOfData : 0xc300 0xc188 AddressOfData : 0xc2f8 0xc184 AddressOfData : 0xc2ea 0xc180 AddressOfData : 0xc2e0 0xc17c AddressOfData : 0xc2d8 0xc178 AddressOfData : 0xc2cc Note that the AddressOfData values don't increase by a fixed amount; that's because the data contained at those addresses is variable length. So what is the data contained at those addresses? Starting at 0xc2cc is an array of IMAGE_IMPORT_BY_NAME structs. Each such struct contains the name of a function (imported by this module which in this case is MSVCR80.DLL) as well as a "Hint" for this function. --------------------------------------------------------------------- Continuing the above example, we have the following values in memory: --------------------------------------------------------------------- ... 0xc312 2-byte hint for mbstowcs 0xc308 "mbstowcs" // null-terminated is 9 bytes long 0xc306 2-byte hint for fopen 0xc300 "fopen" // null-terminated is 6 bytes long 0xc2fe 2-byte hint for atoi 0xc2f8 "atoi" // null-terminated is 5 bytes long 0xc2f6 2-byte hint for _vsnprintf 0xc2ea "_vsnprintf" // null-terminated is 11 bytes long 0xc2e8 2-byte hint for _strdup 0xc2e0 "_strdup" // null-terminated is 8 bytes long 0xc2de 2-byte hint for _open 0xc2d8 "_open" // null-terminated is 6 bytes long 0xc2d6 2-byte hint for _memccpy 0xc2cc "_memccpy" // null-terminated is 9 bytes long 0xc2c0 "MSVCR80.DLL" // null-terminated is 12 bytes long So now you see what the AddressOfData values -- contained in the Import Name Table and the Import Address Table -- refer to. One small detail, when the null-terminated name of a function is an odd number, then an extra byte must be added to the name so that the WORD hint field starts on a WORD-aligned boundary. This is why, for example, the size of the IMAGE_IMPORT_BY_NAME struct for _memccpy is 12 bytes even though null-terminated "_memccpy" is 9 bytes and the WORD hint adds 2 bytes (total of 11 bytes); that extra byte is to align the hint on a WORD boundary. =============================================== So why do we have two copies of the same table? =============================================== Perceptive question, Watson! These two tables are used by the PE loader to map the function names to addresses when loading the PE. Since we don't know at compile time WHERE the various functions will exist in the address space (since these functions are part of DLLs), the loader does that resolution for us. In particular the loader will replace each entry in the Import Address Table (IAT) with the actual address of the function. For example, the first entry in the MSVCR80.DLL IAT is 0xc2cc and that corresponds to "_memccpy". Usually MSVCR80.DLL loads at address 0x00360000, i.e. that's the base address for this DLL. Moreover the RVA for _memccpy is 0x49F10. Therefore if MSVCR80.dll is loaded in its usual spot then the value stored at 0xc178 (which is originally 0xc2cc) will be changed to ( 0x00360000 + 0x49F10 ) == 0x003a9f10 since that's the address where the _memccpy code lives in memory at the time this PE is loaded. Note that because we have *two* copies of this same initial table, after we replace 0xc2cc in the IAT with the address for _memccpy (as above), if for any reason we need to go in the other direction (i.e. to know which function that address corresponds to) we can consult the Import Name Table at 0xc030 which still contains the RVA 0xc2cc and see that "_memccpy" is the function for this resolved address.=========================== From : C:/lcc/include/win.h =========================== // // there's one IMAGE_IMPORT_DESCRIPTOR for every DLL that the PE uses // typedef struct _IMAGE_IMPORT_DESCRIPTOR { union { DWORD Characteristics; // points to Import Name Table (INT) // contains RVA of an array of IMAGE_THUNK_DATA structs PIMAGE_THUNK_DATA OriginalFirstThunk; } ; DWORD TimeDateStamp; DWORD ForwarderChain; DWORD Name; // e.g. "USER32.dll" PIMAGE_THUNK_DATA FirstThunk; // points to IAT } IMAGE_IMPORT_DESCRIPTOR,*PIMAGE_IMPORT_DESCRIPTOR; // // the IAT is an array of IMAGE_THUNK_DATA structs // typedef struct _IMAGE_THUNK_DATA { union { // is one of the following 4 things PBYTE ForwarderString; // 4 bytes PDWORD Function; // 4 bytes DWORD Ordinal; // 4 bytes // ------------- is usually this ------------- // PIMAGE_IMPORT_BY_NAME AddressOfData; // 4 bytes } ; } IMAGE_THUNK_DATA,*PIMAGE_THUNK_DATA; // // as above, usually we interpret an IMAGE_THUNK_DATA struct as a POINTER to an : // "IMAGE_IMPORT_BY_NAME" struct // typedef struct _IMAGE_IMPORT_BY_NAME { WORD Hint; // 2 bytes // contaisn index into the export table of the DLL // from which we get (import) this function BYTE Name[1]; // 1 byte; contains name of the import function // is an ASCIIZ string } IMAGE_IMPORT_BY_NAME,*PIMAGE_IMPORT_BY_NAME;
windows下一些Structure定义(pe、IMAGE_IMPORT_DESCRIPTOR
最新推荐文章于 2022-10-24 00:21:37 发布