1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
5 // EXE executable formats
7 #include <deark-private.h>
8 #include <deark-fmtutil.h>
9 DE_DECLARE_MODULE(de_module_exe
);
13 #define EXE_FMT_PE32 3
14 #define EXE_FMT_PE32PLUS 4
18 #define MAX_RESOURCES 10000
20 #define DE_RT_CURSOR 1
21 #define DE_RT_BITMAP 2
23 #define DE_RT_FONTDIR 7
25 #define DE_RT_GROUP_CURSOR 12
26 #define DE_RT_GROUP_ICON 14
27 #define DE_RT_ANICURSOR 21
28 #define DE_RT_ANIICON 22
29 #define DE_RT_MANIFEST 24
31 struct rsrc_type_info_struct
;
33 typedef struct localctx_struct
{
35 i64 ext_header_offset
;
37 i64 ne_rsrc_tbl_offset
;
38 unsigned int ne_align_shift
;
41 const struct rsrc_type_info_struct
*ne_rsrc_type_info
;
43 i64 lx_page_offset_shift
;
44 i64 lx_object_tbl_offset
;
45 i64 lx_object_tbl_entries
;
46 i64 lx_object_page_tbl_offset
;
47 i64 lx_rsrc_tbl_offset
;
48 i64 lx_rsrc_tbl_entries
;
49 i64 lx_data_pages_offset
;
52 i64 pe_sections_offset
;
53 i64 pe_number_of_sections
;
55 // File offset where the resources start. Some addresses are relative
59 i64 pe_cur_section_virt_addr
;
60 i64 pe_cur_section_data_offset
;
62 i64 pe_cur_name_offset
; // 0 if no name
65 const struct rsrc_type_info_struct
*cur_rsrc_type_info
;
70 struct rsrc_type_info_struct
;
72 typedef void (*rsrc_decoder_fn
)(deark
*c
, lctx
*d
, i64 pos
, i64 len
, de_finfo
*fi
);
74 struct rsrc_type_info_struct
{
78 rsrc_decoder_fn decoder_fn
;
81 static void do_certificate(deark
*c
, lctx
*d
, i64 pos1
, i64 len
)
87 // This is a WIN_CERTIFICATE structure.
88 if(pos1
<1 || len
<=8 || (pos1
+len
> c
->infile
->len
)) return;
90 de_dbg(c
, "certificate data at %d", (int)pos1
);
92 dlen
= de_getu32le(pos1
);
93 de_dbg(c
, "length: %d", (int)dlen
); // Includes the 8-byte header
94 revision
= de_getu16le(pos1
+4);
95 de_dbg(c
, "revision: 0x%04x", (unsigned int)revision
);
96 certtype
= de_getu16le(pos1
+6);
97 de_dbg(c
, "cert type: %d", (int)certtype
);
98 if(dlen
<=8 || dlen
> len
) goto done
;
99 if(c
->extract_level
>=2) {
101 if(certtype
==2) ext
="p7b";
103 dbuf_create_file_from_slice(c
->infile
, pos1
+8, dlen
-8, ext
, NULL
, 0);
106 de_dbg_indent(c
, -1);
110 static void do_opt_coff_data_dirs(deark
*c
, lctx
*d
, i64 pos
)
115 i64 pe_security_size
;
117 de_dbg(c
, "COFF/PE optional header (data directories) at %d", (int)pos
);
119 rsrc_tbl_rva
= de_getu32le(pos
+16);
120 // I don't know if rsrc_tbl_rva will be needed for anything. It seems redundant.
121 rsrc_tbl_size
= de_getu32le(pos
+20);
122 de_dbg(c
, "resource table RVA=0x%08x, size=%d", (unsigned int)rsrc_tbl_rva
,
125 pe_security_pos
= de_getu32le(pos
+32);
126 pe_security_size
= de_getu32le(pos
+36);
127 de_dbg(c
, "security pos=0x%08x, size=%d", (unsigned int)pe_security_pos
,
128 (int)pe_security_size
);
129 if(pe_security_pos
>0) {
131 do_certificate(c
, d
, pe_security_pos
, pe_security_size
);
132 de_dbg_indent(c
, -1);
135 de_dbg_indent(c
, -1);
138 static const char *get_subsys_desc(i64 subsystem
)
141 case 2: return " (Windows GUI)";
142 case 3: return " (console)";
147 static void do_opt_coff_nt_header(deark
*c
, lctx
*d
, i64 pos
)
152 de_dbg(c
, "COFF/PE optional header (Windows NT) at %d", (int)pos
);
155 x
= de_getu32le(pos
);
156 de_dbg(c
, "image base offset: 0x%08x", (unsigned int)x
);
158 subsystem
= de_getu16le(pos
+40);
159 de_dbg(c
, "subsystem: %d%s", (int)subsystem
, get_subsys_desc(subsystem
));
161 de_dbg_indent(c
, -1);
164 static void do_opt_coff_nt_header_64(deark
*c
, lctx
*d
, i64 pos
)
169 de_dbg(c
, "COFF/PE32+ optional header (Windows NT) at %d", (int)pos
);
172 base_offset
= de_geti64le(pos
);
173 de_dbg(c
, "image base offset: 0x%016" U64_FMTx
"", (u64
)base_offset
);
175 subsystem
= de_getu16le(pos
+44);
176 de_dbg(c
, "subsystem: %d%s", (int)subsystem
, get_subsys_desc(subsystem
));
178 de_dbg_indent(c
, -1);
181 static void do_opt_coff_header(deark
*c
, lctx
*d
, i64 pos
, i64 len
)
184 i64 coff_opt_hdr_size
;
186 de_dbg(c
, "COFF/PE optional header at %d, size=%d", (int)pos
, (int)len
);
189 sig
= de_getu16le(pos
);
190 de_dbg(c
, "signature: 0x%04x", (int)sig
);
193 coff_opt_hdr_size
= 28;
195 coff_opt_hdr_size
= 24;
198 d
->fmt
= EXE_FMT_PE32
;
199 de_declare_fmt(c
, "PE32");
200 do_opt_coff_nt_header(c
, d
, pos
+coff_opt_hdr_size
);
201 do_opt_coff_data_dirs(c
, d
, pos
+coff_opt_hdr_size
+68);
203 else if(sig
==0x020b) {
204 d
->fmt
= EXE_FMT_PE32PLUS
;
205 de_declare_fmt(c
, "PE32+");
206 do_opt_coff_nt_header_64(c
, d
, pos
+coff_opt_hdr_size
);
207 do_opt_coff_data_dirs(c
, d
, pos
+coff_opt_hdr_size
+88);
209 else if(sig
==0x0107) {
210 de_declare_fmt(c
, "PE ROM image");
213 de_declare_fmt(c
, "Unknown PE file type");
216 de_dbg_indent(c
, -1);
219 static void do_pe_characteristics(deark
*c
, lctx
*d
, unsigned int v
)
221 de_ucstring
*s
= NULL
;
222 s
= ucstring_create(c
);
224 if(v
&0x0001) ucstring_append_flags_item(s
, "relocs_stripped");
225 if(v
&0x0002) ucstring_append_flags_item(s
, "valid_executable");
226 if(v
&0x0004) ucstring_append_flags_item(s
, "COFF_line_numbers_stripped");
227 if(v
&0x0008) ucstring_append_flags_item(s
, "COFF_local_stripped");
228 if(v
&0x0020) ucstring_append_flags_item(s
, "large_address_aware");
229 if(v
&0x0100) ucstring_append_flags_item(s
, "32-bit");
230 if(v
&0x0200) ucstring_append_flags_item(s
, "stripped");
231 if(v
&0x2000) ucstring_append_flags_item(s
, "DLL");
232 // TODO: There are more flags than this.
233 de_dbg(c
, "characteristics: 0x%04x (%s)", v
, ucstring_getpsz(s
));
237 static const char *get_machine_type_name(unsigned int n
)
240 struct mtn_struct
{ unsigned int id
; const char *name
; };
241 static const struct mtn_struct mtn_arr
[] = {
242 { 0x0000, "neutral" },
244 { 0x0166, "MIPS LE" },
245 { 0x0169, "MIPS LE WCE v2" },
246 { 0x01a2, "Hitachi SH3" },
247 { 0x01a3, "Hitachi SH3 DSP" },
248 { 0x01a6, "Hitachi SH4" },
249 { 0x01a8, "Hitachi SH5" },
250 { 0x01c0, "ARM LE" },
251 { 0x01c2, "ARM or Thumb" },
252 { 0x01c4, "ARMv7+ Thumb" },
253 { 0x01d3, "Matsushita AM33" },
254 { 0x01f0, "Power PC LE" },
255 { 0x01f1, "Power PC w/FP" },
256 { 0x0200, "Itanium" },
257 { 0x0266, "MIPS16" },
258 { 0x0366, "MIPS with FPU" },
259 { 0x0466, "MIPS16 with FPU" },
260 { 0x0ebc, "EFI byte code" },
262 { 0x9041, "Mitsubishi M32R LE" },
263 { 0xaa64, "ARMv8 64-bit" }
266 for(i
=0; i
<DE_ARRAYCOUNT(mtn_arr
); i
++) {
267 if(mtn_arr
[i
].id
== n
) {
268 return mtn_arr
[i
].name
;
274 static void do_Rich_segment(deark
*c
, lctx
*d
)
287 segment_end
= d
->ext_header_offset
;
288 if(segment_end
%8) segment_end
-= segment_end
%8;
289 if(segment_end
- segment_start
< 24) return; // No place for a Rich segment
291 // Try to find the "Rich" signature", which starts 8 bytes from the end of
293 // Based on limited research, the Rich signature usually starts 16, 24, or 32
294 // bytes before the "PE" signature.
296 for(p
= segment_end
-8; p
>= segment_start
+16; p
-= 8 ) {
297 n
= (u32
)de_getu32le(p
);
298 if(n
==0x68636952U
) { // "Rich"
304 return; // Rich segment not found
307 // Likely "Rich" signature found at sig_pos
309 key
= (u32
)de_getu32le(sig_pos
+4);
311 // Decode and verify the "start" signature
312 n
= (u32
)de_getu32le(segment_start
);
313 if((n
^ key
) != 0x536e6144U
) { // "Dans"
314 // False positive? Or maybe our detection logic isn't perfect?
318 de_dbg(c
, "\"Rich\" segment detected at %d, sig at %d, len=%d",
319 (int)segment_start
, (int)sig_pos
,
320 (int)(sig_pos
+8 - segment_start
));
324 pos
= segment_start
+ 16;
325 num_entries
= (sig_pos
- pos
)/8;
326 for(k
=0; k
<num_entries
; k
++) {
332 id_and_value
= (u32
)de_getu32le(pos
+8*k
);
333 use_count
= (u32
)de_getu32le(pos
+8*k
+4);
336 id
= (id_and_value
&0xffff0000U
)>>16;
337 value
= id_and_value
&0x0000ffffU
;
338 // TODO: Provide additional information, based on the 'type' and 'build'?
339 de_dbg(c
, "entry[%d]: type=%d, build=%d, use_count=%u",
340 (int)k
, (int)id
, (int)value
, (unsigned int)use_count
);
343 de_dbg_indent(c
, -1);
346 // 'pos' is the start of the 4-byte PE signature.
347 // Following it is a 20-byte COFF header.
348 static void do_pe_coff_header(deark
*c
, lctx
*d
, i64 pos
)
353 de_dbg(c
, "PE header at %d", (int)d
->ext_header_offset
);
356 // a.k.a. "Machine". TODO: Decode this.
357 arch
= (unsigned int)de_getu16le(pos
+4+0);
358 de_dbg(c
, "target architecture: 0x%04x (%s)", arch
,
359 get_machine_type_name(arch
));
361 d
->pe_number_of_sections
= de_getu16le(pos
+4+2);
362 de_dbg(c
, "number of sections: %d", (int)d
->pe_number_of_sections
);
364 d
->pe_opt_hdr_size
= de_getu16le(pos
+4+16);
365 de_dbg(c
, "optional header size: %d", (int)d
->pe_opt_hdr_size
);
367 n
= de_getu16le(pos
+4+18);
368 do_pe_characteristics(c
, d
, (unsigned int)n
);
370 if(d
->pe_opt_hdr_size
>0) {
371 do_opt_coff_header(c
, d
, pos
+4+20, d
->pe_opt_hdr_size
);
372 d
->pe_sections_offset
= pos
+4+20+d
->pe_opt_hdr_size
;
376 de_dbg_indent(c
, -1);
379 static void do_ne_program_flags(deark
*c
, lctx
*d
, u8 flags
)
381 de_ucstring
*s
= NULL
;
382 s
= ucstring_create(c
);
385 case 1: ucstring_append_flags_item(s
, "dgroup_type=single_shared"); break;
386 case 2: ucstring_append_flags_item(s
, "dgroup_type=multiple"); break;
387 case 3: ucstring_append_flags_item(s
, "dgroup_type=null"); break;
390 if(flags
&0x4) ucstring_append_flags_item(s
, "global init");
391 if(flags
&0x8) ucstring_append_flags_item(s
, "protected mode");
392 if(flags
&0x10) ucstring_append_flags_item(s
, "8086");
393 if(flags
&0x20) ucstring_append_flags_item(s
, "80286");
394 if(flags
&0x40) ucstring_append_flags_item(s
, "80386");
395 if(flags
&0x80) ucstring_append_flags_item(s
, "80x87");
397 de_dbg(c
, "program flags: 0x%02x (%s)", (unsigned int)flags
,
403 static void do_ne_app_flags(deark
*c
, lctx
*d
, u8 flags
)
405 de_ucstring
*s
= NULL
;
406 s
= ucstring_create(c
);
409 case 0x1: ucstring_append_flags_item(s
, "type=non-windowed"); break;
410 case 0x2: ucstring_append_flags_item(s
, "type=windowed-compatible"); break;
411 case 0x3: ucstring_append_flags_item(s
, "type=windowed"); break;
414 if(flags
&0x08) ucstring_append_flags_item(s
, "OS/2");
415 if(flags
&0x80) ucstring_append_flags_item(s
, "DLL");
417 de_dbg(c
, "application flags: 0x%02x (%s)", (unsigned int)flags
,
423 static void do_ne_ext_header(deark
*c
, lctx
*d
, i64 pos
)
429 de_dbg(c
, "NE extended header at %d", (int)pos
);
432 b1
= de_getbyte(pos
+2);
433 b2
= de_getbyte(pos
+3);
434 de_dbg(c
, "linker version: %d.%d", (int)b1
,(int)b2
);
436 // 4-5: Offset of entry table
437 // 6-7: length of entry table
438 // 8-11: file load CRC
440 do_ne_program_flags(c
, d
, de_getbyte(pos
+12));
442 do_ne_app_flags(c
, d
, de_getbyte(pos
+13));
444 d
->ne_rsrc_tbl_offset
= de_getu16le(pos
+36);
445 d
->ne_rsrc_tbl_offset
+= pos
;
446 de_dbg(c
, "offset of resource table: %d", (int)d
->ne_rsrc_tbl_offset
);
448 target_os
= de_getbyte(pos
+54);
450 case 1: desc
="OS/2"; break;
451 case 2: desc
="Windows"; break;
452 case 3: desc
="European MS-DOS 4.x"; break;
453 case 4: desc
="Windows 386"; break;
454 case 5: desc
="Borland Operating System Services"; break;
457 de_dbg(c
, "target OS: %d (%s)", (int)target_os
, desc
);
459 de_dbg_indent(c
, -1);
462 static void do_lx_or_le_ext_header(deark
*c
, lctx
*d
, i64 pos
)
466 de_dbg(c
, "%s header at %d", d
->fmt
==EXE_FMT_LE
?"LE":"LX", (int)pos
);
467 x1
= (u8
)de_getbyte(pos
+2);
468 x2
= (u8
)de_getbyte(pos
+3);
469 de_dbg(c
, "byte order, word order: %d, %d", (int)x1
, (int)x2
);
471 de_err(c
, "Unsupported byte order.");
475 if(d
->fmt
==EXE_FMT_LE
) {
476 x1
= de_getu32le(pos
+0x2c);
477 de_dbg(c
, "bytes on last page: %d", (int)x1
);
480 d
->lx_page_offset_shift
= de_getu32le(pos
+0x2c);
481 de_dbg(c
, "page offset shift: %d", (int)d
->lx_page_offset_shift
);
484 x1
= de_getu32le(pos
+0x40);
485 d
->lx_object_tbl_offset
= pos
+ x1
;
486 d
->lx_object_tbl_entries
= de_getu32le(pos
+0x44);
487 de_dbg(c
, "object table offset=%d, entries=%d", (int)d
->lx_object_tbl_offset
, (int)d
->lx_object_tbl_entries
);
489 x1
= de_getu32le(pos
+0x48);
490 d
->lx_object_page_tbl_offset
= pos
+ x1
;
491 de_dbg(c
, "object page table offset=%d", (int)d
->lx_object_page_tbl_offset
);
493 x1
= de_getu32le(pos
+0x50);
494 d
->lx_rsrc_tbl_offset
= pos
+ x1
;
495 d
->lx_rsrc_tbl_entries
= de_getu32le(pos
+0x54);
496 de_dbg(c
, "resource table offset=%d entries=%d", (int)d
->lx_rsrc_tbl_offset
, (int)d
->lx_rsrc_tbl_entries
);
498 d
->lx_data_pages_offset
= de_getu32le(pos
+0x80);
499 de_dbg(c
, "data pages offset=%d", (int)d
->lx_data_pages_offset
);
502 static void do_ext_header(deark
*c
, lctx
*d
)
506 if(d
->ext_header_offset
== 0 || d
->ext_header_offset
>= c
->infile
->len
) {
507 // Give up if ext_header_offset is obviously bad.
511 de_read(buf
, d
->ext_header_offset
, 4);
512 if(!de_memcmp(buf
, "PE\0\0", 4)) {
513 do_Rich_segment(c
, d
);
514 do_pe_coff_header(c
, d
, d
->ext_header_offset
);
515 // If do_pe_coff_header didn't figure out the format...
516 de_declare_fmt(c
, "PE");
518 else if(!de_memcmp(buf
, "NE", 2)) {
519 // TODO: Do "Rich" segments ever exist in files that are not PE files?
520 de_declare_fmt(c
, "NE");
522 do_ne_ext_header(c
, d
, d
->ext_header_offset
);
524 else if(!de_memcmp(buf
, "LX", 2)) {
525 de_declare_fmt(c
, "LX Linear Executable");
527 do_lx_or_le_ext_header(c
, d
, d
->ext_header_offset
);
529 else if(!de_memcmp(buf
, "LE", 2)) {
530 de_declare_fmt(c
, "LE Linear Executable");
532 do_lx_or_le_ext_header(c
, d
, d
->ext_header_offset
);
536 // If we still don't know the format...
537 de_declare_fmt(c
, "Unknown EXE format (maybe MS-DOS)");
540 static void do_fileheader(deark
*c
, lctx
*d
)
542 i64 reloc_tbl_offset
;
544 reloc_tbl_offset
= de_getu16le(24);
545 de_dbg(c
, "relocation table offset: %d", (int)reloc_tbl_offset
);
547 if(reloc_tbl_offset
>=28 && reloc_tbl_offset
<64) {
548 de_declare_fmt(c
, "MS-DOS EXE");
549 d
->fmt
= EXE_FMT_DOS
;
552 d
->ext_header_offset
= de_getu32le(60);
553 de_dbg(c
, "extended header offset: %d", (int)d
->ext_header_offset
);
558 static void do_decode_ddb(deark
*c
, lctx
*d
, i64 pos1
, i64 len
, de_finfo
*fi
)
560 de_module_params
*mparams
= NULL
;
562 de_dbg(c
, "BITMAP16 at %"I64_FMT
, pos1
);
564 mparams
= de_malloc(c
, sizeof(de_module_params
));
565 mparams
->in_params
.fi
= fi
;
566 de_run_module_by_id_on_slice(c
, "ddb", mparams
, c
->infile
, pos1
, len
);
567 de_dbg_indent(c
, -1);
571 // Extract a raw DIB, and write it to a file as a BMP.
572 static void do_extract_BITMAP(deark
*c
, lctx
*d
, i64 pos
, i64 len
, de_finfo
*fi
)
576 if((d
->fmt
==EXE_FMT_NE
) && (de_getbyte(pos
)==0x02)) {
577 do_decode_ddb(c
, d
, pos
, len
, fi
);
582 de_run_module_by_id_on_slice2(c
, "dib", "X", c
->infile
, pos
, len
);
583 de_dbg_indent(c
, -1);
586 static void do_extract_ico_cur(deark
*c
, lctx
*d
, i64 pos
, i64 len
,
587 int is_cur
, i64 hotspot_x
, i64 hotspot_y
, de_finfo
*fi
)
592 struct de_bmpinfo bi
;
594 // I guess we have to manufacture an ICO/CUR header?
595 // There's usually a GROUP_ICON resource that seems to contain (most of) an
596 // ICO header, but I don't know exactly how it's connected to the icon image(s).
598 if(!fmtutil_get_bmpinfo(c
, c
->infile
, &bi
, pos
, len
, DE_BMPINFO_ICO_FORMAT
)) {
599 de_err(c
, "Invalid bitmap");
603 if(bi
.file_format
==DE_BMPINFO_FMT_PNG
) {
604 dbuf_create_file_from_slice(c
->infile
, pos
, len
, "png", fi
, 0);
608 f
= dbuf_create_output_file(c
, is_cur
?"cur":"ico", fi
, 0);
610 // Write the 6-byte file header.
611 dbuf_writeu16le(f
, 0); // Reserved
612 dbuf_writeu16le(f
, is_cur
?2:1); // Resource ID
613 dbuf_writeu16le(f
, 1); // Number of icons/cursors
619 ncolors
= bi
.num_colors
;
620 if(ncolors
>255) ncolors
= 0;
622 if(bi
.total_size
< len
) {
623 // Strip off useless padding at the end of the image.
627 // Write the 16-byte index entry for the one icon/cursor.
628 dbuf_writebyte(f
, (u8
)w
);
629 dbuf_writebyte(f
, (u8
)h
);
630 dbuf_writebyte(f
, (u8
)ncolors
);
632 dbuf_writebyte(f
, 0);
633 dbuf_writeu16le(f
, hotspot_x
);
634 dbuf_writeu16le(f
, hotspot_y
);
637 dbuf_write_zeroes(f
, 5);
639 dbuf_writeu32le(f
, len
); // Icon/cursor size
640 dbuf_writeu32le(f
, 6+16); // Icon/cursor file offset
642 // Write the non-manufactured part of the file.
643 dbuf_copy(c
->infile
, pos
, len
, f
);
647 static void do_extract_CURSOR(deark
*c
, lctx
*d
, i64 pos
, i64 len
, de_finfo
*fi
)
649 unsigned int firstword
;
650 i64 hotspot_x
, hotspot_y
;
653 firstword
= (unsigned int)de_getu16le(pos
);
655 // For Win3 icons, the first word is the x hotspot.
656 // For Win1 icons, it is one of the type codes below.
657 if(d
->fmt
==EXE_FMT_NE
&& (firstword
==0x0003 || firstword
==0x0103 ||
660 unsigned int fourthword
;
661 // For Win3 icons, the 4th word is the high word of the
662 // bitmap-info-header-size (definitely 0).
663 // For Win1 icons, it is the width (definitely not 0).
664 fourthword
= (unsigned int)de_getu16le(pos
+6);
666 dbuf_create_file_from_slice(c
->infile
, pos
, len
, "win1.cur", fi
, 0);
671 hotspot_x
= (i64
)firstword
;
672 hotspot_y
= de_getu16le(pos
+2);
673 de_dbg(c
, "hotspot: %d,%d", (int)hotspot_x
, (int)hotspot_y
);
674 do_extract_ico_cur(c
, d
, pos
+4, len
-4, 1, hotspot_x
, hotspot_y
, fi
);
677 static void do_extract_ICON(deark
*c
, lctx
*d
, i64 pos
, i64 len
, de_finfo
*fi
)
679 if(d
->fmt
==EXE_FMT_NE
&& len
>14) {
680 unsigned int firstword
;
682 firstword
= (unsigned int)de_getu16le(pos
);
683 // For Win3 icons, the first word is the low word of bitmap-info-header-size
684 // (usually 40, definitely not one of the Win1 type codes).
685 // For Win1 icons, it is one of the type codes below.
686 if(firstword
==0x0001 || firstword
==0x0101 || firstword
==0x0201) {
687 dbuf_create_file_from_slice(c
->infile
, pos
, len
, "win1.ico", fi
, 0);
692 do_extract_ico_cur(c
, d
, pos
, len
, 0, 0, 0, fi
);
695 // Try to get the face name and 'points' from a font resource. If successful,
696 // set the filename of the 'fi' object accordingly.
697 // This code is somewhat duplicated in fnt.c, but it's not worth consolidating.
698 static void get_font_facename(deark
*c
, lctx
*d
, i64 pos
, i64 len
, de_finfo
*fi
)
700 unsigned int fnt_version
;
701 unsigned int dfPoints
;
703 de_ucstring
*s
= NULL
;
706 if(len
<109) goto done
;
707 fnt_version
= (unsigned int)de_getu16le(pos
);
708 if(fnt_version
< 0x0200) goto done
;
709 dfPoints
= (unsigned int)de_getu16le(pos
+68);
710 dfFace
= de_getu32le(pos
+105);
711 if(dfFace
>=len
) goto done
;
712 s
= ucstring_create(c
);
713 dbuf_read_to_ucstring_n(c
->infile
, pos
+dfFace
, 64, len
-dfFace
, s
,
714 DE_CONVFLAG_STOP_AT_NUL
, DE_ENCODING_ASCII
);
715 if(s
->len
<1) goto done
;
716 ucstring_printf(s
, DE_ENCODING_LATIN1
, "-%u", dfPoints
);
717 de_finfo_set_name_from_ucstring(c
, fi
, s
, 0);
723 static void do_extract_FONT(deark
*c
, lctx
*d
, i64 pos
, i64 len
, de_finfo
*fi
)
728 // The "file size" is stored at offset 2. Respect it if possible.
729 fntlen
= de_getu32le(pos
+2);
730 if(fntlen
<6 || fntlen
>len
) {
734 get_font_facename(c
, d
, pos
, fntlen
, fi
);
736 dbuf_create_file_from_slice(c
->infile
, pos
, fntlen
, "fnt", fi
, 0);
739 static void do_extract_MANIFEST(deark
*c
, lctx
*d
, i64 pos
, i64 len
, de_finfo
*fi
)
741 if(c
->extract_level
>=2) {
742 dbuf_create_file_from_slice(c
->infile
, pos
, len
, "manifest", fi
, DE_CREATEFLAG_IS_AUX
);
746 static const struct rsrc_type_info_struct rsrc_type_info_arr
[] = {
747 { DE_RT_CURSOR
, 0, "RT_CURSOR", do_extract_CURSOR
},
748 { DE_RT_BITMAP
, 0, "RT_BITMAP", do_extract_BITMAP
},
749 { DE_RT_ICON
, 0, "RT_ICON", do_extract_ICON
},
750 { 4, 0, "RT_MENU", NULL
},
751 { 5, 0, "RT_DIALOG", NULL
},
752 { 6, 0, "RT_STRING", NULL
},
753 { DE_RT_FONTDIR
, 0, "RT_FONTDIR", NULL
},
754 { DE_RT_FONT
, 0, "RT_FONT", do_extract_FONT
},
755 { 9, 0, "RT_ACCELERATOR", NULL
},
756 { 10, 0, "RT_RCDATA", NULL
},
757 { 11, 0, "RT_MESSAGETABLE", NULL
},
758 { DE_RT_GROUP_CURSOR
, 0, "RT_GROUP_CURSOR", NULL
},
759 { DE_RT_GROUP_ICON
, 0, "RT_GROUP_ICON", NULL
},
760 { 16, 0, "RT_VERSION", NULL
},
761 { DE_RT_ANICURSOR
, 0, "RT_ANICURSOR", NULL
},
762 { DE_RT_ANIICON
, 0, "RT_ANIICON", NULL
},
763 { DE_RT_MANIFEST
, 0, "RT_MANIFEST", do_extract_MANIFEST
}
766 static const struct rsrc_type_info_struct
*get_rsrc_type_info(u32 id
)
770 for(i
=0; i
<DE_ARRAYCOUNT(rsrc_type_info_arr
); i
++) {
771 if(id
== rsrc_type_info_arr
[i
].id
) {
772 return &rsrc_type_info_arr
[i
];
778 static int ne_pe_resource_type_is_supported(deark
*c
, lctx
*d
, u32 type_id
)
791 static void do_ne_pe_extract_resource(deark
*c
, lctx
*d
,
792 u32 type_id
, const struct rsrc_type_info_struct
*rsrci
,
793 i64 pos
, i64 len
, de_finfo
*fi
)
795 if(len
<1 || len
>DE_MAX_SANE_OBJECT_SIZE
) return;
797 if(rsrci
&& rsrci
->decoder_fn
) {
798 rsrci
->decoder_fn(c
, d
, pos
, len
, fi
);
803 static void de_finfo_set_name_from_pe_string(deark
*c
, de_finfo
*fi
, dbuf
*f
,
806 i64 nlen
; // in UTF-16 code units (2 bytes each)
807 de_ucstring
*fname
= NULL
;
809 if(!c
->filenames_from_file
) goto done
;
811 // The string length is stored in a two-byte prefix.
812 nlen
= de_getu16le(pos
);
813 if(nlen
<1) goto done
;
815 fname
= ucstring_create(c
);
816 dbuf_read_to_ucstring(c
->infile
, pos
+2, nlen
*2, fname
, 0, DE_ENCODING_UTF16LE
);
817 de_finfo_set_name_from_ucstring(c
, fi
, fname
, 0);
820 ucstring_destroy(fname
);
823 static void do_pe_resource_data_entry(deark
*c
, lctx
*d
, i64 rel_pos
)
827 i64 data_real_offset
;
830 const char *rsrcname
;
832 type_id
= d
->cur_rsrc_type
;
833 if(d
->cur_rsrc_type_info
&& d
->cur_rsrc_type_info
->name
)
834 rsrcname
= d
->cur_rsrc_type_info
->name
;
838 de_dbg(c
, "resource data entry at %d(%d) rsrc_type=%d (%s)",
839 (int)(d
->pe_cur_base_addr
+rel_pos
), (int)rel_pos
, (int)type_id
, rsrcname
);
842 data_virt_addr
= de_getu32le(d
->pe_cur_base_addr
+rel_pos
);
843 data_size
= de_getu32le(d
->pe_cur_base_addr
+rel_pos
+4);
844 de_dbg(c
, "resource data virt. addr=%d (0x%08x), size=%d",
845 (int)data_virt_addr
, (unsigned int)data_virt_addr
, (int)data_size
);
847 data_real_offset
= data_virt_addr
- d
->pe_cur_section_virt_addr
+ d
->pe_cur_section_data_offset
;
848 de_dbg(c
, "data offset in file: %d",
849 (int)data_real_offset
);
851 fi
= de_finfo_create(c
);
853 if(d
->pe_cur_name_offset
) {
854 de_finfo_set_name_from_pe_string(c
, fi
, c
->infile
, d
->pe_cur_name_offset
);
857 do_ne_pe_extract_resource(c
, d
, type_id
, d
->cur_rsrc_type_info
, data_real_offset
, data_size
, fi
);
859 de_finfo_destroy(c
, fi
);
860 de_dbg_indent(c
, -1);
863 static void do_pe_resource_dir_table(deark
*c
, lctx
*d
, i64 rel_pos
, int level
);
865 static void do_pe_resource_node(deark
*c
, lctx
*d
, i64 rel_pos
, int level
)
869 int has_name
, is_branch_node
;
872 orig_indent
= c
->dbg_indent_amount
;
874 d
->rsrc_item_count
++;
875 if(d
->rsrc_item_count
>MAX_RESOURCES
) {
876 de_err(c
, "Too many resources.");
883 name_or_id
= (u32
)de_getu32le(d
->pe_cur_base_addr
+rel_pos
);
884 if(name_or_id
& 0x80000000U
) {
886 name_or_id
-= 0x80000000U
;
888 next_offset
= de_getu32le(d
->pe_cur_base_addr
+rel_pos
+4);
889 if(next_offset
& 0x80000000U
) {
891 next_offset
-= 0x80000000U
;
895 d
->cur_rsrc_type
= name_or_id
;
896 d
->cur_rsrc_type_info
= get_rsrc_type_info((u32
)d
->cur_rsrc_type
);
899 de_dbg(c
, "level %d node at %d(%d) id=%d next-offset=%d is-named=%d is-branch=%d",
900 level
, (int)(d
->pe_cur_base_addr
+rel_pos
), (int)rel_pos
,
901 (int)name_or_id
, (int)next_offset
, has_name
, is_branch_node
);
904 if(!ne_pe_resource_type_is_supported(c
, d
, d
->cur_rsrc_type
)) {
905 const char *rsrcname
;
906 if(d
->cur_rsrc_type_info
&& d
->cur_rsrc_type_info
->name
)
907 rsrcname
= d
->cur_rsrc_type_info
->name
;
911 // We don't support this type of resource, so don't go down this path.
912 de_dbg(c
, "resource type %d (%s) not supported", (int)d
->cur_rsrc_type
, rsrcname
);
916 // If a resource has a name (at level 2), keep track of it so we can
917 // use it in the filename.
920 d
->pe_cur_name_offset
= d
->pe_cur_section_data_offset
+ name_or_id
;
921 de_dbg(c
, "resource name at %d", (int)d
->pe_cur_name_offset
);
924 d
->pe_cur_name_offset
= 0;
928 d
->pe_cur_name_offset
= 0;
931 // If high bit is 1, we need to go deeper.
933 do_pe_resource_dir_table(c
, d
, next_offset
, level
+1);
936 do_pe_resource_data_entry(c
, d
, next_offset
);
940 c
->dbg_indent_amount
= orig_indent
;
943 static void do_pe_resource_dir_table(deark
*c
, lctx
*d
, i64 rel_pos
, int level
)
945 i64 named_node_count
;
946 i64 unnamed_node_count
;
950 // 16-byte "Resource node header" a.k.a "Resource directory table"
953 de_warn(c
, "Resource tree too deep");
957 de_dbg(c
, "resource directory table at %d(%d), level=%d",
958 (unsigned int)(d
->pe_cur_base_addr
+rel_pos
), (unsigned int)rel_pos
, level
);
960 named_node_count
= de_getu16le(d
->pe_cur_base_addr
+rel_pos
+12);
961 unnamed_node_count
= de_getu16le(d
->pe_cur_base_addr
+rel_pos
+14);
962 de_dbg(c
, "number of node entries: named=%d, unnamed=%d", (unsigned int)named_node_count
,
963 (unsigned int)unnamed_node_count
);
965 node_count
= named_node_count
+ unnamed_node_count
;
967 // An array of 8-byte "Resource node entries" follows the Resource node header.
968 for(i
=0; i
<node_count
; i
++) {
969 do_pe_resource_node(c
, d
, rel_pos
+16+8*i
, level
);
973 static void do_pe_resource_section(deark
*c
, lctx
*d
, i64 pos
, i64 len
)
975 d
->pe_cur_base_addr
= pos
;
976 d
->rsrc_item_count
= 0;
977 do_pe_resource_dir_table(c
, d
, 0, 1);
980 static void do_pe_section_header(deark
*c
, lctx
*d
, i64 section_index
, i64 pos
)
982 i64 section_data_size
;
983 struct de_stringreaderdata
*srd
= NULL
;
985 de_dbg(c
, "section[%d] header at %d", (int)section_index
, (unsigned int)pos
);
988 // Section name: "An 8-byte, null-padded UTF-8 encoded string"
989 srd
= dbuf_read_string(c
->infile
, pos
, 8, 8, DE_CONVFLAG_STOP_AT_NUL
, DE_ENCODING_UTF8
);
990 de_dbg(c
, "section name: \"%s\"", ucstring_getpsz(srd
->str
));
992 d
->pe_cur_section_virt_addr
= de_getu32le(pos
+12);
993 section_data_size
= de_getu32le(pos
+16);
994 d
->pe_cur_section_data_offset
= de_getu32le(pos
+20);
996 de_dbg(c
, "section virt. addr=%d (0x%08x)", (int)d
->pe_cur_section_virt_addr
, (unsigned int)d
->pe_cur_section_virt_addr
);
997 de_dbg(c
, "section data offset=%d, size=%d", (int)d
->pe_cur_section_data_offset
, (int)section_data_size
);
999 if(!de_strcmp(srd
->sz
, ".rsrc")) {
1000 do_pe_resource_section(c
, d
, d
->pe_cur_section_data_offset
, section_data_size
);
1003 de_destroy_stringreaderdata(c
, srd
);
1004 de_dbg_indent(c
, -1);
1007 static void do_pe_section_table(deark
*c
, lctx
*d
)
1012 pos
= d
->pe_sections_offset
;
1013 de_dbg(c
, "section table at %d", (int)pos
);
1014 de_dbg_indent(c
, 1);
1015 for(i
=0; i
<d
->pe_number_of_sections
; i
++) {
1016 do_pe_section_header(c
, d
, i
, pos
+ 40*i
);
1018 de_dbg_indent(c
, -1);
1021 static void do_ne_one_nameinfo(deark
*c
, lctx
*d
, i64 npos
)
1029 de_finfo
*fi
= NULL
;
1030 int saved_indent_level
;
1032 de_dbg_indent_save(c
, &saved_indent_level
);
1033 rsrc_offset
= de_getu16le(npos
);
1034 if(d
->ne_align_shift
>0) rsrc_offset
<<= d
->ne_align_shift
;
1035 rsrc_size
= de_getu16le(npos
+2);
1036 if(d
->ne_align_shift
>0) rsrc_size
<<= d
->ne_align_shift
;
1038 de_dbg(c
, "NAMEINFO at %d, dpos=%d, dlen=%d", (int)npos
, (int)rsrc_offset
, (int)rsrc_size
);
1039 de_dbg_indent(c
, 1);
1043 x
= de_getu16le(npos
+6);
1050 rnNameOffset
= d
->ne_rsrc_tbl_offset
+ x
;
1054 de_dbg(c
, "id name offset: %d", (int)rnNameOffset
);
1057 de_dbg(c
, "id number: %d", (int)rnID
);
1060 if(!d
->ne_have_type
) goto done
;
1062 fi
= de_finfo_create(c
);
1065 // Names are prefixed with a single-byte length.
1066 x
= (i64
)de_getbyte(rnNameOffset
);
1068 de_ucstring
*rname
= NULL
;
1070 rname
= ucstring_create(c
);
1071 dbuf_read_to_ucstring(c
->infile
, rnNameOffset
+1, x
, rname
, 0, DE_ENCODING_ASCII
);
1072 de_dbg(c
, "resource name: \"%s\"", ucstring_getpsz(rname
));
1073 if(c
->filenames_from_file
)
1074 de_finfo_set_name_from_ucstring(c
, fi
, rname
, 0);
1075 ucstring_destroy(rname
);
1080 const char *rsrcname
;
1082 if(d
->ne_rsrc_type_info
&& d
->ne_rsrc_type_info
->name
)
1083 rsrcname
= d
->ne_rsrc_type_info
->name
;
1087 de_dbg(c
, "resource at %"I64_FMT
", len=%"I64_FMT
", type_id=%d (%s)", rsrc_offset
,
1088 rsrc_size
, (int)d
->ne_rsrc_type_id
, rsrcname
);
1089 de_dbg_indent(c
, 1);
1090 do_ne_pe_extract_resource(c
, d
, d
->ne_rsrc_type_id
, d
->ne_rsrc_type_info
,
1091 rsrc_offset
, rsrc_size
, fi
);
1092 de_dbg_indent(c
, -1);
1096 de_dbg_indent_restore(c
, saved_indent_level
);
1097 de_finfo_destroy(c
, fi
);
1100 static void do_ne_rsrc_tbl(deark
*c
, lctx
*d
)
1108 i64 tot_resources
= 0;
1109 int saved_indent_level
;
1111 de_dbg_indent_save(c
, &saved_indent_level
);
1112 pos
= d
->ne_rsrc_tbl_offset
;
1114 de_dbg(c
, "resource table at %d", (int)pos
);
1115 de_dbg_indent(c
, 1);
1117 d
->ne_align_shift
= (unsigned int)de_getu16le(pos
);
1118 de_dbg(c
, "rscAlignShift: %u", d
->ne_align_shift
);
1120 if(d
->ne_align_shift
>24) {
1121 de_err(c
, "Unreasonable rscAlignShift setting");
1127 x
= de_getu16le(pos
);
1129 // A "type_id" of 0 marks the end of the array
1130 de_dbg(c
, "end of TYPEINFO array found at %d", (int)pos
);
1133 de_dbg(c
, "TYPEINFO #%d at %d", (int)i
, (int)pos
);
1134 de_dbg_indent(c
, 1);
1137 d
->ne_rsrc_type_id
= (u32
)(x
-0x8000);
1138 d
->ne_rsrc_type_info
= get_rsrc_type_info(d
->ne_rsrc_type_id
);
1139 d
->ne_have_type
= 1;
1142 // x represents a relative offset to a name in rscResourceNames.
1143 // TODO: Could the name ever be a standard type (e.g. "ICON"), that
1144 // we ought to support?
1145 d
->ne_rsrc_type_id
= 0;
1146 d
->ne_rsrc_type_info
= NULL
;
1147 d
->ne_have_type
= 0;
1148 // name_offset = d->ne_rsrc_tbl_offset + x;
1151 rsrc_count
= de_getu16le(pos
+2);
1153 de_dbg(c
, "resource type=%d, count=%d", (int)d
->ne_rsrc_type_id
, (int)rsrc_count
);
1155 de_dbg(c
, "resource type=?, count=%d", (int)rsrc_count
);
1157 tot_resources
+= rsrc_count
;
1159 if(tot_resources
>MAX_RESOURCES
) {
1160 de_err(c
, "Too many resources, or invalid resource table.");
1164 // Read the array of NAMEINFO structures.
1165 // (NAMEINFO seems like a misnomer to me. It contains data, not names.)
1166 for(j
=0; j
<rsrc_count
; j
++) {
1167 npos
= pos
+8 + j
*12;
1168 do_ne_one_nameinfo(c
, d
, npos
);
1171 de_dbg_indent(c
, -1);
1172 pos
+= 8 + 12*rsrc_count
;
1176 de_dbg_indent(c
, -1);
1179 de_dbg_indent_restore(c
, saved_indent_level
);
1182 // Sniff the resource data, and return a suitable filename extension.
1183 // Or NULL, if unidentified.
1184 static const char *identify_lx_rsrc(deark
*c
, lctx
*d
, i64 pos
, i64 len
)
1189 if(len
<16) return NULL
;
1190 de_read(buf
, pos
, 2);
1191 if(!de_memcmp(buf
, "BA", 2)) {
1192 // Bitmap Array container format. Read the real type.
1193 de_read(buf
, pos
+14, 2);
1197 if(!de_memcmp(buf
, "BM", 2)) {
1198 return is_ba
? "ba.bmp" : "bmp";
1200 if(!de_memcmp(buf
, "CI", 2) || !de_memcmp(buf
, "IC", 2)) {
1201 return is_ba
? "ba.os2.ico" : "os2.ico";
1203 if(!de_memcmp(buf
, "CP", 2) || !de_memcmp(buf
, "PT", 2)) {
1204 return is_ba
? "ba.ptr" : "ptr";
1209 // Extract a resource from an LX file, given the information from an Object Table
1211 static void do_lx_rsrc(deark
*c
, lctx
*d
,
1212 i64 obj_num
, i64 rsrc_offset
, i64 rsrc_size
, i64 rsrc_type
)
1216 i64 reloc_base_addr
;
1218 i64 page_table_index
;
1219 i64 page_table_entries
;
1220 i64 rsrc_offset_real
;
1221 i64 pg_data_offset_raw
;
1225 if(obj_num
<1 || obj_num
>d
->lx_object_tbl_entries
) {
1226 de_err(c
, "Invalid object number (%d).", (int)obj_num
);
1230 // Read the Object Table
1231 lpos
= d
->lx_object_tbl_offset
+ 24*(obj_num
-1);
1232 de_dbg(c
, "LX object table entry at %d", (int)lpos
);
1234 vsize
= de_getu32le(lpos
);
1235 reloc_base_addr
= de_getu32le(lpos
+4);
1236 flags
= de_getu32le(lpos
+8);
1237 page_table_index
= de_getu32le(lpos
+12);
1238 page_table_entries
= de_getu32le(lpos
+16);
1239 de_dbg(c
, "object #%d: vsize=%d raddr=%d flags=0x%x pti=%d pte=%d", (int)obj_num
,
1240 (int)vsize
, (int)reloc_base_addr
, (unsigned int)flags
, (int)page_table_index
,
1241 (int)page_table_entries
);
1243 if(page_table_index
<1) return;
1245 // Now read the Object Page table
1246 lpos
= d
->lx_object_page_tbl_offset
+ 8*(page_table_index
-1);
1247 de_dbg(c
, "LX page table entry at %d", (int)lpos
);
1249 pg_data_offset_raw
= de_getu32le(lpos
);
1250 //data_size = de_getu16le(lpos+4);
1252 rsrc_offset_real
= pg_data_offset_raw
;
1253 if(d
->lx_page_offset_shift
> 0 ) {
1254 rsrc_offset_real
<<= (unsigned int)d
->lx_page_offset_shift
;
1256 rsrc_offset_real
+= d
->lx_data_pages_offset
;
1257 rsrc_offset_real
+= rsrc_offset
;
1258 de_dbg(c
, "resource offset: %d", (int)rsrc_offset_real
);
1261 // TODO: Support other types of resources.
1262 case 1: // Icon or cursor (?)
1263 case 2: // Bitmap (?)
1264 ext
= identify_lx_rsrc(c
, d
, rsrc_offset_real
, rsrc_size
);
1266 // TODO: This assumes the resource is stored contiguously in the file, but
1267 // for all I know that isn't always the case.
1269 // Unlike in NE and PE format, it seems that image resources in LX files
1270 // include the BITMAPFILEHEADER. That makes it easy.
1271 dbuf_create_file_from_slice(c
->infile
, rsrc_offset_real
, rsrc_size
, ext
, NULL
, 0);
1276 static void do_lx_or_le_rsrc_tbl(deark
*c
, lctx
*d
)
1286 de_dbg(c
, "%s resource table at %d", d
->fmt
==EXE_FMT_LE
?"LE":"LX", (int)d
->lx_rsrc_tbl_offset
);
1287 if(d
->lx_rsrc_tbl_entries
>MAX_RESOURCES
) {
1288 de_err(c
, "Too many resources.");
1292 for(i
=0; i
<d
->lx_rsrc_tbl_entries
; i
++) {
1293 lpos
= d
->lx_rsrc_tbl_offset
+ 14*i
;
1295 type_id
= de_getu16le(lpos
);
1296 name_id
= de_getu16le(lpos
+2);
1297 rsrc_size
= de_getu32le(lpos
+4);
1298 rsrc_object
= de_getu16le(lpos
+8);
1299 rsrc_offset
= de_getu32le(lpos
+10);
1301 de_dbg(c
, "resource #%d: type=%d name=%d size=%d obj=%d offset=%d", (int)i
,
1302 (int)type_id
, (int)name_id
, (int)rsrc_size
, (int)rsrc_object
, (int)rsrc_offset
);
1304 de_dbg_indent(c
, 1);
1305 do_lx_rsrc(c
, d
, rsrc_object
, rsrc_offset
, rsrc_size
, type_id
);
1306 de_dbg_indent(c
, -1);
1310 static void de_run_exe(deark
*c
, de_module_params
*mparams
)
1315 d
= de_malloc(c
, sizeof(lctx
));
1317 do_fileheader(c
, d
);
1319 if((d
->fmt
==EXE_FMT_PE32
|| d
->fmt
==EXE_FMT_PE32PLUS
) && d
->pe_sections_offset
>0) {
1320 do_pe_section_table(c
, d
);
1322 else if(d
->fmt
==EXE_FMT_NE
&& d
->ne_rsrc_tbl_offset
>0) {
1323 do_ne_rsrc_tbl(c
, d
);
1325 else if((d
->fmt
==EXE_FMT_LX
|| d
->fmt
==EXE_FMT_LE
) && d
->lx_rsrc_tbl_offset
>0) {
1326 do_lx_or_le_rsrc_tbl(c
, d
);
1329 if(c
->detection_data
&& c
->detection_data
->zip_eocd_looked_for
) {
1330 // Note: It isn't necessarily possible to get here - It depends on the details
1331 // of how other modules' identify() functions work.
1332 zip_eocd_found
= (int)c
->detection_data
->zip_eocd_found
;
1335 i64 zip_eocd_pos
= 0;
1336 zip_eocd_found
= fmtutil_find_zip_eocd(c
, c
->infile
, &zip_eocd_pos
);
1338 if(zip_eocd_found
) {
1339 de_info(c
, "Note: This might be a self-extracting ZIP file (try \"-m zip\").");
1345 static int de_identify_exe(deark
*c
)
1350 if(buf
[0]=='M' && buf
[1]=='Z') return 80;
1354 void de_module_exe(deark
*c
, struct deark_module_info
*mi
)
1357 mi
->desc
= "EXE executable (PE, NE, etc.)";
1358 mi
->run_fn
= de_run_exe
;
1359 mi
->identify_fn
= de_identify_exe
;