Codebase list python-minidump / 046f415
Update upstream source from tag 'upstream/0.0.21' Update to upstream version '0.0.21' with Debian dir b1ed11699c2fbc629de5ca143225ef684ee3290c Sophie Brun 2 years ago
35 changed file(s) with 1855 addition(s) and 120 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.21
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.
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5
6 import logging
7 import asyncio
8 from minidump.aminidumpfile import AMinidumpFile
9 from minidump.common_structs import hexdump
10 from minidump._version import __banner__
11
12 async def run():
13 import argparse
14
15 parser = argparse.ArgumentParser(description='A parser for minidumnp files')
16 parser.add_argument('minidumpfile', help='path to the minidump file of lsass.exe')
17 parser.add_argument('-v', '--verbose', action='count', default=0)
18 parser.add_argument('--header', action='store_true', help='File header info')
19 parser.add_argument('--modules', action='store_true', help='List modules')
20 parser.add_argument('--threads', action='store_true', help='List threads')
21 parser.add_argument('--memory', action='store_true', help='List memory')
22 parser.add_argument('--sysinfo', action='store_true', help='Show sysinfo')
23 parser.add_argument('--comments', action='store_true', help='Show comments')
24 parser.add_argument('--exception', action='store_true', help='Show exception records')
25 parser.add_argument('--handles', action='store_true', help='List handles')
26 parser.add_argument('--misc', action='store_true', help='Show misc info')
27 parser.add_argument('--all', action='store_true', help='Show all info')
28 parser.add_argument('-r', '--read-addr', type=lambda x: int(x,0), help='Dump a memory region from the process\'s addres space')
29 parser.add_argument('-s', '--read-size', type=lambda x: int(x,0), default = 0x20, help='Dump a memory region from the process\'s addres space')
30
31 args = parser.parse_args()
32 if args.verbose == 0:
33 logging.basicConfig(level=logging.INFO)
34 elif args.verbose == 1:
35 logging.basicConfig(level=logging.DEBUG)
36 else:
37 logging.basicConfig(level=1)
38
39 print(__banner__)
40
41
42 mf = await AMinidumpFile.parse(args.minidumpfile)
43 reader = mf.get_reader()
44
45 if args.all or args.threads:
46 if mf.threads is not None:
47 print(str(mf.threads))
48 if mf.threads_ex is not None:
49 print(str(mf.threads_ex))
50 if mf.thread_info is not None:
51 print(str(mf.thread_info))
52 if args.all or args.modules:
53 if mf.modules is not None:
54 print(str(mf.modules))
55 if mf.unloaded_modules is not None:
56 print(str(mf.unloaded_modules))
57 if args.all or args.memory:
58 if mf.memory_segments is not None:
59 print(str(mf.memory_segments))
60 if mf.memory_segments_64 is not None:
61 print(str(mf.memory_segments_64))
62 if mf.memory_info is not None:
63 print(str(mf.memory_info))
64 if args.all or args.sysinfo:
65 if mf.sysinfo is not None:
66 print(str(mf.sysinfo))
67 if args.all or args.exception:
68 if mf.exception is not None:
69 print(str(mf.exception))
70 if args.all or args.comments:
71 if mf.comment_a is not None:
72 print(str(mf.comment_a))
73 if mf.comment_w is not None:
74 print(str(mf.comment_w))
75 if args.all or args.handles:
76 if mf.handles is not None:
77 print(str(mf.handles))
78 if args.all or args.misc:
79 if mf.misc_info is not None:
80 print(str(mf.misc_info))
81 if args.all or args.header:
82 print(str(mf.header))
83
84 if args.read_addr:
85 buff_reader = reader.get_buffered_reader()
86 await buff_reader.move(args.read_addr)
87 data = await buff_reader.peek(args.read_size)
88 print(hexdump(data, start = args.read_addr))
89
90 def main():
91 asyncio.run(run())
92
93 if __name__ == '__main__':
94 main()
00
1 __version__ = "0.0.12"
1 __version__ = "0.0.21"
22 __banner__ = \
33 """
44 # minidump %s
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5
6 import io
7 import sys
8 import enum
9 import struct
10 import logging
11
12 from minidump.header import MinidumpHeader
13 from minidump.aminidumpreader import AMinidumpFileReader
14 from minidump.streams import *
15 from minidump.common_structs import *
16 from minidump.constants import MINIDUMP_STREAM_TYPE
17 from minidump.directory import MINIDUMP_DIRECTORY
18
19
20 class AsyncFile:
21 def __init__(self, filename):
22 self.filename = filename
23 self.fhandle = open(filename, 'rb')
24
25 async def read(self, n = -1):
26 return self.fhandle.read(n)
27
28 async def seek(self, n, beg = 0):
29 return self.fhandle.seek(n, beg)
30
31 def tell(self):
32 return self.fhandle.tell()
33
34 class AMinidumpFile:
35 def __init__(self):
36 self.filename = None
37 self.file_handle = None
38 self.header = None
39 self.directories = []
40
41 self.threads_ex = None
42 self.threads = None
43 self.modules = None
44 self.memory_segments = None
45 self.memory_segments_64 = None
46 self.sysinfo = None
47 self.comment_a = None
48 self.comment_w = None
49 self.exception = None
50 self.handles = None
51 self.unloaded_modules = None
52 self.misc_info = None
53 self.memory_info = None
54 self.thread_info = None
55
56 @staticmethod
57 async def parse(filename):
58 mf = AMinidumpFile()
59 mf.filename = filename
60 mf.file_handle = AsyncFile(filename)
61 await mf._parse()
62 return mf
63
64 @staticmethod
65 async def parse_external(file_handle, filename = ''):
66 """
67 External file handle must be an object that exposes basic file IO functionality
68 that you'd get by python's file buffer (read, seek, tell etc.)
69 """
70 mf = AMinidumpFile()
71 mf.filename = filename
72 mf.file_handle = file_handle
73 await mf._parse()
74 return mf
75
76 @staticmethod
77 async def parse_bytes(data):
78 return await AMinidumpFile.parse_buff(io.BytesIO(data))
79
80 @staticmethod
81 def parse_buff(buffer):
82 mf = AMinidumpFile()
83 mf.file_handle = buffer
84 mf._parse()
85 return mf
86
87 def get_reader(self):
88 return AMinidumpFileReader(self)
89
90 async def _parse(self):
91 await self.__parse_header()
92 await self.__parse_directories()
93
94 async def __parse_header(self):
95 self.header = await MinidumpHeader.aparse(self.file_handle)
96 for i in range(0, self.header.NumberOfStreams):
97 await self.file_handle.seek(self.header.StreamDirectoryRva + i * 12, 0 )
98 minidump_dir = await MINIDUMP_DIRECTORY.aparse(self.file_handle)
99
100 if minidump_dir:
101 self.directories.append(minidump_dir)
102 else:
103 await self.file_handle.seek(self.header.StreamDirectoryRva + i * 12, 0 )
104 t = await self.file_handle.read(4)
105 user_stream_type_value = int.from_bytes(t, byteorder = 'little', signed = False)
106 logging.debug('Found Unknown UserStream directory Type: %x' % (user_stream_type_value))
107
108 async def __parse_directories(self):
109
110 for dir in self.directories:
111 if dir.StreamType == MINIDUMP_STREAM_TYPE.UnusedStream:
112 logging.debug('Found UnusedStream @%x Size: %d' % (dir.Location.Rva, dir.Location.DataSize))
113 continue # Reserved. Do not use this enumeration value.
114 elif dir.StreamType == MINIDUMP_STREAM_TYPE.ReservedStream0:
115 logging.debug('Found ReservedStream0 @%x Size: %d' % (dir.Location.Rva, dir.Location.DataSize))
116 continue # Reserved. Do not use this enumeration value.
117 elif dir.StreamType == MINIDUMP_STREAM_TYPE.ReservedStream1:
118 logging.debug('Found ReservedStream1 @%x Size: %d' % (dir.Location.Rva, dir.Location.DataSize))
119 continue # Reserved. Do not use this enumeration value.
120 elif dir.StreamType == MINIDUMP_STREAM_TYPE.ThreadListStream:
121 logging.debug('Found ThreadListStream @%x Size: %d' % (dir.Location.Rva, dir.Location.DataSize))
122 self.threads = await MinidumpThreadList.aparse(dir, self.file_handle)
123 continue
124 elif dir.StreamType == MINIDUMP_STREAM_TYPE.ModuleListStream:
125 logging.debug('Found ModuleListStream @%x Size: %d' % (dir.Location.Rva, dir.Location.DataSize))
126 self.modules = await MinidumpModuleList.aparse(dir, self.file_handle)
127 #logging.debug(str(modules_list))
128 continue
129 elif dir.StreamType == MINIDUMP_STREAM_TYPE.MemoryListStream:
130 logging.debug('Found MemoryListStream @%x Size: %d' % (dir.Location.Rva, dir.Location.DataSize))
131 self.memory_segments = await MinidumpMemoryList.aparse(dir, self.file_handle)
132 #logging.debug(str(self.memory_segments))
133 continue
134 elif dir.StreamType == MINIDUMP_STREAM_TYPE.SystemInfoStream:
135 logging.debug('Found SystemInfoStream @%x Size: %d' % (dir.Location.Rva, dir.Location.DataSize))
136 self.sysinfo = await MinidumpSystemInfo.aparse(dir, self.file_handle)
137 #logging.debug(str(self.sysinfo))
138 continue
139 elif dir.StreamType == MINIDUMP_STREAM_TYPE.ThreadExListStream:
140 logging.debug('Found ThreadExListStream @%x Size: %d' % (dir.Location.Rva, dir.Location.DataSize))
141 self.threads_ex = await MinidumpThreadExList.aparse(dir, self.file_handle)
142 #logging.debug(str(self.threads_ex))
143 continue
144 elif dir.StreamType == MINIDUMP_STREAM_TYPE.Memory64ListStream:
145 logging.debug('Found Memory64ListStream @%x Size: %d' % (dir.Location.Rva, dir.Location.DataSize))
146 self.memory_segments_64 = await MinidumpMemory64List.aparse(dir, self.file_handle)
147 #logging.debug(str(self.memory_segments_64))
148 continue
149 elif dir.StreamType == MINIDUMP_STREAM_TYPE.CommentStreamA:
150 logging.debug('Found CommentStreamA @%x Size: %d' % (dir.Location.Rva, dir.Location.DataSize))
151 self.comment_a = await CommentStreamA.aparse(dir, self.file_handle)
152 #logging.debug(str(self.comment_a))
153 continue
154 elif dir.StreamType == MINIDUMP_STREAM_TYPE.CommentStreamW:
155 logging.debug('Found CommentStreamW @%x Size: %d' % (dir.Location.Rva, dir.Location.DataSize))
156 self.comment_w = await CommentStreamW.aparse(dir, self.file_handle)
157 #logging.debug(str(self.comment_w))
158 continue
159 elif dir.StreamType == MINIDUMP_STREAM_TYPE.ExceptionStream:
160 logging.debug('Found ExceptionStream @%x Size: %d' % (dir.Location.Rva, dir.Location.DataSize))
161 self.exception = await ExceptionList.aparse(dir, self.file_handle)
162 #logging.debug(str(self.comment_w))
163 continue
164 elif dir.StreamType == MINIDUMP_STREAM_TYPE.HandleDataStream:
165 logging.debug('Found HandleDataStream @%x Size: %d' % (dir.Location.Rva, dir.Location.DataSize))
166 self.handles = await MinidumpHandleDataStream.aparse(dir, self.file_handle)
167 #logging.debug(str(self.handles))
168 continue
169
170 elif dir.StreamType == MINIDUMP_STREAM_TYPE.FunctionTableStream:
171 logging.debug('Found FunctionTableStream @%x Size: %d' % (dir.Location.Rva, dir.Location.DataSize))
172 logging.debug('Parsing of this stream type is not yet implemented!')
173 continue
174
175 elif dir.StreamType == MINIDUMP_STREAM_TYPE.UnloadedModuleListStream:
176 logging.debug('Found UnloadedModuleListStream @%x Size: %d' % (dir.Location.Rva, dir.Location.DataSize))
177 self.unloaded_modules = await MinidumpUnloadedModuleList.aparse(dir, self.file_handle)
178 #logging.debug(str(self.unloaded_modules))
179 continue
180 elif dir.StreamType == MINIDUMP_STREAM_TYPE.MiscInfoStream:
181 logging.debug('Found MiscInfoStream @%x Size: %d' % (dir.Location.Rva, dir.Location.DataSize))
182 self.misc_info = await MinidumpMiscInfo.aparse(dir, self.file_handle)
183 #logging.debug(str(self.misc_info))
184 continue
185 elif dir.StreamType == MINIDUMP_STREAM_TYPE.MemoryInfoListStream:
186 logging.debug('Found MemoryInfoListStream @%x Size: %d' % (dir.Location.Rva, dir.Location.DataSize))
187 self.memory_info = await MinidumpMemoryInfoList.aparse(dir, self.file_handle)
188 #logging.debug(str(self.memory_info))
189 continue
190 elif dir.StreamType == MINIDUMP_STREAM_TYPE.ThreadInfoListStream:
191 logging.debug('Found ThreadInfoListStream @%x Size: %d' % (dir.Location.Rva, dir.Location.DataSize))
192 self.thread_info = await MinidumpThreadInfoList.aparse(dir, self.file_handle)
193 logging.debug(str(self.thread_info))
194 continue
195 elif dir.StreamType == MINIDUMP_STREAM_TYPE.SystemMemoryInfoStream:
196 logging.debug('Found SystemMemoryInfoStream @%x Size: %d' % (dir.Location.Rva, dir.Location.DataSize))
197 logging.debug('SystemMemoryInfoStream parsing is not implemented (Missing documentation)')
198 continue
199
200 elif dir.StreamType == MINIDUMP_STREAM_TYPE.JavaScriptDataStream:
201 logging.debug('Found JavaScriptDataStream @%x Size: %d' % (dir.Location.Rva, dir.Location.DataSize))
202 logging.debug('JavaScriptDataStream parsing is not implemented (Missing documentation)')
203
204 elif dir.StreamType == MINIDUMP_STREAM_TYPE.ProcessVmCountersStream:
205 logging.debug('Found ProcessVmCountersStream @%x Size: %d' % (dir.Location.Rva, dir.Location.DataSize))
206 logging.debug('ProcessVmCountersStream parsing is not implemented (Missing documentation)')
207
208 elif dir.StreamType == MINIDUMP_STREAM_TYPE.TokenStream:
209 logging.debug('Found TokenStream @%x Size: %d' % (dir.Location.Rva, dir.Location.DataSize))
210 logging.debug('TokenStream parsing is not implemented (Missing documentation)')
211
212 else:
213 logging.debug('Found Unknown Stream! Type: %s @%x Size: %d' % (dir.StreamType.name, dir.Location.Rva, dir.Location.DataSize))
214
215 """
216 elif dir.StreamType == MINIDUMP_STREAM_TYPE.HandleOperationListStream:
217 elif dir.StreamType == MINIDUMP_STREAM_TYPE.LastReservedStream:
218
219 """
220
221 def __str__(self):
222 t = '== Minidump File ==\n'
223 t += str(self.header)
224 t += str(self.sysinfo)
225 for dir in self.directories:
226 t += str(dir) + '\n'
227 for mod in self.modules:
228 t += str(mod) + '\n'
229 if self.memory_segments is not None:
230 for segment in self.memory_segments:
231 t+= str(segment) + '\n'
232
233 if self.memory_segments_64 is not None:
234 for segment in self.memory_segments_64:
235 t+= str(segment) + '\n'
236
237 return t
0 #!/usr/bin/env python3
1 #
2 # Author:
3 # Tamas Jos (@skelsec)
4 #
5 import struct
6 import ntpath
7 from .common_structs import *
8 from .streams.SystemInfoStream import PROCESSOR_ARCHITECTURE
9
10
11 class VirtualSegment:
12 def __init__(self, start, end, start_file_address):
13 self.start = start
14 self.end = end
15 self.start_file_address = start_file_address
16
17 self.data = None
18
19 def inrange(self, start, end):
20 return self.start <= start and end<= self.end
21
22 class AMinidumpBufferedMemorySegment:
23 def __init__(self, memory_segment, chunksize = 10*1024):
24 self.start_address = memory_segment.start_virtual_address
25 self.end_address = memory_segment.end_virtual_address
26 self.total_size = memory_segment.end_virtual_address - memory_segment.start_virtual_address
27 self.start_file_address = memory_segment.start_file_address
28 self.chunksize = chunksize
29 self.chunks = []
30
31 def inrange(self, position):
32 return self.start_address <= position <= self.end_address
33
34 def remaining_len(self, position):
35 if not self.inrange(position):
36 return None
37 return self.end_address - position
38
39 async def find(self, file_handle, pattern, startpos):
40 data = await self.read(file_handle, 0, -1)
41 return data.find(pattern, startpos)
42
43 async def read(self, file_handle, start, end):
44 if end is None:
45 await file_handle.seek(self.start_file_address + start)
46 return await file_handle.read(self.end_address - (self.start_file_address + start))
47
48 for chunk in self.chunks:
49 if chunk.inrange(start, end):
50 return chunk.data[start - chunk.start: end - chunk.start]
51
52 if self.total_size <= 2*self.chunksize:
53 chunksize = self.total_size
54 vs = VirtualSegment(0, chunksize, self.start_file_address)
55 await file_handle.seek(self.start_file_address)
56 vs.data = await file_handle.read(chunksize)
57 self.chunks.append(vs)
58 return vs.data[start - vs.start: end - vs.start]
59
60 chunksize = max((end-start), self.chunksize)
61 if start + chunksize > self.end_address:
62 chunksize = self.end_address - start
63
64 vs = VirtualSegment(start, start+chunksize, self.start_file_address + start)
65 await file_handle.seek(vs.start_file_address)
66 vs.data = await file_handle.read(chunksize)
67 self.chunks.append(vs)
68
69 return vs.data[start - vs.start: end - vs.start]
70
71 class AMinidumpBufferedReader:
72 def __init__(self, reader, segment_chunk_size = 10*1024):
73 self.reader = reader
74 self.memory_segments = []
75 self.segment_chunk_size = segment_chunk_size
76
77 self.current_segment = None
78 self.current_position = None
79
80 async def _select_segment(self, requested_position):
81 """
82
83 """
84 # check if we have semgnet for requested address in cache
85 for memory_segment in self.memory_segments:
86 if memory_segment.inrange(requested_position):
87 self.current_segment = memory_segment
88 self.current_position = requested_position
89 return
90
91 # not in cache, check if it's present in memory space. if yes then create a new buffered memeory object, and copy data
92 for memory_segment in self.reader.memory_segments:
93 if memory_segment.inrange(requested_position):
94 newsegment = AMinidumpBufferedMemorySegment(memory_segment, chunksize=self.segment_chunk_size)
95 self.memory_segments.append(newsegment)
96 self.current_segment = newsegment
97 self.current_position = requested_position
98 return
99
100 raise Exception('Memory address 0x%08x is not in process memory space' % requested_position)
101
102 async def seek(self, offset, whence = 0):
103 """
104 Changes the current address to an offset of offset. The whence parameter controls from which position should we count the offsets.
105 0: beginning of the current memory segment
106 1: from current position
107 2: from the end of the current memory segment
108 If you wish to move out from the segment, use the 'move' function
109 """
110 if whence == 0:
111 t = self.current_segment.start_address + offset
112 elif whence == 1:
113 t = self.current_position + offset
114 elif whence == 2:
115 t = self.current_segment.end_address - offset
116 else:
117 raise Exception('Seek function whence value must be between 0-2')
118
119 if not self.current_segment.inrange(t):
120 raise Exception('Seek would cross memory segment boundaries (use move)')
121
122 self.current_position = t
123 return
124
125 async def move(self, address):
126 """
127 Moves the buffer to a virtual address specified by address
128 """
129 await self._select_segment(address)
130 return
131
132 async def align(self, alignment = None):
133 """
134 Repositions the current reader to match architecture alignment
135 """
136 if alignment is None:
137 if self.reader.sysinfo.ProcessorArchitecture == PROCESSOR_ARCHITECTURE.AMD64:
138 alignment = 8
139 else:
140 alignment = 4
141 offset = self.current_position % alignment
142 if offset == 0:
143 return
144 offset_to_aligned = (alignment - offset) % alignment
145 await self.seek(offset_to_aligned, 1)
146 return
147
148 def tell(self):
149 """
150 Returns the current virtual address
151 """
152 return self.current_position
153
154 async def peek(self, length):
155 """
156 Returns up to length bytes from the current memory segment
157 """
158 t = self.current_position + length
159 if not self.current_segment.inrange(t):
160 raise Exception('Would read over segment boundaries!')
161 return await self.current_segment.read(self.reader.file_handle, self.current_position - self.current_segment.start_address , t - self.current_segment.start_address)
162
163 async def read(self, size = -1):
164 """
165 Returns data bytes of size size from the current segment. If size is -1 it returns all the remaining data bytes from memory segment
166 """
167 if size < -1:
168 raise Exception('You shouldnt be doing this')
169 if size == -1:
170 t = self.current_segment.remaining_len(self.current_position)
171 if not t:
172 return None
173
174 old_new_pos = self.current_position
175 self.current_position = self.current_segment.end_address
176 return await self.current_segment.read(self.reader.file_handle, old_new_pos - self.current_segment.start_address, None)
177
178 t = self.current_position + size
179 if not self.current_segment.inrange(t):
180 raise Exception('Would read over segment boundaries!')
181
182 old_new_pos = self.current_position
183 self.current_position = t
184 return await self.current_segment.read(self.reader.file_handle, old_new_pos - self.current_segment.start_address, t - self.current_segment.start_address)
185
186 async def read_int(self):
187 """
188 Reads an integer. The size depends on the architecture.
189 Reads a 4 byte small-endian singed int on 32 bit arch
190 Reads an 8 byte small-endian singed int on 64 bit arch
191 """
192 if self.reader.sysinfo.ProcessorArchitecture == PROCESSOR_ARCHITECTURE.AMD64:
193 t = await self.read(8)
194 return int.from_bytes(t, byteorder = 'little', signed = True)
195 else:
196 t = t = await self.read(4)
197 return int.from_bytes(t, byteorder = 'little', signed = True)
198
199 async def read_uint(self):
200 """
201 Reads an integer. The size depends on the architecture.
202 Reads a 4 byte small-endian unsinged int on 32 bit arch
203 Reads an 8 byte small-endian unsinged int on 64 bit arch
204 """
205 if self.reader.sysinfo.ProcessorArchitecture == PROCESSOR_ARCHITECTURE.AMD64:
206 t = await self.read(8)
207 return int.from_bytes(t, byteorder = 'little', signed = False)
208 else:
209 t = await self.read(4)
210 return int.from_bytes(t, byteorder = 'little', signed = False)
211
212 async def find(self, pattern):
213 """
214 Searches for a pattern in the current memory segment
215 """
216 pos = await self.current_segment.find(self.reader.file_handle, pattern)
217 if pos == -1:
218 return -1
219 return pos + self.current_position
220
221 async def find_all(self, pattern):
222 """
223 Searches for all occurrences of a pattern in the current memory segment, returns all occurrences as a list
224 """
225 pos = []
226 last_found = -1
227 while True:
228 last_found = await self.current_segment.find(self.reader.file_handle, pattern, last_found + 1)
229 if last_found == -1:
230 break
231 pos.append(last_found + self.current_segment.start_address)
232
233 return pos
234
235 async def find_global(self, pattern):
236 """
237 Searches for the pattern in the whole process memory space and returns the first occurrence.
238 This is exhaustive!
239 """
240 pos_s = await self.reader.search(pattern)
241 if len(pos_s) == 0:
242 return -1
243
244 return pos_s[0]
245
246 async def find_all_global(self, pattern):
247 """
248 Searches for the pattern in the whole process memory space and returns a list of addresses where the pattern begins.
249 This is exhaustive!
250 """
251 return await self.reader.search(pattern)
252
253 async def get_ptr(self, pos):
254 await self.move(pos)
255 return await self.read_uint()
256 #raw_data = self.read(pos, self.sizeof_ptr)
257 #return struct.unpack(self.unpack_ptr, raw_data)[0]
258
259 async def get_ptr_with_offset(self, pos):
260 if self.reader.sysinfo.ProcessorArchitecture == PROCESSOR_ARCHITECTURE.AMD64:
261 await self.move(pos)
262 t = await self.read(4)
263 ptr = int.from_bytes(t, byteorder = 'little', signed = True)
264 return pos + 4 + ptr
265 else:
266 await self.move(pos)
267 return await self.read_uint()
268
269 async def find_in_module(self, module_name, pattern, find_first = False, reverse_order = False):
270 t = await self.reader.search_module(module_name, pattern, find_first = find_first, reverse_order = reverse_order,chunksize = self.segment_chunk_size)
271 return t
272
273
274
275
276 class AMinidumpFileReader:
277 def __init__(self, minidumpfile):
278 self.modules = minidumpfile.modules.modules
279 self.sysinfo = minidumpfile.sysinfo
280
281 if minidumpfile.memory_segments_64:
282 self.memory_segments = minidumpfile.memory_segments_64.memory_segments
283 self.is_fulldump = True
284
285 else:
286 self.memory_segments = minidumpfile.memory_segments.memory_segments
287 self.is_fulldump = False
288
289 self.filename = minidumpfile.filename
290 self.file_handle = minidumpfile.file_handle
291
292 #reader params
293 self.sizeof_long = 4
294 self.unpack_long = '<L'
295 if minidumpfile.sysinfo.ProcessorArchitecture in [PROCESSOR_ARCHITECTURE.AMD64, PROCESSOR_ARCHITECTURE.AARCH64]:
296 self.sizeof_ptr = 8
297 self.unpack_ptr = '<Q'
298 elif self.sysinfo.ProcessorArchitecture in [PROCESSOR_ARCHITECTURE.INTEL,
299 PROCESSOR_ARCHITECTURE.ARM]:
300 self.sizeof_ptr = 4
301 self.unpack_ptr = '<L'
302 else:
303 raise Exception('Unknown processor architecture %s! Please fix and submit PR!' % self.sysinfo.ProcessorArchitecture)
304
305 def get_buffered_reader(self, segment_chunk_size = 10*1024):
306 return AMinidumpBufferedReader(self, segment_chunk_size = segment_chunk_size)
307
308 def get_module_by_name(self, module_name):
309 for mod in self.modules:
310 if ntpath.basename(mod.name).lower().find(module_name.lower()) != -1:
311 return mod
312 return None
313
314 async def search_module(self, module_name, pattern, find_first = False, reverse_order = False, chunksize = 10*1024):
315 mod = self.get_module_by_name(module_name)
316 if mod is None:
317 raise Exception('Could not find module! %s' % module_name)
318 needles = []
319 for ms in self.memory_segments:
320 if mod.baseaddress <= ms.start_virtual_address < mod.endaddress:
321 needles += await ms.asearch(pattern, self.file_handle, find_first = find_first, chunksize = chunksize)
322 if len(needles) > 0 and find_first is True:
323 return needles
324
325 return needles
326
327 async def search(self, pattern, find_first = False, chunksize = 10*1024):
328 t = []
329 for ms in self.memory_segments:
330 t += await ms.asearch(pattern, self.file_handle, find_first = find_first, chunksize = chunksize)
331
332 return t
333
334 async def read(self, virt_addr, size):
335 for segment in self.memory_segments:
336 if segment.inrange(virt_addr):
337 return await segment.aread(virt_addr, size, self.file_handle)
338 raise Exception('Address not in memory range! %s' % hex(virt_addr))
339
1818 mld.DataSize = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
1919 mld.Rva = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
2020 return mld
21
22 @staticmethod
23 async def aparse(buff):
24 mld = MINIDUMP_LOCATION_DESCRIPTOR()
25 t = await buff.read(4)
26 mld.DataSize = int.from_bytes(t, byteorder = 'little', signed = False)
27 t = await buff.read(4)
28 mld.Rva = int.from_bytes(t, byteorder = 'little', signed = False)
29 return mld
2130
2231 def __str__(self):
2332 t = 'Size: %s File offset: %s' % (self.DataSize, self.Rva)
5867 ms.Length = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
5968 ms.Buffer = buff.read(ms.Length)
6069 return ms
70
71 @staticmethod
72 async def aparse(buff):
73 ms = MINIDUMP_STRING()
74 t = await buff.read(4)
75 ms.Length = int.from_bytes(t, byteorder = 'little', signed = False)
76 ms.Buffer = await buff.read(ms.Length)
77 return ms
6178
6279 @staticmethod
6380 def get_from_rva(rva, buff):
6582 buff.seek(rva, 0)
6683 ms = MINIDUMP_STRING.parse(buff)
6784 buff.seek(pos, 0)
85 return ms.Buffer.decode('utf-16-le')
86
87 @staticmethod
88 async def aget_from_rva(rva, buff):
89 pos = buff.tell()
90 await buff.seek(rva, 0)
91 ms = await MINIDUMP_STRING.aparse(buff)
92 await buff.seek(pos, 0)
6893 return ms.Buffer.decode('utf-16-le')
6994
7095 class MinidumpMemorySegment:
88113 return mms
89114
90115 @staticmethod
91 def parse_full(memory_decriptor, buff, rva):
116 def parse_full(memory_decriptor, rva):
92117 mms = MinidumpMemorySegment()
93118 mms.start_virtual_address = memory_decriptor.StartOfMemoryRange
94119 mms.size = memory_decriptor.DataSize
95120 mms.start_file_address = rva
96121 mms.end_virtual_address = mms.start_virtual_address + mms.size
97 return mms
98
122 return mms
99123
100124 def inrange(self, virt_addr):
101125 if virt_addr >= self.start_virtual_address and virt_addr < self.end_virtual_address:
102126 return True
103127 return False
128
104129 def read(self, virtual_address, size, file_handler):
105130 if virtual_address > self.end_virtual_address or virtual_address < self.start_virtual_address:
106131 raise Exception('Reading from wrong segment!')
114139 data = file_handler.read(size)
115140 file_handler.seek(pos, 0)
116141 return data
117
118 def search(self, pattern, file_handler):
142
143 async def aread(self, virtual_address, size, file_handler):
144 if virtual_address > self.end_virtual_address or virtual_address < self.start_virtual_address:
145 raise Exception('Reading from wrong segment!')
146
147 if virtual_address+size > self.end_virtual_address:
148 raise Exception('Read would cross boundaries!')
149
150 pos = file_handler.tell()
151 offset = virtual_address - self.start_virtual_address
152 await file_handler.seek(self.start_file_address + offset, 0)
153 data = await file_handler.read(size)
154 await file_handler.seek(pos, 0)
155 return data
156
157 def search(self, pattern, file_handler, find_first = False, chunksize = 50*1024):
119158 if len(pattern) > self.size:
120159 return []
121160 pos = file_handler.tell()
122161 file_handler.seek(self.start_file_address, 0)
123 data = file_handler.read(self.size)
162 fl = []
163 if find_first is True:
164 chunksize = min(chunksize, self.size)
165 data = b''
166 i = 0
167 while len(data) < self.size:
168 i += 1
169 if chunksize > (self.size - len(data)):
170 chunksize = (self.size - len(data))
171 data += file_handler.read(chunksize)
172 marker = data.find(pattern)
173 if marker != -1:
174 #print('FOUND! size: %s i: %s read: %s perc: %s' % (self.size, i, i*chunksize, 100*((i*chunksize)/self.size)))
175 file_handler.seek(pos, 0)
176 return [self.start_virtual_address + marker]
177
178
179 #print('NOTFOUND! size: %s i: %s read: %s perc %s' % (self.size, i, len(data), 100*(len(data)/self.size) ))
180
181 else:
182 data = file_handler.read(self.size)
183 file_handler.seek(pos, 0)
184
185 offset = 0
186 while len(data) > len(pattern):
187 marker = data.find(pattern)
188 if marker == -1:
189 return fl
190 fl.append(marker + offset + self.start_virtual_address)
191 data = data[marker+1:]
192 offset += marker + 1
193 if find_first is True:
194 return fl
195
124196 file_handler.seek(pos, 0)
197 return fl
198
199 async def asearch(self, pattern, file_handler, find_first = False, chunksize = 50*1024):
200 if len(pattern) > self.size:
201 return []
202 pos = file_handler.tell()
203 await file_handler.seek(self.start_file_address, 0)
125204 fl = []
126 offset = 0
127 while len(data) > len(pattern):
128 marker = data.find(pattern)
129 if marker == -1:
130 return fl
131 fl.append(marker + offset + self.start_virtual_address)
132 data = data[marker+1:]
133 offset = marker + 1
134
205
206 if find_first is True:
207 chunksize = min(chunksize, self.size)
208 data = b''
209 i = 0
210 while len(data) < self.size:
211 i += 1
212 if chunksize > (self.size - len(data)):
213 chunksize = (self.size - len(data))
214 data += await file_handler.read(chunksize)
215 marker = data.find(pattern)
216 if marker != -1:
217 #print('FOUND! size: %s i: %s read: %s perc: %s' % (self.size, i, i*chunksize, 100*((i*chunksize)/self.size)))
218 await file_handler.seek(pos, 0)
219 return [self.start_virtual_address + marker]
220
221
222 #print('NOTFOUND! size: %s i: %s read: %s perc %s' % (self.size, i, len(data), 100*(len(data)/self.size) ))
223
224 else:
225 offset = 0
226 data = await file_handler.read(self.size)
227 await file_handler.seek(pos, 0)
228 while len(data) > len(pattern):
229 marker = data.find(pattern)
230 if marker == -1:
231 return fl
232 fl.append(marker + offset + self.start_virtual_address)
233 data = data[marker+1:]
234 offset += marker + 1
235 if find_first is True:
236 return fl
237
238 await file_handler.seek(pos, 0)
135239 return fl
240
136241
137242 @staticmethod
138243 def get_header():
3333 md.Location = MINIDUMP_LOCATION_DESCRIPTOR.parse(buff)
3434 return md
3535
36 @staticmethod
37 async def aparse(buff):
38
39 t = await buff.read(4)
40 raw_stream_type_value = int.from_bytes(t, byteorder = 'little', signed = False)
41
42 # StreamType value that are over 0xffff are considered MINIDUMP_USER_STREAM streams
43 # and their format depends on the client used to create the minidump.
44 # As per the documentation, this stream should be ignored : https://docs.microsoft.com/en-us/windows/win32/api/minidumpapiset/ne-minidumpapiset-minidumminidump_dirp_stream_type#remarks
45 is_user_stream = raw_stream_type_value > MINIDUMP_STREAM_TYPE.LastReservedStream.value
46 is_stream_supported = raw_stream_type_value in MINIDUMP_STREAM_TYPE._value2member_map_
47 if is_user_stream and not is_stream_supported:
48 return None
49
50 md = MINIDUMP_DIRECTORY()
51 md.StreamType = MINIDUMP_STREAM_TYPE(raw_stream_type_value)
52 md.Location = await MINIDUMP_LOCATION_DESCRIPTOR.aparse(buff)
53 return md
54
3655 def __str__(self):
3756 t = 'StreamType: %s %s' % (self.StreamType, self.Location)
3857 return t
00 from minidump.constants import MINIDUMP_TYPE
11 from minidump.exceptions import MinidumpHeaderFlagsException, MinidumpHeaderSignatureMismatchException
2 import io
23
34 # https://msdn.microsoft.com/en-us/library/windows/desktop/ms680378(v=vs.85).aspx
45 class MinidumpHeader:
4647
4748 return mh
4849
50 @staticmethod
51 async def aparse(abuff):
52 adata = await abuff.read(32)
53 buff = io.BytesIO(adata)
54 return MinidumpHeader.parse(buff)
55
4956 def __str__(self):
5057 t = '== MinidumpHeader ==\n'
5158 t+= 'Signature: %s\n' % self.Signature
1515 from minidump.common_structs import *
1616 from minidump.constants import MINIDUMP_STREAM_TYPE
1717 from minidump.directory import MINIDUMP_DIRECTORY
18 from minidump.streams.SystemInfoStream import PROCESSOR_ARCHITECTURE
1819
1920
2021 class MinidumpFile:
202203 elif dir.StreamType == MINIDUMP_STREAM_TYPE.LastReservedStream:
203204
204205 """
206 try:
207 self.__parse_thread_context()
208 except Exception as e:
209 logging.exception('Thread context parsing error!')
210
211 def __parse_thread_context(self):
212 if not self.sysinfo or not self.threads:
213 return
214 for thread in self.threads.threads:
215 rva = thread.ThreadContext.Rva
216 self.file_handle.seek(rva)
217 if self.sysinfo.ProcessorArchitecture == PROCESSOR_ARCHITECTURE.AMD64:
218 thread.ContextObject = CONTEXT.parse(self.file_handle)
219 elif self.sysinfo.ProcessorArchitecture == PROCESSOR_ARCHITECTURE.INTEL:
220 thread.ContextObject = WOW64_CONTEXT.parse(self.file_handle)
221
205222
206223 def __str__(self):
207224 t = '== Minidump File ==\n'
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
10 class VirtualSegment:
11 def __init__(self, start, end, start_file_address):
12 self.start = start
13 self.end = end
14 self.start_file_address = start_file_address
15
16
17
18 self.data = None
19
20 def inrange(self, start, end):
21 return self.start <= start and end<= self.end
22
1023 class MinidumpBufferedMemorySegment:
11 def __init__(self, memory_segment, file_handle):
24 def __init__(self, memory_segment, file_handle, chunksize = 10*1024):
1225 self.start_address = memory_segment.start_virtual_address
1326 self.end_address = memory_segment.end_virtual_address
14
15 file_handle.seek(memory_segment.start_file_address)
16 self.data = file_handle.read(memory_segment.size)
17
27 self.total_size = memory_segment.end_virtual_address - memory_segment.start_virtual_address
28 self.start_file_address = memory_segment.start_file_address
29 self.chunksize = chunksize
30 self.chunks = []
31
1832 def inrange(self, position):
1933 return self.start_address <= position <= self.end_address
20
34
2135 def remaining_len(self, position):
2236 if not self.inrange(position):
2337 return None
2438 return self.end_address - position
25
39
40 def find(self, file_handle, pattern, startpos):
41 data = self.read(file_handle, 0, -1)
42 return data.find(pattern, startpos)
43
44 def read(self, file_handle, start, end):
45 if end is None:
46 file_handle.seek(self.start_file_address + start)
47 return file_handle.read(self.end_address - (self.start_file_address + start))
48
49 for chunk in self.chunks:
50 if chunk.inrange(start, end):
51 return chunk.data[start - chunk.start: end - chunk.start]
52
53 if self.total_size <= 2*self.chunksize:
54 chunksize = self.total_size
55 vs = VirtualSegment(0, chunksize, self.start_file_address)
56 file_handle.seek(self.start_file_address)
57 vs.data = file_handle.read(chunksize)
58 self.chunks.append(vs)
59 return vs.data[start - vs.start: end - vs.start]
60
61 chunksize = max((end-start), self.chunksize)
62 if start + chunksize > self.end_address:
63 chunksize = self.end_address - start
64
65 vs = VirtualSegment(start, start+chunksize, self.start_file_address + start)
66 file_handle.seek(vs.start_file_address)
67 vs.data = file_handle.read(chunksize)
68 self.chunks.append(vs)
69
70 return vs.data[start - vs.start: end - vs.start]
71
72
73
2674 class MinidumpBufferedReader:
27 def __init__(self, reader):
75 def __init__(self, reader, segment_chunk_size = 10*1024):
2876 self.reader = reader
77 self.segment_chunk_size = segment_chunk_size
2978 self.memory_segments = []
30
79
3180 self.current_segment = None
3281 self.current_position = None
33
82
3483 def _select_segment(self, requested_position):
3584 """
36
85
3786 """
3887 # check if we have semgnet for requested address in cache
3988 for memory_segment in self.memory_segments:
4190 self.current_segment = memory_segment
4291 self.current_position = requested_position
4392 return
44
93
4594 # not in cache, check if it's present in memory space. if yes then create a new buffered memeory object, and copy data
4695 for memory_segment in self.reader.memory_segments:
4796 if memory_segment.inrange(requested_position):
48 newsegment = MinidumpBufferedMemorySegment(memory_segment, self.reader.file_handle)
97 newsegment = MinidumpBufferedMemorySegment(memory_segment, self.reader.file_handle, chunksize=self.segment_chunk_size)
4998 self.memory_segments.append(newsegment)
5099 self.current_segment = newsegment
51100 self.current_position = requested_position
52101 return
53
102
54103 raise Exception('Memory address 0x%08x is not in process memory space' % requested_position)
55
104
105 def get_reader(self):
106 return self.reader
107
56108 def seek(self, offset, whence = 0):
57109 """
58110 Changes the current address to an offset of offset. The whence parameter controls from which position should we count the offsets.
69121 t = self.current_segment.end_address - offset
70122 else:
71123 raise Exception('Seek function whence value must be between 0-2')
72
124
73125 if not self.current_segment.inrange(t):
74126 raise Exception('Seek would cross memory segment boundaries (use move)')
75
127
76128 self.current_position = t
77129 return
78
130
79131 def move(self, address):
80132 """
81133 Moves the buffer to a virtual address specified by address
82134 """
83135 self._select_segment(address)
84136 return
85
137
86138 def align(self, alignment = None):
87139 """
88140 Repositions the current reader to match architecture alignment
98150 offset_to_aligned = (alignment - offset) % alignment
99151 self.seek(offset_to_aligned, 1)
100152 return
101
153
102154 def tell(self):
103155 """
104156 Returns the current virtual address
105157 """
106158 return self.current_position
107
159
108160 def peek(self, length):
109161 """
110162 Returns up to length bytes from the current memory segment
112164 t = self.current_position + length
113165 if not self.current_segment.inrange(t):
114166 raise Exception('Would read over segment boundaries!')
115 return self.current_segment.data[self.current_position - self.current_segment.start_address :t - self.current_segment.start_address]
116
167 return self.current_segment.read(self.reader.file_handle, self.current_position - self.current_segment.start_address , t - self.current_segment.start_address)
168
117169 def read(self, size = -1):
118170 """
119171 Returns data bytes of size size from the current segment. If size is -1 it returns all the remaining data bytes from memory segment
124176 t = self.current_segment.remaining_len(self.current_position)
125177 if not t:
126178 return None
127
179
128180 old_new_pos = self.current_position
129181 self.current_position = self.current_segment.end_address
130 return self.current_segment.data[old_new_pos - self.current_segment.start_address:]
131
182 return self.current_segment.read(self.reader.file_handle, old_new_pos - self.current_segment.start_address, None)
183
132184 t = self.current_position + size
133185 if not self.current_segment.inrange(t):
134186 raise Exception('Would read over segment boundaries!')
135
187
136188 old_new_pos = self.current_position
137 self.current_position = t
138 return self.current_segment.data[old_new_pos - self.current_segment.start_address :t - self.current_segment.start_address]
139
189 self.current_position = t
190 return self.current_segment.read(self.reader.file_handle, old_new_pos - self.current_segment.start_address, t - self.current_segment.start_address)
191
140192 def read_int(self):
141193 """
142 Reads an integer. The size depends on the architecture.
194 Reads an integer. The size depends on the architecture.
143195 Reads a 4 byte small-endian singed int on 32 bit arch
144196 Reads an 8 byte small-endian singed int on 64 bit arch
145197 """
147199 return int.from_bytes(self.read(8), byteorder = 'little', signed = True)
148200 else:
149201 return int.from_bytes(self.read(4), byteorder = 'little', signed = True)
150
202
151203 def read_uint(self):
152204 """
153 Reads an integer. The size depends on the architecture.
205 Reads an integer. The size depends on the architecture.
154206 Reads a 4 byte small-endian unsinged int on 32 bit arch
155207 Reads an 8 byte small-endian unsinged int on 64 bit arch
156208 """
158210 return int.from_bytes(self.read(8), byteorder = 'little', signed = False)
159211 else:
160212 return int.from_bytes(self.read(4), byteorder = 'little', signed = False)
161
213
162214 def find(self, pattern):
163215 """
164216 Searches for a pattern in the current memory segment
165217 """
166 pos = self.current_segment.data.find(pattern)
218 pos = self.current_segment.find(self.reader.file_handle, pattern)
167219 if pos == -1:
168220 return -1
169221 return pos + self.current_position
170
222
171223 def find_all(self, pattern):
172224 """
173225 Searches for all occurrences of a pattern in the current memory segment, returns all occurrences as a list
175227 pos = []
176228 last_found = -1
177229 while True:
178 last_found = self.current_segment.data.find(pattern, last_found + 1)
230 last_found = self.current_segment.find(self.reader.file_handle, pattern, last_found + 1)
179231 if last_found == -1:
180232 break
181233 pos.append(last_found + self.current_segment.start_address)
182
234
183235 return pos
184
236
185237 def find_global(self, pattern):
186238 """
187239 Searches for the pattern in the whole process memory space and returns the first occurrence.
190242 pos_s = self.reader.search(pattern)
191243 if len(pos_s) == 0:
192244 return -1
193
245
194246 return pos_s[0]
195
247
196248 def find_all_global(self, pattern):
197249 """
198250 Searches for the pattern in the whole process memory space and returns a list of addresses where the pattern begins.
199251 This is exhaustive!
200252 """
201253 return self.reader.search(pattern)
202
254
203255 def get_ptr(self, pos):
204256 self.move(pos)
205257 return self.read_uint()
206258 #raw_data = self.read(pos, self.sizeof_ptr)
207259 #return struct.unpack(self.unpack_ptr, raw_data)[0]
208
260
209261 def get_ptr_with_offset(self, pos):
210262 if self.reader.sysinfo.ProcessorArchitecture == PROCESSOR_ARCHITECTURE.AMD64:
211263 self.move(pos)
214266 else:
215267 self.move(pos)
216268 return self.read_uint()
217
218 def find_in_module(self, module_name, pattern):
219 t = self.reader.search_module(module_name, pattern)
269
270 def find_in_module(self, module_name, pattern, find_first = False, reverse_order = False):
271 t = self.reader.search_module(module_name, pattern, find_first = find_first, reverse_order = reverse_order, chunksize = self.segment_chunk_size)
220272 return t
221
222
223
224
273
274
275
276
225277 class MinidumpFileReader:
226278 def __init__(self, minidumpfile):
227279 self.modules = minidumpfile.modules.modules
280 self.unloaded_modules = []
281 if minidumpfile.unloaded_modules is not None:
282 self.unloaded_modules = minidumpfile.unloaded_modules.modules
283
228284 self.sysinfo = minidumpfile.sysinfo
229
285
230286 if minidumpfile.memory_segments_64:
231287 self.memory_segments = minidumpfile.memory_segments_64.memory_segments
232288 self.is_fulldump = True
233
289
234290 else:
235291 self.memory_segments = minidumpfile.memory_segments.memory_segments
236292 self.is_fulldump = False
237
293
238294 self.filename = minidumpfile.filename
239295 self.file_handle = minidumpfile.file_handle
240
296
241297 #reader params
242298 self.sizeof_long = 4
243299 self.unpack_long = '<L'
244 if minidumpfile.sysinfo.ProcessorArchitecture == PROCESSOR_ARCHITECTURE.AMD64:
300 if minidumpfile.sysinfo.ProcessorArchitecture in [PROCESSOR_ARCHITECTURE.AMD64, PROCESSOR_ARCHITECTURE.AARCH64]:
245301 self.sizeof_ptr = 8
246302 self.unpack_ptr = '<Q'
247 elif self.sysinfo.ProcessorArchitecture == PROCESSOR_ARCHITECTURE.INTEL:
303 elif self.sysinfo.ProcessorArchitecture in [PROCESSOR_ARCHITECTURE.INTEL,
304 PROCESSOR_ARCHITECTURE.ARM]:
248305 self.sizeof_ptr = 4
249306 self.unpack_ptr = '<L'
250307 else:
251308 raise Exception('Unknown processor architecture %s! Please fix and submit PR!' % self.sysinfo.ProcessorArchitecture)
252
253 def get_buffered_reader(self):
254 return MinidumpBufferedReader(self)
255
309
310 def get_handler(self):
311 return self.file_handle
312
313 def get_memory(self):
314 return self.memory_segments
315
316 def get_buffered_reader(self, segment_chunk_size = 10*1024):
317 return MinidumpBufferedReader(self, segment_chunk_size = segment_chunk_size)
318
256319 def get_module_by_name(self, module_name):
257320 for mod in self.modules:
321 if ntpath.basename(mod.name).lower().find(module_name.lower()) != -1:
322 return mod
323 return None
324
325 def get_unloaded_by_name(self, module_name):
326 for mod in self.unloaded_modules:
258327 if ntpath.basename(mod.name).find(module_name) != -1:
259328 return mod
260329 return None
261
262 def search_module(self, module_name, pattern):
330
331 def search_module(self, module_name, pattern, find_first = False, reverse_order = False, chunksize = 10*1024):
263332 mod = self.get_module_by_name(module_name)
264333 if mod is None:
265 raise Exception('Could not find module! %s' % module_name)
334 mod = self.get_unloaded_by_name(module_name)
335 if mod is None:
336 raise Exception('Could not find module! %s' % module_name)
337
338 needles = []
339 for ms in self.memory_segments:
340 if mod.baseaddress <= ms.start_virtual_address < mod.endaddress:
341 needles+= ms.search(pattern, self.file_handle, find_first = find_first, chunksize = chunksize)
342 if len(needles) > 0 and find_first is True:
343 return needles
344
345
346 return needles
347
348 def search(self, pattern, find_first = False, chunksize = 10*1024):
266349 t = []
267350 for ms in self.memory_segments:
268 if mod.baseaddress <= ms.start_virtual_address < mod.endaddress:
269 t+= ms.search(pattern, self.file_handle)
270
351 t+= ms.search(pattern, self.file_handle, find_first = find_first, chunksize = chunksize)
352
271353 return t
272
273 def search(self, pattern):
274 t = []
275 for ms in self.memory_segments:
276 t+= ms.search(pattern, self.file_handle)
277
278 return t
279
354
280355 def read(self, virt_addr, size):
281356 for segment in self.memory_segments:
282357 if segment.inrange(virt_addr):
1515 buff.seek(dir.Location.Rva)
1616 csa.data = buff.read(dir.Location.DataSize).decode()
1717 return csa
18
19 @staticmethod
20 async def aparse(dir, buff):
21 csa = CommentStreamA()
22 await buff.seek(dir.Location.Rva)
23 csdata = await buff.read(dir.Location.DataSize)
24 csa.data = csdata.decode()
25 return csa
1826
1927 def __str__(self):
2028 return 'CommentA: %s' % self.data
1515 buff.seek(dir.Location.Rva)
1616 csa.data = buff.read(dir.Location.DataSize).decode('utf-16-le')
1717 return csa
18
19 @staticmethod
20 async def aparse(dir, buff):
21 csa = CommentStreamW()
22 await buff.seek(dir.Location.Rva)
23 csdata = await buff.read(dir.Location.DataSize)
24 csa.data = csdata.decode('utf-16-le')
25 return csa
1826
1927 def __str__(self):
2028 return 'CommentW: %s' % self.data
0 #!/usr/bin/env python3
1
2 # https://www.vergiliusproject.com/kernels/x64/Windows%2010%20%7C%202016/1507%20Threshold%201/_M128A
3 class M128A:
4 def __init__(self):
5 self.Low = 0 # 0x0 ULONGLONG
6 self.High = 0 # 0x8 LONGLONG
7
8 @classmethod
9 def parse(cls, buff):
10 m128a = cls()
11
12 m128a.Low = int.from_bytes(buff.read(8), byteorder = 'little', signed = False)
13 m128a.High = int.from_bytes(buff.read(8), byteorder = 'little', signed = True)
14
15 return m128a
16
17 @classmethod
18 def parse_array(cls, buff, length):
19 arr = []
20 for i in range(length):
21 arr.append(cls.parse(buff))
22 return arr
23
24 def __str__(self):
25 s = ""
26 s += "Low: %x (%d)" % (self.Low, self.Low)
27 s += "High: %x (%d)\n" % (self.High, self.High)
28 return s
29
30
31 # https://doxygen.reactos.org/df/d06/sdk_2include_2xdk_2arm_2ke_8h_source.html#l00229
32 class NEON128(M128A):
33 # looks to be the same as M128A
34 pass
35
36
37 # https://www.vergiliusproject.com/kernels/x64/Windows%20Vista%20%7C%202008/SP2/_XMM_SAVE_AREA32
38 class XMM_SAVE_AREA32:
39 def __init__(self):
40 self.ControlWord = 0 # 0x0 USHORT
41 self.StatusWord = 0 # 0x2 USHORT
42 self.TagWord = 0 # 0x4 UCHAR
43 self.Reserved1 = 0 # 0x5 UCHAR
44 self.ErrorOpcode = 0 # 0x6 USHORT
45 self.ErrorOffset = 0 # 0x8 ULONG
46 self.ErrorSelector = 0 # 0xc USHORT
47 self.Reserved2 = 0 # 0xe USHORT
48 self.DataOffset = 0 # 0x10 ULONG
49 self.DataSelector = 0 # 0x14 USHORT
50 self.Reserved3 = 0 # 0x16 USHORT
51 self.MxCsr = 0 # 0x18 ULONG
52 self.MxCsr_Mask = 0 # 0x1c ULONG
53 self.FloatRegisters = [] # 0x20 struct M128A[8]
54 self.XmmRegisters = [] # 0xa0 struct M128A[16]
55 self.Reserved4 = [] # 0x1a0 UCHAR[96]
56
57 @classmethod
58 def parse(cls, buff):
59 xmm = cls()
60
61 xmm.ControlWord = int.from_bytes(buff.read(2), byteorder = 'little', signed = False)
62 xmm.StatusWord = int.from_bytes(buff.read(2), byteorder = 'little', signed = False)
63 xmm.TagWord = chr(int.from_bytes(buff.read(1), byteorder = 'little', signed = False))
64 xmm.Reserved1 = chr(int.from_bytes(buff.read(1), byteorder = 'little', signed = False))
65 xmm.ErrorOpcode = int.from_bytes(buff.read(2), byteorder = 'little', signed = False)
66 xmm.ErrorOffset = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
67 xmm.ErrorSelector = int.from_bytes(buff.read(2), byteorder = 'little', signed = False)
68 xmm.Reserved2 = int.from_bytes(buff.read(2), byteorder = 'little', signed = False)
69 xmm.DataOffset = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
70 xmm.DataSelector = int.from_bytes(buff.read(2), byteorder = 'little', signed = False)
71 xmm.Reserved3 = int.from_bytes(buff.read(2), byteorder = 'little', signed = False)
72 xmm.MxCsr = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
73 xmm.MxCsr_Mask = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
74 xmm.FloatRegisters = M128A.parse_array(buff, 8)
75 xmm.XmmRegisters = M128A.parse_array(buff, 16)
76 xmm.Reserved4 = [
77 chr(int.from_bytes(buff.read(1), byteorder = 'little', signed = False))
78 for i in range(96)
79 ]
80
81 return xmm
82
83 def __str__(self):
84 s = ""
85 s += "%s: %x (%d)\n" % ("ControlWord", self.ControlWord, self.ControlWord)
86 s += "%s: %x (%d)\n" % ("StatusWord", self.StatusWord, self.StatusWord)
87 s += "%s: %s\n" % ("TagWord", self.TagWord)
88 s += "%s: %s\n" % ("Reserved1", self.Reserved1)
89 s += "%s: %x (%d)\n" % ("ErrorOpcode", self.ErrorOpcode, self.ErrorOpcode)
90 s += "%s: %x (%d)\n" % ("ErrorOffset", self.ErrorOffset, self.ErrorOffset)
91 s += "%s: %x (%d)\n" % ("ErrorSelector", self.ErrorSelector, self.ErrorSelector)
92 s += "%s: %x (%d)\n" % ("Reserved2", self.Reserved2, self.Reserved2)
93 s += "%s: %x (%d)\n" % ("DataOffset", self.DataOffset, self.DataOffset)
94 s += "%s: %x (%d)\n" % ("DataSelector", self.DataSelector, self.DataSelector)
95 s += "%s: %x (%d)\n" % ("Reserved3", self.Reserved3, self.Reserved3)
96 s += "%s: %x (%d)\n" % ("MxCsr", self.MxCsr, self.MxCsr)
97 s += "%s: %x (%d)\n" % ("MxCsr_Mask", self.MxCsr_Mask, self.MxCsr_Mask)
98 s += "%s:\n" % ("FloatRegisters:")
99 for freg in self.FloatRegisters:
100 s += "\t%s" % (freg)
101 s += "%s:\n" % ("XmmRegisters")
102 for xreg in self.XmmRegisters:
103 s += "\t%s" % (xreg)
104 s += "%s: %s\n" % ("Reserved4", "".join(self.Reserved4))
105
106 return s
107
108
109 class CTX_DUMMYSTRUCTNAME:
110 def __init__(self):
111 # all are M128A
112 self.Header = [] # [2]
113 self.Legacy = [] # [8]
114 self.Xmm0 = 0
115 self.Xmm1 = 0
116 self.Xmm2 = 0
117 self.Xmm3 = 0
118 self.Xmm4 = 0
119 self.Xmm5 = 0
120 self.Xmm6 = 0
121 self.Xmm7 = 0
122 self.Xmm8 = 0
123 self.Xmm9 = 0
124 self.Xmm10 = 0
125 self.Xmm11 = 0
126 self.Xmm12 = 0
127 self.Xmm13 = 0
128 self.Xmm14 = 0
129 self.Xmm15 = 0
130
131 @classmethod
132 def parse(cls, buff):
133 dsn = cls()
134
135 dsn.Header = M128A.parse_array(buff, 2)
136 dsn.Legacy = M128A.parse_array(buff, 8)
137 dsn.Xmm0 = M128A.parse(buff)
138 dsn.Xmm1 = M128A.parse(buff)
139 dsn.Xmm2 = M128A.parse(buff)
140 dsn.Xmm3 = M128A.parse(buff)
141 dsn.Xmm4 = M128A.parse(buff)
142 dsn.Xmm5 = M128A.parse(buff)
143 dsn.Xmm6 = M128A.parse(buff)
144 dsn.Xmm7 = M128A.parse(buff)
145 dsn.Xmm8 = M128A.parse(buff)
146 dsn.Xmm9 = M128A.parse(buff)
147 dsn.Xmm10 = M128A.parse(buff)
148 dsn.Xmm11 = M128A.parse(buff)
149 dsn.Xmm12 = M128A.parse(buff)
150 dsn.Xmm13 = M128A.parse(buff)
151 dsn.Xmm14 = M128A.parse(buff)
152 dsn.Xmm15 = M128A.parse(buff)
153
154 return dsn
155
156 def __str__(self):
157 s = ""
158 s += "%s:\n" % ("Header")
159 for head in self.Header:
160 s += "\t%s" % (head)
161 s += "%s:\n" % ("Legacy")
162 for leg in self.Legacy:
163 s += "\t%s" % (leg)
164 s += "%s: %s" % ("Xmm0", self.Xmm0)
165 s += "%s: %s" % ("Xmm1", self.Xmm1)
166 s += "%s: %s" % ("Xmm2", self.Xmm2)
167 s += "%s: %s" % ("Xmm3", self.Xmm3)
168 s += "%s: %s" % ("Xmm4", self.Xmm4)
169 s += "%s: %s" % ("Xmm5", self.Xmm5)
170 s += "%s: %s" % ("Xmm6", self.Xmm6)
171 s += "%s: %s" % ("Xmm7", self.Xmm7)
172 s += "%s: %s" % ("Xmm8", self.Xmm8)
173 s += "%s: %s" % ("Xmm9", self.Xmm9)
174 s += "%s: %s" % ("Xmm10", self.Xmm10)
175 s += "%s: %s" % ("Xmm11", self.Xmm11)
176 s += "%s: %s" % ("Xmm12", self.Xmm12)
177 s += "%s: %s" % ("Xmm13", self.Xmm13)
178 s += "%s: %s" % ("Xmm14", self.Xmm14)
179 s += "%s: %s" % ("Xmm15", self.Xmm15)
180
181 return s
182
183
184 class CTX_DUMMYUNIONNAME:
185 def __init__(self):
186 self.FltSave = [] # XMM_SAVE_AREA32
187 self.Q = [] # NEON128 [16]
188 self.D = [] # ULONGLONG [32]
189 self.DUMMYSTRUCTNAME = []
190 self.S = [] # DWORD [32]
191
192 @classmethod
193 def parse(cls, buff):
194 dun = cls()
195
196 dun.FltSave = XMM_SAVE_AREA32.parse(buff)
197 dun.Q = NEON128.parse_array(buff, 16)
198 dun.D = [
199 int.from_bytes(buff.read(8), byteorder = 'little', signed = False)
200 for i in range(32)
201 ]
202 dun.DUMMYSTRUCTNAME = CTX_DUMMYSTRUCTNAME.parse(buff)
203 dun.S = [
204 int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
205 for i in range(32)
206 ]
207 return dun
208
209 def __str__(self):
210 s = ""
211 s += "%s: %s\n" % ("FltSave", self.FltSave)
212 s += "%s:\n" % ("Q")
213 for q in self.Q:
214 s += "\t%s" % (q.__str__())
215 for d in self.D:
216 s += "\t%d" % (d)
217 s += "%s: %s" % ("DUMMYSTRUCTNAME", self.DUMMYSTRUCTNAME)
218 s += "%s:\n" %("S")
219 for e in self.S:
220 s += "\t%d" % (e)
221
222 return s
223
224
225 # https:# docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-context
226 class CONTEXT:
227 def __init__(self):
228 self.P1Home = 0 # DWORD64
229 self.P2Home = 0 # DWORD64
230 self.P3Home = 0 # DWORD64
231 self.P4Home = 0 # DWORD64
232 self.P5Home = 0 # DWORD64
233 self.P6Home = 0 # DWORD64
234 self.ContextFlags = 0 # DWORD
235 self.MxCsr = 0 # DWORD
236 self.SegCs = 0 # WORD
237 self.SegDs = 0 # WORD
238 self.SegEs = 0 # WORD
239 self.SegFs = 0 # WORD
240 self.SegGs = 0 # WORD
241 self.SegSs = 0 # WORD
242 self.EFlags = 0 # DWORD
243 self.Dr0 = 0 # DWORD64
244 self.Dr1 = 0 # DWORD64
245 self.Dr2 = 0 # DWORD64
246 self.Dr3 = 0 # DWORD64
247 self.Dr6 = 0 # DWORD64
248 self.Dr7 = 0 # DWORD64
249 self.Rax = 0 # DWORD64
250 self.Rcx = 0 # DWORD64
251 self.Rdx = 0 # DWORD64
252 self.Rbx = 0 # DWORD64
253 self.Rsp = 0 # DWORD64
254 self.Rbp = 0 # DWORD64
255 self.Rsi = 0 # DWORD64
256 self.Rdi = 0 # DWORD64
257 self.R8 = 0 # DWORD64
258 self.R9 = 0 # DWORD64
259 self.R10 = 0 # DWORD64
260 self.R11 = 0 # DWORD64
261 self.R12 = 0 # DWORD64
262 self.R13 = 0 # DWORD64
263 self.R14 = 0 # DWORD64
264 self.R15 = 0 # DWORD64
265 self.Rip = 0 # DWORD64
266 self.DUMMYUNIONNAME = None
267
268 self.VectorRegister = [] # M128A [26]
269 self.VectorControl = 0 # DWORD64
270 self.DebugControl = 0 # DWORD64
271 self.LastBranchToRip = 0 # DWORD64
272 self.LastBranchFromRip = 0 # DWORD64
273 self.LastExceptionToRip = 0 # DWORD64
274 self.LastExceptionFromRip = 0 # DWORD64
275
276 @classmethod
277 def parse(cls, buff):
278 ctx = cls()
279
280 ctx.P1Home = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) # DWORD64
281 ctx.P2Home = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) # DWORD64
282 ctx.P3Home = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) # DWORD64
283 ctx.P4Home = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) # DWORD64
284 ctx.P5Home = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) # DWORD64
285 ctx.P6Home = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) # DWORD64
286 ctx.ContextFlags = int.from_bytes(buff.read(4), byteorder = 'little', signed = False) # DWORD
287 ctx.MxCsr = int.from_bytes(buff.read(4), byteorder = 'little', signed = False) # DWORD
288 ctx.SegCs = int.from_bytes(buff.read(2), byteorder = 'little', signed = False) # WORD
289 ctx.SegDs = int.from_bytes(buff.read(2), byteorder = 'little', signed = False) # WORD
290 ctx.SegEs = int.from_bytes(buff.read(2), byteorder = 'little', signed = False) # WORD
291 ctx.SegFs = int.from_bytes(buff.read(2), byteorder = 'little', signed = False) # WORD
292 ctx.SegGs = int.from_bytes(buff.read(2), byteorder = 'little', signed = False) # WORD
293 ctx.SegSs = int.from_bytes(buff.read(2), byteorder = 'little', signed = False) # WORD
294 ctx.EFlags = int.from_bytes(buff.read(4), byteorder = 'little', signed = False) # DWORD
295 ctx.Dr0 = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) # DWORD64
296 ctx.Dr1 = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) # DWORD64
297 ctx.Dr2 = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) # DWORD64
298 ctx.Dr3 = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) # DWORD64
299 ctx.Dr6 = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) # DWORD64
300 ctx.Dr7 = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) # DWORD64
301 ctx.Rax = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) # DWORD64
302 ctx.Rcx = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) # DWORD64
303 ctx.Rdx = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) # DWORD64
304 ctx.Rbx = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) # DWORD64
305 ctx.Rsp = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) # DWORD64
306 ctx.Rbp = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) # DWORD64
307 ctx.Rsi = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) # DWORD64
308 ctx.Rdi = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) # DWORD64
309 ctx.R8 = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) # DWORD64
310 ctx.R9 = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) # DWORD64
311 ctx.R10 = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) # DWORD64
312 ctx.R11 = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) # DWORD64
313 ctx.R12 = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) # DWORD64
314 ctx.R13 = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) # DWORD64
315 ctx.R14 = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) # DWORD64
316 ctx.R15 = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) # DWORD64
317 ctx.Rip = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) # DWORD64
318 ctx.DUMMYUNIONNAME = CTX_DUMMYUNIONNAME.parse(buff)
319
320 ctx.VectorRegister = M128A.parse_array(buff, 26) # M128A [26]
321 ctx.VectorControl = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) # DWORD64
322 ctx.DebugControl = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) # DWORD64
323 ctx.LastBranchToRip = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) # DWORD64
324 ctx.LastBranchFromRip = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) # DWORD64
325 ctx.LastExceptionToRip = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) # DWORD64
326 ctx.LastExceptionFromRip = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) # DWORD64
327
328 return ctx
329
330 def __str__(self):
331 s = ""
332 s += "%s: 0x%x (%d)\n" % ("P1Home",self.P1Home,self.P1Home)
333 s += "%s: 0x%x (%d)\n" % ("P2Home",self.P2Home,self.P2Home)
334 s += "%s: 0x%x (%d)\n" % ("P3Home",self.P3Home,self.P3Home)
335 s += "%s: 0x%x (%d)\n" % ("P4Home",self.P4Home,self.P4Home)
336 s += "%s: 0x%x (%d)\n" % ("P5Home",self.P5Home,self.P5Home)
337 s += "%s: 0x%x (%d)\n" % ("P6Home",self.P6Home,self.P6Home)
338 s += "%s: 0x%x (%d)\n" % ("ContextFlags",self.ContextFlags,self.ContextFlags)
339 s += "%s: 0x%x (%d)\n" % ("MxCsr",self.MxCsr,self.MxCsr)
340 s += "%s: 0x%x (%d)\n" % ("SegCs",self.SegCs,self.SegCs)
341 s += "%s: 0x%x (%d)\n" % ("SegDs",self.SegDs,self.SegDs)
342 s += "%s: 0x%x (%d)\n" % ("SegEs",self.SegEs,self.SegEs)
343 s += "%s: 0x%x (%d)\n" % ("SegFs",self.SegFs,self.SegFs)
344 s += "%s: 0x%x (%d)\n" % ("SegGs",self.SegGs,self.SegGs)
345 s += "%s: 0x%x (%d)\n" % ("SegSs",self.SegSs,self.SegSs)
346 s += "%s: 0x%x (%d)\n" % ("EFlags",self.EFlags,self.EFlags)
347 s += "%s: 0x%x (%d)\n" % ("Dr0",self.Dr0,self.Dr0)
348 s += "%s: 0x%x (%d)\n" % ("Dr1",self.Dr1,self.Dr1)
349 s += "%s: 0x%x (%d)\n" % ("Dr2",self.Dr2,self.Dr2)
350 s += "%s: 0x%x (%d)\n" % ("Dr3",self.Dr3,self.Dr3)
351 s += "%s: 0x%x (%d)\n" % ("Dr6",self.Dr6,self.Dr6)
352 s += "%s: 0x%x (%d)\n" % ("Dr7",self.Dr7,self.Dr7)
353 s += "%s: 0x%x (%d)\n" % ("Rax",self.Rax,self.Rax)
354 s += "%s: 0x%x (%d)\n" % ("Rcx",self.Rcx,self.Rcx)
355 s += "%s: 0x%x (%d)\n" % ("Rdx",self.Rdx,self.Rdx)
356 s += "%s: 0x%x (%d)\n" % ("Rbx",self.Rbx,self.Rbx)
357 s += "%s: 0x%x (%d)\n" % ("Rsp",self.Rsp,self.Rsp)
358 s += "%s: 0x%x (%d)\n" % ("Rbp",self.Rbp,self.Rbp)
359 s += "%s: 0x%x (%d)\n" % ("Rsi",self.Rsi,self.Rsi)
360 s += "%s: 0x%x (%d)\n" % ("Rdi",self.Rdi,self.Rdi)
361 s += "%s: 0x%x (%d)\n" % ("R8",self.R8,self.R8)
362 s += "%s: 0x%x (%d)\n" % ("R9",self.R9,self.R9)
363 s += "%s: 0x%x (%d)\n" % ("R10",self.R10,self.R10)
364 s += "%s: 0x%x (%d)\n" % ("R11",self.R11,self.R11)
365 s += "%s: 0x%x (%d)\n" % ("R12",self.R12,self.R12)
366 s += "%s: 0x%x (%d)\n" % ("R13",self.R13,self.R13)
367 s += "%s: 0x%x (%d)\n" % ("R14",self.R14,self.R14)
368 s += "%s: 0x%x (%d)\n" % ("R15",self.R15,self.R15)
369 s += "%s: 0x%x (%d)\n" % ("Rip",self.Rip,self.Rip)
370 s += "%s:" % ("DUMMYUNIONNAME")
371 s += self.DUMMYUNIONNAME.__str__()
372
373 return s
374
375
376 # https:# docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-wow64_floating_save_area
377 class WOW64_FLOATING_SAVE_AREA:
378 def __init__(self):
379 self.ControlWord = 0 # DWORD
380 self.StatusWord = 0 # DWORD
381 self.TagWord = 0 # DWORD
382 self.ErrorOffset = 0 # DWORD
383 self.ErrorSelector = 0 # DWORD
384 self.DataOffset = 0 # DWORD
385 self.DataSelector = 0 # DWORD
386 self.RegisterArea = [] # BYTE
387 self.Cr0NpxState = 0 # DWORD
388
389 @classmethod
390 def parse(cls, buff):
391 ctx = cls()
392 ctx.ControlWord = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
393 ctx.StatusWord = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
394 ctx.TagWord = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
395 ctx.ErrorOffset = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
396 ctx.ErrorSelector = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
397 ctx.DataOffset = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
398 ctx.DataSelector = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
399 ctx.RegisterArea = int.from_bytes(buff.read(80), byteorder = 'little', signed = False)
400 ctx.Cr0NpxState = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
401 return ctx
402
403 def __str__(self):
404 s = ''
405 s += "ControlWord: %x (%d)\n" % (self.ControlWord, self.ControlWord)
406 s += "StatusWord: %x (%d)\n" % (self.StatusWord, self.StatusWord)
407 s += "TagWord: %x (%d)\n" % (self.TagWord, self.TagWord)
408 s += "ErrorOffset: %x (%d)\n" % (self.ErrorOffset, self.ErrorOffset)
409 s += "ErrorSelector: %x (%d)\n" % (self.ErrorSelector, self.ErrorSelector)
410 s += "DataOffset: %x (%d)\n" % (self.DataOffset, self.DataOffset)
411 s += "DataSelector: %x (%d)\n" % (self.DataSelector, self.DataSelector)
412 s += "RegisterArea: %s\n" % str(self.RegisterArea)
413 s += "Cr0NpxState: %x (%d)" % (self.Cr0NpxState, self.Cr0NpxState)
414 return s
415
416 # https:# docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-wow64_context
417 class WOW64_CONTEXT:
418 def __init__(self):
419 self.ContextFlags = 0 # DWORD
420 self.Dr0 = 0 # DWORD
421 self.Dr1 = 0 # DWORD
422 self.Dr2 = 0 # DWORD
423 self.Dr3 = 0 # DWORD
424 self.Dr6 = 0 # DWORD
425 self.Dr7 = 0 # DWORD
426 self.FloatSave = 0 # WOW64_FLOATING_SAVE_AREA
427 self.SegGs = 0 # DWORD
428 self.SegFs = 0 # DWORD
429 self.SegEs = 0 # DWORD
430 self.SegDs = 0 # DWORD
431 self.Edi = 0 # DWORD
432 self.Esi = 0 # DWORD
433 self.Ebx = 0 # DWORD
434 self.Edx = 0 # DWORD
435 self.Ecx = 0 # DWORD
436 self.Eax = 0 # DWORD
437 self.Ebp = 0 # DWORD
438 self.Eip = 0 # DWORD
439 self.SegCs = 0 # DWORD
440 self.EFlags = 0 # DWORD
441 self.Esp = 0 # DWORD
442 self.SegSs = 0 # DWORD
443 self.ExtendedRegisters = [] # BYTE
444
445 @classmethod
446 def parse(cls, buff):
447 ctx = cls()
448
449 ctx.ContextFlags = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
450 ctx.Dr0 = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
451 ctx.Dr1 = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
452 ctx.Dr2 = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
453 ctx.Dr3 = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
454 ctx.Dr6 = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
455 ctx.Dr7 = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
456 ctx.FloatSave = WOW64_FLOATING_SAVE_AREA.parse(buff)
457 ctx.SegGs = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
458 ctx.SegFs = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
459 ctx.SegEs = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
460 ctx.SegDs = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
461 ctx.Edi = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
462 ctx.Esi = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
463 ctx.Ebx = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
464 ctx.Edx = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
465 ctx.Ecx = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
466 ctx.Eax = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
467 ctx.Ebp = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
468 ctx.Eip = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
469 ctx.SegCs = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
470 ctx.EFlags = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
471 ctx.Esp = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
472 ctx.SegSs = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
473 ctx.ExtendedRegisters = [
474 int.from_bytes(buff.read(1), byteorder = 'little', signed = False)
475 for i in range(512)
476 ]
477 return ctx
478
479 def __str__(self):
480 s = ''
481 s += "%s: %x (%d)\n" % ("ContextFlags", self.ContextFlags, self.ContextFlags)
482 s += "%s: %x (%d)\n" % ("Dr0", self.Dr0, self.Dr0)
483 s += "%s: %x (%d)\n" % ("Dr1", self.Dr1, self.Dr1)
484 s += "%s: %x (%d)\n" % ("Dr2", self.Dr2, self.Dr2)
485 s += "%s: %x (%d)\n" % ("Dr3", self.Dr3, self.Dr3)
486 s += "%s: %x (%d)\n" % ("Dr6", self.Dr6, self.Dr6)
487 s += "%s: %x (%d)\n" % ("Dr7", self.Dr7, self.Dr7)
488 s += "%s: %s\n" % ("FloatSave", self.FloatSave.__str__())
489 s += "%s: %x (%d)\n" % ("SegGs", self.SegGs, self.SegGs)
490 s += "%s: %x (%d)\n" % ("SegFs", self.SegFs, self.SegFs)
491 s += "%s: %x (%d)\n" % ("SegEs", self.SegEs, self.SegEs)
492 s += "%s: %x (%d)\n" % ("SegDs", self.SegDs, self.SegDs)
493 s += "%s: %x (%d)\n" % ("Edi", self.Edi, self.Edi)
494 s += "%s: %x (%d)\n" % ("Esi", self.Esi, self.Esi)
495 s += "%s: %x (%d)\n" % ("Ebx", self.Ebx, self.Ebx)
496 s += "%s: %x (%d)\n" % ("Edx", self.Edx, self.Edx)
497 s += "%s: %x (%d)\n" % ("Ecx", self.Ecx, self.Ecx)
498 s += "%s: %x (%d)\n" % ("Eax", self.Eax, self.Eax)
499 s += "%s: %x (%d)\n" % ("Ebp", self.Ebp, self.Ebp)
500 s += "%s: %x (%d)\n" % ("Eip", self.Eip, self.Eip)
501 s += "%s: %x (%d)\n" % ("SegCs", self.SegCs, self.SegCs)
502 s += "%s: %x (%d)\n" % ("EFlags", self.EFlags, self.EFlags)
503 s += "%s: %x (%d)\n" % ("Esp", self.Esp, self.Esp)
504 s += "%s: %x (%d)\n" % ("SegSs", self.SegSs, self.SegSs)
505 s += "%s: %s\n" % ("ExtendedRegisters", str(self.ExtendedRegisters))
506
507 return s
5050
5151 class ExceptionCode(enum.Enum):
5252 # Not a real exception code, it's just a placeholder to prevent the parser from raising an error
53 EXCEPTION_UNKNOWN = 'EXCEPTION_UNKNOWN_CHECK_RAW'
5354 EXCEPTION_NONE = 0x00
5455
5556 # Linux SIG values (for crashpad generated dumps)
116117 self.NumberParameters = None
117118 self.__unusedAlignment = None
118119 self.ExceptionInformation = []
120 self.ExceptionCode_raw = None
119121
120122 @staticmethod
121123 def parse(buff):
122124 me = MINIDUMP_EXCEPTION()
123 me.ExceptionCode = ExceptionCode(int.from_bytes(buff.read(4), byteorder = 'little', signed = False))
125 me.ExceptionCode_raw = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
126 try:
127 me.ExceptionCode = ExceptionCode(me.ExceptionCode_raw)
128 except:
129 me.ExceptionCode = ExceptionCode.EXCEPTION_UNKNOWN
130
124131 me.ExceptionFlags = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
125132 me.ExceptionRecord = int.from_bytes(buff.read(8), byteorder = 'little', signed = False)
126133 me.ExceptionAddress = int.from_bytes(buff.read(8), byteorder = 'little', signed = False)
127134 me.NumberParameters = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
128135 me.__unusedAlignment = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
129 for i in range(me.NumberParameters):
136 for _ in range(me.NumberParameters):
130137 me.ExceptionInformation.append(int.from_bytes(buff.read(8), byteorder = 'little', signed = False))
131138
132139 return me
191198 t.exception_records.append(mes)
192199
193200 return t
201
202 @staticmethod
203 async def aparse(dir, buff):
204 t = ExceptionList()
205
206 await buff.seek(dir.Location.Rva)
207 chunk_data = await buff.read(dir.Location.DataSize)
208 chunk = io.BytesIO(chunk_data)
209
210 # Unfortunately, we don't have a certain way to figure out how many exception records
211 # there is in the stream, so we have to fallback on heuristics (EOF or bad data read)
212 #
213 # NB : some tool only read one exception record : https://github.com/GregTheDev/MinidumpExplorer/blob/a6dd974757c16142eefcfff7d99be10b14f87eaf/MinidumpExplorer/MinidumpExplorer/MainForm.cs#L257
214 # but it's incorrect since we can have an exception chain (double fault, exception catched and re-raised, etc.)
215 while chunk.tell() < dir.Location.DataSize:
216 mes = MINIDUMP_EXCEPTION_STREAM.parse(chunk)
217
218 # a minidump exception stream is usally padded with zeroes
219 # so whenever we parse an exception record with the code EXCEPTION_NONE
220 # we can stop.
221 if mes.ExceptionRecord.ExceptionCode == ExceptionCode.EXCEPTION_NONE:
222 break
223
224 t.exception_records.append(mes)
225
226 return t
194227
195228 def to_table(self):
196229 t = []
103103 mhoi.SizeOfInfo = int.from_bytes(buff.read(4), byteorder = 'little', signed = False)
104104 mhoi.info_bytes = buff.read(mhoi.SizeOfInfo)
105105 return mhoi
106
107 @staticmethod
108 async def aparse(buff):
109 mhoi = MINIDUMP_HANDLE_OBJECT_INFORMATION()
110 t = await buff.read(4)
111 mhoi.NextInfoRva = int.from_bytes(t, byteorder = 'little', signed = False)
112 t = await buff.read(4)
113 mhoi.InfoType = int.from_bytes(t, byteorder = 'little', signed = False)
114 t = await buff.read(4)
115 mhoi.SizeOfInfo = int.from_bytes(t, byteorder = 'little', signed = False)
116 mhoi.info_bytes = await buff.read(mhoi.SizeOfInfo)
117 return mhoi
106118
107119 class MinidumpHandleObjectInformation:
108120 def __init__(self):
112124 self.info_bytes = None
113125
114126 @staticmethod
115 def parse(mhoi, buff):
127 def parse(mhoi):
116128 t = MinidumpHandleObjectInformation()
117129 t.InfoType = mhoi.InfoType
118130 t.SizeOfInfo = mhoi.SizeOfInfo
151163 if t.ObjectInfoRva is not None and t.ObjectInfoRva != 0:
152164 MinidumpHandleDescriptor.walk_objectinfo(mhd, t.ObjectInfoRva, buff)
153165 return mhd
166
167 @staticmethod
168 async def aparse(t, buff):
169 mhd = MinidumpHandleDescriptor()
170 mhd.Handle = t.Handle
171 if t.TypeNameRva != 0:
172 mhd.TypeName = await MINIDUMP_STRING.aget_from_rva(t.TypeNameRva, buff)
173 if t.ObjectNameRva != 0:
174 mhd.ObjectName = await MINIDUMP_STRING.aget_from_rva(t.ObjectNameRva, buff)
175 mhd.Attributes = t.Attributes
176 mhd.GrantedAccess = t.GrantedAccess
177 mhd.HandleCount = t.HandleCount
178 mhd.PointerCount = t.PointerCount
179 if isinstance(t, MINIDUMP_HANDLE_DESCRIPTOR_2):
180 if t.ObjectInfoRva is not None and t.ObjectInfoRva != 0:
181 await MinidumpHandleDescriptor.awalk_objectinfo(mhd, t.ObjectInfoRva, buff)
182 return mhd
154183
155184 @staticmethod
156185 def walk_objectinfo(mhd, start, buff):
157186 while start is not None and start != 0:
158187 buff.seek(start)
159188 mhoi = MINIDUMP_HANDLE_OBJECT_INFORMATION.parse(buff)
160 t = MinidumpHandleObjectInformation.parse(mhoi, buff)
189 t = MinidumpHandleObjectInformation.parse(mhoi)
190 mhd.ObjectInfos.append(t)
191 start = t.NextInfo
192
193 @staticmethod
194 async def awalk_objectinfo(mhd, start, buff):
195 while start is not None and start != 0:
196 await buff.seek(start)
197 mhoi = await MINIDUMP_HANDLE_OBJECT_INFORMATION.aparse(buff)
198 t = MinidumpHandleObjectInformation.parse(mhoi)
161199 mhd.ObjectInfos.append(t)
162200 start = t.NextInfo
163201
194232 mhd = MINIDUMP_HANDLE_DESCRIPTOR_2.parse(chunk)
195233 t.handles.append(MinidumpHandleDescriptor.parse(mhd, buff))
196234 return t
235
236 @staticmethod
237 async def aparse(dir, buff):
238 t = MinidumpHandleDataStream()
239 await buff.seek(dir.Location.Rva)
240 chunk_data = await buff.read(dir.Location.DataSize)
241 chunk = io.BytesIO(chunk_data)
242 t.header = MINIDUMP_HANDLE_DATA_STREAM.parse(chunk)
243 for _ in range(t.header.NumberOfDescriptors):
244 if t.header.SizeOfDescriptor == MINIDUMP_HANDLE_DESCRIPTOR.size:
245 mhd = MINIDUMP_HANDLE_DESCRIPTOR.parse(chunk)
246 r = await MinidumpHandleDescriptor.aparse(mhd, buff)
247 t.handles.append(r)
248 else:
249 mhd = MINIDUMP_HANDLE_DESCRIPTOR_2.parse(chunk)
250 r = await MinidumpHandleDescriptor.aparse(mhd, buff)
251 t.handles.append(r)
252 return t
197253
198254 def __str__(self):
199255 t = '== MinidumpHandleDataStream ==\n'
7979 mtl = MINIDUMP_MEMORY64_LIST.parse(chunk)
8080 rva = mtl.BaseRva
8181 for mod in mtl.MemoryRanges:
82 t.memory_segments.append(MinidumpMemorySegment.parse_full(mod, buff, rva))
82 t.memory_segments.append(MinidumpMemorySegment.parse_full(mod, rva))
8383 rva += mod.DataSize
8484 return t
85
86 @staticmethod
87 async def aparse(dir, buff):
88 mml = MinidumpMemory64List()
89 await buff.seek(dir.Location.Rva)
90 chunk_data = await buff.read(dir.Location.DataSize)
91 chunk = io.BytesIO(chunk_data)
92 mtl = MINIDUMP_MEMORY64_LIST.parse(chunk)
93 rva = mtl.BaseRva
94 for mod in mtl.MemoryRanges:
95 ms = MinidumpMemorySegment.parse_full(mod, rva)
96 mml.memory_segments.append(ms)
97 rva += mod.DataSize
98 return mml
8599
86100 def to_table(self):
87101 t = []
210210 t.infos.append(MinidumpMemoryInfo.parse(mi, buff))
211211
212212 return t
213
214 @staticmethod
215 async def aparse(dir, buff):
216 t = MinidumpMemoryInfoList()
217 await buff.seek(dir.Location.Rva)
218 data = await buff.read(dir.Location.DataSize)
219 chunk = io.BytesIO(data)
220 t.header = MINIDUMP_MEMORY_INFO_LIST.parse(chunk)
221 for _ in range(t.header.NumberOfEntries):
222 mi = MINIDUMP_MEMORY_INFO.parse(chunk)
223 t.infos.append(MinidumpMemoryInfo.parse(mi, None))
224
225 return t
213226
214227 def to_table(self):
215228 t = []
8585 for mod in mtl.MemoryRanges:
8686 t.memory_segments.append(MinidumpMemorySegment.parse_mini(mod, buff))
8787 return t
88
89 @staticmethod
90 async def aparse(dir, buff):
91 t = MinidumpMemoryList()
92 await buff.seek(dir.Location.Rva)
93 chunk_data = await buff.read(dir.Location.DataSize)
94 chunk = io.BytesIO(chunk_data)
95 mtl = MINIDUMP_MEMORY_LIST.parse(chunk)
96 for mod in mtl.MemoryRanges:
97 t.memory_segments.append(MinidumpMemorySegment.parse_mini(mod, buff))
98 return t
8899
89100 def __str__(self):
90101 t = '== MinidumpMemoryList ==\n'
122122 t.ProcessorMaxIdleState = misc.ProcessorMaxIdleState
123123 t.ProcessorCurrentIdleState = misc.ProcessorCurrentIdleState
124124 return t
125
126 @staticmethod
127 async def aparse(dir, buff):
128 t = MinidumpMiscInfo()
129 await buff.seek(dir.Location.Rva)
130 chunk_data = await buff.read(dir.Location.DataSize)
131 chunk = io.BytesIO(chunk_data)
132 if dir.Location.DataSize == MINIDUMP_MISC_INFO.size:
133 misc = MINIDUMP_MISC_INFO.parse(chunk)
134 t.ProcessId = misc.ProcessId
135 t.ProcessCreateTime = misc.ProcessCreateTime
136 t.ProcessUserTime = misc.ProcessUserTime
137 t.ProcessKernelTime = misc.ProcessKernelTime
138 else:
139 misc = MINIDUMP_MISC_INFO_2.parse(chunk)
140 t.ProcessId = misc.ProcessId
141 t.ProcessCreateTime = misc.ProcessCreateTime
142 t.ProcessUserTime = misc.ProcessUserTime
143 t.ProcessKernelTime = misc.ProcessKernelTime
144 t.ProcessorMaxMhz = misc.ProcessorMaxMhz
145 t.ProcessorCurrentMhz = misc.ProcessorCurrentMhz
146 t.ProcessorMhzLimit = misc.ProcessorMhzLimit
147 t.ProcessorMaxIdleState = misc.ProcessorMaxIdleState
148 t.ProcessorCurrentIdleState = misc.ProcessorCurrentIdleState
149 return t
125150
126151 def __str__(self):
127152 t = '== MinidumpMiscInfo ==\n'
3131 mm.versioninfo = mod.VersionInfo
3232 mm.endaddress = mm.baseaddress + mm.size
3333 return mm
34
35 @staticmethod
36 async def aparse(mod, buff):
37 """
38 mod: MINIDUMP_MODULE
39 buff: file handle
40 """
41 mm = MinidumpModule()
42 mm.baseaddress = mod.BaseOfImage
43 mm.size = mod.SizeOfImage
44 mm.checksum = mod.CheckSum
45 mm.timestamp = mod.TimeDateStamp
46 mm.name = await MINIDUMP_STRING.aget_from_rva(mod.ModuleNameRva, buff)
47 mm.versioninfo = mod.VersionInfo
48 mm.endaddress = mm.baseaddress + mm.size
49 return mm
3450
3551 def inrange(self, memory_loc):
3652 return self.baseaddress <= memory_loc < self.endaddress
4258 'BaseAddress',
4359 'Size',
4460 'Endaddress',
61 'Timestamp',
4562 ]
4663
4764 def to_row(self):
5067 '0x%08x' % self.baseaddress,
5168 hex(self.size),
5269 '0x%08x' % self.endaddress,
70 '0x%08x' % self.timestamp,
5371 ]
5472
5573
211229 for mod in mtl.Modules:
212230 t.modules.append(MinidumpModule.parse(mod, buff))
213231 return t
232
233 @staticmethod
234 async def aparse(dir, buff):
235 t = MinidumpModuleList()
236 await buff.seek(dir.Location.Rva)
237 chunk_data = await buff.read(dir.Location.DataSize)
238 chunk = io.BytesIO(chunk_data)
239 mtl = MINIDUMP_MODULE_LIST.parse(chunk)
240 for mod in mtl.Modules:
241 x = await MinidumpModule.aparse(mod, buff)
242 t.modules.append(x)
243 return t
214244
215245 def to_table(self):
216246 t = []
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 @staticmethod
253 async def aparse(dir, buff):
254 t = MinidumpSystemInfo()
255 await buff.seek(dir.Location.Rva)
256 chunk_data = await buff.read(dir.Location.DataSize)
257 chunk = io.BytesIO(chunk_data)
258 si = MINIDUMP_SYSTEM_INFO.parse(chunk)
259 t.ProcessorArchitecture = si.ProcessorArchitecture
260 t.ProcessorLevel = si.ProcessorLevel
261 t.ProcessorRevision = si.ProcessorRevision
262 t.NumberOfProcessors = si.NumberOfProcessors
263 t.ProductType = si.ProductType
264 t.MajorVersion = si.MajorVersion
265 t.MinorVersion = si.MinorVersion
266 t.BuildNumber = si.BuildNumber
267 t.PlatformId = si.PlatformId
268 t.CSDVersion = await MINIDUMP_STRING.aget_from_rva(si.CSDVersionRva, buff)
269 t.SuiteMask = si.SuiteMask
270 t.VendorId = si.VendorId
271 t.VersionInformation = si.VersionInformation
272 t.FeatureInformation = si.FeatureInformation
273 t.AMDExtendedCpuFeatures = si.AMDExtendedCpuFeatures
274 t.ProcessorFeatures = si.ProcessorFeatures
275 try:
276 t.guess_os()
277 except Exception as e:
278 logging.log(1, 'Failed to guess OS! MajorVersion: %s MinorVersion %s BuildNumber %s ProductType: %s' % (t.MajorVersion, t.MinorVersion, t.BuildNumber, t.ProductType ))
279 t.OperatingSystem = None
280 return t
281
282
252283 def __str__(self):
253284 t = '== System Info ==\n'
254285 t += 'ProcessorArchitecture %s\n' % self.ProcessorArchitecture
268299 t += 'FeatureInformation %s\n' % self.FeatureInformation
269300 t += 'AMDExtendedCpuFeatures %s\n' % self.AMDExtendedCpuFeatures
270301 t += 'ProcessorFeatures %s\n' % ' '.join( [hex(x) for x in self.ProcessorFeatures] )
271
272 return t
302
303 return t
8383 mtl = MINIDUMP_THREAD_EX_LIST.parse(chunk)
8484 t.threads = mtl.Threads
8585 return t
86
87 @staticmethod
88 async def aparse(dir, buff):
89 t = MinidumpThreadExList()
90 await buff.seek(dir.Location.Rva)
91 chunk_data = await buff.read(dir.Location.DataSize)
92 chunk = io.BytesIO(chunk_data)
93 mtl = MINIDUMP_THREAD_EX_LIST.parse(chunk)
94 t.threads = mtl.Threads
95 return t
8696
8797 def to_table(self):
8898 t = []
159159 data = buff.read(dir.Location.DataSize)
160160 chunk = io.BytesIO(data)
161161 t.header = MINIDUMP_THREAD_INFO_LIST.parse(chunk)
162 for i in range(t.header.NumberOfEntries):
162 for _ in range(t.header.NumberOfEntries):
163163 mi = MINIDUMP_THREAD_INFO.parse(chunk)
164164 t.infos.append(MinidumpThreadInfo.parse(mi, buff))
165
166 return t
167
168 @staticmethod
169 async def aparse(dir, buff):
170 t = MinidumpThreadInfoList()
171 await buff.seek(dir.Location.Rva)
172 data = await buff.read(dir.Location.DataSize)
173 chunk = io.BytesIO(data)
174 t.header = MINIDUMP_THREAD_INFO_LIST.parse(chunk)
175 for _ in range(t.header.NumberOfEntries):
176 mi = MINIDUMP_THREAD_INFO.parse(chunk)
177 t.infos.append(MinidumpThreadInfo.parse(mi, None))
165178
166179 return t
167180
9595 mtl = MINIDUMP_THREAD_LIST.parse(chunk)
9696 t.threads = mtl.Threads
9797 return t
98
99 @staticmethod
100 async def aparse(dir, buff):
101 t = MinidumpThreadList()
102 await buff.seek(dir.Location.Rva)
103 chunk_data = await buff.read(dir.Location.DataSize)
104 chunk = io.BytesIO(chunk_data)
105 mtl = MINIDUMP_THREAD_LIST.parse(chunk)
106 t.threads = mtl.Threads
107 return t
98108
99109 def to_table(self):
100110 t = []
7979 mm.name = MINIDUMP_STRING.get_from_rva(mod.ModuleNameRva, buff)
8080 mm.endaddress = mm.baseaddress + mm.size
8181 return mm
82
83 @staticmethod
84 async def aparse(mod, buff):
85 """
86 mod: MINIDUMP_MODULE
87 buff: file handle
88 """
89 mm = MinidumpUnloadedModule()
90 mm.baseaddress = mod.BaseOfImage
91 mm.size = mod.SizeOfImage
92 mm.checksum = mod.CheckSum
93 mm.timestamp = mod.TimeDateStamp
94 mm.name = await MINIDUMP_STRING.aget_from_rva(mod.ModuleNameRva, buff)
95 mm.endaddress = mm.baseaddress + mm.size
96 return mm
8297
8398 def assign_memory_regions(self, segments):
8499 for segment in segments:
121136 t.modules.append(MinidumpUnloadedModule.parse(mod, buff))
122137
123138 return t
139
140 @staticmethod
141 async def aparse(dir, buff):
142 t = MinidumpUnloadedModuleList()
143 await buff.seek(dir.Location.Rva)
144 chunk_data = await buff.read(dir.Location.DataSize)
145 chunk = io.BytesIO(chunk_data)
146 muml = MINIDUMP_UNLOADED_MODULE_LIST.parse(chunk)
147 for _ in range(muml.NumberOfEntries):
148 mod = MINIDUMP_UNLOADED_MODULE.parse(chunk)
149 dr = await MinidumpUnloadedModule.aparse(mod, buff)
150 t.modules.append(dr)
151
152 return t
124153
125154 def to_table(self):
126155 t = []
00 from .CommentStreamA import *
11 from .CommentStreamW import *
2 from .ContextStream import *
23 from .ExceptionStream import *
34 from .FunctionTableStream import *
45 from .HandleDataStream import *
2324
2425 __CommentStreamA__ = ['CommentStreamA']
2526 __CommentStreamW__ = ['CommentStreamW']
27 __ContextStream__ = ['CONTEXT', 'CTX_DUMMYSTRUCTNAME', 'CTX_DUMMYUNIONNAME', 'M128A', 'NEON128', 'WOW64_CONTEXT', 'WOW64_FLOATING_SAVE_AREA', 'XMM_SAVE_AREA32']
2628 __ExceptionStream__ = ['ExceptionList']
2729 __FunctionTableStream__ = ['MINIDUMP_FUNCTION_TABLE_STREAM']
2830 __HandleDataStream__ = ['MinidumpHandleDataStream','MINIDUMP_HANDLE_DATA_STREAM']
3739 __ProcessVmCountersStream__ = []
3840 __SystemInfoStream__ = ['MinidumpSystemInfo','PROCESSOR_ARCHITECTURE','PROCESSOR_LEVEL', 'PRODUCT_TYPE', 'PLATFORM_ID','SUITE_MASK','MINIDUMP_SYSTEM_INFO']
3941 __SystemMemoryInfoStream__ = []
40 __ThreadExListStream__ = ['MINIDUMP_THREAD_EX', 'MINIDUMP_THREAD_EX_LIST']
42 __ThreadExListStream__ = ['MinidumpThreadExList', 'MINIDUMP_THREAD_EX', 'MINIDUMP_THREAD_EX_LIST']
4143 __ThreadInfoListStream__ = ['MinidumpThreadInfoList','MINIDUMP_THREAD_INFO_LIST', 'MINIDUMP_THREAD_INFO', 'DumpFlags']
4244 __ThreadListStream__ = ['MinidumpThreadList','MINIDUMP_THREAD', 'MINIDUMP_THREAD_LIST']
4345 __TokenStream__ = []
4446 __UnloadedModuleListStream__ = ['MinidumpUnloadedModuleList', 'MINIDUMP_UNLOADED_MODULE', 'MINIDUMP_UNLOADED_MODULE_LIST']
4547
46 __all__ = __CommentStreamA__ + __CommentStreamW__ + __ExceptionStream__ + __FunctionTableStream__ + __HandleDataStream__ + __HandleOperationListStream__ + __JavaScriptDataStream__ + __LastReservedStream__ + __Memory64ListStream__ + __MemoryInfoListStream__ + __MemoryListStream__ + __MiscInfoStream__ + __ModuleListStream__ + __ProcessVmCountersStream__ + __SystemInfoStream__ + __SystemMemoryInfoStream__ + __ThreadExListStream__ + __ThreadInfoListStream__ + __ThreadListStream__ + __TokenStream__ + __UnloadedModuleListStream__
48 __all__ = __CommentStreamA__ + __CommentStreamW__ + __ContextStream__ + __ExceptionStream__ + __FunctionTableStream__ + __HandleDataStream__ + __HandleOperationListStream__ + __JavaScriptDataStream__ + __LastReservedStream__ + __Memory64ListStream__ + __MemoryInfoListStream__ + __MemoryListStream__ + __MiscInfoStream__ + __ModuleListStream__ + __ProcessVmCountersStream__ + __SystemInfoStream__ + __SystemMemoryInfoStream__ + __ThreadExListStream__ + __ThreadInfoListStream__ + __ThreadListStream__ + __TokenStream__ + __UnloadedModuleListStream__
88 from ctypes.wintypes import HANDLE, BOOL, DWORD, HWND, HINSTANCE, HKEY, LPVOID, LPWSTR, PBOOL
99 from ctypes import c_ulong, c_char_p, c_int, c_void_p, WinError, get_last_error, windll
1010
11 from privileges import enable_debug_privilege
11 from minidump.utils.privileges import enable_debug_privilege
1212
1313 if platform.system() != 'Windows':
1414 raise Exception('This script will ovbiously only work on Windows')
00 Metadata-Version: 1.2
11 Name: minidump
2 Version: 0.0.12
2 Version: 0.0.21
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
4 minidump/__amain__.py
25 minidump/__init__.py
36 minidump/__main__.py
47 minidump/_version.py
8 minidump/aminidumpfile.py
9 minidump/aminidumpreader.py
510 minidump/common_structs.py
611 minidump/constants.py
712 minidump/directory.py
1621 minidump.egg-info/SOURCES.txt
1722 minidump.egg-info/dependency_links.txt
1823 minidump.egg-info/entry_points.txt
24 minidump.egg-info/not-zip-safe
1925 minidump.egg-info/top_level.txt
20 minidump.egg-info/zip-safe
2126 minidump/streams/CommentStreamA.py
2227 minidump/streams/CommentStreamW.py
28 minidump/streams/ContextStream.py
2329 minidump/streams/ExceptionStream.py
2430 minidump/streams/FunctionTableStream.py
2531 minidump/streams/HandleDataStream.py
+0
-1
minidump.egg-info/zip-safe less more
0
3030 # Details
3131 url="https://github.com/skelsec/minidump",
3232
33 zip_safe = True,
33 zip_safe = False,
3434 #
3535 # license="LICENSE.txt",
3636 description="Python library to parse Windows minidump file format",