DEADSOFTWARE

added PE export dumper ('cause why, Wyoming?)
[d2df-sdl.git] / src / lib / libjit / pedump / pe32U.pas
1 // pe32U.pas v0.0.1
2 // PE32 headers (and other shit)
3 // collected from the various sources by Ketmar // Invisible Vector
4 // public domain
5 {$INCLUDE a_modes.inc}
6 unit pe32U;
8 interface
11 const
12 IMAGE_DOS_SIGNATURE = $5A4D; // MZ
13 IMAGE_DOS_MAGIC = $5A4D; // MZ
14 IMAGE_NT_SIGNATURE = $00004550; // PE00
16 IMAGE_SIZEOF_STD_OPTIONAL_HEADER = 28;
17 IMAGE_SIZEOF_NT_OPTIONAL_HEADER = 224;
18 IMAGE_NT_OPTIONAL_HDR_MAGIC = $010B;
20 IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;
22 IMAGE_FILE_EXECUTABLE_IMAGE = $0002; // file is executable (i.e. no unresolved externel references)
23 IMAGE_FILE_32BIT_MACHINE = $0100; // 32 bit word machine
24 IMAGE_FILE_DLL = $2000; // file is a DLL
26 IMAGE_FILE_MACHINE_I386 = $14C;
28 IMAGE_SUBSYSTEM_WINDOWS_GUI = 2; // windoze GUI subsystem
29 IMAGE_SUBSYSTEM_WINDOWS_CUI = 3; // windoze character subsystem (console mode)
31 IMAGE_DIRECTORY_ENTRY_EXPORT = 0; // export Directory
32 IMAGE_DIRECTORY_ENTRY_IMPORT = 1; // import Directory
33 IMAGE_DIRECTORY_ENTRY_RESOURCE = 2; // resource Directory
34 IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3; // exception Directory
35 IMAGE_DIRECTORY_ENTRY_SECURITY = 4; // security Directory
36 IMAGE_DIRECTORY_ENTRY_BASERELOC = 5; // base Relocation Table
37 IMAGE_DIRECTORY_ENTRY_DEBUG = 6; // debug Directory
38 IMAGE_DIRECTORY_ENTRY_COPYRIGHT = 7; // description String
39 IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8; // machine Value (MIPS GP)
40 IMAGE_DIRECTORY_ENTRY_TLS = 9; // TLS Directory
41 IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10; // load Configuration Directory
42 IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11; // Bound Import Directory in headers
43 IMAGE_DIRECTORY_ENTRY_IAT = 12; // Import Address Table
45 IMAGE_SIZEOF_SECTION_HEADER = 40;
46 IMAGE_SIZEOF_SHORT_NAME = 8; // section name
48 // section characteristics
49 IMAGE_SCN_CNT_CODE = $00000020; // contains code
50 IMAGE_SCN_CNT_INITIALIZED_DATA = $00000040; // contains initialized data
51 IMAGE_SCN_CNT_UNINITIALIZED_DATA = $00000080; // contains uninitialized data
53 IMAGE_SCN_MEM_DISCARDABLE = $02000000; // can be discarded
54 IMAGE_SCN_MEM_NOT_CACHED = $04000000; // is not cachable
55 IMAGE_SCN_MEM_NOT_PAGED = $08000000; // is not pageable
56 IMAGE_SCN_MEM_SHARED = $10000000; // is shareable
57 IMAGE_SCN_MEM_EXECUTE = $20000000; // is executable
58 IMAGE_SCN_MEM_READ = $40000000; // is readable
59 IMAGE_SCN_MEM_WRITE = LongWord($80000000); // is writeable
61 PE_DIR_EXPORT = 0;
62 PE_DIR_IMPORT = 1;
63 PE_DIR_RESOURCE = 2;
64 PE_DIR_EXCEPTION = 3;
65 PE_DIR_SECURITY = 4;
66 PE_DIR_FIXUP = 5;
67 PE_DIR_DEBUG = 6;
68 PE_DIR_DESCRIPTION = 7;
69 PE_DIR_MACHINE = 8;
70 PE_DIR_TLS = 9;
71 PE_DIR_LOADCONFIG = 10;
72 PE_DIR_BOUNDIMPORT = 11;
73 PE_DIR_IMPORTADDR = 12;
74 PE_DIR_DELAYIMPORT = 13;
75 PE_DIR_COMPLUSHEADER = 14;
77 RT_CURSOR = 1;
78 RT_BITMAP = 2;
79 RT_ICON = 3;
80 RT_MENU = 4;
81 RT_DIALOG = 5;
82 RT_STRING_TABLE = 6;
83 RT_FONT_DIR = 7;
84 RT_FONT = 8;
85 RT_ACCELERATORS = 9;
86 RT_RCDATA = 10; // ???
87 RT_MESSAGE_TABLE = 11;
88 RT_GROUP_CURSOR = 12;
89 RT_GROUP_ICON = 14;
90 RT_VERSION_INFO = 16;
91 RT_XP_MANIFEST = 24;
94 type
95 // DOS .EXE header
96 TImageDOSHeader = packed record
97 e_magic: Word; // magic number
98 e_cblp: Word; // bytes on last page of file
99 e_cp: Word; // pages in file
100 e_crlc: Word; // relocations
101 e_cparhdr: Word; // size of header in paragraphs
102 e_minalloc: Word; // minimum extra paragraphs needed
103 e_maxalloc: Word; // maximum extra paragraphs needed
104 e_ss: Word; // initial (relative) SS value
105 e_sp: Word; // initial SP value
106 e_csum: Word; // checksum
107 e_ip: Word; // initial IP value
108 e_cs: Word; // initial (relative) CS value
109 e_lfarlc: Word; // file address of relocation table
110 e_ovno: Word; // overlay number
111 e_res: packed array [0..3] of Word; // reserved words
112 e_oemid: Word; // OEM identifier (for e_oeminfo)
113 e_oeminfo: Word; // OEM information; e_oemid specific
114 e_res2: packed array [0..9] of Word; // reserved words
115 _lfanew: LongWord; // +$3C; file address of new exe header
116 end;
118 PPEHeader = ^TPEHeader;
119 TPEHeader = packed record
120 signature: LongWord; //+$00; 'PE'#0#0
121 machine: Word; //+$04; 0x14C-0x14F - x86-compatible
122 numberOfSections: Word; //+$06; max - 96 (why? %-)
123 timeDateStamp: LongWord; //+$08; shit. PE is full of shit...
124 pointerToSymbolTable: LongWord; //+$0C; COFF symbol table. another shit.
125 numberOfSymbols: LongWord; //+$10; and shit again.
126 ntHdrSize: Word; //+$14; for COFF(obj) - 0, for pe - not 0. ;-)
127 flags: Word; //+$16
128 // ok, let me tell you 'bout this stuff... %-)
129 // 0: =1 - relocations is stripped. for COFF %-) PE ignores this
130 // 1: =0 - image cannot be executed (really?)
131 //2,3: =1 - COFF line numbers and local symbols was stripped
132 // 4: =1 - aggresively trim working set (does nothing, afaik)
133 // 5: =1 - application can handle >2Gb addresses (newer saw one)
134 // 6: =0 - reserved, should be zero (the best flag of all! %-)
135 // 7: =1 - bytes swaped (x86-like) (windoze ignores this. at least XP)
136 // 8: =1 - 32-bit machine
137 // 9: =1 - debug info stripped (COFF again?)
138 // 10: =1 - copy to swap if executed from removable media (shit)
139 // 11: =0 - reserved
140 // 12: =1 - system file (and what it means?)
141 // 13: =1 - DLL, else -- application (which, in fact, can be loaded like
142 // any normal DLL. if it has the export section, we even can import
143 // something from EXE. but there will be no ititialization or
144 // shutdown for EXEs (alas...%-( )
145 // note: Win2K (may be NT/XP too?) doesn't apply fixups. asshole.
146 // chorus behind the scene: I LOVE MICRO$OFT!!!
147 // 14: =1 - "File should be run only on a UP machine". UP means "uniprocessor"
148 // 15: =1 - big endian machine (i.e. motorolas, etc.)
149 // let's drop a sight at bit 7. WHY IT IS HERE?!
150 // chorus: I *REALLY* LOVE MICRO$OFT!!!
151 // and "optional" header which is not optional at all %-)
152 magic: Word; //+$18; $010B - PE32
153 linkerVersion: Word; //+$1A; shit
154 sizeOfCode: LongWord; //+$1C; 0
155 sizeOfData: LongWord; //+$20; 0
156 sizeOfBSS: LongWord; //+$24; 0
157 entryRVA: LongWord; //+$28; rva. optional for DLLs (yep? ;-)
158 baseOfCode: LongWord; //+$2C; rva again
159 baseOfData: LongWord; //+$30; and one more rva, unused in PE
160 // NT additional fields
161 imageBase: LongWord; //+$34; preferred loading address (not an RVA)
162 sectionAlignment: LongWord;//+$38; default value is 0x1000 (page size)
163 fileAlignment: LongWord; //+$3C; power of 2(512-64K), if sectionAlignment is less then page size then this must match SA
164 osMajor: Word; //+$40; how's interesting...
165 osMinor: Word; //+$42; ...
166 userMajor: Word; //+$44; another meaningful fields...
167 userMinor: Word; //+$46; ...
168 subsysMajor: Word; //+$48; they driving me crazy... ;-)
169 subsysMinor: Word; //+$4A; ...
170 w32VerValue: LongWord; //+$4C; noting...
171 imageSize: LongWord; //+$50; size, in bytes, of image, including all headers; must be a multiple of Section Alignment
172 headerSize: LongWord; //+$54; combined size of MS-DOS stub, PE header, and section headers rounded up to a multiple of fileAlignment
173 fileChecksum: LongWord; //+$58; significant for all drivers, any DLL loaded at boot time, and any DLL that ends up in the server. see IMAGHELP.DLL for algorithm
174 subsystem: Word; //+$5C; 2 - GUI, 3 - CONSOLE, other is shit
175 dllFlags: Word; //+$5E; bits 0..3 are reserved, bit 15 - terminal server aware, bit 13 - WDM driver, bit 11 - do not bind image
176 stackReserveSize: LongWord; //+$60; max stack
177 stackCommitSize: LongWord; //+$64; stack growing up by stackCommitSize bytes
178 heapReserveSize: LongWord; //+$68; the same for heap
179 heapCommitSize: LongWord; //+$6C; ...
180 loaderFlags: LongWord; //+$70; nothing. obsolete field
181 numberOfRvaAndSizes: LongWord; //+$74; # of valid objects in the following directory
183 //+$78; IMAGE_DATA_DIRECTORY - 16 entries
184 case Integer of
185 0: (
186 exportTableRVA: LongWord; //+$78
187 totalExportDataSize: LongWord; //+$7C
188 importTableRVA: LongWord; //+$80
189 totalImportDataSize: LongWord; //+$84
190 resourceTableRVA: LongWord; //+$88
191 totalResourceDataSize: LongWord; //+$8C
192 exceptionTableRVA: LongWord; //+$90 not used on x86
193 totalExceptionDataSize: LongWord; //+$94
194 securityTableRVA: LongWord; //+$98 this will NOT be load into memory
195 totalSecurityDataSize: LongWord; //+$9C
196 relocationTableRVA: LongWord; //+$A0
197 totalRelocationDataSize: LongWord; //+$A4
198 debugTableRVA: LongWord; //+$A8
199 totalDebugDataSize: LongWord; //+$AC
200 descriptionRVA: LongWord; //+$B0
201 totalDescriptionDataSize: LongWord;//+$B4
202 machineSpecificRVA: LongWord; //+$B8
203 totalMachineSpecificDataSize: LongWord;//+$BC
204 tlsTableRVA: LongWord; //+$C0 statical data must NOT be used in DLLs that can be LoadLibrary'ed
205 totalTLSDataSize: LongWord; //+$C4
206 loadConfigurationTableRVA: LongWord;//+$C8
207 totalLoadConfigurationDataSize: LongWord;//+$CC
208 boundImportTableRVA: LongWord; //+$D0
209 totalBoundImportDataSize: LongWord;//+$D4
210 iatTableRVA: LongWord; //+$D8
211 totalIATDataSize: LongWord; //+$DC
212 delayImportTableRVA: LongWord; //+$E0
213 totalDelayImportDataSize: LongWord;//+$E4
214 comPlusRuntimeHeaderRVA: LongWord; //+$E8
215 comPlusRuntimeHeaderDataSize: LongWord;//+$EC
216 resObjData: packed array [0..1] of LongWord;//+$F0
217 );
218 1: (dirEntries: packed array [0..15] of record rva, size: LongWord; end);
219 // if rva = 0 - field is unused
220 end;
222 PPESectionInfo = ^TPESectionInfo;
223 TPESectionInfo = packed record
224 name: packed array [0..7] of Char;
225 virtualSize: LongWord; //+$08; if this value is greater than PhysicalSize, the section is zero-padded
226 rva: LongWord; //+$0C;
227 physSize: LongWord; //+$10; on disk
228 physOffset: LongWord; //+$14; in file, can be 0 if section contains only uninitialized data
229 fixupPointer: LongWord; //+$18; file pointer, COFF only
230 lineNumberPointer: LongWord; //+$1C; COFF only
231 fixupCount: Word; //+$20; COFF only
232 lineCount: Word; //+$22; COFF only
233 flags: LongWord; //+$24
234 // bits 0-5 are reserved
235 // 6: =1 - contains executable code
236 // 7: =1 - contains initialized data
237 // 8: =1 - contains uninitialized data
238 // 9-24: reserved
239 // 25: =1 - can be discarded
240 // 26: =1 - cannot be cached
241 // 27: =1 - not pageable
242 // 28: =1 - can be shared in memory (can or should???)
243 // 29: =1 - can be executed
244 // 30: =1 - can be read
245 // 31: =1 - can be written to
246 end;
248 PPEImportRec = ^TPEImportRec;
249 TPEImportRec = packed record
250 origFirstThunkRVA: LongWord; //+$00, can be 0, then take firstThunkRVA
251 timeDateStamp: LongWord; //+$04
252 forwarderChainRVA: LongWord; //+$08
253 nameRVA: LongWord; //+$0C
254 firstThunkRVA: LongWord; //+$10
255 end;
257 PPEImportByName = ^TPEImportByName;
258 TPEImportByName = packed record
259 hint: Word;
260 name: packed array [0..0] of Char;
261 end;
263 PPEExportDir = ^TPEExportDir;
264 TPEExportDir = packed record
265 characteristics: LongWord;
266 timeDateStamp: LongWord;
267 majorVersion: Word;
268 minorVersion: Word;
269 nameRVA: LongWord;
270 base: LongWord;
271 functionCount: LongWord;
272 nameCount: LongWord;
273 functionsRVA: LongWord;
274 namesRVA: LongWord;
275 nameOrdinalsRVA: LongWord;
276 end;
278 PPETLSCallback = procedure (dllHandle: Pointer; reason: LongWord; reserved: Pointer); stdcall;
280 PPETLSDir = ^TPETLSDir;
281 TPETLSDir = packed record
282 rawDataStart: LongWord; // ~rva, add baseOfCode to obtain rva
283 rawDataEnd: LongWord; // ~rva, add baseOfCode to obtain rva
284 indexRVA: LongWord; // rva (PDWORD, as Jedi says). wtf???
285 callbacksRVA: LongWord;
286 // rva; array of PPETLSCallback (ends with zero dword)
287 sizeOfZeroFill: LongWord; // ???
288 characteristics: LongWord; // zero (afais)
289 end;
291 PPEResourceDirTable = ^TPEResourceDirTable;
292 TPEResourceDirTable = packed record
293 characteristics: LongWord; // unused?
294 timeDateStamp: LongWord;
295 majorVersion: Word;
296 minorVersion: Word;
297 numberOfNamedEntries: Word;
298 numberOfIdEntries: Word;
299 end;
301 // this immediately follows TPEResourceDirTable, named first
302 PPEResourceDirEntry = ^TPEResourceDirEntry;
303 TPEResourceDirEntry = packed record
304 id: LongWord;
305 // bit 31:
306 // =0: number
307 // =1: name, ofs from the beginning of the resource raw data,
308 // unicode string, first word is len, no trailing zero
309 // for ROOT dir (level 0): RT_xxx
310 // level 1: resource id or resource name
311 // level 2: language-id (must be a number)
312 subdirOfs: LongWord;
313 // offset from the beginning of the resource raw data
314 // bit 31:
315 // =0: to TPEResourceDataEntry
316 // =1: to the next dir (TPEResourceDirTable)
317 end;
319 PPEResourceDataEntry = ^TPEResourceDataEntry;
320 TPEResourceDataEntry = packed record
321 dataOfs: LongWord;
322 size: LongWord;
323 codePage: LongWord;
324 reserved: LongWord;
325 end;
327 PPEResourceTableDirEntry = ^TPEResourceTableDirEntry;
328 TPEResourceTableDirEntry = packed record
329 table: TPEResourceDirTable;
330 directory: TPEResourceDirEntry;
331 end;
333 PPEIconDirEntry = ^TPEIconDirEntry;
334 TPEIconDirEntry = packed record
335 width: Byte;
336 height: Byte;
337 colorCount: Byte;
338 reserved: Byte;
339 planes: Word;
340 bitCount: Word;
341 bytesInRes: LongWord;
342 id: Word;
343 end;
345 PPEIconDir = ^TPEIconDir;
346 TPEIconDir = packed record
347 reserved: Word;
348 resType: Word;
349 count: Word;
350 //entries: packed array [0..31] of TPEIconDirEntry;
351 end;
353 // peImg: buffer with PE image (loaded as-is)
354 // functions doesn't validate PE
356 // can return nil (out of image data)
357 // but this can be BSS!
358 function rva2ptr (peImg: Pointer; rva: LongWord): Pointer;
360 function getPEHeaderPtr (peImg: Pointer): PPEHeader;
361 function getPESectionsPtr (peImg: Pointer): PPESectionInfo;
363 // simple checks only!
364 function isValidPE (peImg: Pointer): Boolean;
367 type
368 PResData = ^TResData;
369 TResData = record
370 rtype: LongWord;
371 data: PChar; // читаем данные отсюда
372 size: LongWord;
373 rvaPatch: PLongWord; // ставится не здесь
374 end;
376 // ищем первый ресурс с типом rt
377 function peFindFirstRT (pe: Pointer; rt: LongWord; out rdo: TResData; cnt: Integer=-1): Boolean;
380 implementation
383 function getPEHeaderPtr (peImg: Pointer): PPEHeader;
384 begin
385 result := PPEHeader(PtrUInt(PtrUInt(peImg)+PInteger(PtrUInt(peImg)+$3C)^));
386 end;
389 function getPESectionsPtr (peImg: Pointer): PPESectionInfo;
390 var
391 h: PPEHeader;
392 begin
393 h := getPEHeaderPtr(peImg);
394 result := PPESectionInfo(PtrUInt(PtrUInt(h)+h.ntHdrSize+$18));
395 end;
398 function rva2ptr (peImg: Pointer; rva: LongWord): Pointer;
399 var
400 h: PPEHeader;
401 si: PPESectionInfo;
402 c: Integer;
403 begin
404 h := getPEHeaderPtr(peImg);
405 c := h.numberOfSections;
406 si := Pointer(PtrUInt(PtrUInt(h)+h.ntHdrSize+$18));
407 while (c > 0) do
408 begin
409 if (si.rva <> 0) and (si.physSize <> 0) and
410 (rva >= si.rva) and (rva < si.rva+si.physSize) then
411 begin
412 result := Pointer(PtrUInt(PtrUInt(peImg)+si.physOffset+(rva-si.rva)));
413 exit;
414 end;
415 Dec(c);
416 Inc(si);
417 end;
418 if (rva < h.headerSize) then result := Pointer(PtrUInt(PtrUInt(peImg)+rva)) else result := nil;
419 end;
422 function isValidPE (peImg: Pointer): Boolean;
423 var
424 lfanew: LongWord;
425 h: PPEHeader;
426 begin
427 result := false;
428 if (peImg = nil) then exit;
429 try
430 //if IsBadReadPtr(peImg, $40) then exit;
431 if (PWord(peImg)^ <> IMAGE_DOS_MAGIC) then exit;
432 lfanew := PLongWord(PtrUInt(peImg)+$3C)^;
433 if (lfanew = 0) then exit;
434 h := Pointer(PtrUInt(PtrUInt(peImg)+lfanew));
435 //if IsBadReadPtr(h, SizeOf(TPEHeader)) then exit;
436 if (h.signature <> IMAGE_NT_SIGNATURE) or
437 (h.magic <> IMAGE_NT_OPTIONAL_HDR_MAGIC) or
438 (h.machine < $14C) or (h.machine > $14F) then exit;
439 except // sorry
440 exit;
441 end;
442 result := true;
443 end;
446 function peFindFirstRT (pe: Pointer; rt: LongWord; out rdo: TResData; cnt: Integer=-1): Boolean;
447 var
448 h: PPEHeader;
449 rptr: PtrUInt;
450 rtbl: PPEResourceDirTable;
451 re, re1: PPEResourceDirEntry;
452 rd: PPEResourceDataEntry;
453 f: Integer;
454 nc: Integer;
455 begin
456 result := false;
457 rdo.data := nil;
458 rdo.size := 0;
459 rdo.rvaPatch := nil;
460 h := getPEHeaderPtr(pe);
461 if (h.resourceTableRVA = 0) or (h.totalResourceDataSize = 0) then exit;
462 rptr := PtrUInt(rva2ptr(pe, h.resourceTableRVA));
463 if (rptr = 0) then exit;
464 rtbl := PPEResourceDirTable(rptr);
465 nc := Integer(rtbl.numberOfNamedEntries);
466 f := Integer(rtbl.numberOfIdEntries);
467 Inc(f, nc);
468 if (f = 0) then exit;
469 re := Pointer(PtrUInt(rptr+sizeof(TPEResourceDirTable)));
470 while (f > 0) do
471 begin
472 if (re.id = rt) then
473 begin
474 if cnt > 0 then
475 begin
476 Dec(cnt);
477 end
478 else
479 begin
480 // ищем собственно ресурс
481 re1 := re;
482 while ((re1.subdirOfs and $80000000) <> 0) do
483 begin
484 rtbl := PPEResourceDirTable(PtrUInt(rptr+(re1.subdirOfs and $7FFFFFFF)));
485 //nc := Integer(rtbl.numberOfNamedEntries)+Integer(rtbl.numberOfIdEntries);
486 //if nc = 0 then Error('resource shit!');
487 re1 := PPEResourceDirEntry(PtrUInt(PtrUInt(rtbl)+sizeof(TPEResourceDirTable)));
488 end;
489 rd := PPEResourceDataEntry(PtrUInt(rptr+re1.subdirOfs));
490 rdo.size := rd.size;
491 // а здесь -- RVA; заебись некроблядь суперпоследовательна
492 rdo.data := rva2ptr(pe, rd.dataOfs);
493 if (rdo.data = nil) then rdo.size := 0 else begin rdo.rtype := rt; result := true; exit; end;
494 end;
495 end;
496 Inc(re);
497 Dec(f);
498 end;
499 end;
502 end.