1#!/usr/bin/env python3
2#
3# Author:
4#  Tamas Jos (@skelsec)
5#
6import io
7from minidump.common_structs import *
8
9class MinidumpModule:
10	def __init__(self):
11		self.name = None
12		self.baseaddress = None
13		self.size = None
14		self.endaddress = None
15
16		self.versioninfo = None
17		self.checksum = None
18		self.timestamp = None
19
20	@staticmethod
21	def parse(mod, buff):
22		"""
23		mod: MINIDUMP_MODULE
24		buff: file handle
25		"""
26		mm = MinidumpModule()
27		mm.baseaddress = mod.BaseOfImage
28		mm.size = mod.SizeOfImage
29		mm.checksum = mod.CheckSum
30		mm.timestamp = mod.TimeDateStamp
31		mm.name = MINIDUMP_STRING.get_from_rva(mod.ModuleNameRva, buff)
32		mm.versioninfo = mod.VersionInfo
33		mm.endaddress = mm.baseaddress + mm.size
34		return mm
35
36	@staticmethod
37	async def aparse(mod, buff):
38		"""
39		mod: MINIDUMP_MODULE
40		buff: file handle
41		"""
42		mm = MinidumpModule()
43		mm.baseaddress = mod.BaseOfImage
44		mm.size = mod.SizeOfImage
45		mm.checksum = mod.CheckSum
46		mm.timestamp = mod.TimeDateStamp
47		mm.name = await MINIDUMP_STRING.aget_from_rva(mod.ModuleNameRva, buff)
48		mm.versioninfo = mod.VersionInfo
49		mm.endaddress = mm.baseaddress + mm.size
50		return mm
51
52	def inrange(self, memory_loc):
53		return self.baseaddress <= memory_loc < self.endaddress
54
55	@staticmethod
56	def get_header():
57		return [
58			'Module name',
59			'BaseAddress',
60			'Size',
61			'Endaddress',
62			'Timestamp',
63		]
64
65	def to_row(self):
66		return [
67			str(self.name),
68			'0x%08x' % self.baseaddress,
69			hex(self.size),
70			'0x%08x' % self.endaddress,
71			'0x%08x' % self.timestamp,
72		]
73
74
75	def __str__(self):
76		return 'Module name: %s BaseAddress: 0x%08x Size: 0x%x Endaddress: 0x%08x' % (self.name, self.baseaddress, self.size, self.endaddress)
77
78# https://msdn.microsoft.com/en-us/library/windows/desktop/ms646997(v=vs.85).aspx
79class VS_FIXEDFILEINFO:
80	def __init__(self):
81		self.dwSignature = None
82		self.dwStrucVersion = None
83		self.dwFileVersionMS = None
84		self.dwFileVersionLS = None
85		self.dwProductVersionMS = None
86		self.dwProductVersionLS = None
87		self.dwFileFlagsMask = None
88		self.dwFileFlags = None
89		self.dwFileOS = None
90		self.dwFileType = None
91		self.dwFileSubtype = None
92		self.dwFileDateMS = None
93		self.dwFileDateLS = None
94
95	def get_size(self):
96		return 13*4
97
98	def to_bytes(self):
99		t = self.dwSignature.to_bytes(4, byteorder = 'little', signed = False)
100		t += self.dwStrucVersion.to_bytes(4, byteorder = 'little', signed = False)
101		t += self.dwFileVersionMS.to_bytes(4, byteorder = 'little', signed = False)
102		t += self.dwFileVersionLS.to_bytes(4, byteorder = 'little', signed = False)
103		t += self.dwProductVersionMS.to_bytes(4, byteorder = 'little', signed = False)
104		t += self.dwProductVersionLS.to_bytes(4, byteorder = 'little', signed = False)
105		t += self.dwFileFlagsMask.to_bytes(4, byteorder = 'little', signed = False)
106		t += self.dwFileFlags.to_bytes(4, byteorder = 'little', signed = False)
107		t += self.dwFileOS.to_bytes(4, byteorder = 'little', signed = False)
108		t += self.dwFileType.to_bytes(4, byteorder = 'little', signed = False)
109		t += self.dwFileSubtype.to_bytes(4, byteorder = 'little', signed = False)
110		t += self.dwFileDateMS.to_bytes(4, byteorder = 'little', signed = False)
111		t += self.dwFileDateLS.to_bytes(4, byteorder = 'little', signed = False)
112		return t
113
114	@staticmethod
115	def from_bytes(data):
116		return VS_FIXEDFILEINFO.parse(io.BytesIO(data))
117
118	@staticmethod
119	def parse(buff):
120		vf = VS_FIXEDFILEINFO()
121		vf.dwSignature = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
122		vf.dwStrucVersion = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
123		vf.dwFileVersionMS = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
124		vf.dwFileVersionLS = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
125		vf.dwProductVersionMS = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
126		vf.dwProductVersionLS = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
127		vf.dwFileFlagsMask = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
128		vf.dwFileFlags = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
129		vf.dwFileOS = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
130		vf.dwFileType = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
131		vf.dwFileSubtype = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
132		vf.dwFileDateMS = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
133		vf.dwFileDateLS = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
134		return vf
135
136	def __str__(self):
137		t = ''
138		for k in self.__dict__:
139			t += '%s : %s\r\n' % (k, str(self.__dict__[k]))
140		return t
141
142# https://msdn.microsoft.com/en-us/library/windows/desktop/ms680392(v=vs.85).aspx
143class MINIDUMP_MODULE:
144	def __init__(self):
145		self.BaseOfImage = None
146		self.SizeOfImage = None
147		self.CheckSum = 0
148		self.TimeDateStamp = None
149		self.ModuleNameRva = None
150		self.VersionInfo = None
151		self.CvRecord = None
152		self.MiscRecord = None
153		self.Reserved0 = 0
154		self.Reserved1 = 0
155
156		#for writer
157		self.ModuleName = None
158
159	def get_size(self):
160		return 8+4+4+4+4+8+8+VS_FIXEDFILEINFO().get_size() + 2 * MINIDUMP_LOCATION_DESCRIPTOR().get_size()
161
162	def to_bytes(self):
163		t = self.BaseOfImage.to_bytes(8, byteorder = 'little', signed = False)
164		t += self.SizeOfImage.to_bytes(4, byteorder = 'little', signed = False)
165		t += self.CheckSum.to_bytes(4, byteorder = 'little', signed = False)
166		t += self.TimeDateStamp.to_bytes(4, byteorder = 'little', signed = False)
167		t += self.ModuleNameRva.to_bytes(4, byteorder = 'little', signed = False)
168		t += self.VersionInfo.to_bytes()
169		t += self.CvRecord.to_bytes()
170		t += self.MiscRecord.to_bytes()
171		t += self.Reserved0.to_bytes(8, byteorder = 'little', signed = False)
172		t += self.Reserved1.to_bytes(8, byteorder = 'little', signed = False)
173		return t
174
175	@staticmethod
176	def parse(buff):
177		mm = MINIDUMP_MODULE()
178		mm.BaseOfImage = int.from_bytes(buff.read(8), byteorder = 'little', signed = False)
179		mm.SizeOfImage = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
180		mm.CheckSum = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
181		mm.TimeDateStamp = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
182		mm.ModuleNameRva = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
183		mm.VersionInfo = VS_FIXEDFILEINFO.parse(buff)
184		mm.CvRecord = MINIDUMP_LOCATION_DESCRIPTOR.parse(buff)
185		mm.MiscRecord = MINIDUMP_LOCATION_DESCRIPTOR.parse(buff)
186		mm.Reserved0 = int.from_bytes(buff.read(8), byteorder = 'little', signed = False)
187		mm.Reserved1 = int.from_bytes(buff.read(8), byteorder = 'little', signed = False)
188		return mm
189
190	def __str__(self):
191		t = ''
192		for k in self.__dict__:
193			t += '%s : %s\r\n' % (k, str(self.__dict__[k]))
194		return t
195
196# https://msdn.microsoft.com/en-us/library/windows/desktop/ms680391(v=vs.85).aspx
197class MINIDUMP_MODULE_LIST:
198	def __init__(self):
199		self.NumberOfModules = None
200		self.Modules = []
201
202	def get_size(self):
203		return 4 + len(self.Modules) * MINIDUMP_MODULE().get_size()
204
205	def to_bytes(self):
206		t = len(self.Modules).to_bytes(4, byteorder = 'little', signed = False)
207		for module in self.Modules:
208			t += module.to_bytes()
209		return t
210
211	@staticmethod
212	def parse(buff):
213		mml = MINIDUMP_MODULE_LIST()
214		mml.NumberOfModules = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
215		for _ in range(mml.NumberOfModules):
216			mml.Modules.append(MINIDUMP_MODULE.parse(buff))
217
218		return mml
219
220class MinidumpModuleList:
221	def __init__(self):
222		self.modules = []
223
224	@staticmethod
225	def parse(dir, buff):
226		t = MinidumpModuleList()
227		buff.seek(dir.Location.Rva)
228		chunk = io.BytesIO(buff.read(dir.Location.DataSize))
229		mtl = MINIDUMP_MODULE_LIST.parse(chunk)
230		for mod in mtl.Modules:
231			t.modules.append(MinidumpModule.parse(mod, buff))
232		return t
233
234	@staticmethod
235	async def aparse(dir, buff):
236		t = MinidumpModuleList()
237		await buff.seek(dir.Location.Rva)
238		chunk_data = await buff.read(dir.Location.DataSize)
239		chunk = io.BytesIO(chunk_data)
240		mtl = MINIDUMP_MODULE_LIST.parse(chunk)
241		for mod in mtl.Modules:
242			x = await MinidumpModule.aparse(mod, buff)
243			t.modules.append(x)
244		return t
245
246	def to_table(self):
247		t = []
248		t.append(MinidumpModule.get_header())
249		for mod in self.modules:
250			t.append(mod.to_row())
251		return t
252
253	def __str__(self):
254		t  = '== ModuleList ==\n' + construct_table(self.to_table())
255		return t
256