├── fuzzer.py └── recv_fuzzer.py /fuzzer.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import optparse 3 | 4 | from winappdbg import win32 5 | from winappdbg import Debug, EventHandler, System, Process, MemoryAddresses 6 | from winappdbg import HexInput, HexDump, Logger 7 | 8 | logger = Logger(logfile=None, verbose=True) 9 | 10 | def main(argv): 11 | options = parse_cmdline(argv) 12 | 13 | # Create the event handler object 14 | eventHandler = EventForwarder(MemoryWatcher, options) 15 | 16 | # Create the debug object 17 | debug = Debug(eventHandler, bKillOnExit=True) 18 | 19 | try: 20 | 21 | # Attach to the targets 22 | for pid in options.attach: 23 | logger.log_text("Attaching to %d" % pid) 24 | debug.attach(pid) 25 | 26 | # Run the debug loop 27 | debug.loop() 28 | 29 | # Stop the debugger 30 | finally: 31 | debug.stop() 32 | 33 | def parse_cmdline( argv ): 34 | 35 | # Help message and version string 36 | version = ("In Memory fuzzer\n") 37 | 38 | usage = ( 39 | "\n" 40 | "\n" 41 | " Attach to a running process (by filename):\n" 42 | " %prog [options] -a \n" 43 | "\n" 44 | " Attach to a running process (by ID):\n" 45 | " %prog [options] -a " 46 | ) 47 | 48 | parser = optparse.OptionParser( 49 | usage=usage, 50 | version=version, 51 | ) 52 | 53 | # Commands 54 | commands = optparse.OptionGroup(parser, "Commands") 55 | 56 | commands.add_option("-a", "--attach", action="append", type="string", 57 | metavar="PROCESS", 58 | help="Attach to a running process") 59 | 60 | parser.add_option_group(commands) 61 | 62 | # SEH test options 63 | fuzzer_opts = optparse.OptionGroup(parser, "Fuzzer options") 64 | 65 | fuzzer_opts.add_option("--snapshot_address", metavar="ADDRESS", 66 | help="take snapshot point address") 67 | 68 | fuzzer_opts.add_option("--restore_address", metavar="ADDRESS", 69 | help="restore snapshot point address") 70 | 71 | fuzzer_opts.add_option("--buffer_address", metavar="ADDRESS", 72 | help="address of the buffer to be modified in memory") 73 | 74 | fuzzer_opts.add_option("--buffer_size", metavar="ADDRESS", 75 | help="size of the buffer to be modified in memory") 76 | 77 | fuzzer_opts.add_option("-o", "--output", metavar="FILE", 78 | help="write the output to FILE") 79 | 80 | fuzzer_opts.add_option("--debuglog", metavar="FILE", 81 | help="set FILE as a debug log (extremely verbose!)") 82 | 83 | parser.add_option_group(fuzzer_opts) 84 | 85 | # Debugging options 86 | debugging = optparse.OptionGroup(parser, "Debugging options") 87 | 88 | debugging.add_option("--follow", action="store_true", 89 | help="automatically attach to child processes [default]") 90 | 91 | debugging.add_option("--dont-follow", action="store_false", dest="follow", 92 | help="don't automatically attach to child processes") 93 | 94 | parser.add_option_group(debugging) 95 | 96 | # Defaults 97 | parser.set_defaults( 98 | follow = True, 99 | attach = list(), 100 | output = None, 101 | debuglog = None, 102 | ) 103 | 104 | # Parse and validate the command line options 105 | if len(argv) == 1: 106 | argv = argv + [ '--help' ] 107 | (options, args) = parser.parse_args(argv) 108 | args = args[1:] 109 | if not options.attach: 110 | if not args: 111 | parser.error("missing target application(s)") 112 | options.console = [ args ] 113 | else: 114 | if args: 115 | parser.error("don't know what to do with extra parameters: %s" % args) 116 | 117 | if not options.snapshot_address: 118 | parser.error("Snapshot address not specified") 119 | 120 | if not options.restore_address: 121 | parser.error("Restore address not specified") 122 | 123 | if not options.buffer_address: 124 | parser.error("Buffer address not specified") 125 | 126 | if not options.buffer_size: 127 | parser.error("Buffser size not specified") 128 | 129 | 130 | 131 | global logger 132 | if options.output: 133 | logger = Logger(logfile = options.output, verbose = logger.verbose) 134 | 135 | # Open the debug log file if requested 136 | if options.debuglog: 137 | logger = Logger(logfile = options.debuglog, verbose = logger.verbose) 138 | 139 | # Get the list of attach targets 140 | system = System() 141 | system.request_debug_privileges() 142 | system.scan_processes() 143 | attach_targets = list() 144 | 145 | for token in options.attach: 146 | try: 147 | dwProcessId = HexInput.integer(token) 148 | except ValueError: 149 | dwProcessId = None 150 | if dwProcessId is not None: 151 | if not system.has_process(dwProcessId): 152 | parser.error("can't find process %d" % dwProcessId) 153 | try: 154 | process = Process(dwProcessId) 155 | process.open_handle() 156 | process.close_handle() 157 | except WindowsError, e: 158 | parser.error("can't open process %d: %s" % (dwProcessId, e)) 159 | attach_targets.append(dwProcessId) 160 | else: 161 | matched = system.find_processes_by_filename(token) 162 | if not matched: 163 | parser.error("can't find process %s" % token) 164 | for process, name in matched: 165 | dwProcessId = process.get_pid() 166 | try: 167 | process = Process(dwProcessId) 168 | process.open_handle() 169 | process.close_handle() 170 | except WindowsError, e: 171 | parser.error("can't open process %d: %s" % (dwProcessId, e)) 172 | attach_targets.append( process.get_pid() ) 173 | options.attach = attach_targets 174 | 175 | # If no targets were set at all, show an error message 176 | if not options.attach: 177 | parser.error("no targets found!") 178 | 179 | return options 180 | 181 | 182 | class EventForwarder(EventHandler): 183 | def __init__(self, cls, options): 184 | self.cls = cls 185 | self.options = options 186 | self.forward = dict() 187 | super(EventForwarder, self).__init__() 188 | 189 | def event(self, event): 190 | #logger.log_event(event) 191 | 192 | pid = event.get_pid() 193 | if self.forward.has_key(pid): 194 | return self.forward[pid](event) 195 | 196 | def create_process(self, event): 197 | logger.log_event(event) 198 | handler = self.cls(self.options) 199 | self.forward[event.get_pid()] = handler 200 | return handler(event) 201 | 202 | def exit_process(self, event): 203 | logger.log_event(event) 204 | 205 | pid = event.get_pid() 206 | if self.forward.has_key(pid): 207 | retval = self.forward[pid](event) 208 | del self.forward[pid] 209 | return retval 210 | 211 | def breakpoint(self, event): 212 | event.continueStatus = win32.DBG_EXCEPTION_HANDLED 213 | #logger.log_event(event) 214 | 215 | def wow64_breakpoint(self, event): 216 | event.continueStatus = win32.DBG_EXCEPTION_HANDLED 217 | logger.log_event(event) 218 | 219 | def debug_control_c(self, event): 220 | event.continueStatus = win32.DBG_EXCEPTION_HANDLED 221 | logger.log_event(event) 222 | 223 | def invalid_handle(self, event): 224 | event.continueStatus = win32.DBG_EXCEPTION_HANDLED 225 | logger.log_event(event) 226 | 227 | def possible_deadlock(self, event): 228 | event.continueStatus = win32.DBG_EXCEPTION_HANDLED 229 | logger.log_event(event) 230 | 231 | class MemoryFuzzer: 232 | def __init__(self, buffer_address, buffer_size): 233 | self.buffer_adddress = buffer_address 234 | self.buffer_size = buffer_size 235 | self.cur_byte = 0 236 | self.fuzz_values = ['\xca', '\xfe'] 237 | self.cur_fuzz_val = 0 238 | self.buffer_backup = None 239 | 240 | 241 | def iterate(self, event): 242 | #logger.log_text("iterating") 243 | if self.cur_byte >= self.buffer_size: 244 | return False 245 | 246 | p = event.get_process() 247 | 248 | # make a copy of the original buffer 249 | if not self.buffer_backup: 250 | self.buffer_backup= p.read(self.buffer_adddress, self.buffer_size) 251 | 252 | if self.cur_fuzz_val == len(self.fuzz_values): 253 | # restore the original byte 254 | p.write(self.buffer_adddress + self.cur_byte, self.buffer_backup[self.cur_byte]) 255 | 256 | # fuzz next byte using the first fuzz value 257 | self.cur_byte += 1 258 | self.cur_fuzz_val = 0 259 | 260 | 261 | p.write(self.buffer_adddress + self.cur_byte, self.fuzz_values[self.cur_fuzz_val]) 262 | 263 | # next fuzz value 264 | self.cur_fuzz_val += 1 265 | 266 | #from winappdbg import HexDump 267 | #logger.log_text(HexDump.hexblock(self.buffer_backup, self.buffer_adddress)) 268 | 269 | return True 270 | 271 | 272 | class MemoryWatcher(EventHandler): 273 | protect_conversions = { 274 | win32.PAGE_EXECUTE_READWRITE: win32.PAGE_EXECUTE_READ, 275 | win32.PAGE_EXECUTE_WRITECOPY: win32.PAGE_EXECUTE_READ, 276 | win32.PAGE_READWRITE: win32.PAGE_READONLY, 277 | win32.PAGE_WRITECOPY: win32.PAGE_READONLY, 278 | } 279 | 280 | def __init__(self, options): 281 | super(MemoryWatcher, self).__init__() 282 | 283 | self.options = options 284 | 285 | self.fuzzing = False 286 | 287 | # create an instance of our in memory fuzzer 288 | self.fuzzer = MemoryFuzzer(int(self.options.buffer_address, 16), int(self.options.buffer_size)) 289 | 290 | def create_process(self, event): 291 | """ 292 | When attaching to a process, when starting a new process for debugging, 293 | or when the debugee starts a new process and the bFollow flag was set to True. 294 | """ 295 | self.debug = event.debug 296 | self.pid = event.get_pid() 297 | self.process = event.get_process() 298 | 299 | # When a new process is created we need to setup the triggers of snapshots 300 | self.debug.break_at(self.pid, int(self.options.snapshot_address, 16), self.onTakeSnapshotEvent) 301 | self.debug.break_at(self.pid, int(self.options.restore_address, 16), self.onRestoreSnapshotEvent) 302 | 303 | 304 | def onModifyBufferEvent(self, event): 305 | return self.fuzzer.iterate(event) 306 | 307 | def onTakeSnapshotEvent(self, event): 308 | """ 309 | Triggered when the target application is about to use the memory buffer 310 | we are fuzzing. This event will trigger a pseudo snapshot of memory in which 311 | all the pages are set as read only, only when the pages are written by the application 312 | its contents are saved for posterior use (to revert the snapshot). 313 | """ 314 | # if we are currently fuzzing 315 | if self.fuzzing: 316 | # Syntethic event 317 | if not self.onModifyBufferEvent(event): 318 | # We finished fuzzing, let the program run and see if it ends up crashing. 319 | # If it did crash, it could mean that we found a bug or there was some state 320 | # that the memory snapshots could not revert and the application ended up in a 321 | # weird state. 322 | logger.log_text("Finished fuzzing, letting the application run.") 323 | 324 | # Restore the snapshot and set back the original page permissions 325 | self.cleanupSnapshot() 326 | 327 | # Remove all the breakpoints we could have set. 328 | self.debug.erase_all_breakpoints() 329 | 330 | # Resume the rest of the threads. 331 | self.resumeOtherThreads() 332 | 333 | return 334 | else: 335 | self.fuzzing = True 336 | 337 | self.tid = event.get_tid() 338 | self.thread = event.get_thread() 339 | 340 | # We suspend other threads hoping that they have nothing to do with the parsing of the fuzzed buffer. 341 | self.suspendOtherThreads() 342 | 343 | # Set all the memory as read only and only when the pages are written save a copy 344 | self.takeSnapshot() 345 | 346 | def onRestoreSnapshotEvent(self, event): 347 | #logger.log_text(("*" * 8 ) + "restore snapshot event" + ("*" * 8 )) 348 | self.restoreSnapshot() 349 | 350 | def create_thread(self, event): 351 | """ 352 | When the process creates a new thread or when the 353 | _Process.start_thread_ method is called. 354 | """ 355 | #logger.log_text("create_thread()") 356 | pass 357 | 358 | def exception(self, event): 359 | if event.is_first_chance(): 360 | event.continueStatus = win32.DBG_EXCEPTION_NOT_HANDLED 361 | 362 | if self.checkSnapshotPage(event): 363 | # We received an error that had to do with our saved pages 364 | event.continueStatus = win32.DBG_CONTINUE 365 | else: 366 | # This is a completely different error, probably a bug. Log and restart 367 | logger.log_text("") 368 | logger.log_text(("=" * 8) + "Bug found" + ("=" * 8)) 369 | logger.log_event(event) 370 | logger.log_text("=" * 25) 371 | 372 | event.continueStatus = win32.DBG_CONTINUE 373 | 374 | self.restoreSnapshot() 375 | else: 376 | event.continueStatus = win32.DBG_EXCEPTION_HANDLED 377 | self.checkSnapshotPage(event) 378 | 379 | def suspendOtherThreads(self): 380 | logger.log_text("Suspending other threads") 381 | 382 | for thread in self.process.iter_threads(): 383 | if thread.get_tid() != self.tid: 384 | thread.suspend() 385 | 386 | def resumeOtherThreads(self): 387 | logger.log_text("Resuming other threads") 388 | 389 | for thread in self.process.iter_threads(): 390 | if thread.get_tid() != self.tid: 391 | thread.resume() 392 | 393 | def takeSnapshot(self): 394 | """ 395 | Called from the take snapshot event. This was fired by a breakpoint on an address 396 | that the user specified as the point where everything should be restored on the 397 | next fuzz iteration. 398 | 399 | NOTE: Que pasa con los contextos de los otros threads??? Se podram resumir en el mismo estado? 400 | Creo que si y deberiamos hacerlo. 401 | """ 402 | #logger.log_text("Taking snapshot of the process") 403 | 404 | # Take a snapshot of the contex of the current thread 405 | self.context = self.thread.get_context() 406 | 407 | pageSize = System.pageSize 408 | 409 | # Save also special pages like the PEB 410 | self.special_pages = dict() 411 | page = MemoryAddresses.align_address_to_page_start(self.process.get_peb_address()) 412 | self.special_pages[page] = self.process.read(page, pageSize) 413 | 414 | # Also do this for other threads 415 | for thread in self.process.iter_threads(): 416 | page = MemoryAddresses.align_address_to_page_start(thread.get_teb_address()) 417 | self.special_pages[page] = self.process.read(page, pageSize) 418 | 419 | self.memory = dict() 420 | self.tainted = set() 421 | 422 | # For each memory map in memory 423 | for mbi in self.process.get_memory_map(): 424 | # We only care about those who are writable 425 | if mbi.is_writeable(): 426 | page = mbi.BaseAddress 427 | max_page = page + mbi.RegionSize 428 | 429 | # For each page 430 | while page < max_page: 431 | # if it is not a special page 432 | if not self.special_pages.has_key(page): 433 | # Save the old protection permissions 434 | protect = mbi.Protect 435 | new_protect = self.protect_conversions[protect] 436 | try: 437 | self.process.mprotect(page, pageSize, new_protect) 438 | self.memory[page] = (None, protect, new_protect) 439 | except WindowsError: 440 | # if we have a weird error, mark it as a special page 441 | self.special_pages[page] = self.process.read(page, pageSize) 442 | logger.log_text("unexpected special page %s" % HexDump.address(page)) 443 | 444 | # next page 445 | page = page + pageSize 446 | 447 | def restoreSnapshot(self): 448 | #logger.log_text("Restoring snapshot") 449 | 450 | # Restore thread context. 451 | self.thread.set_context(self.context) 452 | 453 | pageSize = System.pageSize 454 | process = self.process 455 | tainted = self.tainted 456 | 457 | # Restore each special page content (PEB etc.) 458 | for page, content in self.special_pages.iteritems(): 459 | process.write(page, content) 460 | 461 | 462 | for page, (content, protect, new_protect) in self.memory.iteritems(): 463 | if page in tainted: 464 | process.write(page, content) 465 | process.mprotect(page, pageSize, new_protect) 466 | tainted.remove(page) 467 | 468 | def checkSnapshotPage(self, event): 469 | if event.get_tid() == self.tid: 470 | try: 471 | fault_type = event.get_fault_type() 472 | except AttributeError: 473 | fault_type = None 474 | except NotImplementedError: 475 | fault_type = None 476 | 477 | if fault_type == win32.EXCEPTION_WRITE_FAULT: 478 | address = event.get_fault_address() 479 | page = MemoryAddresses.align_address_to_page_start(address) 480 | 481 | #logger.log_text("write fault at page %08x address %08x" % (page, address)) 482 | 483 | if self.memory.has_key(page): 484 | (content, protect, new_protect) = self.memory[page] 485 | content = self.process.read(page, System.pageSize) 486 | self.memory[page] = (content, protect, new_protect) 487 | self.tainted.add(page) 488 | self.process.mprotect(page, System.pageSize, protect) 489 | return True 490 | 491 | return False 492 | 493 | def cleanupSnapshot(self): 494 | self.restoreSnapshot() 495 | 496 | pageSize = System.pageSize 497 | for page, (content, protect, new_protect) in self.memory.iteritems(): 498 | self.process.mprotect(page, pageSize, protect) 499 | 500 | from ctypes import windll, Structure, sizeof, WinError, byref, create_string_buffer, c_void_p, cast 501 | from winappdbg.win32.defines import PVOID, ULONG, PULONG, NTSTATUS, DWORD, BYTE, USHORT 502 | from winappdbg.win32.ntdll import RtlNtStatusToDosError 503 | 504 | SYSTEM_INFORMATION_CLASS = DWORD 505 | ACCESS_MASK = DWORD 506 | 507 | # typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO 508 | # { 509 | # ULONG ProcessId; 510 | # BYTE ObjectTypeNumber; 511 | # BYTE Flags; 512 | # USHORT Handle; 513 | # PVOID Object; 514 | # ACCESS_MASK GrantedAccess; 515 | # } SYSTEM_HANDLE_TABLE_ENTRY_INFO, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO; 516 | class SYSTEM_HANDLE_TABLE_ENTRY_INFO(Structure): 517 | _fields_ = [ 518 | ("ProcessId", ULONG), 519 | ("ObjectTypeNumber", BYTE), 520 | ("Flags", BYTE), 521 | ("Handle", USHORT), 522 | ("Object", PVOID), 523 | ("GrantedAccess", ACCESS_MASK), 524 | ] 525 | 526 | # typedef struct _SYSTEM_HANDLE_INFORMATION 527 | # { 528 | # ULONG HandleCount; /* Or NumberOfHandles if you prefer. */ 529 | # SYSTEM_HANDLE Handles[1]; 530 | # } SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION; 531 | class SYSTEM_HANDLE_INFORMATION(Structure): 532 | _fields_ = [ 533 | ("HandleCount", ULONG), 534 | ("Handles", SYSTEM_HANDLE_TABLE_ENTRY_INFO * 1), 535 | ] 536 | 537 | 538 | SystemHandleInformation = 16 539 | STATUS_INFO_LENGTH_MISMATCH = 0xc0000004 540 | 541 | # NTSTATUS WINAPI NtQuerySystemInformation( 542 | # __in SYSTEM_INFORMATION_CLASS SystemInformationClass, 543 | # __inout PVOID SystemInformation, 544 | # __in ULONG SystemInformationLength, 545 | # __out_opt PULONG ReturnLength 546 | # ); 547 | def NtQuerySystemInformation(SystemInformationClass): 548 | _NtQuerySystemInformation = windll.ntdll.NtQuerySystemInformation 549 | _NtQuerySystemInformation.argtypes = [SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG] 550 | _NtQuerySystemInformation.restype = NTSTATUS 551 | 552 | if SystemInformationClass != SystemHandleInformation: 553 | raise NotImplementedError("I am lazy and just implemented what _I_ needed.") 554 | 555 | SystemInformation = SYSTEM_HANDLE_INFORMATION() 556 | SystemInformationLength = sizeof(SYSTEM_HANDLE_INFORMATION) 557 | 558 | ReturnLength = ULONG(0) 559 | 560 | ntstatus = -1 561 | while ntstatus != 0: 562 | ntstatus = _NtQuerySystemInformation(SystemInformationClass, byref(SystemInformation), SystemInformationLength, ReturnLength) 563 | SystemInformationLength *= 2 564 | SystemInformation = create_string_buffer("", SystemInformationLength) 565 | 566 | #from ctypes import pointer, addressof 567 | #vptr = pointer(addressof(*)) 568 | #print vptr 569 | #cptr = cast( vptr, SYSTEM_HANDLE_INFORMATION) 570 | #print cptr 571 | 572 | return SystemInformation 573 | 574 | if __name__ == "__main__": 575 | try: 576 | import psyco 577 | psyco.bind(main) 578 | except ImportError: 579 | pass 580 | 581 | main(sys.argv) 582 | 583 | -------------------------------------------------------------------------------- /recv_fuzzer.py: -------------------------------------------------------------------------------- 1 | from winappdbg import * 2 | from collections import deque 3 | 4 | # Each thread will have an entry with the address of the last buffer filled by recvfrom and 5 | # a circular buffer with the last 32 buffers received and modified. 6 | ThreadLocalStorage = {} 7 | 8 | NumberOfReads = 0 9 | 10 | def FlipRandomBytes(buffer_, max_bytes=10): 11 | """ 12 | Receives a buffer and randomly changes a random number of the bytes 13 | """ 14 | modified_buffer = list(buffer_) 15 | 16 | from random import randint 17 | 18 | # Get the number of random bytes to flip 19 | nbytes = randint(0, max_bytes) 20 | 21 | for i in xrange(0, nbytes + 1): 22 | offset = randint(0, len(modified_buffer) - 1) 23 | o_byte = modified_buffer[offset] 24 | modified_buffer[offset] = chr(ord(o_byte) ^ randint(0, 255)) 25 | 26 | return "".join(modified_buffer) 27 | 28 | def malloc_hook_pre(event, ra, size): 29 | try: 30 | ThreadLocalStorage[event.get_tid()]["last_alloc_size"] = size 31 | except KeyError: 32 | pass 33 | 34 | def malloc_hook_post(event, return_value): 35 | try: 36 | alloc_size = ThreadLocalStorage[event.get_tid()]["last_alloc_size"] 37 | ThreadLocalStorage[event.get_tid()]["last_allocs"].append((return_value, alloc_size)) 38 | except KeyError: 39 | pass 40 | 41 | def recvfrom_hook_pre(event, ra, socket, buf, len, flags, from_addr, fromlen): 42 | try: 43 | # Set the address of the buffer in a thread local storage. This will be accessed by the post hook. 44 | ThreadLocalStorage[event.get_tid()]["buffer_address"] = buf 45 | except KeyError: 46 | pass 47 | 48 | def recvfrom_hook_post(event, return_value): 49 | global NumberOfReads 50 | if NumberOfReads < 100: 51 | NumberOfReads += 1 52 | return 53 | 54 | try: 55 | buffer_address = ThreadLocalStorage[event.get_tid()]["buffer_address"] 56 | 57 | memory = event.get_process().peek(buffer_address, return_value) 58 | modified_buffer = FlipRandomBytes(memory) 59 | 60 | ThreadLocalStorage[event.get_tid()]["last_packets"].append((memory, modified_buffer)) 61 | 62 | event.get_process().poke(buffer_address, modified_buffer) 63 | 64 | print "Original bytes:" 65 | print HexDump.hexblock(memory, buffer_address, width=32) 66 | print "Fuzzed bytes:" 67 | print HexDump.hexblock(modified_buffer, buffer_address, width=32) 68 | except KeyError: 69 | pass 70 | 71 | class MyEventHandler( EventHandler ): 72 | def __init__(self): 73 | self.loaded_dlls = {} 74 | self.logger = Logger("dump.log", False) 75 | super(MyEventHandler, self).__init__() 76 | 77 | def create_thread(self, event): 78 | self.logger.log_event(event, "Thread started") 79 | ThreadLocalStorage[event.get_tid()] = {} 80 | ThreadLocalStorage[event.get_tid()]["last_allocs" ] = deque(maxlen=32) 81 | ThreadLocalStorage[event.get_tid()]["last_packets" ] = deque(maxlen=8) 82 | ThreadLocalStorage[event.get_tid()]["last_alloc_size"] = None 83 | ThreadLocalStorage[event.get_tid()]["buffer_address" ] = None 84 | 85 | def __add_crash(self, event): 86 | # Generate a crash object. 87 | crash = Crash(event) 88 | 89 | """ 90 | fetch_extra_data(self, event, takeMemorySnapshot=0) 91 | source code 92 | Fetch extra data from the Event object. 93 | 94 | Parameters: 95 | event (Event) - Event object for crash. 96 | takeMemorySnapshot (int) - Memory snapshot behavior: 97 | 0 to take no memory information (default). 98 | 1 to take only the memory map. See Process.get_memory_map. 99 | 2 to take a full memory snapshot. See Process.take_memory_snapshot. 100 | 3 to take a full memory snapshot generator. See Process.generate_memory_snapshot. 101 | 102 | crash.fetch_extra_data(event, self.options.memory) 103 | """ 104 | 105 | # Log the event to standard output. 106 | msg = crash.fullReport(bShowNotes = False) 107 | self.logger.log_event(event, msg) 108 | 109 | self.logger.log_event(event, "Last allocations:") 110 | 111 | for tid, tls in ThreadLocalStorage.items(): 112 | try: 113 | self.logger.log_event(event, "Allocations for thread %d" % (tid)) 114 | for allocation in tls["last_allocs"]: 115 | self.logger.log_event(event, " malloc(0x%.8x) = 0x%.8") 116 | except KeyError: 117 | self.logger.log_event(event, " No allocations") 118 | 119 | self.logger.log_event(event, "Disassembly around crash:") 120 | disassembly = event.get_process().disassemble_around_pc(event.get_tid()) 121 | for line in CrashDump.dump_code(disassembly).split("\n"): 122 | self.logger.log_event(event, line) 123 | 124 | self.logger.log_event(event, "Last fuzzed packets:") 125 | # For each of the buffers modified 126 | for tid, tls in ThreadLocalStorage.items(): 127 | self.logger.log_event(event, "Dumping packets for thread %d" % (tid)) 128 | 129 | try: 130 | # For each packet in the circular buffer 131 | for packet in tls["last_packets"]: 132 | self.logger.log_event(event, HexDump.hexblock(packet[1], 0, width=32)) 133 | except KeyError: 134 | self.logger.log_event(event, "No packets") 135 | 136 | # Get the location of the code that triggered the event. 137 | def __get_location(self, event, address): 138 | label = event.get_process().get_label_at_address(address) 139 | if label: 140 | return label 141 | 142 | return HexDump.address(address) 143 | 144 | # Log an exception as a single line of text. 145 | def __log_exception(self, event): 146 | what = event.get_exception_description() 147 | address = event.get_exception_address() 148 | where = self.__get_location(event, address) 149 | 150 | if event.is_first_chance(): 151 | chance = 'first' 152 | else: 153 | chance = 'second' 154 | 155 | msg = "%s (%s chance) at %s (%x)" % (what, chance, where, address) 156 | self.logger.log_event(event, msg) 157 | 158 | # Kill the process if it's a second chance exception. 159 | def _post_exception(self, event): 160 | if event.is_last_chance(): 161 | try: 162 | event.get_thread().set_pc(event.get_process().resolve_symbol('kernel32!ExitProcess')) 163 | except Exception: 164 | event.get_process().kill() 165 | 166 | # Handle all exceptions not handled by the following methods. 167 | def exception(self, event): 168 | self.__log_exception(event) 169 | if event.is_last_chance(): 170 | self.__add_crash(event) 171 | 172 | self._post_exception(event) 173 | 174 | # Unknown (most likely C++) exceptions are not crashes. 175 | def unknown_exception(self, event): 176 | # Log the event to standard output. 177 | self.__log_exception(event) 178 | #if event.is_last_chance(): 179 | # self.__add_crash(event) 180 | 181 | self._post_exception(event) 182 | 183 | # Microsoft Visual C exceptions are not crashes. 184 | def ms_vc_exception(self, event): 185 | # Log the event to standard output. 186 | self.__log_exception(event) 187 | #if event.is_last_chance(): 188 | # self.__add_crash(event) 189 | 190 | self._post_exception(event) 191 | 192 | def load_dll( self, event ): 193 | # Get the new module object 194 | module = event.get_module() 195 | 196 | if module.match_name("Ws2_32.dll") or module.match_name("wsock32.dll"): 197 | # Get the process ID 198 | pid = event.get_pid() 199 | 200 | address = module.resolve( "recvfrom" ) 201 | event.debug.hook_function( pid, address, preCB=recvfrom_hook_pre, postCB=recvfrom_hook_post, paramCount=6) 202 | 203 | #address = module.resolve( "recv" ) 204 | #event.debug.hook_function( pid, address, preCB=recv_hook_pre, postCB=recv_hook_post, paramCount=4) 205 | 206 | """ 207 | if module.match_name("MSVCR90.dll"): 208 | # Get the process ID 209 | pid = event.get_pid() 210 | 211 | address = module.resolve("malloc") 212 | event.debug.hook_function( pid, address, preCB=malloc_hook_pre, postCB=malloc_hook_post, paramCount=1) 213 | """ 214 | 215 | lpBaseOfDll = module.get_base() 216 | fileName = module.get_filename() 217 | 218 | # Do not log already loaded dlls 219 | if self.loaded_dlls.has_key(fileName): 220 | if self.loaded_dlls[fileName] == lpBaseOfDll: 221 | return 222 | 223 | # Update the previous addres of this dll 224 | self.loaded_dlls[fileName] = lpBaseOfDll 225 | 226 | if not fileName: 227 | fileName = "a new module" 228 | 229 | msg = "Loaded %s at %s" 230 | msg = msg % (fileName, HexDump.address(lpBaseOfDll)) 231 | self.logger.log_event(event, msg) 232 | 233 | def simple_debugger( argv ): 234 | # Instance a Debug object, passing it the MyEventHandler instance 235 | debug = Debug( MyEventHandler() ) 236 | try: 237 | 238 | # Start a new process for debugging 239 | debug.execv( argv ) 240 | 241 | # Wait for the debugee to finish 242 | debug.loop() 243 | 244 | # Stop the debugger 245 | finally: 246 | debug.stop() 247 | 248 | 249 | # When invoked from the command line, 250 | # the first argument is an executable file, 251 | # and the remaining arguments are passed to the newly created process 252 | if __name__ == "__main__": 253 | import sys 254 | simple_debugger( sys.argv[1:] ) 255 | --------------------------------------------------------------------------------