nrg: Improved support for v2
[deark.git] / modules / exe.c
blob37bad9b1a2f7828a57bea8da8e85d1f9d6f348df
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);
11 #define EXE_FMT_DOS 1
12 #define EXE_FMT_NE 2
13 #define EXE_FMT_PE32 3
14 #define EXE_FMT_PE32PLUS 4
15 #define EXE_FMT_LX 5
16 #define EXE_FMT_LE 6
18 #define MAX_RESOURCES 10000
20 #define DE_RT_CURSOR 1
21 #define DE_RT_BITMAP 2
22 #define DE_RT_ICON 3
23 #define DE_RT_FONTDIR 7
24 #define DE_RT_FONT 8
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 {
34 int fmt;
35 i64 ext_header_offset;
37 i64 ne_rsrc_tbl_offset;
38 unsigned int ne_align_shift;
39 int ne_have_type;
40 u32 ne_rsrc_type_id;
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;
51 i64 pe_opt_hdr_size;
52 i64 pe_sections_offset;
53 i64 pe_number_of_sections;
55 // File offset where the resources start. Some addresses are relative
56 // to this.
57 i64 pe_cur_base_addr;
59 i64 pe_cur_section_virt_addr;
60 i64 pe_cur_section_data_offset;
62 i64 pe_cur_name_offset; // 0 if no name
64 u32 cur_rsrc_type;
65 const struct rsrc_type_info_struct *cur_rsrc_type_info;
67 i64 rsrc_item_count;
68 } lctx;
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 {
75 u32 id;
76 u8 flags;
77 const char *name;
78 rsrc_decoder_fn decoder_fn;
81 static void do_certificate(deark *c, lctx *d, i64 pos1, i64 len)
83 i64 dlen;
84 i64 revision;
85 i64 certtype;
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);
91 de_dbg_indent(c, 1);
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) {
100 const char *ext;
101 if(certtype==2) ext="p7b";
102 else ext="crt";
103 dbuf_create_file_from_slice(c->infile, pos1+8, dlen-8, ext, NULL, 0);
105 done:
106 de_dbg_indent(c, -1);
110 static void do_opt_coff_data_dirs(deark *c, lctx *d, i64 pos)
112 i64 rsrc_tbl_rva;
113 i64 rsrc_tbl_size;
114 i64 pe_security_pos;
115 i64 pe_security_size;
117 de_dbg(c, "COFF/PE optional header (data directories) at %d", (int)pos);
118 de_dbg_indent(c, 1);
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,
123 (int)rsrc_tbl_size);
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) {
130 de_dbg_indent(c, 1);
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)
140 switch(subsystem) {
141 case 2: return " (Windows GUI)";
142 case 3: return " (console)";
144 return "";
147 static void do_opt_coff_nt_header(deark *c, lctx *d, i64 pos)
149 i64 x;
150 i64 subsystem;
152 de_dbg(c, "COFF/PE optional header (Windows NT) at %d", (int)pos);
153 de_dbg_indent(c, 1);
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)
166 i64 base_offset;
167 i64 subsystem;
169 de_dbg(c, "COFF/PE32+ optional header (Windows NT) at %d", (int)pos);
170 de_dbg_indent(c, 1);
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)
183 i64 sig;
184 i64 coff_opt_hdr_size;
186 de_dbg(c, "COFF/PE optional header at %d, size=%d", (int)pos, (int)len);
187 de_dbg_indent(c, 1);
189 sig = de_getu16le(pos);
190 de_dbg(c, "signature: 0x%04x", (int)sig);
192 if(sig==0x010b)
193 coff_opt_hdr_size = 28;
194 else
195 coff_opt_hdr_size = 24;
197 if(sig==0x010b) {
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");
212 else {
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));
234 ucstring_destroy(s);
237 static const char *get_machine_type_name(unsigned int n)
239 size_t i;
240 struct mtn_struct { unsigned int id; const char *name; };
241 static const struct mtn_struct mtn_arr[] = {
242 { 0x0000, "neutral" },
243 { 0x014c, "386+" },
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" },
261 { 0x8664, "x64" },
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;
271 return "?";
274 static void do_Rich_segment(deark *c, lctx *d)
276 i64 segment_start;
277 i64 segment_end;
278 i64 sig_pos;
279 i64 pos;
280 i64 p;
281 i64 k;
282 u32 n;
283 u32 key;
284 i64 num_entries;
286 segment_start = 128;
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
292 // the Rich segment.
293 // Based on limited research, the Rich signature usually starts 16, 24, or 32
294 // bytes before the "PE" signature.
295 sig_pos = 0;
296 for(p = segment_end-8; p >= segment_start+16; p -= 8 ) {
297 n = (u32)de_getu32le(p);
298 if(n==0x68636952U) { // "Rich"
299 sig_pos = p;
300 break;
303 if(sig_pos==0) {
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?
315 return;
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));
322 de_dbg_indent(c, 1);
324 pos = segment_start + 16;
325 num_entries = (sig_pos - pos)/8;
326 for(k=0; k<num_entries; k++) {
327 u32 id_and_value;
328 u32 id;
329 u32 value;
330 u32 use_count;
332 id_and_value = (u32)de_getu32le(pos+8*k);
333 use_count = (u32)de_getu32le(pos+8*k+4);
334 id_and_value ^= key;
335 use_count ^= key;
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)
350 unsigned int arch;
351 i64 n;
353 de_dbg(c, "PE header at %d", (int)d->ext_header_offset);
354 de_dbg_indent(c, 1);
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);
384 switch(flags&0x03) {
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,
398 ucstring_getpsz(s));
400 ucstring_destroy(s);
403 static void do_ne_app_flags(deark *c, lctx *d, u8 flags)
405 de_ucstring *s = NULL;
406 s = ucstring_create(c);
408 switch(flags&0x07) {
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,
418 ucstring_getpsz(s));
420 ucstring_destroy(s);
423 static void do_ne_ext_header(deark *c, lctx *d, i64 pos)
425 u8 target_os;
426 const char *desc;
427 u8 b1, b2;
429 de_dbg(c, "NE extended header at %d", (int)pos);
430 de_dbg_indent(c, 1);
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);
449 switch(target_os) {
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;
455 default: desc="?";
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)
464 i64 x1, x2;
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);
470 if(x1!=0 || x2!=0) {
471 de_err(c, "Unsupported byte order.");
472 return;
475 if(d->fmt==EXE_FMT_LE) {
476 x1 = de_getu32le(pos+0x2c);
477 de_dbg(c, "bytes on last page: %d", (int)x1);
479 else {
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)
504 u8 buf[4];
506 if(d->ext_header_offset == 0 || d->ext_header_offset >= c->infile->len) {
507 // Give up if ext_header_offset is obviously bad.
508 goto done;
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");
521 d->fmt = EXE_FMT_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");
526 d->fmt = EXE_FMT_LX;
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");
531 d->fmt = EXE_FMT_LE;
532 do_lx_or_le_ext_header(c, d, d->ext_header_offset);
535 done:
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;
551 else {
552 d->ext_header_offset = de_getu32le(60);
553 de_dbg(c, "extended header offset: %d", (int)d->ext_header_offset);
554 do_ext_header(c, d);
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);
563 de_dbg_indent(c, 1);
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);
568 de_free(c, mparams);
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)
574 if(len<12) return;
576 if((d->fmt==EXE_FMT_NE) && (de_getbyte(pos)==0x02)) {
577 do_decode_ddb(c, d, pos, len, fi);
578 return;
581 de_dbg_indent(c, 1);
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)
589 dbuf *f;
590 i64 w, h;
591 i64 ncolors;
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");
600 return;
603 if(bi.file_format==DE_BMPINFO_FMT_PNG) {
604 dbuf_create_file_from_slice(c->infile, pos, len, "png", fi, 0);
605 return;
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
615 w = bi.width;
616 if(w>255) w=0;
617 h = bi.height;
618 if(h>255) h=0;
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.
624 len = bi.total_size;
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);
631 if(is_cur) {
632 dbuf_writebyte(f, 0);
633 dbuf_writeu16le(f, hotspot_x);
634 dbuf_writeu16le(f, hotspot_y);
636 else {
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);
644 dbuf_close(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;
652 if(len<8) return;
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 ||
658 firstword==0x0203))
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);
665 if(fourthword!=0) {
666 dbuf_create_file_from_slice(c->infile, pos, len, "win1.cur", fi, 0);
667 return;
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);
688 return;
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;
702 i64 dfFace;
703 de_ucstring *s = NULL;
705 if(!fi) goto done;
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);
719 done:
720 ucstring_destroy(s);
723 static void do_extract_FONT(deark *c, lctx *d, i64 pos, i64 len, de_finfo *fi)
725 i64 fntlen;
727 if(len<6) return;
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) {
731 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)
768 size_t i;
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];
775 return NULL;
778 static int ne_pe_resource_type_is_supported(deark *c, lctx *d, u32 type_id)
780 switch(type_id) {
781 case DE_RT_CURSOR:
782 case DE_RT_BITMAP:
783 case DE_RT_ICON:
784 case DE_RT_FONT:
785 case DE_RT_MANIFEST:
786 return 1;
788 return 0;
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);
799 return;
803 static void de_finfo_set_name_from_pe_string(deark *c, de_finfo *fi, dbuf *f,
804 i64 pos)
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);
819 done:
820 ucstring_destroy(fname);
823 static void do_pe_resource_data_entry(deark *c, lctx *d, i64 rel_pos)
825 i64 data_size;
826 i64 data_virt_addr;
827 i64 data_real_offset;
828 u32 type_id;
829 de_finfo *fi = NULL;
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;
835 else
836 rsrcname = "?";
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);
840 de_dbg_indent(c, 1);
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)
867 u32 name_or_id;
868 i64 next_offset;
869 int has_name, is_branch_node;
870 int orig_indent;
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.");
877 goto done;
880 has_name = 0;
881 is_branch_node = 0;
883 name_or_id = (u32)de_getu32le(d->pe_cur_base_addr+rel_pos);
884 if(name_or_id & 0x80000000U) {
885 has_name = 1;
886 name_or_id -= 0x80000000U;
888 next_offset = de_getu32le(d->pe_cur_base_addr+rel_pos+4);
889 if(next_offset & 0x80000000U) {
890 is_branch_node = 1;
891 next_offset -= 0x80000000U;
894 if(level==1) {
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);
902 de_dbg_indent(c, 1);
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;
908 else
909 rsrcname = "?";
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);
913 goto done;
916 // If a resource has a name (at level 2), keep track of it so we can
917 // use it in the filename.
918 if(level==2) {
919 if(has_name) {
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);
923 else {
924 d->pe_cur_name_offset = 0;
927 else if(level<2) {
928 d->pe_cur_name_offset = 0;
931 // If high bit is 1, we need to go deeper.
932 if(is_branch_node) {
933 do_pe_resource_dir_table(c, d, next_offset, level+1);
935 else {
936 do_pe_resource_data_entry(c, d, next_offset);
939 done:
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;
947 i64 node_count;
948 i64 i;
950 // 16-byte "Resource node header" a.k.a "Resource directory table"
952 if(level>3) {
953 de_warn(c, "Resource tree too deep");
954 return;
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);
986 de_dbg_indent(c, 1);
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)
1009 i64 pos;
1010 i64 i;
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)
1023 i64 rsrc_offset;
1024 i64 rsrc_size;
1025 i64 is_named;
1026 i64 rnID;
1027 i64 rnNameOffset;
1028 i64 x;
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);
1041 rnID = 0;
1042 rnNameOffset = 0;
1043 x = de_getu16le(npos+6);
1044 if(x&0x8000) {
1045 is_named = 0;
1046 rnID = x-0x8000;
1048 else {
1049 is_named = 1;
1050 rnNameOffset = d->ne_rsrc_tbl_offset + x;
1053 if(is_named) {
1054 de_dbg(c, "id name offset: %d", (int)rnNameOffset);
1056 else {
1057 de_dbg(c, "id number: %d", (int)rnID);
1060 if(!d->ne_have_type) goto done;
1062 fi = de_finfo_create(c);
1064 if(is_named) {
1065 // Names are prefixed with a single-byte length.
1066 x = (i64)de_getbyte(rnNameOffset);
1067 if(x>0) {
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);
1079 if(rsrc_size>0) {
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;
1084 else
1085 rsrcname = "?";
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);
1095 done:
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)
1102 i64 pos;
1103 i64 npos;
1104 i64 x;
1105 i64 i;
1106 i64 j;
1107 i64 rsrc_count;
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);
1119 pos += 2;
1120 if(d->ne_align_shift>24) {
1121 de_err(c, "Unreasonable rscAlignShift setting");
1122 goto done;
1125 i = 0;
1126 while(1) {
1127 x = de_getu16le(pos);
1128 if(x==0) {
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);
1131 goto done;
1133 de_dbg(c, "TYPEINFO #%d at %d", (int)i, (int)pos);
1134 de_dbg_indent(c, 1);
1136 if(x & 0x8000) {
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;
1141 else {
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);
1152 if(d->ne_have_type)
1153 de_dbg(c, "resource type=%d, count=%d", (int)d->ne_rsrc_type_id, (int)rsrc_count);
1154 else
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.");
1161 goto done;
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;
1173 i++;
1176 de_dbg_indent(c, -1);
1178 done:
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)
1186 u8 buf[2];
1187 int is_ba = 0;
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);
1194 is_ba = 1;
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";
1206 return NULL;
1209 // Extract a resource from an LX file, given the information from an Object Table
1210 // entry.
1211 static void do_lx_rsrc(deark *c, lctx *d,
1212 i64 obj_num, i64 rsrc_offset, i64 rsrc_size, i64 rsrc_type)
1214 i64 lpos;
1215 i64 vsize;
1216 i64 reloc_base_addr;
1217 i64 flags;
1218 i64 page_table_index;
1219 i64 page_table_entries;
1220 i64 rsrc_offset_real;
1221 i64 pg_data_offset_raw;
1222 const char *ext;
1223 //i64 data_size;
1225 if(obj_num<1 || obj_num>d->lx_object_tbl_entries) {
1226 de_err(c, "Invalid object number (%d).", (int)obj_num);
1227 return;
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);
1260 switch(rsrc_type) {
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);
1265 if(!ext) break;
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);
1272 break;
1276 static void do_lx_or_le_rsrc_tbl(deark *c, lctx *d)
1278 i64 i;
1279 i64 lpos;
1280 i64 type_id;
1281 i64 name_id;
1282 i64 rsrc_size;
1283 i64 rsrc_object;
1284 i64 rsrc_offset;
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.");
1289 return;
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)
1312 lctx *d = NULL;
1313 int zip_eocd_found;
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;
1334 else {
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\").");
1342 de_free(c, d);
1345 static int de_identify_exe(deark *c)
1347 u8 buf[2];
1348 de_read(buf, 0, 2);
1350 if(buf[0]=='M' && buf[1]=='Z') return 80;
1351 return 0;
1354 void de_module_exe(deark *c, struct deark_module_info *mi)
1356 mi->id = "exe";
1357 mi->desc = "EXE executable (PE, NE, etc.)";
1358 mi->run_fn = de_run_exe;
1359 mi->identify_fn = de_identify_exe;