pedump 0.4.16 → 0.5.0
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.
- data/Gemfile +2 -0
- data/Gemfile.lock +5 -1
- data/VERSION +1 -1
- data/lib/pedump.rb +21 -18
- data/lib/pedump/cli.rb +40 -46
- data/lib/pedump/core.rb +0 -47
- data/lib/pedump/core_ext/try.rb +57 -0
- data/lib/pedump/loader.rb +284 -22
- data/lib/pedump/loader/minidump.rb +187 -0
- data/lib/pedump/loader/section.rb +9 -3
- data/lib/pedump/ne.rb +8 -8
- data/lib/pedump/ne/version_info.rb +7 -7
- data/lib/pedump/pe.rb +2 -0
- data/lib/pedump/resources.rb +8 -8
- data/lib/pedump/security.rb +1 -1
- data/lib/pedump/tls.rb +2 -2
- data/lib/pedump/unpacker/aspack.rb +7 -2
- data/lib/pedump/version.rb +2 -2
- data/lib/pedump/version_info.rb +7 -7
- data/pedump.gemspec +12 -2
- data/spec/loader/names_spec.rb +24 -0
- data/spec/loader/va_spec.rb +44 -0
- metadata +67 -31
@@ -0,0 +1,187 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'iostruct'
|
4
|
+
|
5
|
+
class PEdump
|
6
|
+
|
7
|
+
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms680378(v=vs.85).aspx
|
8
|
+
class MINIDUMP_HEADER < IOStruct.new 'a4LLLLLQ',
|
9
|
+
:Signature,
|
10
|
+
:Version,
|
11
|
+
:NumberOfStreams,
|
12
|
+
:StreamDirectoryRva,
|
13
|
+
:CheckSum,
|
14
|
+
:TimeDateStamp,
|
15
|
+
:Flags
|
16
|
+
|
17
|
+
def valid?
|
18
|
+
self.Signature == 'MDMP'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
MINIDUMP_LOCATION_DESCRIPTOR = IOStruct.new 'LL', :DataSize, :Rva
|
23
|
+
|
24
|
+
class MINIDUMP_DIRECTORY < IOStruct.new 'L', :StreamType, :Location
|
25
|
+
def self.read io
|
26
|
+
r = super
|
27
|
+
r.Location = MINIDUMP_LOCATION_DESCRIPTOR.read(io)
|
28
|
+
r
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
MINIDUMP_MEMORY_INFO = IOStruct.new 'QQLLQLLLL',
|
33
|
+
:BaseAddress,
|
34
|
+
:AllocationBase,
|
35
|
+
:AllocationProtect,
|
36
|
+
:__alignment1,
|
37
|
+
:RegionSize,
|
38
|
+
:State,
|
39
|
+
:Protect,
|
40
|
+
:Type,
|
41
|
+
:__alignment2
|
42
|
+
|
43
|
+
class MINIDUMP_MEMORY_INFO_LIST < IOStruct.new 'LLQ',
|
44
|
+
:SizeOfHeader,
|
45
|
+
:SizeOfEntry,
|
46
|
+
:NumberOfEntries,
|
47
|
+
:entries
|
48
|
+
|
49
|
+
def self.read io
|
50
|
+
r = super
|
51
|
+
r.entries = r.NumberOfEntries.times.map{ MINIDUMP_MEMORY_INFO.read(io) }
|
52
|
+
r
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
MINIDUMP_MEMORY_DESCRIPTOR64 = IOStruct.new 'QQ',
|
57
|
+
:StartOfMemoryRange,
|
58
|
+
:DataSize
|
59
|
+
|
60
|
+
class MINIDUMP_MEMORY64_LIST < IOStruct.new 'QQ',
|
61
|
+
:NumberOfMemoryRanges,
|
62
|
+
:BaseRva,
|
63
|
+
:MemoryRanges
|
64
|
+
|
65
|
+
def self.read io
|
66
|
+
r = super
|
67
|
+
r.MemoryRanges = r.NumberOfMemoryRanges.times.map{ MINIDUMP_MEMORY_DESCRIPTOR64.read(io) }
|
68
|
+
r
|
69
|
+
end
|
70
|
+
|
71
|
+
def entries; self.MemoryRanges; end
|
72
|
+
end
|
73
|
+
|
74
|
+
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms680394(v=vs.85).aspx
|
75
|
+
MINIDUMP_STREAM_TYPE = {
|
76
|
+
0 => :UnusedStream,
|
77
|
+
1 => :ReservedStream0,
|
78
|
+
2 => :ReservedStream1,
|
79
|
+
3 => :ThreadListStream,
|
80
|
+
4 => :ModuleListStream,
|
81
|
+
5 => :MemoryListStream,
|
82
|
+
6 => :ExceptionStream,
|
83
|
+
7 => :SystemInfoStream,
|
84
|
+
8 => :ThreadExListStream,
|
85
|
+
9 => :Memory64ListStream, # MINIDUMP_MEMORY64_LIST
|
86
|
+
10 => :CommentStreamA,
|
87
|
+
11 => :CommentStreamW,
|
88
|
+
12 => :HandleDataStream,
|
89
|
+
13 => :FunctionTableStream,
|
90
|
+
14 => :UnloadedModuleListStream,
|
91
|
+
15 => :MiscInfoStream,
|
92
|
+
16 => :MemoryInfoListStream, # MINIDUMP_MEMORY_INFO_LIST
|
93
|
+
17 => :ThreadInfoListStream,
|
94
|
+
18 => :HandleOperationListStream,
|
95
|
+
0xffff => :LastReservedStream
|
96
|
+
}
|
97
|
+
|
98
|
+
class Loader
|
99
|
+
class Minidump
|
100
|
+
attr_accessor :hdr, :streams, :io
|
101
|
+
|
102
|
+
def initialize io
|
103
|
+
@io = io
|
104
|
+
@hdr = MINIDUMP_HEADER.read(@io)
|
105
|
+
raise "invalid minidump" unless @hdr.valid?
|
106
|
+
end
|
107
|
+
|
108
|
+
def streams
|
109
|
+
@streams ||=
|
110
|
+
begin
|
111
|
+
@io.seek(@hdr.StreamDirectoryRva)
|
112
|
+
@hdr.NumberOfStreams.times.map do
|
113
|
+
dir = MINIDUMP_DIRECTORY.read(io)
|
114
|
+
dir.Location.empty? ? nil : dir
|
115
|
+
end.compact
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def memory_info_list
|
120
|
+
# MINIDUMP_MEMORY_INFO_LIST
|
121
|
+
stream = streams.find{ |s| s.StreamType == 16 }
|
122
|
+
return nil unless stream
|
123
|
+
io.seek stream.Location.Rva
|
124
|
+
MINIDUMP_MEMORY_INFO_LIST.read io
|
125
|
+
end
|
126
|
+
|
127
|
+
def memory_list
|
128
|
+
# MINIDUMP_MEMORY64_LIST
|
129
|
+
stream = streams.find{ |s| s.StreamType == 9 }
|
130
|
+
return nil unless stream
|
131
|
+
io.seek stream.Location.Rva
|
132
|
+
MINIDUMP_MEMORY64_LIST.read io
|
133
|
+
end
|
134
|
+
|
135
|
+
MemoryRange = Struct.new :file_offset, :va, :size
|
136
|
+
|
137
|
+
# set options[:merge] = true to merge adjacent memory ranges
|
138
|
+
def memory_ranges options = {}
|
139
|
+
ml = memory_list
|
140
|
+
file_offset = ml.BaseRva
|
141
|
+
r = []
|
142
|
+
if options[:merge]
|
143
|
+
ml.entries.each do |x|
|
144
|
+
if r.last && r.last.va + r.last.size == x.StartOfMemoryRange
|
145
|
+
# if section VA == prev_section.VA + prev_section.SIZE
|
146
|
+
# then just increase the size of previous section
|
147
|
+
r.last.size += x.DataSize
|
148
|
+
else
|
149
|
+
r << MemoryRange.new( file_offset, x.StartOfMemoryRange, x.DataSize )
|
150
|
+
end
|
151
|
+
file_offset += x.DataSize
|
152
|
+
end
|
153
|
+
else
|
154
|
+
ml.entries.each do |x|
|
155
|
+
r << MemoryRange.new( file_offset, x.StartOfMemoryRange, x.DataSize )
|
156
|
+
file_offset += x.DataSize
|
157
|
+
end
|
158
|
+
end
|
159
|
+
r
|
160
|
+
end
|
161
|
+
|
162
|
+
end # class Minidump
|
163
|
+
end # class Loader
|
164
|
+
end # module PEdump
|
165
|
+
|
166
|
+
##############################################
|
167
|
+
|
168
|
+
if $0 == __FILE__
|
169
|
+
require 'pp'
|
170
|
+
|
171
|
+
raise "gimme a fname" if ARGV.empty?
|
172
|
+
io = open(ARGV.first,"rb")
|
173
|
+
|
174
|
+
md = PEdump::Loader::Minidump.new io
|
175
|
+
pp md.hdr
|
176
|
+
puts
|
177
|
+
puts "[.] #{md.memory_ranges.size} memory ranges"
|
178
|
+
puts "[.] #{md.memory_ranges(:merge => true).size} merged memory ranges"
|
179
|
+
puts
|
180
|
+
|
181
|
+
# pp md.memory_info_list
|
182
|
+
# pp md.memory_list
|
183
|
+
|
184
|
+
md.memory_ranges(:merge => true).each do |mr|
|
185
|
+
printf "[.] %8x %8x %8x\n", mr.file_offset, mr.va, mr.size
|
186
|
+
end
|
187
|
+
end
|
@@ -13,10 +13,12 @@ class PEdump::Loader
|
|
13
13
|
@deferred_load_io = args[:deferred_load_io]
|
14
14
|
@deferred_load_pos = args[:deferred_load_pos] || (@hdr && @hdr.PointerToRawData)
|
15
15
|
@deferred_load_size = args[:deferred_load_size] || (@hdr && @hdr.SizeOfRawData)
|
16
|
+
@image_base = args[:image_base] || 0
|
16
17
|
end
|
17
18
|
|
18
19
|
def name; @hdr.Name; end
|
19
|
-
def va ; @hdr.VirtualAddress; end
|
20
|
+
def va ; @hdr.VirtualAddress + @image_base; end
|
21
|
+
def rva ; @hdr.VirtualAddress; end
|
20
22
|
def vsize; @hdr.VirtualSize; end
|
21
23
|
def flags; @hdr.Characteristics; end
|
22
24
|
def flags= f; @hdr.Characteristics= f; end
|
@@ -42,10 +44,14 @@ class PEdump::Loader
|
|
42
44
|
end
|
43
45
|
|
44
46
|
def inspect
|
45
|
-
"#<Section
|
46
|
-
|
47
|
+
r = "#<Section"
|
48
|
+
r << (" name=%-10s" % name.inspect) if name
|
49
|
+
r << " va=%8x vsize=%8x rawsize=%8s" % [
|
50
|
+
va, vsize,
|
47
51
|
@data.size > 0 ? @data.size.to_s(16) : (@deferred_load_io ? "<defer>" : 0)
|
48
52
|
]
|
53
|
+
r << (" dlpos=%8x" % @deferred_load_pos) if @deferred_load_pos
|
54
|
+
r << ">"
|
49
55
|
end
|
50
56
|
end
|
51
57
|
end
|
data/lib/pedump/ne.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
class PEdump
|
2
2
|
# from wine's winnt.h
|
3
|
-
class NE <
|
3
|
+
class NE < IOStruct.new 'a2CCvvVv4VVv8Vv3CCv4',
|
4
4
|
:ne_magic, # 00 NE signature 'NE'
|
5
5
|
:ne_ver, # 02 Linker version number
|
6
6
|
:ne_rev, # 03 Linker revision number
|
@@ -52,7 +52,7 @@ class PEdump
|
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
|
-
class Segment <
|
55
|
+
class Segment < IOStruct.new 'v4',
|
56
56
|
:offset, :size, :flags, :min_alloc_size,
|
57
57
|
# manual:
|
58
58
|
:file_offset, :relocs
|
@@ -86,7 +86,7 @@ class PEdump
|
|
86
86
|
end
|
87
87
|
end
|
88
88
|
|
89
|
-
class Reloc <
|
89
|
+
class Reloc < IOStruct.new 'CCvvv',
|
90
90
|
:source, :type,
|
91
91
|
:offset, # offset of the relocation item within the segment
|
92
92
|
|
@@ -121,7 +121,7 @@ class PEdump
|
|
121
121
|
end
|
122
122
|
end
|
123
123
|
|
124
|
-
class ResourceGroup <
|
124
|
+
class ResourceGroup < IOStruct.new 'vvV',
|
125
125
|
:type_id, :count, :reserved,
|
126
126
|
# manual:
|
127
127
|
:type, :children
|
@@ -143,7 +143,7 @@ class PEdump
|
|
143
143
|
end
|
144
144
|
end
|
145
145
|
|
146
|
-
class ResourceInfo <
|
146
|
+
class ResourceInfo < IOStruct.new 'v4V',
|
147
147
|
:offset, :size, :flags, :name_offset, :reserved,
|
148
148
|
# manual:
|
149
149
|
:name
|
@@ -338,11 +338,11 @@ class PEdump
|
|
338
338
|
# 0xFE = the entry does not refer to a segment but refers to a constant defined within the module.
|
339
339
|
# else it is a segment index.
|
340
340
|
|
341
|
-
class Bundle <
|
341
|
+
class Bundle < IOStruct.new 'CC', :num_entries, :seg_idx,
|
342
342
|
:entries # manual
|
343
343
|
|
344
|
-
FixedEntry =
|
345
|
-
MovableEntry =
|
344
|
+
FixedEntry = IOStruct.new 'Cv', :flag, :offset
|
345
|
+
MovableEntry = IOStruct.new 'CvCv', :flag, :int3F, :seg_idx, :offset
|
346
346
|
|
347
347
|
def movable?
|
348
348
|
seg_idx == 0xff
|
@@ -1,7 +1,7 @@
|
|
1
1
|
class PEdump; end
|
2
2
|
|
3
3
|
class PEdump::NE
|
4
|
-
class VS_VERSIONINFO <
|
4
|
+
class VS_VERSIONINFO < IOStruct.new( 'v2a16',
|
5
5
|
:wLength,
|
6
6
|
:wValueLength,
|
7
7
|
:szKey, # ASCII string "VS_VERSION_INFO".
|
@@ -41,7 +41,7 @@ class PEdump::NE
|
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
-
class VS_FIXEDFILEINFO <
|
44
|
+
class VS_FIXEDFILEINFO < IOStruct.new( 'V13',
|
45
45
|
:dwSignature,
|
46
46
|
:dwStrucVersion,
|
47
47
|
:dwFileVersionMS,
|
@@ -65,7 +65,7 @@ class PEdump::NE
|
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
|
-
class StringFileInfo <
|
68
|
+
class StringFileInfo < IOStruct.new( 'v2a15',
|
69
69
|
:wLength,
|
70
70
|
:wValueLength, # always 0
|
71
71
|
:szKey, # The ASCII string "StringFileInfo"
|
@@ -85,7 +85,7 @@ class PEdump::NE
|
|
85
85
|
end
|
86
86
|
end
|
87
87
|
|
88
|
-
class StringTable <
|
88
|
+
class StringTable < IOStruct.new( 'v2a9',
|
89
89
|
:wLength, # The length, in bytes, of this StringTable structure,
|
90
90
|
# including all structures indicated by the Children member.
|
91
91
|
:wValueLength, # always 0
|
@@ -106,7 +106,7 @@ class PEdump::NE
|
|
106
106
|
end
|
107
107
|
end
|
108
108
|
|
109
|
-
class VersionString <
|
109
|
+
class VersionString < IOStruct.new( 'v2',
|
110
110
|
:wLength, # The length, in bytes, of this String structure.
|
111
111
|
:wValueLength, # The size, in words, of the Value member
|
112
112
|
:szKey, # An arbitrary ASCII string
|
@@ -137,7 +137,7 @@ class PEdump::NE
|
|
137
137
|
end
|
138
138
|
end
|
139
139
|
|
140
|
-
class VarFileInfo <
|
140
|
+
class VarFileInfo < IOStruct.new( 'v2a12',
|
141
141
|
:wLength,
|
142
142
|
:wValueLength, # always 0
|
143
143
|
:szKey, # The ASCII string "VarFileInfo"
|
@@ -153,7 +153,7 @@ class PEdump::NE
|
|
153
153
|
end
|
154
154
|
end
|
155
155
|
|
156
|
-
class Var <
|
156
|
+
class Var < IOStruct.new( 'v2a12',
|
157
157
|
:wLength,
|
158
158
|
:wValueLength, # The length, in bytes, of the Value member
|
159
159
|
:szKey, # The ASCII string "Translation"
|
data/lib/pedump/pe.rb
CHANGED
data/lib/pedump/resources.rb
CHANGED
@@ -236,7 +236,7 @@ class PEdump
|
|
236
236
|
|
237
237
|
# see also http://www.informit.com/articles/article.aspx?p=1186882 about icons format
|
238
238
|
|
239
|
-
class BITMAPINFOHEADER <
|
239
|
+
class BITMAPINFOHEADER < IOStruct.new 'V3v2V6',
|
240
240
|
:biSize, # BITMAPINFOHEADER::SIZE
|
241
241
|
:biWidth,
|
242
242
|
:biHeight,
|
@@ -255,13 +255,13 @@ class PEdump
|
|
255
255
|
end
|
256
256
|
|
257
257
|
# http://www.devsource.com/c/a/Architecture/Resources-From-PE-I/2/
|
258
|
-
CUR_ICO_HEADER =
|
258
|
+
CUR_ICO_HEADER = IOStruct.new('v3',
|
259
259
|
:wReserved, # always 0
|
260
260
|
:wResID, # always 2
|
261
261
|
:wNumImages # Number of cursor images/directory entries
|
262
262
|
)
|
263
263
|
|
264
|
-
CURDIRENTRY =
|
264
|
+
CURDIRENTRY = IOStruct.new 'v4Vv',
|
265
265
|
:wWidth,
|
266
266
|
:wHeight, # Divide by 2 to get the actual height.
|
267
267
|
:wPlanes,
|
@@ -269,9 +269,9 @@ class PEdump
|
|
269
269
|
:dwBytesInImage,
|
270
270
|
:wID
|
271
271
|
|
272
|
-
CURSOR_HOTSPOT =
|
272
|
+
CURSOR_HOTSPOT = IOStruct.new 'v2', :x, :y
|
273
273
|
|
274
|
-
ICODIRENTRY =
|
274
|
+
ICODIRENTRY = IOStruct.new 'C4v2Vv',
|
275
275
|
:bWidth,
|
276
276
|
:bHeight,
|
277
277
|
:bColors,
|
@@ -286,14 +286,14 @@ class PEdump
|
|
286
286
|
%w'MESSAGETABLE GROUP_CURSOR' + [nil] + %w'GROUP_ICON' + [nil] +
|
287
287
|
%w'VERSION DLGINCLUDE' + [nil] + %w'PLUGPLAY VXD ANICURSOR ANIICON HTML MANIFEST'
|
288
288
|
|
289
|
-
IMAGE_RESOURCE_DIRECTORY_ENTRY =
|
289
|
+
IMAGE_RESOURCE_DIRECTORY_ENTRY = IOStruct.new 'V2',
|
290
290
|
:Name, :OffsetToData,
|
291
291
|
:name, :data
|
292
292
|
|
293
|
-
IMAGE_RESOURCE_DATA_ENTRY =
|
293
|
+
IMAGE_RESOURCE_DATA_ENTRY = IOStruct.new 'V4',
|
294
294
|
:OffsetToData, :Size, :CodePage, :Reserved
|
295
295
|
|
296
|
-
IMAGE_RESOURCE_DIRECTORY =
|
296
|
+
IMAGE_RESOURCE_DIRECTORY = IOStruct.new 'V2v4',
|
297
297
|
:Characteristics, :TimeDateStamp, # 2dw
|
298
298
|
:MajorVersion, :MinorVersion, :NumberOfNamedEntries, :NumberOfIdEntries, # 4w
|
299
299
|
:entries # manual
|
data/lib/pedump/security.rb
CHANGED
@@ -27,7 +27,7 @@ class PEdump
|
|
27
27
|
# WIN_CERT_TYPE_PKCS1_SIGN (0x0009) bCertificate contains PKCS1_MODULE_SIGN fields.
|
28
28
|
|
29
29
|
# http://msdn.microsoft.com/en-us/library/aa447037.aspx
|
30
|
-
class WIN_CERTIFICATE <
|
30
|
+
class WIN_CERTIFICATE < IOStruct.new 'Vvv',
|
31
31
|
:dwLength,
|
32
32
|
:wRevision,
|
33
33
|
:wCertificateType,
|
data/lib/pedump/tls.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
class PEdump
|
2
|
-
IMAGE_TLS_DIRECTORY32 =
|
2
|
+
IMAGE_TLS_DIRECTORY32 = IOStruct.new 'V6',
|
3
3
|
:StartAddressOfRawData,
|
4
4
|
:EndAddressOfRawData,
|
5
5
|
:AddressOfIndex,
|
@@ -7,7 +7,7 @@ class PEdump
|
|
7
7
|
:SizeOfZeroFill,
|
8
8
|
:Characteristics
|
9
9
|
|
10
|
-
IMAGE_TLS_DIRECTORY64 =
|
10
|
+
IMAGE_TLS_DIRECTORY64 = IOStruct.new 'Q4V2',
|
11
11
|
:StartAddressOfRawData,
|
12
12
|
:EndAddressOfRawData,
|
13
13
|
:AddressOfIndex,
|
@@ -22,7 +22,12 @@ class PEdump::Unpacker::ASPack
|
|
22
22
|
attr_accessor :logger
|
23
23
|
|
24
24
|
def initialize io, params = {}
|
25
|
-
params[:logger]
|
25
|
+
params[:logger] ||= PEdump::Logger.create(params)
|
26
|
+
|
27
|
+
# XXX aspack unpacker code does not distinguish RVA from VA, so set
|
28
|
+
# image base to zero for RVA be equal VA
|
29
|
+
params[:image_base] ||= 0
|
30
|
+
|
26
31
|
@logger = params[:logger]
|
27
32
|
@ldr = PEdump::Loader.new(io, params)
|
28
33
|
@io = io
|
@@ -311,7 +316,7 @@ class PEdump::Unpacker::ASPack
|
|
311
316
|
74 jz short exit_relocs_loop
|
312
317
|
EOC
|
313
318
|
|
314
|
-
SECTION_INFO =
|
319
|
+
SECTION_INFO = IOStruct.new 'VlV', :va, :size, :flags
|
315
320
|
|
316
321
|
########################################################################
|
317
322
|
|