Hi all,
I was digging into the PE (i.e. Microsoft Portable Executable and Common Object File Format) Specification recently. I used the following program for testing:
DATA SECTION
;
KEEP DD 0
;
CODE SECTION
;
START:
PUSH -11
CALL GetStdHandle
PUSH 0,ADDR KEEP
PUSH 24, 'Hello World (from GoAsm)'
PUSH EAX
CALL WriteFile
XOR EAX,EAX
RET
Using
GoAsm.exe test.asm
GoLink.exe /console Kernel32.dll test.obj
I produced a console executable. The program runs fine and all, however the .exe file does not seem to comply to PE File Format Specification. More specifically, the .idata section (starting at 0x600h) seems to be corrupt. For instance, the ImportLookupTable RVA is 0x303425ff, which is absurd. Also the Date/Time stamp field isn't set to zero, as it should be.
Hi Shtuka
Looking at the PE file specs it says about the TimeDateStamp as follows:-
QuoteThe low 32 bits of the number of seconds since 00:00 January 1, 1970 (a C run-time time_t value), that indicates when the file was created.
So this value should not be zero, but should contain the time when the exe was made by GoLink.
The idata section cannot be corrupt if the exe works (it has imports). You say that ImportLookupTableRVA contains a certain value. However whatever tool you are using may not be looking at the value containing ImportLookupTableRVA. In the specifications this value is pointed to by the Import Directory Table. The position of the Import Lookup Table will vary from linker to linker - it is not always in the same place in the PE file.
A really good tool to look at PE files is Wayne Radburn's PEView available from www.magma.ca/~wjr/ (http://www.magma.ca/~wjr/). This tool allows for variations which can occur within different types of exe's whilst still complying with the PE specification.
Hi Shtuka,
I have dismantled a lot of GoAsm executables, including with the DbgHelp DLL functions and have had no issues with it at all. Both the import/export tables and the symbol tables are fine. If you download WinExplorer from my website you will find a routine to dump the PE header as well as imports and exports and it works equally well with GoAsm and other executables. Also, PEBrowsePro from Smidgeon, which I use quite often, and OllyDbg have no issues with GoAsm files.
@jorgon:
I am not sure what exactly you are referring to, but the relevant part of the MS PE & COFF File Format Specification (page 76 in revision 8.2) reads:
QuoteThe stamp that is set to zero until the image is bound. After the image is bound, this field is set to the time/data stamp of the DLL.
As I said, the program executes. I don't see how this proves that the PE file conforms to the specification. Having experimented with some other linkers, it seems to me that the Win32 Process Loader is rather tolerant. For instance, the alink linker puts the import section under "imports" instead of ".idata" as it should be, but the PL does not mind.l
@donkey:
I have used PE Explorer on this particular PE file, and it ran without problems. It seems to simply ignore the "bad part" of the .idata section, even though it escapes me how it does that.
Could you post the EXE.
The PE file has evolved over the years along with the Microsoft Linkers (C700, Win95, WinNT, and beyond), and Borland variants. The structures and sections have also been constructed in various forms, assuming a ridged interpretation of the specs tends to ignore the facts on the ground. For that matter WinXP will refuse to handle files which are arguably valid.
Okay, here you go. As I already mentioned, the problem (to me) is in the import section starting at 0x600h. The file should have exactly two imports, but it first seems to start with some bogus import named "Y0", which has an invalid Import Lookup RVA.
Quote from: Shtuka on March 05, 2011, 05:34:06 PM
Okay, here you go. As I already mentioned, the problem (to me) is in the import section starting at 0x600h. The file should have exactly two imports, but it first seems to start with some bogus import named "Y0", which has an invalid Import Lookup RVA.
I think you're just confused, the 0x600 is a RAW FILE OFFSET for the data, as the file data is aligned on 0x200 (Sector) boundaries within the file. This is not the ADDRESS where it is LOADED, which is 0x3000. All the other addresses within the file refer to those in memory. More recently Microsoft uses 0x1000 (Page) boundaries, simply because those provide direct mapping when pinned into the page file and memory.
test2.exe (hex) (dec)
.EXE size (bytes) 6C 108
Minimum load size (bytes) 4C 76
Overlay number 0 0
Initial CS:IP 0000:0011
Initial SS:SP 0000:0000 0
Minimum allocation (para) 0 0
Maximum allocation (para) FFFF 65535
Header size (para) 2 2
Relocation table offset 40 64
Relocation entries 0 0
Portable Executable starts at 60
Signature 00004550 (PE)
Machine 014C (Intel 386)
Sections 0003
Time Date Stamp 4D6D61A0 Tue Mar 01 15:14:08 2011
Symbol Table 00000000
Number of Symbols 00000000
Optional header size 00E0
Characteristics 010F
Relocation information stripped
Executable Image
Line numbers stripped
Local symbols stripped
32 bit word machine
Magic 010B
Linker Version 0.38
Size of Code 00000200
Size of Initialized Data 00000400
Size of Uninitialized Data 00000000
Address of Entry Point 00001000
Base of Code 00001000
Base of Data 00002000
Image Base 00400000
Section Alignment 00001000
File Alignment 00000200
Operating System Version 4.00
Image Version 0.00
Subsystem Version 4.00
reserved 00000000
Image Size 00004000
Header Size 00000200
Checksum 00008D32
Subsystem 0003 (Console)
DLL Characteristics 0000
Size Of Stack Reserve 00100000
Size Of Stack Commit 00010000
Size Of Heap Reserve 00100000
Size Of Heap Commit 00001000
Loader Flags 00000000
Number of Directories 00000010
Directory Name VirtAddr VirtSize
-------------------------------------- -------- --------
Export 00000000 00000000
Import 0000300C 00000028
Resource 00000000 00000000
Exception 00000000 00000000
Security 00000000 00000000
Base Relocation 00000000 00000000
Debug 00000000 00000000
Decription/Architecture 00000000 00000000
Machine Value (MIPS GP) 00000000 00000000
Thread Storage 00000000 00000000
Load Configuration 00000000 00000000
Bound Import 00000000 00000000
Import Address Table 00003034 0000000C
Delay Import 00000000 00000000
COM Runtime Descriptor 00000000 00000000
(reserved) 00000000 00000000
Section Table
-------------
01 code Virtual Address 00001000
Virtual Size 00000020
Raw Data Offset 00000200
Raw Data Size 00000200
Relocation Offset 00000000
Relocation Count 0000
Line Number Offset 00000000
Line Number Count 0000
Characteristics 60000020
Code
Executable
Readable
02 data Virtual Address 00002000
Virtual Size 00000020
Raw Data Offset 00000400
Raw Data Size 00000200
Relocation Offset 00000000
Relocation Count 0000
Line Number Offset 00000000
Line Number Count 0000
Characteristics C0000040
Initialized Data
Readable
Writeable
03 .idata Virtual Address 00003000
Virtual Size 00000074
Raw Data Offset 00000600
Raw Data Size 00000200
Relocation Offset 00000000
Relocation Count 0000
Line Number Offset 00000000
Line Number Count 0000
Characteristics 60000020
Code
Executable
Readable
Imp Addr Hint Import Name from KERNEL32.dll - Not Bound
-------- ---- ---------------------------------------------------------------
00003034 1B0 GetStdHandle
00003038 390 WriteFile
IAT Entry
00000000: 00003059 00003068 - 00000000
Disassembly
00401000 start:
00401000 6AF5 push 0FFFFFFF5h
00401002 E8F91F0000 call jmp_GetStdHandle
00401007 6A00 push 0
00401009 6800204000 push offset off_00402000
0040100E 6A18 push 18h
00401010 6804204000 push offset off_00402004 ; 'Hello World (from GoAsm)',000h
00401015 50 push eax
00401016 E8EB1F0000 call jmp_WriteFile
0040101B 31C0 xor eax,eax
0040101D C3 ret
0040101E 0000 db 2 dup (000h)
00402000 off_00402000: ; Xref 00401009
00402000 00 00 00 00 ....
00402004 off_00402004: ; Xref 00401010
00402004 48656C6C6F20576F726C.. db 'Hello World (from GoAsm)',000h
0040201D 00 00 00 ...
00403000 jmp_GetStdHandle: ; Xref 00401002
00403000 FF2534304000 jmp dword ptr [GetStdHandle]
00403006 jmp_WriteFile: ; Xref 00401016
00403006 FF2538304000 jmp dword ptr [WriteFile]
0040300C KERNEL32_Characteristics:
0040300C 40304000 dd offset GetStdHandle_ByName
00403010 KERNEL32_TimeDateStamp:
00403010 00000000 dd 00000000h
00403014 KERNEL32_ForwarderChain:
00403014 00000000 dd 00000000h
00403018 KERNEL32_Name:
00403018 4C304000 dd offset KERNEL32 ; 'KERNEL32.dll',000h
0040301C KERNEL32_FirstThunk:
0040301C 34304000 dd offset GetStdHandle
00403020 NULL_Characteristics:
00403020 00000000 dd 00000000h
00403024 NULL_TimeDateStamp:
00403024 00000000 dd 00000000h
00403028 NULL_ForwarderChain:
00403028 00000000 dd 00000000h
0040302C NULL_Name:
0040302C 00000000 dd 00000000h
00403030 NULL_FirstThunk:
00403030 00000000 dd 00000000h
00403034 GetStdHandle: ; Xref 00403000 0040301C
00403034 59304000 dd offset GetStdHandle_Hint
00403038 WriteFile: ; Xref 00403006
00403038 68304000 dd offset WriteFile_Hint
0040303C NULL_Thunk_KERNEL32:
0040303C 00000000 dd 00000000h
00403040 GetStdHandle_ByName: ; Xref 0040300C
00403040 59304000 dd offset GetStdHandle_Hint
00403044 WriteFile_ByName:
00403044 68304000 dd offset WriteFile_Hint
00403048 NULL_Characteristics_KERNEL32:
00403048 00000000 dd 00000000h
0040304C KERNEL32: ; Xref 00403018
0040304C 4B45524E454C33322E64.. db 'KERNEL32.dll',000h
00403059 GetStdHandle_Hint: ; Xref 00403034 00403040
00403059 B001 dw 001B0h
0040305B GetStdHandle_Name:
0040305B 47657453746448616E64.. db 'GetStdHandle',000h
00403068 WriteFile_Hint: ; Xref 00403038 00403044
00403068 9003 dw 00390h
0040306A WriteFile_Name:
0040306A 577269746546696C6500 db 'WriteFile',000h
End of Disassembly
Quote from: clive on March 05, 2011, 09:52:27 PM
I think you're just confused, the 0x600 is a RAW FILE OFFSET for the data, as the file data is aligned on 0x200 (Sector) boundaries within the file. This is not the ADDRESS where it is LOADED, which is 0x3000.
I didn't suppose that the raw file offset would be equal to the relative virtual address where the section is loaded into memory. I think I should clarify what exactly is the problem. The problem is that the first entry in the Import Directory Table has 0x303425ff in the ImportLookupTable RVA field. But this points nowhere, since the file has only three sections as follows:
SectionName: code VirtualAddress: 0x1000 VirtualSize: 0x20
SectionName: data VirtualAddress: 0x2000 VirtualSize: 0x20
SectionName: .idata VirtualAddress: 0x3000 VirtualSize: 0x74
So 0x303425ff does not point into any of these sections when loaded into memory.
Quote from: Shtuka on March 07, 2011, 07:41:44 PM
I didn't suppose that the raw file offset would be equal to the relative virtual address where the section is loaded into memory. I think I should clarify what exactly is the problem. The problem is that the first entry in the Import Directory Table has 0x303425ff in the ImportLookupTable RVA field. But this points nowhere, since the file has only three sections as follows:
SectionName: code VirtualAddress: 0x1000 VirtualSize: 0x20
SectionName: data VirtualAddress: 0x2000 VirtualSize: 0x20
SectionName: .idata VirtualAddress: 0x3000 VirtualSize: 0x74
So 0x303425ff does not point into any of these sections when loaded into memory.
None of the tables point to 0x3000 (0xFF 0x25, 0x34, 0x30, 0x40, 0x00 - jmp [0x00403034]) , the import table starts at 0x0040300C (0x300C - Import Directory), and is preceded by two indirect jumps that reference imported address fields fixed up by the loader. They reference inside the import address table (0x3034 - IAT).
Ah, I see. I think I figured it out now: The loader finds the relevant data (e.g. import table) through the optional header data directories, and hence they (the tables) can be located anywhere, in particular in a section different from ".idata". The PE Spec however is rather fuzzy at this point, it suggests the ".idata" section would have a fixed structure and/or would contain the import table.
Thanks for you help!