fileshunter 0.1.0.20130725

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,505 @@
1
+ module FilesHunter
2
+
3
+ module Decoders
4
+
5
+ class EXE < BeginPatternDecoder
6
+
7
+ BEGIN_PATTERN_EXE = Regexp.new("MZ....\x00\x00.\x00.\x00..\x00\x00..\x00\x00\x00\x00\x00\x00.\x00.\x00\x00\x00....\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00....\x00\x00\x00\x00\x00\x00\x00\x00", nil, 'n')
8
+
9
+ KNOWN_MACHINE_TYPES = [
10
+ "\x00\x00".force_encoding(Encoding::ASCII_8BIT),
11
+ "\xd3\x01".force_encoding(Encoding::ASCII_8BIT),
12
+ "\x64\x86".force_encoding(Encoding::ASCII_8BIT),
13
+ "\xc0\x01".force_encoding(Encoding::ASCII_8BIT),
14
+ "\xc4\x01".force_encoding(Encoding::ASCII_8BIT),
15
+ "\x64\xaa".force_encoding(Encoding::ASCII_8BIT),
16
+ "\xbc\x0e".force_encoding(Encoding::ASCII_8BIT),
17
+ "\x4c\x01".force_encoding(Encoding::ASCII_8BIT),
18
+ "\x00\x02".force_encoding(Encoding::ASCII_8BIT),
19
+ "\x41\x90".force_encoding(Encoding::ASCII_8BIT),
20
+ "\x66\x02".force_encoding(Encoding::ASCII_8BIT),
21
+ "\x66\x03".force_encoding(Encoding::ASCII_8BIT),
22
+ "\x66\x04".force_encoding(Encoding::ASCII_8BIT),
23
+ "\xf0\x01".force_encoding(Encoding::ASCII_8BIT),
24
+ "\xf1\x01".force_encoding(Encoding::ASCII_8BIT),
25
+ "\x66\x01".force_encoding(Encoding::ASCII_8BIT),
26
+ "\xa2\x01".force_encoding(Encoding::ASCII_8BIT),
27
+ "\xa3\x01".force_encoding(Encoding::ASCII_8BIT),
28
+ "\xa6\x01".force_encoding(Encoding::ASCII_8BIT),
29
+ "\xa8\x01".force_encoding(Encoding::ASCII_8BIT),
30
+ "\xc2\x01".force_encoding(Encoding::ASCII_8BIT),
31
+ "\x69\x01".force_encoding(Encoding::ASCII_8BIT)
32
+ ]
33
+
34
+ PE_SIGNATURE = "PE\x00\x00".force_encoding(Encoding::ASCII_8BIT)
35
+ NE_SIGNATURE = 'NE'.force_encoding(Encoding::ASCII_8BIT)
36
+ OPTIONAL_HEADER_ID = "\x0B".force_encoding(Encoding::ASCII_8BIT)
37
+ PE32_ID = "\x01".force_encoding(Encoding::ASCII_8BIT)
38
+ KNOWN_OPTIONAL_HEADER_TYPES = [
39
+ PE32_ID,
40
+ "\x02".force_encoding(Encoding::ASCII_8BIT)
41
+ ]
42
+ SECTION_TEXT_ID = ".text\x00\x00\x00".force_encoding(Encoding::ASCII_8BIT)
43
+
44
+ def get_begin_pattern
45
+ return BEGIN_PATTERN_EXE, { :offset_inc => 60, :max_regexp_size => 60 }
46
+ end
47
+
48
+ def decode(offset)
49
+ ending_offset = nil
50
+
51
+ # Go directly to the PE file header
52
+ cursor = offset + BinData::Uint32le.read(@data[offset+60..offset+63])
53
+ progress(cursor)
54
+ # Decode PE file header
55
+ invalid_data("@#{cursor} - Invalid PE/NE header") if ((@data[cursor..cursor+3] != PE_SIGNATURE) and (@data[cursor..cursor+1] != NE_SIGNATURE))
56
+ if (@data[cursor..cursor+1] == NE_SIGNATURE)
57
+ # NE format
58
+ # Reference: http://www.fileformat.info/format/exe/corion-ne.htm
59
+ # Reference: http://itee.uq.edu.au/~cristina/students/david/honoursThesis96/appendix.htm
60
+ # Reference: http://www.program-transformation.org/Transform/NeFormat
61
+ # Reference: http://www.chiark.greenend.org.uk/~sgtatham/fonts/dewinfont
62
+ ne_offset = cursor
63
+ cursor += 2
64
+ #linker_major_version = @data[cursor].ord
65
+ #linker_minor_version = @data[cursor+1].ord
66
+ entry_table_offset = BinData::Uint16le.read(@data[cursor+2..cursor+3])
67
+ entry_table_size = BinData::Uint16le.read(@data[cursor+4..cursor+5])
68
+ #file_load_crc = BinData::Uint32le.read(@data[cursor+6..cursor+9])
69
+ #program_flags = @data[cursor+10].ord
70
+ #application_flags = @data[cursor+11].ord
71
+ #auto_data_segment_index = BinData::Uint16le.read(@data[cursor+12..cursor+13])
72
+ #initial_local_heap_size = BinData::Uint16le.read(@data[cursor+14..cursor+15])
73
+ #initial_stack_size = BinData::Uint16le.read(@data[cursor+16..cursor+17])
74
+ #entry_point = BinData::Uint32le.read(@data[cursor+18..cursor+21])
75
+ #initial_stack_pointer = BinData::Uint32le.read(@data[cursor+22..cursor+25])
76
+ nbr_segments = BinData::Uint16le.read(@data[cursor+26..cursor+27])
77
+ nbr_module_reference = BinData::Uint16le.read(@data[cursor+28..cursor+29])
78
+ non_resident_names_table_size = BinData::Uint16le.read(@data[cursor+30..cursor+31])
79
+ segment_table_offset = BinData::Uint16le.read(@data[cursor+32..cursor+33])
80
+ resource_table_offset = BinData::Uint16le.read(@data[cursor+34..cursor+35])
81
+ resident_names_table_offset = BinData::Uint16le.read(@data[cursor+36..cursor+37])
82
+ module_reference_table_offset = BinData::Uint16le.read(@data[cursor+38..cursor+39])
83
+ imported_names_table_offset = BinData::Uint16le.read(@data[cursor+40..cursor+41])
84
+ non_resident_names_table_offset = BinData::Uint32le.read(@data[cursor+42..cursor+45])
85
+ #moveable_entry_point_count = BinData::Uint16le.read(@data[cursor+46..cursor+47])
86
+ #file_alignment = BinData::Uint16le.read(@data[cursor+48..cursor+49])
87
+ #nbr_resource_table_entries = BinData::Uint16le.read(@data[cursor+50..cursor+51])
88
+ #target_operating_system = @data[cursor+52].ord
89
+ #other_flags = @data[cursor+53].ord
90
+ #return_thunks_offset = BinData::Uint16le.read(@data[cursor+54..cursor+55])
91
+ #segment_reference_thunks_offset = BinData::Uint16le.read(@data[cursor+56..cursor+57])
92
+ #code_swap_area_size = BinData::Uint16le.read(@data[cursor+58..cursor+59])
93
+ #expected_win_version_minor = @data[cursor+60].ord
94
+ #expected_win_version_major = @data[cursor+61].ord
95
+ cursor += 62
96
+ progress(cursor)
97
+ log_debug "@#{cursor} - NE header: entry_table_offset=#{entry_table_offset} entry_table_size=#{entry_table_size} nbr_segments=#{nbr_segments} nbr_module_reference=#{nbr_module_reference} non_resident_names_table_size=#{non_resident_names_table_size} segment_table_offset=#{segment_table_offset} resource_table_offset=#{resource_table_offset} resident_names_table_offset=#{resident_names_table_offset} module_reference_table_offset=#{module_reference_table_offset} imported_names_table_offset=#{imported_names_table_offset} non_resident_names_table_offset=#{non_resident_names_table_offset}"
98
+ # Segment table
99
+ log_debug "@#{cursor} - Segment table"
100
+ invalid_data("@#{cursor} - Segment table offset (#{segment_table_offset}) should have been set here (#{cursor-ne_offset}).") if (segment_table_offset != cursor-ne_offset)
101
+ # map< SegmentOffset, SegmentSize >
102
+ segment_data = {}
103
+ nbr_segments.times do |idx_segment|
104
+ segment_data_offset = BinData::Uint16le.read(@data[cursor..cursor+1])
105
+ if (segment_data_offset > 0)
106
+ segment_data_size = BinData::Uint16le.read(@data[cursor+2..cursor+3])
107
+ segment_data_size = 65536 if (segment_data_size == 0)
108
+ segment_data[segment_data_offset] = segment_data_size
109
+ end
110
+ cursor += 8
111
+ end
112
+ progress(cursor)
113
+ # Now we track the maximal cursor encountered
114
+ max_cursor = cursor
115
+ # Resource table
116
+ log_debug "@#{cursor} - Resource table"
117
+ invalid_data("@#{cursor} - Resource table offset (#{resource_table_offset}) should have been set here (#{cursor-ne_offset}).") if (resource_table_offset != cursor-ne_offset)
118
+ # Ignore nbr_resource_table_entries
119
+ alignment_shift_count = BinData::Uint16le.read(@data[cursor..cursor+1])
120
+ offset_factor = (1 << alignment_shift_count)
121
+ # map< SegmentOffset, SegmentSize >
122
+ resource_data = {}
123
+ type_id = BinData::Uint16le.read(@data[cursor+2..cursor+3])
124
+ cursor += 4
125
+ while (type_id != 0)
126
+ # If the Type ID is a string, read it
127
+ if ((type_id & 0b10000000_00000000) == 0)
128
+ str_offset = ne_offset + resource_table_offset + type_id
129
+ end_str_cursor = str_offset + 1 + @data[str_offset].ord
130
+ max_cursor = end_str_cursor if (max_cursor < end_str_cursor)
131
+ end
132
+ nbr_resources_for_this_type = BinData::Uint16le.read(@data[cursor..cursor+1])
133
+ reserved = BinData::Uint32le.read(@data[cursor+2..cursor+5])
134
+ invalid_data("@#{cursor} - Reserved data should have been null: #{reserved}") if (reserved != 0)
135
+ cursor += 6
136
+ nbr_resources_for_this_type.times do |idx_resource_for_this_type|
137
+ resource_offset = BinData::Uint16le.read(@data[cursor..cursor+1])
138
+ resource_size = BinData::Uint16le.read(@data[cursor+2..cursor+3])
139
+ resource_flags = BinData::Uint16le.read(@data[cursor+4..cursor+5])
140
+ resource_id = BinData::Uint16le.read(@data[cursor+6..cursor+7])
141
+ real_offset = resource_offset * offset_factor
142
+ real_size = resource_size * offset_factor
143
+ log_debug "@#{cursor} - Found resource ##{resource_id} @#{real_offset} of size #{real_size} with flags #{resource_flags}"
144
+ reserved = BinData::Uint32le.read(@data[cursor+8..cursor+11])
145
+ invalid_data("@#{cursor} - Reserved data should have been null: #{reserved}") if (reserved != 0)
146
+ resource_data[real_offset] = real_size
147
+ # If the Resource ID is a string, read it
148
+ if ((resource_id & 0b10000000_00000000) == 0)
149
+ str_offset = ne_offset + resource_table_offset + resource_id
150
+ end_str_cursor = str_offset + 1 + @data[str_offset].ord
151
+ max_cursor = end_str_cursor if (max_cursor < end_str_cursor)
152
+ end
153
+ cursor += 12
154
+ progress(cursor)
155
+ end
156
+ type_id = BinData::Uint16le.read(@data[cursor..cursor+1])
157
+ cursor += 2
158
+ progress(cursor)
159
+ end
160
+ progress(cursor)
161
+ max_cursor = cursor if (max_cursor < cursor)
162
+ # Resident names table
163
+ cursor = ne_offset + resident_names_table_offset
164
+ log_debug "@#{cursor} - Resident names table"
165
+ next_string_length = @data[cursor].ord
166
+ cursor += 1
167
+ while (next_string_length > 0)
168
+ str_name = @data[cursor..cursor+next_string_length-1]
169
+ log_debug "@#{cursor} - Found resident name string: #{str_name.inspect}"
170
+ cursor += next_string_length
171
+ next_string_length = @data[cursor+2].ord
172
+ cursor += 3
173
+ progress(cursor)
174
+ end
175
+ max_cursor = cursor if (max_cursor < cursor)
176
+ # Module reference table
177
+ cursor = ne_offset + module_reference_table_offset
178
+ log_debug "@#{cursor} - Module reference table"
179
+ nbr_module_reference.times do |idx_module_reference|
180
+ cursor += 2
181
+ end
182
+ progress(cursor)
183
+ max_cursor = cursor if (max_cursor < cursor)
184
+ # Imported name table
185
+ cursor = ne_offset + imported_names_table_offset
186
+ log_debug "@#{cursor} - Imported names table"
187
+ next_string_length = @data[cursor].ord
188
+ cursor += 1
189
+ while (next_string_length > 0)
190
+ str_name = @data[cursor..cursor+next_string_length-1]
191
+ log_debug "@#{cursor} - Found imported name string: #{str_name.inspect}"
192
+ cursor += next_string_length
193
+ next_string_length = @data[cursor].ord
194
+ cursor += 1
195
+ progress(cursor)
196
+ end
197
+ max_cursor = cursor if (max_cursor < cursor)
198
+ # Entry table
199
+ cursor = ne_offset + entry_table_offset
200
+ log_debug "@#{cursor} - Entry table"
201
+ nbr_entries_in_bundle = @data[cursor].ord
202
+ #segment_indicator_for_bundle = @data[cursor+1].ord
203
+ cursor += 2
204
+ while (nbr_entries_in_bundle > 0)
205
+ # TODO
206
+ invalid_data("@#{cursor} - Cannot decode entry tables from NE executable files yet. Sorry.")
207
+
208
+ nbr_entries_in_bundle = @data[cursor].ord
209
+ end
210
+ invalid_data("@#{cursor} - Declared entry table size (#{entry_table_size}) is smaller than what has been read: #{cursor-ne_offset-entry_table_offset}") if (entry_table_size < cursor-ne_offset-entry_table_offset)
211
+ # Make sure we get padding too
212
+ cursor = ne_offset + entry_table_offset + entry_table_size
213
+ max_cursor = cursor if (max_cursor < cursor)
214
+ # Non resident names table
215
+ cursor = offset + non_resident_names_table_offset
216
+ log_debug "@#{cursor} - Non resident name table"
217
+ next_string_length = @data[cursor].ord
218
+ cursor += 1
219
+ while (next_string_length > 0)
220
+ str_name = @data[cursor..cursor+next_string_length-1]
221
+ log_debug "@#{cursor} - Found non resident name string: #{str_name.inspect}"
222
+ cursor += next_string_length
223
+ next_string_length = @data[cursor+2].ord
224
+ cursor += 3
225
+ progress(cursor)
226
+ end
227
+ invalid_data("@#{cursor} - Declared non resident name table size (#{non_resident_names_table_size}) is smaller than what has been read: #{cursor-offset-non_resident_names_table_offset}") if (non_resident_names_table_size < cursor-offset-non_resident_names_table_offset)
228
+ # Make sure we get padding too
229
+ cursor = offset + non_resident_names_table_offset + non_resident_names_table_size
230
+ max_cursor = cursor if (max_cursor < cursor)
231
+ # Data and resource segments
232
+ segment_data.merge(resource_data).each do |data_offset, data_size|
233
+ cursor = offset + data_offset
234
+ cursor += data_size
235
+ max_cursor = cursor if (max_cursor < cursor)
236
+ end
237
+ ending_offset = max_cursor
238
+ found_relevant_data(:fon)
239
+ else
240
+ # PE format
241
+ cursor += 4
242
+ target_machine = @data[cursor..cursor+1]
243
+ invalid_data("@#{cursor} - Invalid machine type: #{target_machine.inspect}") if (!KNOWN_MACHINE_TYPES.include?(target_machine))
244
+ nbr_sections = BinData::Uint16le.read(@data[cursor+2..cursor+3])
245
+ #creation_time = BinData::Uint32le.read(@data[cursor+4..cursor+7])
246
+ symbol_table_offset = BinData::Uint32le.read(@data[cursor+8..cursor+11])
247
+ nbr_symbols = BinData::Uint32le.read(@data[cursor+12..cursor+15])
248
+ optional_header_size = BinData::Uint16le.read(@data[cursor+16..cursor+17])
249
+ characteristics = BinData::Uint16le.read(@data[cursor+18..cursor+19])
250
+ invalid_data("@#{cursor+18} - Invalid characteristics #{characteristics}: bits should be 0") if ((characteristics & 80) != 0)
251
+ # We can have a first guess on the extension
252
+ file_type = ((characteristics & 8192) == 0) ? :exe : ((optional_header_size == 0) ? :obj : :dll)
253
+ found_relevant_data((file_type == :exe) ? [ :exe, :sys ] : ((file_type == :obj) ? :obj : [ :dll, :drv, :ocx ]))
254
+ metadata(
255
+ :target_machine => target_machine,
256
+ :nbr_sections => nbr_sections,
257
+ :symbol_table_offset => symbol_table_offset,
258
+ :nbr_symbols => nbr_symbols,
259
+ :optional_header_size => optional_header_size,
260
+ :characteristics => characteristics
261
+ )
262
+ cursor += 20
263
+ progress(cursor)
264
+ file_alignment = nil
265
+ certificate_table_offset = nil
266
+ certificate_table_size = nil
267
+ #delay_import_table_offset = nil
268
+ #delay_import_table_size = nil
269
+ # Decode optional header if any
270
+ optional_header_end_offset = cursor + optional_header_size
271
+ if (optional_header_size > 0)
272
+ c_1 = @data[cursor+1]
273
+ invalid_data("@#{cursor} - Invalid optional header") if ((@data[cursor] != OPTIONAL_HEADER_ID) or (!KNOWN_OPTIONAL_HEADER_TYPES.include?(c_1)))
274
+ mode_pe32 = (c_1 == PE32_ID)
275
+ #linker_version_major = @data[cursor+2].ord
276
+ #linker_version_minor = @data[cursor+3].ord
277
+ #code_size = BinData::Uint32le.read(@data[cursor+4..cursor+7])
278
+ #init_data_size = BinData::Uint32le.read(@data[cursor+8..cursor+11])
279
+ #uninit_data_size = BinData::Uint32le.read(@data[cursor+12..cursor+15])
280
+ #log_debug "@#{cursor} - code_size=#{code_size} init_data_size=#{init_data_size} uninit_data_size=#{uninit_data_size}"
281
+ #entry_point_address = BinData::Uint32le.read(@data[cursor+16..cursor+19])
282
+ #base_of_code = BinData::Uint32le.read(@data[cursor+20..cursor+23])
283
+ #if mode_pe32
284
+ #base_of_data = BinData::Uint32le.read(@data[cursor+24..cursor+27])
285
+ #image_base = BinData::Uint32le.read(@data[cursor+28..cursor+31])
286
+ #else
287
+ #image_base = BinData::Uint64le.read(@data[cursor+24..cursor+31])
288
+ #end
289
+ #section_alignment = BinData::Uint32le.read(@data[cursor+32..cursor+35])
290
+ file_alignment = BinData::Uint32le.read(@data[cursor+36..cursor+39])
291
+ #os_version_major = BinData::Uint16le.read(@data[cursor+40..cursor+41])
292
+ #os_version_minor = BinData::Uint16le.read(@data[cursor+42..cursor+43])
293
+ #image_version_major = BinData::Uint16le.read(@data[cursor+44..cursor+45])
294
+ #image_version_minor = BinData::Uint16le.read(@data[cursor+46..cursor+47])
295
+ #subsystem_version_major = BinData::Uint16le.read(@data[cursor+48..cursor+49])
296
+ #subsystem_version_minor = BinData::Uint16le.read(@data[cursor+50..cursor+51])
297
+ win32_version = BinData::Uint32le.read(@data[cursor+52..cursor+55])
298
+ invalid_data("@#{cursor+52} - Invalid Win32 version: #{win32_version}") if (win32_version != 0)
299
+ #image_size = BinData::Uint32le.read(@data[cursor+56..cursor+59])
300
+ headers_size = BinData::Uint32le.read(@data[cursor+60..cursor+64])
301
+ #checksum = BinData::Uint32le.read(@data[cursor+64..cursor+67])
302
+ subsystem = BinData::Uint16le.read(@data[cursor+68..cursor+69])
303
+ if (subsystem == 1)
304
+ case file_type
305
+ when :dll
306
+ file_type = :drv
307
+ found_relevant_data(:drv)
308
+ when :exe
309
+ file_type = :sys
310
+ found_relevant_data(:sys)
311
+ end
312
+ end
313
+ dll_characteristics = BinData::Uint16le.read(@data[cursor+70..cursor+71])
314
+ invalid_data("@#{cursor+70} - Invalid DLL characteristics #{dll_characteristics}: bits should be 0") if ((dll_characteristics & 4111) != 0)
315
+ nbr_rva_and_sizes = nil
316
+ if mode_pe32
317
+ # stack_reserve_size = BinData::Uint32le.read(@data[cursor+72..cursor+75])
318
+ # stack_commit_size = BinData::Uint32le.read(@data[cursor+76..cursor+79])
319
+ # heap_reserve_size = BinData::Uint32le.read(@data[cursor+80..cursor+83])
320
+ # heap_commit_size = BinData::Uint32le.read(@data[cursor+84..cursor+87])
321
+ # loader_flags = BinData::Uint32le.read(@data[cursor+88..cursor+91])
322
+ nbr_rva_and_sizes = BinData::Uint32le.read(@data[cursor+92..cursor+95])
323
+ cursor += 96
324
+ else
325
+ # stack_reserve_size = BinData::Uint64le.read(@data[cursor+72..cursor+79])
326
+ # stack_commit_size = BinData::Uint64le.read(@data[cursor+80..cursor+87])
327
+ # heap_reserve_size = BinData::Uint64le.read(@data[cursor+88..cursor+95])
328
+ # heap_commit_size = BinData::Uint64le.read(@data[cursor+96..cursor+103])
329
+ # loader_flags = BinData::Uint32le.read(@data[cursor+104..cursor+107])
330
+ nbr_rva_and_sizes = BinData::Uint32le.read(@data[cursor+108..cursor+111])
331
+ cursor += 112
332
+ end
333
+ # Get some info from the Data Directories
334
+ if (nbr_rva_and_sizes >= 5)
335
+ certificate_table_offset = BinData::Uint32le.read(@data[cursor+32..cursor+35])
336
+ certificate_table_size = BinData::Uint32le.read(@data[cursor+36..cursor+39])
337
+ end
338
+ #if (nbr_rva_and_sizes >= 14)
339
+ #delay_import_table_offset = BinData::Uint32le.read(@data[cursor+104..cursor+107])
340
+ #delay_import_table_size = BinData::Uint32le.read(@data[cursor+108..cursor+111])
341
+ #end
342
+ cursor += 8*nbr_rva_and_sizes
343
+ progress(cursor)
344
+ log_debug "@#{cursor} - Extended header: mode_pe32=#{mode_pe32} win32_version=#{win32_version} headers_size=#{headers_size} subsystem=#{subsystem} dll_characteristics=#{dll_characteristics} nbr_rva_and_sizes=#{nbr_rva_and_sizes}"
345
+ # We should have reached the end of optional header
346
+ # Sometimes optional_header_end_offset is invalid
347
+ invalid_data("@#{cursor} - Optional headers end at #{cursor} but were supposed to end at #{optional_header_end_offset}") if (cursor != optional_header_end_offset)
348
+ metadata(
349
+ :mode_pe32 => mode_pe32,
350
+ :file_alignment => file_alignment,
351
+ :win32_version => win32_version,
352
+ :headers_size => headers_size,
353
+ :subsystem => subsystem,
354
+ :dll_characteristics => dll_characteristics,
355
+ :nbr_rva_and_sizes => nbr_rva_and_sizes,
356
+ :certificate_table_offset => certificate_table_offset,
357
+ :certificate_table_size => certificate_table_size
358
+ )
359
+ end
360
+ log_debug "@#{cursor} - PE Header: nbr_sections=#{nbr_sections} file_alignment=#{file_alignment} symbol_table_offset=#{symbol_table_offset} nbr_symbols=#{nbr_symbols} certificate_table_offset=#{certificate_table_offset} certificate_table_size=#{certificate_table_size}"
361
+ # Now decode section headers
362
+ log_debug "@#{cursor} - Beginning of section headers"
363
+ # map<offset,size>
364
+ sections = {}
365
+ line_numbers = {}
366
+ text_section_offset = nil
367
+ nbr_sections.times do |idx_section|
368
+ name = @data[cursor..cursor+7]
369
+ #virtual_size = BinData::Uint32le.read(@data[cursor+8..cursor+11])
370
+ #virtual_address = BinData::Uint32le.read(@data[cursor+12..cursor+15])
371
+ raw_data_size = BinData::Uint32le.read(@data[cursor+16..cursor+19])
372
+ raw_data_offset = BinData::Uint32le.read(@data[cursor+20..cursor+23])
373
+ #relocations_offset = BinData::Uint32le.read(@data[cursor+24..cursor+27])
374
+ line_numbers_offset = BinData::Uint32le.read(@data[cursor+28..cursor+31])
375
+ #nbr_relocations = BinData::Uint16le.read(@data[cursor+32..cursor+33])
376
+ nbr_line_numbers = BinData::Uint16le.read(@data[cursor+34..cursor+35])
377
+ #section_characteristics = BinData::Uint32le.read(@data[cursor+36..cursor+39])
378
+ #invalid_data("@#{cursor+70} - Invalid Section characteristics #{section_characteristics}: bits should be 0") if ((section_characteristics & 18) != 0)
379
+ log_debug "@#{cursor} - Found section #{name}: raw_data_offset=#{raw_data_offset} raw_data_size=#{raw_data_size}"
380
+ # Remember the .text section
381
+ text_section_offset = raw_data_offset if (name == SECTION_TEXT_ID)
382
+ cursor += 40
383
+ progress(cursor)
384
+ sections[raw_data_offset] = raw_data_size if (raw_data_size > 0)
385
+ line_numbers[line_numbers_offset] = nbr_line_numbers if (nbr_line_numbers > 0)
386
+ end
387
+ # Get cursor directly at the end of the headers
388
+ cursor = offset + headers_size if (headers_size != nil)
389
+ progress(cursor)
390
+ # Starting from here, tables and sections might not be contiguous. Therefore we keep track of the maximal cursor encountered.
391
+ max_cursor = cursor
392
+ # Now read all sections defined
393
+ log_debug "@#{cursor} - Beginning of sections"
394
+ sections.keys.sort.each_with_index do |section_offset, idx_section|
395
+ # Align cursor on the next file_alignment
396
+ rest = (file_alignment == nil) ? 0 : ((cursor-offset) % file_alignment)
397
+ cursor += file_alignment - rest if (rest > 0)
398
+ # Check beginning of the section data
399
+ if ((cursor-offset) != section_offset)
400
+ log_debug("@#{cursor} - Section #{idx_section} should have been at offset #{cursor-offset}, but is declared at #{section_offset}")
401
+ cursor = offset + section_offset
402
+ end
403
+ # Check OCX
404
+ if ((text_section_offset == section_offset) and
405
+ (file_type == :dll) and
406
+ (@data.index('DllRegisterServer', cursor) != nil))
407
+ file_type = :ocx
408
+ found_relevant_data(:ocx)
409
+ end
410
+ cursor += sections[section_offset]
411
+ max_cursor = cursor if (cursor > max_cursor)
412
+ progress(cursor)
413
+ end
414
+ # Should have the COFF line numbers sections
415
+ log_debug "@#{cursor} - Beginning of COFF line numbers"
416
+ line_numbers.keys.sort.each do |line_number_offset|
417
+ if ((cursor-offset) != line_number_offset)
418
+ log_debug("@#{cursor} - COFF line number section should have been at offset #{cursor-offset}, but is declared at #{line_number_offset}")
419
+ # Have to realign cursor
420
+ cursor = offset + line_number_offset
421
+ end
422
+ cursor += 6*line_numbers[line_number_offset]
423
+ max_cursor = cursor if (cursor > max_cursor)
424
+ progress(cursor)
425
+ end
426
+ # Should have the symbol table
427
+ log_debug "@#{cursor} - Beginning of symbol tables"
428
+ if (symbol_table_offset > 0)
429
+ if ((cursor-offset) != symbol_table_offset)
430
+ log_debug("@#{cursor} - Symbol table should have been at offset #{cursor-offset}, but is declared at #{symbol_table_offset}")
431
+ cursor = offset + symbol_table_offset
432
+ end
433
+ nbr_symbols.times do |idx_symbol|
434
+ #name = @data[cursor..cursor+7]
435
+ #value = BinData::Uint32le.read(@data[cursor+8..cursor+11])
436
+ #section_number = BinData::Uint16le.read(@data[cursor+12..cursor+13])
437
+ #type = BinData::Uint16le.read(@data[cursor+14..cursor+15])
438
+ #storage_class = @data[cursor+16].ord
439
+ #nbr_auxiliary_symbols = @data[cursor+17].ord
440
+ cursor += 18
441
+ progress(cursor)
442
+ log_debug "@#{cursor} - Finished decoding symbol \##{idx_symbol}"
443
+ end
444
+ # Should have the COFF string table
445
+ log_debug "@#{cursor} - Beginning of COFF string table"
446
+ coff_string_table_size = BinData::Uint32le.read(@data[cursor..cursor+3])
447
+ # Should be greater than 4
448
+ invalid_data("@#{cursor} - COFF string table size should be >= 4 (#{coff_string_table_size})") if (coff_string_table_size < 4)
449
+ cursor += coff_string_table_size
450
+ max_cursor = cursor if (cursor > max_cursor)
451
+ progress(cursor)
452
+ end
453
+ # Should have the certificate table
454
+ log_debug "@#{cursor} - Beginning of certificate table"
455
+ if ((certificate_table_offset != nil) and
456
+ (certificate_table_offset > 0))
457
+ if ((cursor-offset) != certificate_table_offset)
458
+ log_debug("@#{cursor} - Certificate table should have been at offset #{cursor-offset}, but is declared at #{certificate_table_offset}")
459
+ cursor = offset + certificate_table_offset
460
+ end
461
+ while (cursor < offset + certificate_table_offset + certificate_table_size)
462
+ log_debug "@#{cursor} - Read certificate"
463
+ certificate_size = BinData::Uint32le.read(@data[cursor..cursor+3])
464
+ log_debug "@#{cursor} - Found certificate of size #{certificate_size}"
465
+ cursor += certificate_size
466
+ # Round to 8 bytes
467
+ rest = (cursor-offset) % 8
468
+ cursor += 8 - rest if (rest > 0)
469
+ progress(cursor)
470
+ end
471
+ max_cursor = cursor if (cursor > max_cursor)
472
+ end
473
+ # Delay import table is part of the .idata section already
474
+ # # Should have the delay import table
475
+ # log_debug "@#{cursor} - Beginning of delay import table"
476
+ # if ((delay_import_table_offset != nil) and
477
+ # (delay_import_table_offset > 0))
478
+ # if ((cursor-offset) != delay_import_table_offset)
479
+ # invalid_data("@#{cursor} - Delay import table should have been at offset #{cursor-offset}, but is declared at #{delay_import_table_offset}")
480
+ # cursor = offset + delay_import_table_offset
481
+ # end
482
+ # attributes = BinData::Uint32le.read(@data[cursor..cursor+3])
483
+ # invalid_data("@#{cursor} - Delay import attributes should be 0 (#{attributes})") if (attributes != 0)
484
+ # name_rva = BinData::Uint32le.read(@data[cursor+4..cursor+7])
485
+ # module_handle = BinData::Uint32le.read(@data[cursor+8..cursor+11])
486
+ # delay_import_address_rva = BinData::Uint32le.read(@data[cursor+12..cursor+15])
487
+ # delay_import_name_rva = BinData::Uint32le.read(@data[cursor+16..cursor+19])
488
+ # bound_delay_import_rva = BinData::Uint32le.read(@data[cursor+20..cursor+23])
489
+ # unload_delay_import_rva = BinData::Uint32le.read(@data[cursor+24..cursor+27])
490
+ # timestamp = BinData::Uint32le.read(@data[cursor+28..cursor+31])
491
+ # cursor += 32
492
+ # max_cursor = cursor if (cursor > max_cursor)
493
+ # end
494
+ # Should be the end
495
+ ending_offset = max_cursor
496
+ end
497
+
498
+ return ending_offset
499
+ end
500
+
501
+ end
502
+
503
+ end
504
+
505
+ end