Codebase list python-minidump / 98548353-c174-46d5-a35f-a37729f5d432/upstream
Import upstream version 0.0.13 Kali Janitor 3 years ago
9 changed file(s) with 106 addition(s) and 70 deletion(s). Raw diff Collapse all Expand all
0 MIT License
1
2 Copyright (c) 2018 Tamas Jos
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in all
12 copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 SOFTWARE.
0 include LICENSE README.md
00 Metadata-Version: 1.2
11 Name: minidump
2 Version: 0.0.12
2 Version: 0.0.13
33 Summary: Python library to parse Windows minidump file format
44 Home-page: https://github.com/skelsec/minidump
55 Author: Tamas Jos
44 Python >= 3.6
55
66 # Basic Usage
7 This module is primarily intended to be used as a library, however for the sake of demonstarting its capabilities there is a command line tool implemented called `minidump`. This tool has the following modes of operation.
8
9 #### Console
10 One-shot parsing and information retrieval.
11
712 ```minidump.py --all <mindidump file> ```
813 See help for possible options.
14 #### Shell
15 There is and interactive command shell to get all info (modules, threads, excetpions etc) and browse the virtual memory of the process dumped (read/read int/read uint/move/peek/tell)
16
17 ```minidump.py -i <mindidump file> ```
18 Once in the shell, all commands are documented. Use the `?` command to see all options.
919
1020 # Advanced usage
1121 After parsing the minidump file, you can use the MinidumpFileReader and MinidumpBufferedReader objects to perform various searches/reads in the dumped process' address space.
00
1 __version__ = "0.0.12"
1 __version__ = "0.0.13"
22 __banner__ = \
33 """
44 # minidump %s
44 #
55 import struct
66 import ntpath
7 from .common_structs import *
7 from .common_structs import *
88 from .streams.SystemInfoStream import PROCESSOR_ARCHITECTURE
99
1010 class MinidumpBufferedMemorySegment:
1111 def __init__(self, memory_segment, file_handle):
1212 self.start_address = memory_segment.start_virtual_address
1313 self.end_address = memory_segment.end_virtual_address
14
14
1515 file_handle.seek(memory_segment.start_file_address)
1616 self.data = file_handle.read(memory_segment.size)
17
17
1818 def inrange(self, position):
1919 return self.start_address <= position <= self.end_address
20
20
2121 def remaining_len(self, position):
2222 if not self.inrange(position):
2323 return None
2424 return self.end_address - position
25
25
2626 class MinidumpBufferedReader:
2727 def __init__(self, reader):
2828 self.reader = reader
2929 self.memory_segments = []
30
30
3131 self.current_segment = None
3232 self.current_position = None
33
33
3434 def _select_segment(self, requested_position):
3535 """
36
36
3737 """
3838 # check if we have semgnet for requested address in cache
3939 for memory_segment in self.memory_segments:
4141 self.current_segment = memory_segment
4242 self.current_position = requested_position
4343 return
44
44
4545 # not in cache, check if it's present in memory space. if yes then create a new buffered memeory object, and copy data
4646 for memory_segment in self.reader.memory_segments:
4747 if memory_segment.inrange(requested_position):
5050 self.current_segment = newsegment
5151 self.current_position = requested_position
5252 return
53
53
5454 raise Exception('Memory address 0x%08x is not in process memory space' % requested_position)
55
55
5656 def seek(self, offset, whence = 0):
5757 """
5858 Changes the current address to an offset of offset. The whence parameter controls from which position should we count the offsets.
6969 t = self.current_segment.end_address - offset
7070 else:
7171 raise Exception('Seek function whence value must be between 0-2')
72
72
7373 if not self.current_segment.inrange(t):
7474 raise Exception('Seek would cross memory segment boundaries (use move)')
75
75
7676 self.current_position = t
7777 return
78
78
7979 def move(self, address):
8080 """
8181 Moves the buffer to a virtual address specified by address
8282 """
8383 self._select_segment(address)
8484 return
85
85
8686 def align(self, alignment = None):
8787 """
8888 Repositions the current reader to match architecture alignment
9898 offset_to_aligned = (alignment - offset) % alignment
9999 self.seek(offset_to_aligned, 1)
100100 return
101
101
102102 def tell(self):
103103 """
104104 Returns the current virtual address
105105 """
106106 return self.current_position
107
107
108108 def peek(self, length):
109109 """
110110 Returns up to length bytes from the current memory segment
113113 if not self.current_segment.inrange(t):
114114 raise Exception('Would read over segment boundaries!')
115115 return self.current_segment.data[self.current_position - self.current_segment.start_address :t - self.current_segment.start_address]
116
116
117117 def read(self, size = -1):
118118 """
119119 Returns data bytes of size size from the current segment. If size is -1 it returns all the remaining data bytes from memory segment
124124 t = self.current_segment.remaining_len(self.current_position)
125125 if not t:
126126 return None
127
127
128128 old_new_pos = self.current_position
129129 self.current_position = self.current_segment.end_address
130130 return self.current_segment.data[old_new_pos - self.current_segment.start_address:]
131
131
132132 t = self.current_position + size
133133 if not self.current_segment.inrange(t):
134134 raise Exception('Would read over segment boundaries!')
135
135
136136 old_new_pos = self.current_position
137 self.current_position = t
137 self.current_position = t
138138 return self.current_segment.data[old_new_pos - self.current_segment.start_address :t - self.current_segment.start_address]
139
139
140140 def read_int(self):
141141 """
142 Reads an integer. The size depends on the architecture.
142 Reads an integer. The size depends on the architecture.
143143 Reads a 4 byte small-endian singed int on 32 bit arch
144144 Reads an 8 byte small-endian singed int on 64 bit arch
145145 """
147147 return int.from_bytes(self.read(8), byteorder = 'little', signed = True)
148148 else:
149149 return int.from_bytes(self.read(4), byteorder = 'little', signed = True)
150
150
151151 def read_uint(self):
152152 """
153 Reads an integer. The size depends on the architecture.
153 Reads an integer. The size depends on the architecture.
154154 Reads a 4 byte small-endian unsinged int on 32 bit arch
155155 Reads an 8 byte small-endian unsinged int on 64 bit arch
156156 """
158158 return int.from_bytes(self.read(8), byteorder = 'little', signed = False)
159159 else:
160160 return int.from_bytes(self.read(4), byteorder = 'little', signed = False)
161
161
162162 def find(self, pattern):
163163 """
164164 Searches for a pattern in the current memory segment
167167 if pos == -1:
168168 return -1
169169 return pos + self.current_position
170
170
171171 def find_all(self, pattern):
172172 """
173173 Searches for all occurrences of a pattern in the current memory segment, returns all occurrences as a list
179179 if last_found == -1:
180180 break
181181 pos.append(last_found + self.current_segment.start_address)
182
182
183183 return pos
184
184
185185 def find_global(self, pattern):
186186 """
187187 Searches for the pattern in the whole process memory space and returns the first occurrence.
190190 pos_s = self.reader.search(pattern)
191191 if len(pos_s) == 0:
192192 return -1
193
193
194194 return pos_s[0]
195
195
196196 def find_all_global(self, pattern):
197197 """
198198 Searches for the pattern in the whole process memory space and returns a list of addresses where the pattern begins.
199199 This is exhaustive!
200200 """
201201 return self.reader.search(pattern)
202
202
203203 def get_ptr(self, pos):
204204 self.move(pos)
205205 return self.read_uint()
206206 #raw_data = self.read(pos, self.sizeof_ptr)
207207 #return struct.unpack(self.unpack_ptr, raw_data)[0]
208
208
209209 def get_ptr_with_offset(self, pos):
210210 if self.reader.sysinfo.ProcessorArchitecture == PROCESSOR_ARCHITECTURE.AMD64:
211211 self.move(pos)
214214 else:
215215 self.move(pos)
216216 return self.read_uint()
217
217
218218 def find_in_module(self, module_name, pattern):
219219 t = self.reader.search_module(module_name, pattern)
220220 return t
221
222
223
224
221
222
223
224
225225 class MinidumpFileReader:
226226 def __init__(self, minidumpfile):
227227 self.modules = minidumpfile.modules.modules
228228 self.sysinfo = minidumpfile.sysinfo
229
229
230230 if minidumpfile.memory_segments_64:
231231 self.memory_segments = minidumpfile.memory_segments_64.memory_segments
232232 self.is_fulldump = True
233
233
234234 else:
235235 self.memory_segments = minidumpfile.memory_segments.memory_segments
236236 self.is_fulldump = False
237
237
238238 self.filename = minidumpfile.filename
239239 self.file_handle = minidumpfile.file_handle
240
240
241241 #reader params
242242 self.sizeof_long = 4
243243 self.unpack_long = '<L'
244 if minidumpfile.sysinfo.ProcessorArchitecture == PROCESSOR_ARCHITECTURE.AMD64:
244 if minidumpfile.sysinfo.ProcessorArchitecture in [PROCESSOR_ARCHITECTURE.AMD64, PROCESSOR_ARCHITECTURE.AARCH64]:
245245 self.sizeof_ptr = 8
246246 self.unpack_ptr = '<Q'
247 elif self.sysinfo.ProcessorArchitecture == PROCESSOR_ARCHITECTURE.INTEL:
247 elif self.sysinfo.ProcessorArchitecture in [PROCESSOR_ARCHITECTURE.INTEL,
248 PROCESSOR_ARCHITECTURE.ARM]:
248249 self.sizeof_ptr = 4
249250 self.unpack_ptr = '<L'
250251 else:
251252 raise Exception('Unknown processor architecture %s! Please fix and submit PR!' % self.sysinfo.ProcessorArchitecture)
252
253
253254 def get_buffered_reader(self):
254255 return MinidumpBufferedReader(self)
255
256
256257 def get_module_by_name(self, module_name):
257258 for mod in self.modules:
258259 if ntpath.basename(mod.name).find(module_name) != -1:
259260 return mod
260261 return None
261
262
262263 def search_module(self, module_name, pattern):
263264 mod = self.get_module_by_name(module_name)
264265 if mod is None:
267268 for ms in self.memory_segments:
268269 if mod.baseaddress <= ms.start_virtual_address < mod.endaddress:
269270 t+= ms.search(pattern, self.file_handle)
270
271
271272 return t
272
273
273274 def search(self, pattern):
274275 t = []
275276 for ms in self.memory_segments:
276277 t+= ms.search(pattern, self.file_handle)
277
278
278279 return t
279
280
280281 def read(self, virt_addr, size):
281282 for segment in self.memory_segments:
282283 if segment.inrange(virt_addr):
66 import io
77 import enum
88 import logging
9 from minidump.common_structs import *
9 from minidump.common_structs import *
1010
1111 # https://msdn.microsoft.com/en-us/library/windows/desktop/ms680396(v=vs.85).aspx
1212 class PROCESSOR_ARCHITECTURE(enum.Enum):
1414 ARM = 5 #ARM
1515 IA64 = 6 #Intel Itanium
1616 INTEL = 0 #x86
17 AARCH64 = 0x8003 #ARM64
1718 UNKNOWN = 0xffff #Unknown processor
1819 # https://msdn.microsoft.com/en-us/library/windows/desktop/ms680396(v=vs.85).aspx
1920 class PROCESSOR_LEVEL(enum.Enum):
2122 INTEL_80486 = 4
2223 INTEL_PENTIUM = 5
2324 INTEL_PENTIUM_PRO = 6 #or Pentium II
24 # https://msdn.microsoft.com/en-us/library/windows/desktop/ms680396(v=vs.85).aspx
25 # https://msdn.microsoft.com/en-us/library/windows/desktop/ms680396(v=vs.85).aspx
2526 class PRODUCT_TYPE(enum.Enum):
2627 VER_UNIDENTIFIED_PRODUCT = 0x0000000 # Crashpad des not set ProductType value on non-Windows systems
2728 VER_NT_WORKSTATION = 0x0000001 # The system is running Windows XP, Windows Vista, Windows 7, or Windows 8.
2829 VER_NT_DOMAIN_CONTROLLER = 0x0000002 # The system is a domain controller.
2930 VER_NT_SERVER = 0x0000003 # The system is a server.
30
31
3132 # https://msdn.microsoft.com/en-us/library/windows/desktop/ms680396(v=vs.85).aspx
3233 class PLATFORM_ID(enum.Enum):
3334 VER_PLATFORM_WIN32s = 0 #Not supported
3435 VER_PLATFORM_WIN32_WINDOWS = 1 #Not supported.
3536 VER_PLATFORM_WIN32_NT = 2 #The operating system platform is Windows.
36
37
3738 # source : https://github.com/chromium/crashpad/blob/4b05be4265c0ffacfce26d7db7644ffbf9037696/minidump/minidump_extensions.h#L239
3839 VER_PLATFORM_CRASHPAD_MAC = 0x8101
3940 VER_PLATFORM_CRASHPAD_IOS = 0x8102
124125 else:
125126 for pf in self.ProcessorFeatures:
126127 t += pf.to_bytes(8, byteorder = 'little', signed = False)
127
128
128129 if data_buffer is None:
129130 return t
130131 else:
131132 data_buffer.write(t)
132
133
133134 @staticmethod
134135 def parse(buff):
135136 msi = MINIDUMP_SYSTEM_INFO()
157158 else:
158159 for _ in range(2):
159160 msi.ProcessorFeatures.append(int.from_bytes(buff.read(8), byteorder = 'little', signed = False))
160
161
161162 return msi
162163
163164 def __str__(self):
165166 for k in self.__dict__:
166167 t += '%s : %s\r\n' % (k, str(self.__dict__[k]))
167168 return t
168
169
169170 class MinidumpSystemInfo:
170171 def __init__(self):
171172 self.ProcessorArchitecture = None
184185 self.FeatureInformation = None
185186 self.AMDExtendedCpuFeatures = None
186187 self.ProcessorFeatures = None
187
188
188189 #extra
189190 self.OperatingSystem = None
190
191
191192 def guess_os(self):
192193 if self.MajorVersion == 10 and self.MinorVersion == 0 and self.ProductType == PRODUCT_TYPE.VER_NT_WORKSTATION:
193194 self.OperatingSystem = "Windows 10"
218219 self.OperatingSystem = "Windows XP"
219220 elif self.MajorVersion == 5 and self.MinorVersion == 0:
220221 self.OperatingSystem = "Windows 2000"
221
222
222223 @staticmethod
223224 def parse(dir, buff):
224225 t = MinidumpSystemInfo()
247248 logging.log(1, 'Failed to guess OS! MajorVersion: %s MinorVersion %s BuildNumber %s ProductType: %s' % (t.MajorVersion, t.MinorVersion, t.BuildNumber, t.ProductType ))
248249 t.OperatingSystem = None
249250 return t
250
251
251
252
252253 def __str__(self):
253254 t = '== System Info ==\n'
254255 t += 'ProcessorArchitecture %s\n' % self.ProcessorArchitecture
268269 t += 'FeatureInformation %s\n' % self.FeatureInformation
269270 t += 'AMDExtendedCpuFeatures %s\n' % self.AMDExtendedCpuFeatures
270271 t += 'ProcessorFeatures %s\n' % ' '.join( [hex(x) for x in self.ProcessorFeatures] )
271
272 return t
272
273 return t
00 Metadata-Version: 1.2
11 Name: minidump
2 Version: 0.0.12
2 Version: 0.0.13
33 Summary: Python library to parse Windows minidump file format
44 Home-page: https://github.com/skelsec/minidump
55 Author: Tamas Jos
0 LICENSE
1 MANIFEST.in
02 README.md
13 setup.py
24 minidump/__init__.py