pedump 0.4.8 → 0.4.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,167 @@
1
+ class PEdump; end
2
+
3
+ class PEdump::NE
4
+ class VS_VERSIONINFO < PEdump.create_struct( 'v2a16',
5
+ :wLength,
6
+ :wValueLength,
7
+ :szKey, # ASCII string "VS_VERSION_INFO".
8
+ :Padding1,
9
+ # manual:
10
+ :Value, # VS_FIXEDFILEINFO
11
+ :Padding2,
12
+ :Children
13
+ )
14
+ def self.read f, size = SIZE
15
+ super.tap do |vi|
16
+ vi.szKey.chomp!("\x00")
17
+ vi.Padding1 = f.tell%4 > 0 ? f.read(4 - f.tell%4) : nil
18
+ vi.Value = VS_FIXEDFILEINFO.read(f,vi.wValueLength)
19
+ # As many zero words as necessary to align the Children member on a 32-bit boundary.
20
+ # These bytes are not included in wValueLength. This member is optional.
21
+ vi.Padding2 = f.tell%4 > 0 ? f.read(4 - f.tell%4) : nil
22
+ vi.Children = [] # An array of zero or one StringFileInfo structures,
23
+ # and zero or one VarFileInfo structures
24
+
25
+ 2.times do
26
+ pos = f.tell
27
+ f.seek(pos+4) # seek 4 bytes forward
28
+ t = f.read(3)
29
+ f.seek(pos) # return back
30
+ case t
31
+ when "Var"
32
+ vi.Children << VarFileInfo.read(f)
33
+ when "Str"
34
+ vi.Children << StringFileInfo.read(f)
35
+ else
36
+ PEdump.logger.warn "[?] invalid VS_VERSIONINFO child type #{t.inspect}"
37
+ break
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ class VS_FIXEDFILEINFO < PEdump.create_struct( 'V13',
45
+ :dwSignature,
46
+ :dwStrucVersion,
47
+ :dwFileVersionMS,
48
+ :dwFileVersionLS,
49
+ :dwProductVersionMS,
50
+ :dwProductVersionLS,
51
+ :dwFileFlagsMask,
52
+ :dwFileFlags,
53
+ :dwFileOS,
54
+ :dwFileType,
55
+ :dwFileSubtype,
56
+ :dwFileDateMS,
57
+ :dwFileDateLS,
58
+ # manual:
59
+ :valid
60
+ )
61
+ def self.read f, size = SIZE
62
+ super.tap do |ffi|
63
+ ffi.valid = (ffi.dwSignature == 0xFEEF04BD)
64
+ end
65
+ end
66
+ end
67
+
68
+ class StringFileInfo < PEdump.create_struct( 'v2a15',
69
+ :wLength,
70
+ :wValueLength, # always 0
71
+ :szKey, # The ASCII string "StringFileInfo"
72
+ :Padding, # As many zero words as necessary to align the Children member on a 32-bit boundary
73
+ :Children # An array of one or more StringTable structures
74
+ )
75
+ def self.read f, size = SIZE
76
+ pos0 = f.tell
77
+ super.tap do |x|
78
+ x.szKey.chomp!("\x00")
79
+ x.Padding = f.tell%4 > 0 ? f.read(4 - f.tell%4) : nil
80
+ x.Children = []
81
+ while !f.eof? && f.tell < pos0+x.wLength
82
+ x.Children << StringTable.read(f)
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ class StringTable < PEdump.create_struct( 'v2a9',
89
+ :wLength, # The length, in bytes, of this StringTable structure,
90
+ # including all structures indicated by the Children member.
91
+ :wValueLength, # always 0
92
+ :szKey, # An 8-digit hexadecimal number stored as a ASCII string
93
+ :Padding, # As many zero words as necessary to align the Children member on a 32-bit boundary
94
+ :Children # An array of one or more String structures.
95
+ )
96
+ def self.read f, size = SIZE
97
+ pos0 = f.tell
98
+ super.tap do |x|
99
+ x.szKey.chomp!("\x00")
100
+ x.Padding = f.tell%4 > 0 ? f.read(4 - f.tell%4) : nil
101
+ x.Children = []
102
+ while !f.eof? && f.tell < pos0+x.wLength
103
+ x.Children << VersionString.read(f)
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ class VersionString < PEdump.create_struct( 'v2',
110
+ :wLength, # The length, in bytes, of this String structure.
111
+ :wValueLength, # The size, in words, of the Value member
112
+ :szKey, # An arbitrary ASCII string
113
+ :Padding, # As many zero words as necessary to align the Value member on a 32-bit boundary
114
+ :Value # A zero-terminated string. See the szKey member description for more information
115
+ )
116
+ def self.read f, size = SIZE
117
+ pos = f.tell
118
+ super.tap do |x|
119
+ x.szKey = f.gets("\x00").to_s.chomp("\x00")
120
+ x.Padding = f.tell%4 > 0 ? f.read(4 - f.tell%4) : nil
121
+
122
+ value_len = [x.wValueLength*2, x.wLength - (f.tell-pos)].min
123
+ value_len = 0 if value_len < 0
124
+
125
+ cp = PEdump::NE.cp # XXX HACK
126
+
127
+ x.Value = f.read(value_len).to_s.chomp("\x00")
128
+ x.Value.force_encoding("CP#{cp}").encode!('UTF-8').sub!(/\u0000$/,'') rescue nil
129
+ if f.tell%4 > 0
130
+ f.read(4-f.tell%4) # undoc padding?
131
+ end
132
+ end
133
+ end
134
+ end
135
+
136
+ class VarFileInfo < PEdump.create_struct( 'v2a12',
137
+ :wLength,
138
+ :wValueLength, # always 0
139
+ :szKey, # The ASCII string "VarFileInfo"
140
+ :Padding, # As many zero words as necessary to align the Children member on a 32-bit boundary
141
+ :Children # Typically contains a list of languages that the application or DLL supports
142
+ )
143
+ def self.read f, size = SIZE
144
+ super.tap do |x|
145
+ x.szKey.chomp!("\x00")
146
+ x.Padding = f.tell%4 > 0 ? f.read(4 - f.tell%4) : nil
147
+ x.Children = Var.read(f)
148
+ end
149
+ end
150
+ end
151
+
152
+ class Var < PEdump.create_struct( 'v2a12',
153
+ :wLength,
154
+ :wValueLength, # The length, in bytes, of the Value member
155
+ :szKey, # The ASCII string "Translation"
156
+ :Padding, # As many zero words as necessary to align the Children member on a 32-bit boundary
157
+ :Value # An array of one or more values that are language and code page identifier pairs
158
+ )
159
+ def self.read f, size = SIZE
160
+ super.tap do |x|
161
+ x.szKey.chomp!("\x00")
162
+ x.Padding = f.tell%4 > 0 ? f.read(4 - f.tell%4) : nil
163
+ x.Value = f.read(x.wValueLength).unpack('v*')
164
+ end
165
+ end
166
+ end
167
+ end
data/lib/pedump/pe.rb CHANGED
@@ -27,12 +27,12 @@ class PEdump
27
27
 
28
28
  pe_offset = f.tell
29
29
  pe_sig = f.read 4
30
- logger.error "[!] 'NE' format is not supported!" if pe_sig == "NE\x00\x00"
30
+ #logger.error "[!] 'NE' format is not supported!" if pe_sig == "NE\x00\x00"
31
31
  if pe_sig != "PE\x00\x00"
32
32
  if force
33
33
  logger.warn "[?] no PE signature (want: 'PE\\x00\\x00', got: #{pe_sig.inspect})"
34
34
  else
35
- logger.error "[?] no PE signature (want: 'PE\\x00\\x00', got: #{pe_sig.inspect}). (not forced)"
35
+ logger.debug "[?] no PE signature (want: 'PE\\x00\\x00', got: #{pe_sig.inspect}). (not forced)"
36
36
  return nil
37
37
  end
38
38
  end
@@ -91,4 +91,22 @@ class PEdump
91
91
 
92
92
  def self.logger; PEdump.logger; end
93
93
  end
94
+
95
+ def pe f=@io
96
+ @pe ||=
97
+ begin
98
+ pe_offset = mz(f) && mz(f).lfanew
99
+ if pe_offset.nil?
100
+ logger.fatal "[!] NULL PE offset (e_lfanew). cannot continue."
101
+ nil
102
+ elsif pe_offset > f.size
103
+ logger.fatal "[!] PE offset beyond EOF. cannot continue."
104
+ nil
105
+ else
106
+ f.seek pe_offset
107
+ PE.read f, :force => @force
108
+ end
109
+ end
110
+ end
111
+
94
112
  end
@@ -1,7 +1,12 @@
1
1
  class PEdump
2
2
 
3
3
  def resource_directory f=@io
4
- @resource_directory ||= _read_resource_directory_tree(f)
4
+ @resource_directory ||=
5
+ if pe(f)
6
+ _read_resource_directory_tree(f)
7
+ elsif ne(f)
8
+ ne(f).resource_directory(f)
9
+ end
5
10
  end
6
11
 
7
12
  def _read_resource_directory_tree f
@@ -154,7 +159,6 @@ class PEdump
154
159
  end
155
160
  # XXX: check if readed strings summary length is less than resource data length
156
161
  when 'VERSION'
157
- require 'pedump/version_info'
158
162
  f.seek file_offset
159
163
  data << PEdump::VS_VERSIONINFO.read(f)
160
164
  end
@@ -319,7 +323,7 @@ class PEdump
319
323
  end
320
324
  end
321
325
 
322
- def _scan_resources f=@io, dir=nil
326
+ def _scan_pe_resources f=@io, dir=nil
323
327
  dir ||= resource_directory(f)
324
328
  return nil unless dir
325
329
  dir.entries.map do |entry|
@@ -333,12 +337,12 @@ class PEdump
333
337
  else
334
338
  entry.name
335
339
  end
336
- _scan_resources(f,entry.data).each do |res|
340
+ _scan_pe_resources(f,entry.data).each do |res|
337
341
  res.type = entry_type
338
342
  res.parse f
339
343
  end
340
344
  else
341
- _scan_resources(f,entry.data).each do |res|
345
+ _scan_pe_resources(f,entry.data).each do |res|
342
346
  res.name = res.name == "##{res.lang}" ? entry.name : "#{entry.name} / #{res.name}"
343
347
  res.id ||= entry.Name if entry.Name.is_a?(Numeric) && entry.Name < 0x8000_0000
344
348
  end
@@ -2,7 +2,7 @@ class PEdump
2
2
  module Version
3
3
  MAJOR = 0
4
4
  MINOR = 4
5
- PATCH = 8
5
+ PATCH = 9
6
6
  BUILD = nil
7
7
 
8
8
  STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join('.')
data/misc/nedump.c ADDED
@@ -0,0 +1,751 @@
1
+ /* Windows/DOS NE (New Executable) dumper
2
+ *
3
+ * Copyright (C) 2012 Daniel Collins <solemnwarning@solemnwarning.net>
4
+ * All rights reserved.
5
+ *
6
+ * Redistribution and use in source and binary forms, with or without
7
+ * modification, are permitted provided that the following conditions are met:
8
+ *
9
+ * * Redistributions of source code must retain the above copyright
10
+ * notice, this list of conditions and the following disclaimer.
11
+ *
12
+ * * Redistributions in binary form must reproduce the above copyright
13
+ * notice, this list of conditions and the following disclaimer in the
14
+ * documentation and/or other materials provided with the distribution.
15
+ *
16
+ * * Neither the name of the developer nor the names of its contributors
17
+ * may be used to endorse or promote products derived from this software
18
+ * without specific prior written permission.
19
+ *
20
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
21
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ * DISCLAIMED. IN NO EVENT SHALL THE DEVELOPER BE LIABLE FOR ANY DIRECT, INDIRECT,
24
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
26
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
28
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
+ */
31
+
32
+ /* Version history:
33
+ *
34
+ * 2012-01-12:
35
+ * Initial release
36
+ */
37
+
38
+ #include <stdio.h>
39
+ #include <unistd.h>
40
+ #include <stdlib.h>
41
+ #include <string.h>
42
+
43
+ #define FLAG_CAT(dest, src, cond) \
44
+ if(cond) { \
45
+ strcat(dest, dest[0] ? (" | " src) : src); \
46
+ }
47
+
48
+ #define MZ_MAGIC 0x5A4D
49
+
50
+ struct mz_header {
51
+ unsigned short magic;
52
+ unsigned short bytes_in_last_block;
53
+ unsigned short blocks_in_file;
54
+ unsigned short num_relocs;
55
+ unsigned short header_paragraphs;
56
+ unsigned short min_extra_paragraphs;
57
+ unsigned short max_extra_paragraphs;
58
+ unsigned short ss;
59
+ unsigned short sp;
60
+ unsigned short checksum;
61
+ unsigned short ip;
62
+ unsigned short cs;
63
+ unsigned short reloc_table_offset;
64
+ unsigned short overlay_number;
65
+ };
66
+
67
+ #define NE_MAGIC 0x454E
68
+
69
+ #define NE_FLAG_SINGLEDATA 0x0001
70
+ #define NE_FLAG_MULTIPLEDATA 0x0002
71
+ #define NE_FLAG_LIBRARY 0x8000
72
+
73
+ #define NE_EXE_WINDOWS 0x02
74
+
75
+ struct ne_header {
76
+ /* Offsets relative to beginning of NE header */
77
+
78
+ unsigned short magic;
79
+ unsigned char linker_ver;
80
+ unsigned char linker_rev;
81
+ unsigned short entry_table_offset;
82
+ unsigned short entry_table_size;
83
+ unsigned int whole_file_crc;
84
+ unsigned short flags;
85
+ unsigned short auto_data_segment;
86
+ unsigned short init_heap_size;
87
+ unsigned short init_stack_size;
88
+ unsigned short ip;
89
+ unsigned short cs;
90
+ unsigned short ss;
91
+ unsigned short sp;
92
+ unsigned short seg_table_entries;
93
+ unsigned short mod_ref_table_entries;
94
+ unsigned short non_res_name_table_entries;
95
+ unsigned short seg_table_offset;
96
+ unsigned short res_table_offset;
97
+ unsigned short res_name_table_offset;
98
+ unsigned short mod_ref_table_offset;
99
+ unsigned short import_table_offset;
100
+ unsigned int non_res_name_table_offset; /* Relative to start of file */
101
+ unsigned short entry_table_movable_entries;
102
+ unsigned short seg_sector_size_shift;
103
+ unsigned short res_table_entries;
104
+ unsigned char exe_type;
105
+ };
106
+
107
+ struct import_table {
108
+ unsigned char name_len;
109
+ char name[256];
110
+ };
111
+
112
+ struct export_table {
113
+ unsigned char name_len;
114
+ char name[256];
115
+
116
+ unsigned short ordinal;
117
+ } __attribute__((__packed__));
118
+
119
+ struct entry_bundle {
120
+ unsigned char bundle_entries;
121
+ unsigned char segment_indicator;
122
+ };
123
+
124
+ struct fixed_entry {
125
+ unsigned char flags;
126
+ unsigned short seg_offset;
127
+ } __attribute__((__packed__));
128
+
129
+ struct moveable_entry {
130
+ unsigned char flags;
131
+ unsigned short padding;
132
+ unsigned char seg_num;
133
+ unsigned short seg_offset;
134
+ } __attribute__((__packed__));
135
+
136
+ #define SEG_TYPE_MASK 0x0007
137
+ #define SEG_CODE 0x0000
138
+ #define SEG_DATA 0x0001
139
+ #define SEG_MOVEABLE 0x0010
140
+ #define SEG_PRELOAD 0x0040
141
+ #define SEG_RELOCINFO 0x0100
142
+ #define SEG_DISCARD 0xF000
143
+
144
+ struct segment_table {
145
+ unsigned short offset; /* Measured in sectors, zero means no data */
146
+ unsigned short size; /* Measured in bytes, zero means 64KiB */
147
+ unsigned short flags;
148
+ unsigned short alloc; /* Minimum allocation size, zero means 64KiB */
149
+ };
150
+
151
+ #define RELOC_LOBYTE 0x00
152
+ #define RELOC_SEGMENT 0x02
153
+ #define RELOC_FAR_ADDR 0x03
154
+ #define RELOC_OFFSET 0x05
155
+
156
+ #define RELOC_TARGET_MASK 0x03
157
+ #define RELOC_INTERNALREF 0x00
158
+ #define RELOC_IMPORTORDINAL 0x01
159
+ #define RELOC_IMPORTNAME 0x02
160
+ #define RELOC_OSFIXUP 0x03
161
+ #define RELOC_ADDITIVE 0x04
162
+
163
+ struct segment_reloc {
164
+ unsigned char src_type;
165
+ unsigned char flags;
166
+ unsigned short offset;
167
+
168
+ union {
169
+ struct {
170
+ unsigned char seg_num;
171
+ unsigned char zero;
172
+ unsigned short offset;
173
+ } internalref;
174
+
175
+ struct {
176
+ unsigned short mod_index;
177
+ unsigned short name_offset;
178
+ } importname;
179
+
180
+ struct {
181
+ unsigned short mod_index;
182
+ unsigned short ordinal;
183
+ } importordinal;
184
+
185
+ struct {
186
+ unsigned short fixup_type;
187
+ unsigned short zero;
188
+ } osfixup;
189
+
190
+ unsigned char target_b[4];
191
+ unsigned short target_w[2];
192
+ };
193
+ };
194
+
195
+ #define RESOURCE_HO 0x8000
196
+
197
+ struct resource_table_type {
198
+ unsigned short type_id;
199
+ unsigned short res_count;
200
+ unsigned int padding;
201
+ };
202
+
203
+ #define RESOURCE_MOVEABLE 0x0010
204
+ #define RESOURCE_PURE 0x0020
205
+ #define RESOURCE_PRELOAD 0x0040
206
+
207
+ struct resource_table_entry {
208
+ unsigned short offset;
209
+ unsigned short length;
210
+
211
+ unsigned short flags;
212
+ unsigned short res_id;
213
+
214
+ unsigned int padding;
215
+ };
216
+
217
+ struct import_entry {
218
+ char module[256];
219
+
220
+ char name[256];
221
+ int ordinal;
222
+
223
+ struct import_entry *next;
224
+ };
225
+
226
+ FILE *fh;
227
+
228
+ size_t read_data(char *buf, size_t offset, size_t size) {
229
+ if(fseek(fh, offset, SEEK_SET) == -1) {
230
+ return 0;
231
+ }
232
+
233
+ size_t s = 0, r;
234
+
235
+ while(s < size) {
236
+ if((r = fread(buf + s, 1, size - s, fh)) == 0) {
237
+ break;
238
+ }
239
+
240
+ s += r;
241
+ }
242
+
243
+ if(ferror(fh)) {
244
+ fprintf(stderr, "Read error\n");
245
+ exit(1);
246
+ }
247
+
248
+ return s;
249
+ }
250
+
251
+ void add_import(struct import_entry **list, const char *module, const char *name, int ordinal) {
252
+ while(*list && strcasecmp((*list)->module, module) < 0) {
253
+ list = &((*list)->next);
254
+ }
255
+
256
+ if(name) {
257
+ while(*list && strcasecmp((*list)->module, module) == 0 && strcasecmp((*list)->name, name) < 0) {
258
+ list = &((*list)->next);
259
+ }
260
+
261
+ if(*list && strcasecmp((*list)->name, name) == 0) {
262
+ return;
263
+ }
264
+ }else{
265
+ while(*list && strcasecmp((*list)->module, module) == 0 && (*list)->ordinal < ordinal) {
266
+ list = &((*list)->next);
267
+ }
268
+
269
+ if(*list && (*list)->ordinal == ordinal) {
270
+ return;
271
+ }
272
+ }
273
+
274
+ struct import_entry *import = malloc(sizeof(struct import_entry));
275
+ if(!import) {
276
+ fprintf(stderr, "Memory allocation failed\n");
277
+ exit(1);
278
+ }
279
+
280
+ strcpy(import->module, module);
281
+ strcpy(import->name, name ? name : "");
282
+ import->ordinal = ordinal;
283
+
284
+ import->next = *list;
285
+ *list = import;
286
+ }
287
+
288
+ void dump_imports(struct import_entry *imports) {
289
+ printf("Imported names:\n");
290
+
291
+ while(imports) {
292
+ if(imports->name[0]) {
293
+ printf("\t%s\t%s\n", imports->module, imports->name);
294
+ }else{
295
+ printf("\t%s\t@%d\n", imports->module, imports->ordinal);
296
+ }
297
+
298
+ imports = imports->next;
299
+ }
300
+ }
301
+
302
+ void free_imports(struct import_entry *imports) {
303
+ while(imports) {
304
+ struct import_entry *d = imports;
305
+ imports = imports->next;
306
+
307
+ free(d);
308
+ }
309
+ }
310
+
311
+ void dump_names(unsigned int res_off, unsigned int table_entries, unsigned int entry_table_off) {
312
+ unsigned int res_num = 0;
313
+ struct export_table exp;
314
+
315
+ while(res_num < table_entries && read_data((char*)&exp, res_off, sizeof(exp)) >= 2) {
316
+ if(exp.name_len == 0) {
317
+ break;
318
+ }
319
+
320
+ res_off += exp.name_len + 1;
321
+ read_data((char*)&(exp.ordinal), res_off, 2);
322
+ res_off += 2;
323
+
324
+ exp.name[exp.name_len] = '\0';
325
+
326
+ if(res_num++ == 0) {
327
+ continue;
328
+ }
329
+
330
+ /* Step through entry point table to get address */
331
+
332
+ char entry_txt[64] = "WARNING: Entry point not found";
333
+
334
+ unsigned int entry_off = entry_table_off, ordinal = 1, i;
335
+ struct entry_bundle bundle;
336
+
337
+ while(read_data((char*)&bundle, entry_off, sizeof(bundle)) >= 1 && bundle.bundle_entries && ordinal <= exp.ordinal) {
338
+ entry_off += sizeof(bundle);
339
+
340
+ if(bundle.segment_indicator == 0) {
341
+ ordinal += bundle.bundle_entries;
342
+ continue;
343
+ }
344
+
345
+ for(i = 0; i < bundle.bundle_entries; i++) {
346
+ unsigned short seg = 0, off;
347
+
348
+ if(bundle.segment_indicator == 0xFF) {
349
+ struct moveable_entry entry;
350
+ entry_off += read_data((char*)&entry, entry_off, sizeof(entry));
351
+
352
+ seg = entry.seg_num;
353
+ off = entry.seg_offset;
354
+ }else{
355
+ struct fixed_entry entry;
356
+ entry_off += read_data((char*)&entry, entry_off, sizeof(entry));
357
+
358
+ seg = bundle.segment_indicator;
359
+ off = entry.seg_offset;
360
+ }
361
+
362
+ if(ordinal++ == exp.ordinal && seg) {
363
+ sprintf(entry_txt, "Entry point segment: %hu, offset: 0x%04hX", seg, off);
364
+ break;
365
+ }
366
+ }
367
+ }
368
+
369
+ printf("\t%s\t@%hu\t; %s\n", exp.name, exp.ordinal, entry_txt);
370
+ }
371
+ }
372
+
373
+ void dump_ep_table(unsigned int entry_off) {
374
+ printf("Entry point table:\n");
375
+
376
+ unsigned int ordinal = 1, i;
377
+ struct entry_bundle bundle;
378
+
379
+ while(read_data((char*)&bundle, entry_off, sizeof(bundle)) >= 1 && bundle.bundle_entries) {
380
+ entry_off += sizeof(bundle);
381
+
382
+ if(bundle.segment_indicator == 0) {
383
+ ordinal += bundle.bundle_entries;
384
+ continue;
385
+ }
386
+
387
+ for(i = 0; i < bundle.bundle_entries; i++) {
388
+ if(bundle.segment_indicator == 0xFF) {
389
+ struct moveable_entry entry;
390
+ entry_off += read_data((char*)&entry, entry_off, sizeof(entry));
391
+
392
+ printf("\tOrdinal:\t%u\n", ordinal);
393
+ printf("\tType:\t\tMoveable\n");
394
+ printf("\tSegment number:\t%u\n", (unsigned int)entry.seg_num);
395
+ printf("\tOffset:\t\t0x%04hX\n", entry.seg_offset);
396
+ printf("\tFlags:\t\t0x%02X\n\n", (unsigned int)entry.flags);
397
+ }else{
398
+ struct fixed_entry entry;
399
+ entry_off += read_data((char*)&entry, entry_off, sizeof(entry));
400
+
401
+ printf("\tOrdinal:\t%u\n", ordinal);
402
+ printf("\tType:\t\tFixed\n");
403
+ printf("\tSegment number:\t%u\n", (unsigned int)bundle.segment_indicator);
404
+ printf("\tOffset:\t\t0x%04hX\n", entry.seg_offset);
405
+ printf("\tFlags:\t\t0x%02X\n\n", (unsigned int)entry.flags);
406
+ }
407
+
408
+ ordinal++;
409
+ }
410
+ }
411
+ }
412
+
413
+ void get_resource_id(char *buf, unsigned int res_table_off, unsigned short id) {
414
+ if(id & RESOURCE_HO) {
415
+ sprintf(buf, "%hu", id & ~RESOURCE_HO);
416
+ }else{
417
+ struct import_table name;
418
+
419
+ read_data((char*)&name, res_table_off + id, sizeof(name));
420
+ name.name[name.name_len] = '\0';
421
+
422
+ sprintf(buf, "\"%s\"", name.name);
423
+ }
424
+ }
425
+
426
+ void dump_resources(unsigned int res_table_off) {
427
+ unsigned int res_off = res_table_off;
428
+
429
+ unsigned short shift_count;
430
+ res_off += read_data((char*)&shift_count, res_off, sizeof(shift_count));
431
+
432
+ struct resource_table_type rt;
433
+
434
+ while(read_data((char*)&rt, res_off, sizeof(rt)) >= 2 && rt.type_id) {
435
+ res_off += sizeof(rt);
436
+
437
+ char type_id[260];
438
+ get_resource_id(type_id, res_table_off, rt.type_id);
439
+
440
+ unsigned int i;
441
+ struct resource_table_entry re;
442
+
443
+ for(i = 0; i < rt.res_count; i++) {
444
+ read_data((char*)&re, res_off, sizeof(re));
445
+
446
+ char res_id[260];
447
+ get_resource_id(res_id, res_table_off, re.res_id);
448
+
449
+ unsigned int off = (unsigned int)(1 << shift_count) * re.offset;
450
+
451
+ char flags[64] = "";
452
+
453
+ FLAG_CAT(flags, "MOVEABLE", re.flags & RESOURCE_MOVEABLE);
454
+ FLAG_CAT(flags, "PURE", re.flags & RESOURCE_PURE);
455
+ FLAG_CAT(flags, "PRELOAD", re.flags & RESOURCE_PRELOAD);
456
+
457
+ printf("Resource table entry at 0x%04X:\n", res_off);
458
+ printf("\tType ID:\t%s\n", type_id);
459
+ printf("\tResource ID:\t%s\n", res_id);
460
+ printf("\tData offset:\t0x%04X\n", off);
461
+ printf("\tData length:\t0x%04hX\n", re.length);
462
+ printf("\tFlags:\t\t0x%04hX (%s)\n\n", re.flags, flags);
463
+
464
+ res_off += sizeof(re);
465
+ }
466
+ }
467
+ }
468
+
469
+ #define DUMP_IMPORTS (int)(1<<0)
470
+ #define DUMP_SEGMENTS (int)(1<<1)
471
+ #define DUMP_RELOCATION (int)(1<<2)
472
+ #define DUMP_NONRES (int)(1<<3)
473
+ #define DUMP_RESIDENT (int)(1<<4)
474
+ #define DUMP_ENTRY (int)(1<<5)
475
+ #define DUMP_NE (int)(1<<6)
476
+ #define DUMP_MZ (int)(1<<7)
477
+ #define DUMP_RESOURCE (int)(1<<8)
478
+
479
+ int main(int argc, char **argv) {
480
+ int opt;
481
+
482
+ int to_dump = 0;
483
+
484
+ while((opt = getopt(argc, argv, "isrnNehmR")) != -1) {
485
+ switch(opt) {
486
+ case '?':
487
+ goto USAGE;
488
+
489
+ case 'i':
490
+ to_dump |= DUMP_IMPORTS;
491
+ break;
492
+
493
+ case 's':
494
+ to_dump |= DUMP_SEGMENTS;
495
+ break;
496
+
497
+ case 'r':
498
+ to_dump |= DUMP_RELOCATION;
499
+ break;
500
+
501
+ case 'n':
502
+ to_dump |= DUMP_NONRES;
503
+ break;
504
+
505
+ case 'N':
506
+ to_dump |= DUMP_RESIDENT;
507
+ break;
508
+
509
+ case 'e':
510
+ to_dump |= DUMP_ENTRY;
511
+ break;
512
+
513
+ case 'h':
514
+ to_dump |= DUMP_NE;
515
+ break;
516
+
517
+ case 'm':
518
+ to_dump |= DUMP_MZ;
519
+ break;
520
+
521
+ case 'R':
522
+ to_dump |= DUMP_RESOURCE;
523
+ break;
524
+ };
525
+ }
526
+
527
+ if(optind + 1 != argc || !to_dump) {
528
+ USAGE:
529
+
530
+ fprintf(stderr, "Usage: %s -hisrnNemR <file>\n", argv[0]);
531
+
532
+ fprintf(stderr, "\t-h\tDump NE EXE header\n");
533
+ fprintf(stderr, "\t-i\tDump detected imports\n");
534
+ fprintf(stderr, "\t-s\tDump segment table\n");
535
+ fprintf(stderr, "\t-r\tDump relocation information\n");
536
+ fprintf(stderr, "\t-n\tDump exported non-resident names\n");
537
+ fprintf(stderr, "\t-N\tDump exported resident names\n");
538
+ fprintf(stderr, "\t-e\tDump entry points\n");
539
+ fprintf(stderr, "\t-m\tDump MZ (DOS EXE) header\n");
540
+ fprintf(stderr, "\t-R\tDump resource table\n");
541
+
542
+ return 1;
543
+ }
544
+
545
+ if(!(fh = fopen(argv[optind], "rb"))) {
546
+ fprintf(stderr, "Cannot open file\n");
547
+ return 1;
548
+ }
549
+
550
+ struct mz_header mz;
551
+
552
+ if(read_data((char*)&mz, 0, sizeof(mz)) != sizeof(mz) || mz.magic != MZ_MAGIC) {
553
+ fprintf(stderr, "EXE (MZ) header missing or incomplete\n");
554
+ return 1;
555
+ }
556
+
557
+ if(to_dump & DUMP_MZ) {
558
+ printf("MZ header information:\n");
559
+ printf("\tNumber of pages:\t%hu\n", mz.blocks_in_file);
560
+ printf("\tBytes in last page:\t%hu\n", mz.bytes_in_last_block);
561
+ printf("\tRelocation table off.:\t0x%04hX\n", mz.reloc_table_offset);
562
+ printf("\tNumber of relocations:\t%hu\n", mz.num_relocs);
563
+ printf("\tHeader size:\t\t0x%04X\n", (unsigned int)mz.header_paragraphs * 16);
564
+ printf("\tMinimum extra memory:\t0x%04X\n", (unsigned int)mz.min_extra_paragraphs * 16);
565
+ printf("\tMaximum extra memory:\t0x%04X\n", (unsigned int)mz.max_extra_paragraphs * 16);
566
+ printf("\tInitial CS:IP:\t\t0x%04hX:%04hX\n", mz.cs, mz.ip);
567
+ printf("\tInitial SS:SP:\t\t0x%04hX:%04hX\n", mz.ss, mz.sp);
568
+ printf("\tWhole file checksum:\t0x%04hX\n", mz.checksum);
569
+ printf("\tOverlay number:\t\t%hu\n\n", mz.overlay_number);
570
+ }
571
+
572
+ if(mz.reloc_table_offset == 0x40) {
573
+ unsigned int ne_offset;
574
+ struct ne_header ne;
575
+
576
+ if(read_data((char*)&ne_offset, 0x3C, 4) != 4 || read_data((char*)&ne, ne_offset, sizeof(ne)) != sizeof(ne) || ne.magic != NE_MAGIC) {
577
+ goto NOT_NE;
578
+ }
579
+
580
+ if(to_dump & DUMP_NE) {
581
+ printf("NE header information:\n");
582
+
583
+ printf("\tHeader offset:\t\t0x%04X\n", ne_offset);
584
+ printf("\tLinker version:\t\t%u.%u\n", (unsigned int)ne.linker_ver, (unsigned int)ne.linker_rev);
585
+ printf("\tEntry point table:\t0x%04X\n", ne_offset + ne.entry_table_offset);
586
+ printf("\tWhole file CRC-32:\t0x%08X\n", ne.whole_file_crc);
587
+
588
+ char flags[64] = "";
589
+
590
+ FLAG_CAT(flags, "NOAUTODATA", (ne.flags & 0x0003) == 0);
591
+ FLAG_CAT(flags, "SINGLEDATA", ne.flags & 0x0001);
592
+ FLAG_CAT(flags, "MULTIPLEDATA", ne.flags & 0x0002);
593
+ FLAG_CAT(flags, "LIBRARY", ne.flags & 0x8000);
594
+
595
+ printf("\tFlags\t\t\t0x%04X (%s)\n", (unsigned int)ne.flags, flags);
596
+
597
+ printf("\tAutomatic data segment:\t%hu\n", ne.auto_data_segment);
598
+
599
+ printf("\tDynamic heap size:\t0x%04hX\n", ne.init_heap_size);
600
+ printf("\tDynamic stack size:\t0x%04hX\n", ne.init_stack_size);
601
+
602
+ printf("\tInitial CS:IP:\t\t0x%04hX:%04hX\n", ne.cs, ne.ip);
603
+ printf("\tInitial SS:SP:\t\t0x%04hX:%04hX\n", ne.ss, ne.sp);
604
+
605
+ printf("\tNumber of segments:\t%hu\n", ne.seg_table_entries);
606
+
607
+ printf("\tSegment table offset:\t0x%04X\n", ne_offset + ne.seg_table_offset);
608
+ printf("\tResource table offset:\t0x%04X\n", ne_offset + ne.res_table_offset);
609
+ printf("\tResident name table:\t0x%04X\n", ne_offset + ne.res_name_table_offset);
610
+ printf("\tModule reference table:\t0x%04X\n", ne_offset + ne.mod_ref_table_offset);
611
+ printf("\tImported name table:\t0x%04X\n", ne_offset + ne.import_table_offset);
612
+ printf("\tNonResident name table:\t0x%04X\n", ne.non_res_name_table_offset);
613
+
614
+ printf(
615
+ "\tExecutable type:\t0x%02X (%s)\n\n",
616
+ (unsigned int)ne.exe_type,
617
+ (ne.exe_type == 0x02 ? "WINDOWS" : "UNKNOWN")
618
+ );
619
+ }
620
+
621
+ unsigned int segments_offset = ne_offset + ne.seg_table_offset, seg_num = 0;
622
+ struct segment_table seg;
623
+
624
+ struct import_entry *imports = NULL;
625
+
626
+ while(seg_num++ < ne.seg_table_entries && read_data((char*)&seg, segments_offset, sizeof(seg)) == sizeof(seg)) {
627
+ unsigned int seg_offset = seg.offset * (1 << ne.seg_sector_size_shift);
628
+
629
+ if(to_dump & DUMP_SEGMENTS) {
630
+ char flags[64] = "";
631
+
632
+ FLAG_CAT(flags, "CODE", (seg.flags & SEG_TYPE_MASK) == SEG_CODE);
633
+ FLAG_CAT(flags, "DATA", (seg.flags & SEG_TYPE_MASK) == SEG_DATA);
634
+ FLAG_CAT(flags, "MOVEABLE", seg.flags & SEG_MOVEABLE);
635
+ FLAG_CAT(flags, "PRELOAD", seg.flags & SEG_PRELOAD);
636
+ FLAG_CAT(flags, "RELOCINFO", seg.flags & SEG_RELOCINFO);
637
+ FLAG_CAT(flags, "DISCARD", seg.flags & SEG_DISCARD);
638
+
639
+ printf("Segment #%u:\n", seg_num);
640
+ printf("\tData offset:\t\t\t0x%04X\n", seg_offset);
641
+ printf("\tData length:\t\t\t0x%04X\n", (unsigned int)(seg.size ? seg.size : 65536));
642
+ printf("\tMinimum allocation size:\t0x%04X\n", (unsigned int)(seg.size ? seg.size : 65536));
643
+ printf("\tFlags:\t\t\t\t0x%04hX (%s)\n\n", seg.flags, flags);
644
+ }
645
+
646
+ segments_offset += sizeof(seg);
647
+
648
+ if(seg.flags & SEG_RELOCINFO) {
649
+ unsigned int reloc_offset = seg_offset + seg.size;
650
+ unsigned short num_records, i;
651
+
652
+ read_data((char*)&num_records, reloc_offset, 2);
653
+ reloc_offset += 2;
654
+
655
+ for(i = 0; i < num_records; i++) {
656
+ struct segment_reloc reloc;
657
+
658
+ read_data((char*)&reloc, reloc_offset, sizeof(reloc));
659
+
660
+ if(to_dump & DUMP_RELOCATION) {
661
+ unsigned short offset = reloc.offset;
662
+
663
+ char src_type[64] = "", flags[64] = "";
664
+
665
+ FLAG_CAT(src_type, "LOBYTE", reloc.src_type == RELOC_LOBYTE);
666
+ FLAG_CAT(src_type, "SEGMENT", reloc.src_type == RELOC_SEGMENT);
667
+ FLAG_CAT(src_type, "FAR_ADDR", reloc.src_type == RELOC_FAR_ADDR);
668
+ FLAG_CAT(src_type, "OFFSET", reloc.src_type == RELOC_OFFSET);
669
+
670
+ FLAG_CAT(flags, "INTERNALREF", reloc.flags & RELOC_INTERNALREF);
671
+ FLAG_CAT(flags, "IMPORTORDINAL", reloc.flags & RELOC_IMPORTORDINAL);
672
+ FLAG_CAT(flags, "IMPORTNAME", reloc.flags & RELOC_IMPORTNAME);
673
+ FLAG_CAT(flags, "OSFIXUP", reloc.flags & RELOC_OSFIXUP);
674
+
675
+ FLAG_CAT(flags, "ADDITIVE", reloc.flags & RELOC_ADDITIVE);
676
+
677
+ printf("Relocation entry for segment #%u at 0x%04hX:\n", seg_num, reloc_offset);
678
+ printf("\tSource type:\t\t0x%02X (%s)\n", (unsigned int)reloc.src_type, src_type);
679
+ printf("\tFlags:\t\t\t0x%02X (%s)\n", (unsigned int)reloc.flags, flags);
680
+
681
+ do {
682
+ printf("\tOffset within segment:\t0x%04hX\n", offset);
683
+ read_data((char*)&offset, seg_offset + offset, 2);
684
+ } while(!(reloc.flags & RELOC_ADDITIVE) && offset != 0xFFFF);
685
+
686
+ printf("\tTarget bytes:\t\t%02X %02X %02X %02X\n", (unsigned int)reloc.target_b[0], (unsigned int)reloc.target_b[1], (unsigned int)reloc.target_b[2], (unsigned int)reloc.target_b[3]);
687
+ printf("\tTarget words:\t\t%04hX %04hX\n", reloc.target_w[0], reloc.target_w[1]);
688
+
689
+ putchar('\n');
690
+ }
691
+
692
+ reloc_offset += sizeof(reloc);
693
+
694
+ struct import_table mod_name, import;
695
+
696
+ if(reloc.flags & (RELOC_IMPORTNAME | RELOC_IMPORTORDINAL)) {
697
+ unsigned short mod_offset;
698
+ read_data((char*)&mod_offset, ne_offset + ne.mod_ref_table_offset + (2 * (reloc.importordinal.mod_index - 1)), 2);
699
+
700
+ read_data((char*)&mod_name, ne_offset + ne.import_table_offset + mod_offset, sizeof(mod_name));
701
+ mod_name.name[mod_name.name_len] = '\0';
702
+ }
703
+
704
+ if(reloc.flags == RELOC_IMPORTNAME) {
705
+ read_data((char*)&import, ne_offset + ne.import_table_offset + reloc.importname.name_offset, sizeof(import));
706
+ import.name[import.name_len] = '\0';
707
+
708
+ add_import(&imports, mod_name.name, import.name, -1);
709
+ }
710
+
711
+ if(reloc.flags == RELOC_IMPORTORDINAL) {
712
+ add_import(&imports, mod_name.name, NULL, reloc.importordinal.ordinal);
713
+ }
714
+ }
715
+ }
716
+ }
717
+
718
+ if(to_dump & DUMP_RESIDENT) {
719
+ printf("Resident names table:\n");
720
+ dump_names(ne_offset + ne.res_name_table_offset, 0xFFFFFFFF, ne_offset + ne.entry_table_offset);
721
+ putchar('\n');
722
+ }
723
+
724
+ if(to_dump & DUMP_NONRES) {
725
+ printf("Non-Resident names table:\n");
726
+ dump_names(ne.non_res_name_table_offset, ne.non_res_name_table_entries, ne_offset + ne.entry_table_offset);
727
+ putchar('\n');
728
+ }
729
+
730
+ if(to_dump & DUMP_ENTRY) {
731
+ dump_ep_table(ne_offset + ne.entry_table_offset);
732
+ }
733
+
734
+ if(to_dump & DUMP_RESOURCE) {
735
+ dump_resources(ne_offset + ne.res_table_offset);
736
+ }
737
+
738
+ if(to_dump & DUMP_IMPORTS) {
739
+ dump_imports(imports);
740
+ }
741
+
742
+ free_imports(imports);
743
+ }else{
744
+ NOT_NE:
745
+ printf("Supplied file does not appear to be in NE format\n");
746
+ }
747
+
748
+ fclose(fh);
749
+
750
+ return 0;
751
+ }