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
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 |
0 | 0 | Metadata-Version: 1.2 |
1 | 1 | Name: minidump |
2 | Version: 0.0.12 | |
2 | Version: 0.0.21 | |
3 | 3 | Summary: Python library to parse Windows minidump file format |
4 | 4 | Home-page: https://github.com/skelsec/minidump |
5 | 5 | Author: Tamas Jos |
4 | 4 | Python >= 3.6 |
5 | 5 | |
6 | 6 | # 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 | ||
7 | 12 | ```minidump.py --all <mindidump file> ``` |
8 | 13 | 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. | |
9 | 19 | |
10 | 20 | # Advanced usage |
11 | 21 | 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()⏎ |
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 |
18 | 18 | mld.DataSize = int.from_bytes(buff.read(4), byteorder = 'little', signed = False) |
19 | 19 | mld.Rva = int.from_bytes(buff.read(4), byteorder = 'little', signed = False) |
20 | 20 | 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 | |
21 | 30 | |
22 | 31 | def __str__(self): |
23 | 32 | t = 'Size: %s File offset: %s' % (self.DataSize, self.Rva) |
58 | 67 | ms.Length = int.from_bytes(buff.read(4), byteorder = 'little', signed = False) |
59 | 68 | ms.Buffer = buff.read(ms.Length) |
60 | 69 | 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 | |
61 | 78 | |
62 | 79 | @staticmethod |
63 | 80 | def get_from_rva(rva, buff): |
65 | 82 | buff.seek(rva, 0) |
66 | 83 | ms = MINIDUMP_STRING.parse(buff) |
67 | 84 | 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) | |
68 | 93 | return ms.Buffer.decode('utf-16-le') |
69 | 94 | |
70 | 95 | class MinidumpMemorySegment: |
88 | 113 | return mms |
89 | 114 | |
90 | 115 | @staticmethod |
91 | def parse_full(memory_decriptor, buff, rva): | |
116 | def parse_full(memory_decriptor, rva): | |
92 | 117 | mms = MinidumpMemorySegment() |
93 | 118 | mms.start_virtual_address = memory_decriptor.StartOfMemoryRange |
94 | 119 | mms.size = memory_decriptor.DataSize |
95 | 120 | mms.start_file_address = rva |
96 | 121 | mms.end_virtual_address = mms.start_virtual_address + mms.size |
97 | return mms | |
98 | ||
122 | return mms | |
99 | 123 | |
100 | 124 | def inrange(self, virt_addr): |
101 | 125 | if virt_addr >= self.start_virtual_address and virt_addr < self.end_virtual_address: |
102 | 126 | return True |
103 | 127 | return False |
128 | ||
104 | 129 | def read(self, virtual_address, size, file_handler): |
105 | 130 | if virtual_address > self.end_virtual_address or virtual_address < self.start_virtual_address: |
106 | 131 | raise Exception('Reading from wrong segment!') |
114 | 139 | data = file_handler.read(size) |
115 | 140 | file_handler.seek(pos, 0) |
116 | 141 | 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): | |
119 | 158 | if len(pattern) > self.size: |
120 | 159 | return [] |
121 | 160 | pos = file_handler.tell() |
122 | 161 | 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 | ||
124 | 196 | 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) | |
125 | 204 | 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) | |
135 | 239 | return fl |
240 | ||
136 | 241 | |
137 | 242 | @staticmethod |
138 | 243 | def get_header(): |
33 | 33 | md.Location = MINIDUMP_LOCATION_DESCRIPTOR.parse(buff) |
34 | 34 | return md |
35 | 35 | |
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 | ||
36 | 55 | def __str__(self): |
37 | 56 | t = 'StreamType: %s %s' % (self.StreamType, self.Location) |
38 | 57 | return t⏎ |
0 | 0 | from minidump.constants import MINIDUMP_TYPE |
1 | 1 | from minidump.exceptions import MinidumpHeaderFlagsException, MinidumpHeaderSignatureMismatchException |
2 | import io | |
2 | 3 | |
3 | 4 | # https://msdn.microsoft.com/en-us/library/windows/desktop/ms680378(v=vs.85).aspx |
4 | 5 | class MinidumpHeader: |
46 | 47 | |
47 | 48 | return mh |
48 | 49 | |
50 | @staticmethod | |
51 | async def aparse(abuff): | |
52 | adata = await abuff.read(32) | |
53 | buff = io.BytesIO(adata) | |
54 | return MinidumpHeader.parse(buff) | |
55 | ||
49 | 56 | def __str__(self): |
50 | 57 | t = '== MinidumpHeader ==\n' |
51 | 58 | t+= 'Signature: %s\n' % self.Signature |
15 | 15 | from minidump.common_structs import * |
16 | 16 | from minidump.constants import MINIDUMP_STREAM_TYPE |
17 | 17 | from minidump.directory import MINIDUMP_DIRECTORY |
18 | from minidump.streams.SystemInfoStream import PROCESSOR_ARCHITECTURE | |
18 | 19 | |
19 | 20 | |
20 | 21 | class MinidumpFile: |
202 | 203 | elif dir.StreamType == MINIDUMP_STREAM_TYPE.LastReservedStream: |
203 | 204 | |
204 | 205 | """ |
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 | ||
205 | 222 | |
206 | 223 | def __str__(self): |
207 | 224 | t = '== Minidump File ==\n' |
4 | 4 | # |
5 | 5 | import struct |
6 | 6 | import ntpath |
7 | from .common_structs import * | |
7 | from .common_structs import * | |
8 | 8 | from .streams.SystemInfoStream import PROCESSOR_ARCHITECTURE |
9 | 9 | |
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 | ||
10 | 23 | class MinidumpBufferedMemorySegment: |
11 | def __init__(self, memory_segment, file_handle): | |
24 | def __init__(self, memory_segment, file_handle, chunksize = 10*1024): | |
12 | 25 | self.start_address = memory_segment.start_virtual_address |
13 | 26 | 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 | ||
18 | 32 | def inrange(self, position): |
19 | 33 | return self.start_address <= position <= self.end_address |
20 | ||
34 | ||
21 | 35 | def remaining_len(self, position): |
22 | 36 | if not self.inrange(position): |
23 | 37 | return None |
24 | 38 | 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 | ||
26 | 74 | class MinidumpBufferedReader: |
27 | def __init__(self, reader): | |
75 | def __init__(self, reader, segment_chunk_size = 10*1024): | |
28 | 76 | self.reader = reader |
77 | self.segment_chunk_size = segment_chunk_size | |
29 | 78 | self.memory_segments = [] |
30 | ||
79 | ||
31 | 80 | self.current_segment = None |
32 | 81 | self.current_position = None |
33 | ||
82 | ||
34 | 83 | def _select_segment(self, requested_position): |
35 | 84 | """ |
36 | ||
85 | ||
37 | 86 | """ |
38 | 87 | # check if we have semgnet for requested address in cache |
39 | 88 | for memory_segment in self.memory_segments: |
41 | 90 | self.current_segment = memory_segment |
42 | 91 | self.current_position = requested_position |
43 | 92 | return |
44 | ||
93 | ||
45 | 94 | # not in cache, check if it's present in memory space. if yes then create a new buffered memeory object, and copy data |
46 | 95 | for memory_segment in self.reader.memory_segments: |
47 | 96 | 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) | |
49 | 98 | self.memory_segments.append(newsegment) |
50 | 99 | self.current_segment = newsegment |
51 | 100 | self.current_position = requested_position |
52 | 101 | return |
53 | ||
102 | ||
54 | 103 | 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 | ||
56 | 108 | def seek(self, offset, whence = 0): |
57 | 109 | """ |
58 | 110 | Changes the current address to an offset of offset. The whence parameter controls from which position should we count the offsets. |
69 | 121 | t = self.current_segment.end_address - offset |
70 | 122 | else: |
71 | 123 | raise Exception('Seek function whence value must be between 0-2') |
72 | ||
124 | ||
73 | 125 | if not self.current_segment.inrange(t): |
74 | 126 | raise Exception('Seek would cross memory segment boundaries (use move)') |
75 | ||
127 | ||
76 | 128 | self.current_position = t |
77 | 129 | return |
78 | ||
130 | ||
79 | 131 | def move(self, address): |
80 | 132 | """ |
81 | 133 | Moves the buffer to a virtual address specified by address |
82 | 134 | """ |
83 | 135 | self._select_segment(address) |
84 | 136 | return |
85 | ||
137 | ||
86 | 138 | def align(self, alignment = None): |
87 | 139 | """ |
88 | 140 | Repositions the current reader to match architecture alignment |
98 | 150 | offset_to_aligned = (alignment - offset) % alignment |
99 | 151 | self.seek(offset_to_aligned, 1) |
100 | 152 | return |
101 | ||
153 | ||
102 | 154 | def tell(self): |
103 | 155 | """ |
104 | 156 | Returns the current virtual address |
105 | 157 | """ |
106 | 158 | return self.current_position |
107 | ||
159 | ||
108 | 160 | def peek(self, length): |
109 | 161 | """ |
110 | 162 | Returns up to length bytes from the current memory segment |
112 | 164 | t = self.current_position + length |
113 | 165 | if not self.current_segment.inrange(t): |
114 | 166 | 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 | ||
117 | 169 | def read(self, size = -1): |
118 | 170 | """ |
119 | 171 | Returns data bytes of size size from the current segment. If size is -1 it returns all the remaining data bytes from memory segment |
124 | 176 | t = self.current_segment.remaining_len(self.current_position) |
125 | 177 | if not t: |
126 | 178 | return None |
127 | ||
179 | ||
128 | 180 | old_new_pos = self.current_position |
129 | 181 | 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 | ||
132 | 184 | t = self.current_position + size |
133 | 185 | if not self.current_segment.inrange(t): |
134 | 186 | raise Exception('Would read over segment boundaries!') |
135 | ||
187 | ||
136 | 188 | 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 | ||
140 | 192 | def read_int(self): |
141 | 193 | """ |
142 | Reads an integer. The size depends on the architecture. | |
194 | Reads an integer. The size depends on the architecture. | |
143 | 195 | Reads a 4 byte small-endian singed int on 32 bit arch |
144 | 196 | Reads an 8 byte small-endian singed int on 64 bit arch |
145 | 197 | """ |
147 | 199 | return int.from_bytes(self.read(8), byteorder = 'little', signed = True) |
148 | 200 | else: |
149 | 201 | return int.from_bytes(self.read(4), byteorder = 'little', signed = True) |
150 | ||
202 | ||
151 | 203 | def read_uint(self): |
152 | 204 | """ |
153 | Reads an integer. The size depends on the architecture. | |
205 | Reads an integer. The size depends on the architecture. | |
154 | 206 | Reads a 4 byte small-endian unsinged int on 32 bit arch |
155 | 207 | Reads an 8 byte small-endian unsinged int on 64 bit arch |
156 | 208 | """ |
158 | 210 | return int.from_bytes(self.read(8), byteorder = 'little', signed = False) |
159 | 211 | else: |
160 | 212 | return int.from_bytes(self.read(4), byteorder = 'little', signed = False) |
161 | ||
213 | ||
162 | 214 | def find(self, pattern): |
163 | 215 | """ |
164 | 216 | Searches for a pattern in the current memory segment |
165 | 217 | """ |
166 | pos = self.current_segment.data.find(pattern) | |
218 | pos = self.current_segment.find(self.reader.file_handle, pattern) | |
167 | 219 | if pos == -1: |
168 | 220 | return -1 |
169 | 221 | return pos + self.current_position |
170 | ||
222 | ||
171 | 223 | def find_all(self, pattern): |
172 | 224 | """ |
173 | 225 | Searches for all occurrences of a pattern in the current memory segment, returns all occurrences as a list |
175 | 227 | pos = [] |
176 | 228 | last_found = -1 |
177 | 229 | 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) | |
179 | 231 | if last_found == -1: |
180 | 232 | break |
181 | 233 | pos.append(last_found + self.current_segment.start_address) |
182 | ||
234 | ||
183 | 235 | return pos |
184 | ||
236 | ||
185 | 237 | def find_global(self, pattern): |
186 | 238 | """ |
187 | 239 | Searches for the pattern in the whole process memory space and returns the first occurrence. |
190 | 242 | pos_s = self.reader.search(pattern) |
191 | 243 | if len(pos_s) == 0: |
192 | 244 | return -1 |
193 | ||
245 | ||
194 | 246 | return pos_s[0] |
195 | ||
247 | ||
196 | 248 | def find_all_global(self, pattern): |
197 | 249 | """ |
198 | 250 | Searches for the pattern in the whole process memory space and returns a list of addresses where the pattern begins. |
199 | 251 | This is exhaustive! |
200 | 252 | """ |
201 | 253 | return self.reader.search(pattern) |
202 | ||
254 | ||
203 | 255 | def get_ptr(self, pos): |
204 | 256 | self.move(pos) |
205 | 257 | return self.read_uint() |
206 | 258 | #raw_data = self.read(pos, self.sizeof_ptr) |
207 | 259 | #return struct.unpack(self.unpack_ptr, raw_data)[0] |
208 | ||
260 | ||
209 | 261 | def get_ptr_with_offset(self, pos): |
210 | 262 | if self.reader.sysinfo.ProcessorArchitecture == PROCESSOR_ARCHITECTURE.AMD64: |
211 | 263 | self.move(pos) |
214 | 266 | else: |
215 | 267 | self.move(pos) |
216 | 268 | 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) | |
220 | 272 | return t |
221 | ||
222 | ||
223 | ||
224 | ||
273 | ||
274 | ||
275 | ||
276 | ||
225 | 277 | class MinidumpFileReader: |
226 | 278 | def __init__(self, minidumpfile): |
227 | 279 | 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 | ||
228 | 284 | self.sysinfo = minidumpfile.sysinfo |
229 | ||
285 | ||
230 | 286 | if minidumpfile.memory_segments_64: |
231 | 287 | self.memory_segments = minidumpfile.memory_segments_64.memory_segments |
232 | 288 | self.is_fulldump = True |
233 | ||
289 | ||
234 | 290 | else: |
235 | 291 | self.memory_segments = minidumpfile.memory_segments.memory_segments |
236 | 292 | self.is_fulldump = False |
237 | ||
293 | ||
238 | 294 | self.filename = minidumpfile.filename |
239 | 295 | self.file_handle = minidumpfile.file_handle |
240 | ||
296 | ||
241 | 297 | #reader params |
242 | 298 | self.sizeof_long = 4 |
243 | 299 | self.unpack_long = '<L' |
244 | if minidumpfile.sysinfo.ProcessorArchitecture == PROCESSOR_ARCHITECTURE.AMD64: | |
300 | if minidumpfile.sysinfo.ProcessorArchitecture in [PROCESSOR_ARCHITECTURE.AMD64, PROCESSOR_ARCHITECTURE.AARCH64]: | |
245 | 301 | self.sizeof_ptr = 8 |
246 | 302 | 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]: | |
248 | 305 | self.sizeof_ptr = 4 |
249 | 306 | self.unpack_ptr = '<L' |
250 | 307 | else: |
251 | 308 | 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 | ||
256 | 319 | def get_module_by_name(self, module_name): |
257 | 320 | 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: | |
258 | 327 | if ntpath.basename(mod.name).find(module_name) != -1: |
259 | 328 | return mod |
260 | 329 | 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): | |
263 | 332 | mod = self.get_module_by_name(module_name) |
264 | 333 | 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): | |
266 | 349 | t = [] |
267 | 350 | 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 | ||
271 | 353 | 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 | ||
280 | 355 | def read(self, virt_addr, size): |
281 | 356 | for segment in self.memory_segments: |
282 | 357 | if segment.inrange(virt_addr): |
15 | 15 | buff.seek(dir.Location.Rva) |
16 | 16 | csa.data = buff.read(dir.Location.DataSize).decode() |
17 | 17 | 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 | |
18 | 26 | |
19 | 27 | def __str__(self): |
20 | 28 | return 'CommentA: %s' % self.data⏎ |
15 | 15 | buff.seek(dir.Location.Rva) |
16 | 16 | csa.data = buff.read(dir.Location.DataSize).decode('utf-16-le') |
17 | 17 | 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 | |
18 | 26 | |
19 | 27 | def __str__(self): |
20 | 28 | 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 |
50 | 50 | |
51 | 51 | class ExceptionCode(enum.Enum): |
52 | 52 | # 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' | |
53 | 54 | EXCEPTION_NONE = 0x00 |
54 | 55 | |
55 | 56 | # Linux SIG values (for crashpad generated dumps) |
116 | 117 | self.NumberParameters = None |
117 | 118 | self.__unusedAlignment = None |
118 | 119 | self.ExceptionInformation = [] |
120 | self.ExceptionCode_raw = None | |
119 | 121 | |
120 | 122 | @staticmethod |
121 | 123 | def parse(buff): |
122 | 124 | 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 | ||
124 | 131 | me.ExceptionFlags = int.from_bytes(buff.read(4), byteorder = 'little', signed = False) |
125 | 132 | me.ExceptionRecord = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) |
126 | 133 | me.ExceptionAddress = int.from_bytes(buff.read(8), byteorder = 'little', signed = False) |
127 | 134 | me.NumberParameters = int.from_bytes(buff.read(4), byteorder = 'little', signed = False) |
128 | 135 | 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): | |
130 | 137 | me.ExceptionInformation.append(int.from_bytes(buff.read(8), byteorder = 'little', signed = False)) |
131 | 138 | |
132 | 139 | return me |
191 | 198 | t.exception_records.append(mes) |
192 | 199 | |
193 | 200 | 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 | |
194 | 227 | |
195 | 228 | def to_table(self): |
196 | 229 | t = [] |
103 | 103 | mhoi.SizeOfInfo = int.from_bytes(buff.read(4), byteorder = 'little', signed = False) |
104 | 104 | mhoi.info_bytes = buff.read(mhoi.SizeOfInfo) |
105 | 105 | 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 | |
106 | 118 | |
107 | 119 | class MinidumpHandleObjectInformation: |
108 | 120 | def __init__(self): |
112 | 124 | self.info_bytes = None |
113 | 125 | |
114 | 126 | @staticmethod |
115 | def parse(mhoi, buff): | |
127 | def parse(mhoi): | |
116 | 128 | t = MinidumpHandleObjectInformation() |
117 | 129 | t.InfoType = mhoi.InfoType |
118 | 130 | t.SizeOfInfo = mhoi.SizeOfInfo |
151 | 163 | if t.ObjectInfoRva is not None and t.ObjectInfoRva != 0: |
152 | 164 | MinidumpHandleDescriptor.walk_objectinfo(mhd, t.ObjectInfoRva, buff) |
153 | 165 | 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 | |
154 | 183 | |
155 | 184 | @staticmethod |
156 | 185 | def walk_objectinfo(mhd, start, buff): |
157 | 186 | while start is not None and start != 0: |
158 | 187 | buff.seek(start) |
159 | 188 | 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) | |
161 | 199 | mhd.ObjectInfos.append(t) |
162 | 200 | start = t.NextInfo |
163 | 201 | |
194 | 232 | mhd = MINIDUMP_HANDLE_DESCRIPTOR_2.parse(chunk) |
195 | 233 | t.handles.append(MinidumpHandleDescriptor.parse(mhd, buff)) |
196 | 234 | 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 | |
197 | 253 | |
198 | 254 | def __str__(self): |
199 | 255 | t = '== MinidumpHandleDataStream ==\n' |
79 | 79 | mtl = MINIDUMP_MEMORY64_LIST.parse(chunk) |
80 | 80 | rva = mtl.BaseRva |
81 | 81 | 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)) | |
83 | 83 | rva += mod.DataSize |
84 | 84 | 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 | |
85 | 99 | |
86 | 100 | def to_table(self): |
87 | 101 | t = [] |
210 | 210 | t.infos.append(MinidumpMemoryInfo.parse(mi, buff)) |
211 | 211 | |
212 | 212 | 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 | |
213 | 226 | |
214 | 227 | def to_table(self): |
215 | 228 | t = [] |
85 | 85 | for mod in mtl.MemoryRanges: |
86 | 86 | t.memory_segments.append(MinidumpMemorySegment.parse_mini(mod, buff)) |
87 | 87 | 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 | |
88 | 99 | |
89 | 100 | def __str__(self): |
90 | 101 | t = '== MinidumpMemoryList ==\n' |
122 | 122 | t.ProcessorMaxIdleState = misc.ProcessorMaxIdleState |
123 | 123 | t.ProcessorCurrentIdleState = misc.ProcessorCurrentIdleState |
124 | 124 | 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 | |
125 | 150 | |
126 | 151 | def __str__(self): |
127 | 152 | t = '== MinidumpMiscInfo ==\n' |
31 | 31 | mm.versioninfo = mod.VersionInfo |
32 | 32 | mm.endaddress = mm.baseaddress + mm.size |
33 | 33 | 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 | |
34 | 50 | |
35 | 51 | def inrange(self, memory_loc): |
36 | 52 | return self.baseaddress <= memory_loc < self.endaddress |
42 | 58 | 'BaseAddress', |
43 | 59 | 'Size', |
44 | 60 | 'Endaddress', |
61 | 'Timestamp', | |
45 | 62 | ] |
46 | 63 | |
47 | 64 | def to_row(self): |
50 | 67 | '0x%08x' % self.baseaddress, |
51 | 68 | hex(self.size), |
52 | 69 | '0x%08x' % self.endaddress, |
70 | '0x%08x' % self.timestamp, | |
53 | 71 | ] |
54 | 72 | |
55 | 73 | |
211 | 229 | for mod in mtl.Modules: |
212 | 230 | t.modules.append(MinidumpModule.parse(mod, buff)) |
213 | 231 | 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 | |
214 | 244 | |
215 | 245 | def to_table(self): |
216 | 246 | t = [] |
6 | 6 | import io |
7 | 7 | import enum |
8 | 8 | import logging |
9 | from minidump.common_structs import * | |
9 | from minidump.common_structs import * | |
10 | 10 | |
11 | 11 | # https://msdn.microsoft.com/en-us/library/windows/desktop/ms680396(v=vs.85).aspx |
12 | 12 | class PROCESSOR_ARCHITECTURE(enum.Enum): |
14 | 14 | ARM = 5 #ARM |
15 | 15 | IA64 = 6 #Intel Itanium |
16 | 16 | INTEL = 0 #x86 |
17 | AARCH64 = 0x8003 #ARM64 | |
17 | 18 | UNKNOWN = 0xffff #Unknown processor |
18 | 19 | # https://msdn.microsoft.com/en-us/library/windows/desktop/ms680396(v=vs.85).aspx |
19 | 20 | class PROCESSOR_LEVEL(enum.Enum): |
21 | 22 | INTEL_80486 = 4 |
22 | 23 | INTEL_PENTIUM = 5 |
23 | 24 | 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 | |
25 | 26 | class PRODUCT_TYPE(enum.Enum): |
26 | 27 | VER_UNIDENTIFIED_PRODUCT = 0x0000000 # Crashpad des not set ProductType value on non-Windows systems |
27 | 28 | VER_NT_WORKSTATION = 0x0000001 # The system is running Windows XP, Windows Vista, Windows 7, or Windows 8. |
28 | 29 | VER_NT_DOMAIN_CONTROLLER = 0x0000002 # The system is a domain controller. |
29 | 30 | VER_NT_SERVER = 0x0000003 # The system is a server. |
30 | ||
31 | ||
31 | 32 | # https://msdn.microsoft.com/en-us/library/windows/desktop/ms680396(v=vs.85).aspx |
32 | 33 | class PLATFORM_ID(enum.Enum): |
33 | 34 | VER_PLATFORM_WIN32s = 0 #Not supported |
34 | 35 | VER_PLATFORM_WIN32_WINDOWS = 1 #Not supported. |
35 | 36 | VER_PLATFORM_WIN32_NT = 2 #The operating system platform is Windows. |
36 | ||
37 | ||
37 | 38 | # source : https://github.com/chromium/crashpad/blob/4b05be4265c0ffacfce26d7db7644ffbf9037696/minidump/minidump_extensions.h#L239 |
38 | 39 | VER_PLATFORM_CRASHPAD_MAC = 0x8101 |
39 | 40 | VER_PLATFORM_CRASHPAD_IOS = 0x8102 |
124 | 125 | else: |
125 | 126 | for pf in self.ProcessorFeatures: |
126 | 127 | t += pf.to_bytes(8, byteorder = 'little', signed = False) |
127 | ||
128 | ||
128 | 129 | if data_buffer is None: |
129 | 130 | return t |
130 | 131 | else: |
131 | 132 | data_buffer.write(t) |
132 | ||
133 | ||
133 | 134 | @staticmethod |
134 | 135 | def parse(buff): |
135 | 136 | msi = MINIDUMP_SYSTEM_INFO() |
157 | 158 | else: |
158 | 159 | for _ in range(2): |
159 | 160 | msi.ProcessorFeatures.append(int.from_bytes(buff.read(8), byteorder = 'little', signed = False)) |
160 | ||
161 | ||
161 | 162 | return msi |
162 | 163 | |
163 | 164 | def __str__(self): |
165 | 166 | for k in self.__dict__: |
166 | 167 | t += '%s : %s\r\n' % (k, str(self.__dict__[k])) |
167 | 168 | return t |
168 | ||
169 | ||
169 | 170 | class MinidumpSystemInfo: |
170 | 171 | def __init__(self): |
171 | 172 | self.ProcessorArchitecture = None |
184 | 185 | self.FeatureInformation = None |
185 | 186 | self.AMDExtendedCpuFeatures = None |
186 | 187 | self.ProcessorFeatures = None |
187 | ||
188 | ||
188 | 189 | #extra |
189 | 190 | self.OperatingSystem = None |
190 | ||
191 | ||
191 | 192 | def guess_os(self): |
192 | 193 | if self.MajorVersion == 10 and self.MinorVersion == 0 and self.ProductType == PRODUCT_TYPE.VER_NT_WORKSTATION: |
193 | 194 | self.OperatingSystem = "Windows 10" |
218 | 219 | self.OperatingSystem = "Windows XP" |
219 | 220 | elif self.MajorVersion == 5 and self.MinorVersion == 0: |
220 | 221 | self.OperatingSystem = "Windows 2000" |
221 | ||
222 | ||
222 | 223 | @staticmethod |
223 | 224 | def parse(dir, buff): |
224 | 225 | t = MinidumpSystemInfo() |
247 | 248 | logging.log(1, 'Failed to guess OS! MajorVersion: %s MinorVersion %s BuildNumber %s ProductType: %s' % (t.MajorVersion, t.MinorVersion, t.BuildNumber, t.ProductType )) |
248 | 249 | t.OperatingSystem = None |
249 | 250 | 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 | ||
252 | 283 | def __str__(self): |
253 | 284 | t = '== System Info ==\n' |
254 | 285 | t += 'ProcessorArchitecture %s\n' % self.ProcessorArchitecture |
268 | 299 | t += 'FeatureInformation %s\n' % self.FeatureInformation |
269 | 300 | t += 'AMDExtendedCpuFeatures %s\n' % self.AMDExtendedCpuFeatures |
270 | 301 | t += 'ProcessorFeatures %s\n' % ' '.join( [hex(x) for x in self.ProcessorFeatures] ) |
271 | ||
272 | return t⏎ | |
302 | ||
303 | return t |
83 | 83 | mtl = MINIDUMP_THREAD_EX_LIST.parse(chunk) |
84 | 84 | t.threads = mtl.Threads |
85 | 85 | 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 | |
86 | 96 | |
87 | 97 | def to_table(self): |
88 | 98 | t = [] |
159 | 159 | data = buff.read(dir.Location.DataSize) |
160 | 160 | chunk = io.BytesIO(data) |
161 | 161 | t.header = MINIDUMP_THREAD_INFO_LIST.parse(chunk) |
162 | for i in range(t.header.NumberOfEntries): | |
162 | for _ in range(t.header.NumberOfEntries): | |
163 | 163 | mi = MINIDUMP_THREAD_INFO.parse(chunk) |
164 | 164 | 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)) | |
165 | 178 | |
166 | 179 | return t |
167 | 180 |
95 | 95 | mtl = MINIDUMP_THREAD_LIST.parse(chunk) |
96 | 96 | t.threads = mtl.Threads |
97 | 97 | 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 | |
98 | 108 | |
99 | 109 | def to_table(self): |
100 | 110 | t = [] |
79 | 79 | mm.name = MINIDUMP_STRING.get_from_rva(mod.ModuleNameRva, buff) |
80 | 80 | mm.endaddress = mm.baseaddress + mm.size |
81 | 81 | 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 | |
82 | 97 | |
83 | 98 | def assign_memory_regions(self, segments): |
84 | 99 | for segment in segments: |
121 | 136 | t.modules.append(MinidumpUnloadedModule.parse(mod, buff)) |
122 | 137 | |
123 | 138 | 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 | |
124 | 153 | |
125 | 154 | def to_table(self): |
126 | 155 | t = [] |
0 | 0 | from .CommentStreamA import * |
1 | 1 | from .CommentStreamW import * |
2 | from .ContextStream import * | |
2 | 3 | from .ExceptionStream import * |
3 | 4 | from .FunctionTableStream import * |
4 | 5 | from .HandleDataStream import * |
23 | 24 | |
24 | 25 | __CommentStreamA__ = ['CommentStreamA'] |
25 | 26 | __CommentStreamW__ = ['CommentStreamW'] |
27 | __ContextStream__ = ['CONTEXT', 'CTX_DUMMYSTRUCTNAME', 'CTX_DUMMYUNIONNAME', 'M128A', 'NEON128', 'WOW64_CONTEXT', 'WOW64_FLOATING_SAVE_AREA', 'XMM_SAVE_AREA32'] | |
26 | 28 | __ExceptionStream__ = ['ExceptionList'] |
27 | 29 | __FunctionTableStream__ = ['MINIDUMP_FUNCTION_TABLE_STREAM'] |
28 | 30 | __HandleDataStream__ = ['MinidumpHandleDataStream','MINIDUMP_HANDLE_DATA_STREAM'] |
37 | 39 | __ProcessVmCountersStream__ = [] |
38 | 40 | __SystemInfoStream__ = ['MinidumpSystemInfo','PROCESSOR_ARCHITECTURE','PROCESSOR_LEVEL', 'PRODUCT_TYPE', 'PLATFORM_ID','SUITE_MASK','MINIDUMP_SYSTEM_INFO'] |
39 | 41 | __SystemMemoryInfoStream__ = [] |
40 | __ThreadExListStream__ = ['MINIDUMP_THREAD_EX', 'MINIDUMP_THREAD_EX_LIST'] | |
42 | __ThreadExListStream__ = ['MinidumpThreadExList', 'MINIDUMP_THREAD_EX', 'MINIDUMP_THREAD_EX_LIST'] | |
41 | 43 | __ThreadInfoListStream__ = ['MinidumpThreadInfoList','MINIDUMP_THREAD_INFO_LIST', 'MINIDUMP_THREAD_INFO', 'DumpFlags'] |
42 | 44 | __ThreadListStream__ = ['MinidumpThreadList','MINIDUMP_THREAD', 'MINIDUMP_THREAD_LIST'] |
43 | 45 | __TokenStream__ = [] |
44 | 46 | __UnloadedModuleListStream__ = ['MinidumpUnloadedModuleList', 'MINIDUMP_UNLOADED_MODULE', 'MINIDUMP_UNLOADED_MODULE_LIST'] |
45 | 47 | |
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__⏎ |
8 | 8 | from ctypes.wintypes import HANDLE, BOOL, DWORD, HWND, HINSTANCE, HKEY, LPVOID, LPWSTR, PBOOL |
9 | 9 | from ctypes import c_ulong, c_char_p, c_int, c_void_p, WinError, get_last_error, windll |
10 | 10 | |
11 | from privileges import enable_debug_privilege | |
11 | from minidump.utils.privileges import enable_debug_privilege | |
12 | 12 | |
13 | 13 | if platform.system() != 'Windows': |
14 | 14 | raise Exception('This script will ovbiously only work on Windows') |
0 | 0 | Metadata-Version: 1.2 |
1 | 1 | Name: minidump |
2 | Version: 0.0.12 | |
2 | Version: 0.0.21 | |
3 | 3 | Summary: Python library to parse Windows minidump file format |
4 | 4 | Home-page: https://github.com/skelsec/minidump |
5 | 5 | Author: Tamas Jos |
0 | LICENSE | |
1 | MANIFEST.in | |
0 | 2 | README.md |
1 | 3 | setup.py |
4 | minidump/__amain__.py | |
2 | 5 | minidump/__init__.py |
3 | 6 | minidump/__main__.py |
4 | 7 | minidump/_version.py |
8 | minidump/aminidumpfile.py | |
9 | minidump/aminidumpreader.py | |
5 | 10 | minidump/common_structs.py |
6 | 11 | minidump/constants.py |
7 | 12 | minidump/directory.py |
16 | 21 | minidump.egg-info/SOURCES.txt |
17 | 22 | minidump.egg-info/dependency_links.txt |
18 | 23 | minidump.egg-info/entry_points.txt |
24 | minidump.egg-info/not-zip-safe | |
19 | 25 | minidump.egg-info/top_level.txt |
20 | minidump.egg-info/zip-safe | |
21 | 26 | minidump/streams/CommentStreamA.py |
22 | 27 | minidump/streams/CommentStreamW.py |
28 | minidump/streams/ContextStream.py | |
23 | 29 | minidump/streams/ExceptionStream.py |
24 | 30 | minidump/streams/FunctionTableStream.py |
25 | 31 | minidump/streams/HandleDataStream.py |