1#!~/.wine/drive_c/Python25/python.exe 2# -*- coding: utf-8 -*- 3 4# Acknowledgements: 5# Nicolas Economou, for his command line debugger on which this is inspired. 6# http://tinyurl.com/nicolaseconomou 7 8# Copyright (c) 2009-2014, Mario Vilas 9# All rights reserved. 10# 11# Redistribution and use in source and binary forms, with or without 12# modification, are permitted provided that the following conditions are met: 13# 14# * Redistributions of source code must retain the above copyright notice, 15# this list of conditions and the following disclaimer. 16# * Redistributions in binary form must reproduce the above copyright 17# notice,this list of conditions and the following disclaimer in the 18# documentation and/or other materials provided with the distribution. 19# * Neither the name of the copyright holder nor the names of its 20# contributors may be used to endorse or promote products derived from 21# this software without specific prior written permission. 22# 23# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 24# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 27# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 28# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 29# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 30# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 31# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 32# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33# POSSIBILITY OF SUCH DAMAGE. 34 35""" 36Interactive debugging console. 37 38@group Debugging: 39 ConsoleDebugger 40 41@group Exceptions: 42 CmdError 43""" 44 45from __future__ import with_statement 46 47__revision__ = "$Id$" 48 49__all__ = [ 'ConsoleDebugger', 'CmdError' ] 50 51# TODO document this module with docstrings. 52# TODO command to set a last error breakpoint. 53# TODO command to show available plugins. 54 55from winappdbg import win32 56from winappdbg import compat 57from winappdbg.system import System 58from winappdbg.util import PathOperations 59from winappdbg.event import EventHandler, NoEvent 60from winappdbg.textio import HexInput, HexOutput, HexDump, CrashDump, DebugLog 61 62import os 63import sys 64import code 65import time 66import warnings 67import traceback 68 69# too many variables named "cmd" to have a module by the same name :P 70from cmd import Cmd 71 72# lazy imports 73readline = None 74 75#============================================================================== 76 77class DummyEvent (NoEvent): 78 "Dummy event object used internally by L{ConsoleDebugger}." 79 80 def get_pid(self): 81 return self._pid 82 83 def get_tid(self): 84 return self._tid 85 86 def get_process(self): 87 return self._process 88 89 def get_thread(self): 90 return self._thread 91 92#============================================================================== 93 94class CmdError (Exception): 95 """ 96 Exception raised when a command parsing error occurs. 97 Used internally by L{ConsoleDebugger}. 98 """ 99 100#============================================================================== 101 102class ConsoleDebugger (Cmd, EventHandler): 103 """ 104 Interactive console debugger. 105 106 @see: L{Debug.interactive} 107 """ 108 109#------------------------------------------------------------------------------ 110# Class variables 111 112 # Exception to raise when an error occurs executing a command. 113 command_error_exception = CmdError 114 115 # Milliseconds to wait for debug events in the main loop. 116 dwMilliseconds = 100 117 118 # History file name. 119 history_file = '.winappdbg_history' 120 121 # Confirm before quitting? 122 confirm_quit = True 123 124 # Valid plugin name characters. 125 valid_plugin_name_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXY' \ 126 'abcdefghijklmnopqrstuvwxy' \ 127 '012345678' \ 128 '_' 129 130 # Names of the registers. 131 segment_names = ( 'cs', 'ds', 'es', 'fs', 'gs' ) 132 133 register_alias_64_to_32 = { 134 'eax':'Rax', 'ebx':'Rbx', 'ecx':'Rcx', 'edx':'Rdx', 135 'eip':'Rip', 'ebp':'Rbp', 'esp':'Rsp', 'esi':'Rsi', 'edi':'Rdi' 136 } 137 register_alias_64_to_16 = { 'ax':'Rax', 'bx':'Rbx', 'cx':'Rcx', 'dx':'Rdx' } 138 register_alias_64_to_8_low = { 'al':'Rax', 'bl':'Rbx', 'cl':'Rcx', 'dl':'Rdx' } 139 register_alias_64_to_8_high = { 'ah':'Rax', 'bh':'Rbx', 'ch':'Rcx', 'dh':'Rdx' } 140 register_alias_32_to_16 = { 'ax':'Eax', 'bx':'Ebx', 'cx':'Ecx', 'dx':'Edx' } 141 register_alias_32_to_8_low = { 'al':'Eax', 'bl':'Ebx', 'cl':'Ecx', 'dl':'Edx' } 142 register_alias_32_to_8_high = { 'ah':'Eax', 'bh':'Ebx', 'ch':'Ecx', 'dh':'Edx' } 143 144 register_aliases_full_32 = list(segment_names) 145 register_aliases_full_32.extend(compat.iterkeys(register_alias_32_to_16)) 146 register_aliases_full_32.extend(compat.iterkeys(register_alias_32_to_8_low)) 147 register_aliases_full_32.extend(compat.iterkeys(register_alias_32_to_8_high)) 148 register_aliases_full_32 = tuple(register_aliases_full_32) 149 150 register_aliases_full_64 = list(segment_names) 151 register_aliases_full_64.extend(compat.iterkeys(register_alias_64_to_32)) 152 register_aliases_full_64.extend(compat.iterkeys(register_alias_64_to_16)) 153 register_aliases_full_64.extend(compat.iterkeys(register_alias_64_to_8_low)) 154 register_aliases_full_64.extend(compat.iterkeys(register_alias_64_to_8_high)) 155 register_aliases_full_64 = tuple(register_aliases_full_64) 156 157 # Names of the control flow instructions. 158 jump_instructions = ( 159 'jmp', 'jecxz', 'jcxz', 160 'ja', 'jnbe', 'jae', 'jnb', 'jb', 'jnae', 'jbe', 'jna', 'jc', 'je', 161 'jz', 'jnc', 'jne', 'jnz', 'jnp', 'jpo', 'jp', 'jpe', 'jg', 'jnle', 162 'jge', 'jnl', 'jl', 'jnge', 'jle', 'jng', 'jno', 'jns', 'jo', 'js' 163 ) 164 call_instructions = ( 'call', 'ret', 'retn' ) 165 loop_instructions = ( 'loop', 'loopz', 'loopnz', 'loope', 'loopne' ) 166 control_flow_instructions = call_instructions + loop_instructions + \ 167 jump_instructions 168 169#------------------------------------------------------------------------------ 170# Instance variables 171 172 def __init__(self): 173 """ 174 Interactive console debugger. 175 176 @see: L{Debug.interactive} 177 """ 178 Cmd.__init__(self) 179 EventHandler.__init__(self) 180 181 # Quit the debugger when True. 182 self.debuggerExit = False 183 184 # Full path to the history file. 185 self.history_file_full_path = None 186 187 # Last executed command. 188 self.__lastcmd = "" 189 190#------------------------------------------------------------------------------ 191# Debugger 192 193 # Use this Debug object. 194 def start_using_debugger(self, debug): 195 196 # Clear the previous Debug object. 197 self.stop_using_debugger() 198 199 # Keep the Debug object. 200 self.debug = debug 201 202 # Set ourselves as the event handler for the debugger. 203 self.prevHandler = debug.set_event_handler(self) 204 205 # Stop using the Debug object given by start_using_debugger(). 206 # Circular references must be removed, or the destructors never get called. 207 def stop_using_debugger(self): 208 if hasattr(self, 'debug'): 209 debug = self.debug 210 debug.set_event_handler(self.prevHandler) 211 del self.prevHandler 212 del self.debug 213 return debug 214 return None 215 216 # Destroy the Debug object. 217 def destroy_debugger(self, autodetach = True): 218 debug = self.stop_using_debugger() 219 if debug is not None: 220 if not autodetach: 221 debug.kill_all(bIgnoreExceptions=True) 222 debug.lastEvent = None 223 debug.stop() 224 del debug 225 226 @property 227 def lastEvent(self): 228 return self.debug.lastEvent 229 230 def set_fake_last_event(self, process): 231 if self.lastEvent is None: 232 self.debug.lastEvent = DummyEvent(self.debug) 233 self.debug.lastEvent._process = process 234 self.debug.lastEvent._thread = process.get_thread( 235 process.get_thread_ids()[0]) 236 self.debug.lastEvent._pid = process.get_pid() 237 self.debug.lastEvent._tid = self.lastEvent._thread.get_tid() 238 239#------------------------------------------------------------------------------ 240# Input 241 242# TODO 243# * try to guess breakpoints when insufficient data is given 244# * child Cmd instances will have to be used for other prompts, for example 245# when assembling or editing memory - it may also be a good idea to think 246# if it's possible to make the main Cmd instance also a child, instead of 247# the debugger itself - probably the same goes for the EventHandler, maybe 248# it can be used as a contained object rather than a parent class. 249 250 # Join a token list into an argument string. 251 def join_tokens(self, token_list): 252 return self.debug.system.argv_to_cmdline(token_list) 253 254 # Split an argument string into a token list. 255 def split_tokens(self, arg, min_count = 0, max_count = None): 256 token_list = self.debug.system.cmdline_to_argv(arg) 257 if len(token_list) < min_count: 258 raise CmdError("missing parameters.") 259 if max_count and len(token_list) > max_count: 260 raise CmdError("too many parameters.") 261 return token_list 262 263 # Token is a thread ID or name. 264 def input_thread(self, token): 265 targets = self.input_thread_list( [token] ) 266 if len(targets) == 0: 267 raise CmdError("missing thread name or ID") 268 if len(targets) > 1: 269 msg = "more than one thread with that name:\n" 270 for tid in targets: 271 msg += "\t%d\n" % tid 272 msg = msg[:-len("\n")] 273 raise CmdError(msg) 274 return targets[0] 275 276 # Token list is a list of thread IDs or names. 277 def input_thread_list(self, token_list): 278 targets = set() 279 system = self.debug.system 280 for token in token_list: 281 try: 282 tid = self.input_integer(token) 283 if not system.has_thread(tid): 284 raise CmdError("thread not found (%d)" % tid) 285 targets.add(tid) 286 except ValueError: 287 found = set() 288 for process in system.iter_processes(): 289 found.update( system.find_threads_by_name(token) ) 290 if not found: 291 raise CmdError("thread not found (%s)" % token) 292 for thread in found: 293 targets.add( thread.get_tid() ) 294 targets = list(targets) 295 targets.sort() 296 return targets 297 298 # Token is a process ID or name. 299 def input_process(self, token): 300 targets = self.input_process_list( [token] ) 301 if len(targets) == 0: 302 raise CmdError("missing process name or ID") 303 if len(targets) > 1: 304 msg = "more than one process with that name:\n" 305 for pid in targets: 306 msg += "\t%d\n" % pid 307 msg = msg[:-len("\n")] 308 raise CmdError(msg) 309 return targets[0] 310 311 # Token list is a list of process IDs or names. 312 def input_process_list(self, token_list): 313 targets = set() 314 system = self.debug.system 315 for token in token_list: 316 try: 317 pid = self.input_integer(token) 318 if not system.has_process(pid): 319 raise CmdError("process not found (%d)" % pid) 320 targets.add(pid) 321 except ValueError: 322 found = system.find_processes_by_filename(token) 323 if not found: 324 raise CmdError("process not found (%s)" % token) 325 for (process, _) in found: 326 targets.add( process.get_pid() ) 327 targets = list(targets) 328 targets.sort() 329 return targets 330 331 # Token is a command line to execute. 332 def input_command_line(self, command_line): 333 argv = self.debug.system.cmdline_to_argv(command_line) 334 if not argv: 335 raise CmdError("missing command line to execute") 336 fname = argv[0] 337 if not os.path.exists(fname): 338 try: 339 fname, _ = win32.SearchPath(None, fname, '.exe') 340 except WindowsError: 341 raise CmdError("file not found: %s" % fname) 342 argv[0] = fname 343 command_line = self.debug.system.argv_to_cmdline(argv) 344 return command_line 345 346 # Token is an integer. 347 # Only hexadecimal format is supported. 348 def input_hexadecimal_integer(self, token): 349 return int(token, 0x10) 350 351 # Token is an integer. 352 # It can be in any supported format. 353 def input_integer(self, token): 354 return HexInput.integer(token) 355## input_integer = input_hexadecimal_integer 356 357 # Token is an address. 358 # The address can be a integer, a label or a register. 359 def input_address(self, token, pid = None, tid = None): 360 address = None 361 if self.is_register(token): 362 if tid is None: 363 if self.lastEvent is None or pid != self.lastEvent.get_pid(): 364 msg = "can't resolve register (%s) for unknown thread" 365 raise CmdError(msg % token) 366 tid = self.lastEvent.get_tid() 367 address = self.input_register(token, tid) 368 if address is None: 369 try: 370 address = self.input_hexadecimal_integer(token) 371 except ValueError: 372 if pid is None: 373 if self.lastEvent is None: 374 raise CmdError("no current process set") 375 process = self.lastEvent.get_process() 376 elif self.lastEvent is not None and pid == self.lastEvent.get_pid(): 377 process = self.lastEvent.get_process() 378 else: 379 try: 380 process = self.debug.system.get_process(pid) 381 except KeyError: 382 raise CmdError("process not found (%d)" % pid) 383 try: 384 address = process.resolve_label(token) 385 except Exception: 386 raise CmdError("unknown address (%s)" % token) 387 return address 388 389 # Token is an address range, or a single address. 390 # The addresses can be integers, labels or registers. 391 def input_address_range(self, token_list, pid = None, tid = None): 392 if len(token_list) == 2: 393 token_1, token_2 = token_list 394 address = self.input_address(token_1, pid, tid) 395 try: 396 size = self.input_integer(token_2) 397 except ValueError: 398 raise CmdError("bad address range: %s %s" % (token_1, token_2)) 399 elif len(token_list) == 1: 400 token = token_list[0] 401 if '-' in token: 402 try: 403 token_1, token_2 = token.split('-') 404 except Exception: 405 raise CmdError("bad address range: %s" % token) 406 address = self.input_address(token_1, pid, tid) 407 size = self.input_address(token_2, pid, tid) - address 408 else: 409 address = self.input_address(token, pid, tid) 410 size = None 411 return address, size 412 413 # XXX TODO 414 # Support non-integer registers here. 415 def is_register(self, token): 416 if win32.arch == 'i386': 417 if token in self.register_aliases_full_32: 418 return True 419 token = token.title() 420 for (name, typ) in win32.CONTEXT._fields_: 421 if name == token: 422 return win32.sizeof(typ) == win32.sizeof(win32.DWORD) 423 elif win32.arch == 'amd64': 424 if token in self.register_aliases_full_64: 425 return True 426 token = token.title() 427 for (name, typ) in win32.CONTEXT._fields_: 428 if name == token: 429 return win32.sizeof(typ) == win32.sizeof(win32.DWORD64) 430 return False 431 432 # The token is a register name. 433 # Returns None if no register name is matched. 434 def input_register(self, token, tid = None): 435 if tid is None: 436 if self.lastEvent is None: 437 raise CmdError("no current process set") 438 thread = self.lastEvent.get_thread() 439 else: 440 thread = self.debug.system.get_thread(tid) 441 ctx = thread.get_context() 442 443 token = token.lower() 444 title = token.title() 445 446 if title in ctx: 447 return ctx.get(title) # eax -> Eax 448 449 if ctx.arch == 'i386': 450 451 if token in self.segment_names: 452 return ctx.get( 'Seg%s' % title ) # cs -> SegCs 453 454 if token in self.register_alias_32_to_16: 455 return ctx.get( self.register_alias_32_to_16[token] ) & 0xFFFF 456 457 if token in self.register_alias_32_to_8_low: 458 return ctx.get( self.register_alias_32_to_8_low[token] ) & 0xFF 459 460 if token in self.register_alias_32_to_8_high: 461 return (ctx.get( self.register_alias_32_to_8_high[token] ) & 0xFF00) >> 8 462 463 elif ctx.arch == 'amd64': 464 465 if token in self.segment_names: 466 return ctx.get( 'Seg%s' % title ) # cs -> SegCs 467 468 if token in self.register_alias_64_to_32: 469 return ctx.get( self.register_alias_64_to_32[token] ) & 0xFFFFFFFF 470 471 if token in self.register_alias_64_to_16: 472 return ctx.get( self.register_alias_64_to_16[token] ) & 0xFFFF 473 474 if token in self.register_alias_64_to_8_low: 475 return ctx.get( self.register_alias_64_to_8_low[token] ) & 0xFF 476 477 if token in self.register_alias_64_to_8_high: 478 return (ctx.get( self.register_alias_64_to_8_high[token] ) & 0xFF00) >> 8 479 480 return None 481 482 # Token list contains an address or address range. 483 # The prefix is also parsed looking for process and thread IDs. 484 def input_full_address_range(self, token_list): 485 pid, tid = self.get_process_and_thread_ids_from_prefix() 486 address, size = self.input_address_range(token_list, pid, tid) 487 return pid, tid, address, size 488 489 # Token list contains a breakpoint. 490 def input_breakpoint(self, token_list): 491 pid, tid, address, size = self.input_full_address_range(token_list) 492 if not self.debug.is_debugee(pid): 493 raise CmdError("target process is not being debugged") 494 return pid, tid, address, size 495 496 # Token list contains a memory address, and optional size and process. 497 # Sets the results as the default for the next display command. 498 def input_display(self, token_list, default_size = 64): 499 pid, tid, address, size = self.input_full_address_range(token_list) 500 if not size: 501 size = default_size 502 next_address = HexOutput.integer(address + size) 503 self.default_display_target = next_address 504 return pid, tid, address, size 505 506#------------------------------------------------------------------------------ 507# Output 508 509 # Tell the user a module was loaded. 510 def print_module_load(self, event): 511 mod = event.get_module() 512 base = mod.get_base() 513 name = mod.get_filename() 514 if not name: 515 name = '' 516 msg = "Loaded module (%s) %s" 517 msg = msg % (HexDump.address(base), name) 518 print(msg) 519 520 # Tell the user a module was unloaded. 521 def print_module_unload(self, event): 522 mod = event.get_module() 523 base = mod.get_base() 524 name = mod.get_filename() 525 if not name: 526 name = '' 527 msg = "Unloaded module (%s) %s" 528 msg = msg % (HexDump.address(base), name) 529 print(msg) 530 531 # Tell the user a process was started. 532 def print_process_start(self, event): 533 pid = event.get_pid() 534 start = event.get_start_address() 535 if start: 536 start = HexOutput.address(start) 537 print("Started process %d at %s" % (pid, start)) 538 else: 539 print("Attached to process %d" % pid) 540 541 # Tell the user a thread was started. 542 def print_thread_start(self, event): 543 tid = event.get_tid() 544 start = event.get_start_address() 545 if start: 546 with warnings.catch_warnings(): 547 warnings.simplefilter("ignore") 548 start = event.get_process().get_label_at_address(start) 549 print("Started thread %d at %s" % (tid, start)) 550 else: 551 print("Attached to thread %d" % tid) 552 553 # Tell the user a process has finished. 554 def print_process_end(self, event): 555 pid = event.get_pid() 556 code = event.get_exit_code() 557 print("Process %d terminated, exit code %d" % (pid, code)) 558 559 # Tell the user a thread has finished. 560 def print_thread_end(self, event): 561 tid = event.get_tid() 562 code = event.get_exit_code() 563 print("Thread %d terminated, exit code %d" % (tid, code)) 564 565 # Print(debug strings. 566 def print_debug_string(self, event): 567 tid = event.get_tid() 568 string = event.get_debug_string() 569 print("Thread %d says: %r" % (tid, string)) 570 571 # Inform the user of any other debugging event. 572 def print_event(self, event): 573 code = HexDump.integer( event.get_event_code() ) 574 name = event.get_event_name() 575 desc = event.get_event_description() 576 if code in desc: 577 print('') 578 print("%s: %s" % (name, desc)) 579 else: 580 print('') 581 print("%s (%s): %s" % (name, code, desc)) 582 self.print_event_location(event) 583 584 # Stop on exceptions and prompt for commands. 585 def print_exception(self, event): 586 address = HexDump.address( event.get_exception_address() ) 587 code = HexDump.integer( event.get_exception_code() ) 588 desc = event.get_exception_description() 589 if event.is_first_chance(): 590 chance = 'first' 591 else: 592 chance = 'second' 593 if code in desc: 594 msg = "%s at address %s (%s chance)" % (desc, address, chance) 595 else: 596 msg = "%s (%s) at address %s (%s chance)" % (desc, code, address, chance) 597 print('') 598 print(msg) 599 self.print_event_location(event) 600 601 # Show the current location in the code. 602 def print_event_location(self, event): 603 process = event.get_process() 604 thread = event.get_thread() 605 self.print_current_location(process, thread) 606 607 # Show the current location in the code. 608 def print_breakpoint_location(self, event): 609 process = event.get_process() 610 thread = event.get_thread() 611 pc = event.get_exception_address() 612 self.print_current_location(process, thread, pc) 613 614 # Show the current location in any process and thread. 615 def print_current_location(self, process = None, thread = None, pc = None): 616 if not process: 617 if self.lastEvent is None: 618 raise CmdError("no current process set") 619 process = self.lastEvent.get_process() 620 if not thread: 621 if self.lastEvent is None: 622 raise CmdError("no current process set") 623 thread = self.lastEvent.get_thread() 624 thread.suspend() 625 try: 626 if pc is None: 627 pc = thread.get_pc() 628 ctx = thread.get_context() 629 finally: 630 thread.resume() 631 label = process.get_label_at_address(pc) 632 try: 633 disasm = process.disassemble(pc, 15) 634 except WindowsError: 635 disasm = None 636 except NotImplementedError: 637 disasm = None 638 print('') 639 print(CrashDump.dump_registers(ctx),) 640 print("%s:" % label) 641 if disasm: 642 print(CrashDump.dump_code_line(disasm[0], pc, bShowDump = True)) 643 else: 644 try: 645 data = process.peek(pc, 15) 646 except Exception: 647 data = None 648 if data: 649 print('%s: %s' % (HexDump.address(pc), HexDump.hexblock_byte(data))) 650 else: 651 print('%s: ???' % HexDump.address(pc)) 652 653 # Display memory contents using a given method. 654 def print_memory_display(self, arg, method): 655 if not arg: 656 arg = self.default_display_target 657 token_list = self.split_tokens(arg, 1, 2) 658 pid, tid, address, size = self.input_display(token_list) 659 label = self.get_process(pid).get_label_at_address(address) 660 data = self.read_memory(address, size, pid) 661 if data: 662 print("%s:" % label) 663 print(method(data, address),) 664 665#------------------------------------------------------------------------------ 666# Debugging 667 668 # Get the process ID from the prefix or the last event. 669 def get_process_id_from_prefix(self): 670 if self.cmdprefix: 671 pid = self.input_process(self.cmdprefix) 672 else: 673 if self.lastEvent is None: 674 raise CmdError("no current process set") 675 pid = self.lastEvent.get_pid() 676 return pid 677 678 # Get the thread ID from the prefix or the last event. 679 def get_thread_id_from_prefix(self): 680 if self.cmdprefix: 681 tid = self.input_thread(self.cmdprefix) 682 else: 683 if self.lastEvent is None: 684 raise CmdError("no current process set") 685 tid = self.lastEvent.get_tid() 686 return tid 687 688 # Get the process from the prefix or the last event. 689 def get_process_from_prefix(self): 690 pid = self.get_process_id_from_prefix() 691 return self.get_process(pid) 692 693 # Get the thread from the prefix or the last event. 694 def get_thread_from_prefix(self): 695 tid = self.get_thread_id_from_prefix() 696 return self.get_thread(tid) 697 698 # Get the process and thread IDs from the prefix or the last event. 699 def get_process_and_thread_ids_from_prefix(self): 700 if self.cmdprefix: 701 try: 702 pid = self.input_process(self.cmdprefix) 703 tid = None 704 except CmdError: 705 try: 706 tid = self.input_thread(self.cmdprefix) 707 pid = self.debug.system.get_thread(tid).get_pid() 708 except CmdError: 709 msg = "unknown process or thread (%s)" % self.cmdprefix 710 raise CmdError(msg) 711 else: 712 if self.lastEvent is None: 713 raise CmdError("no current process set") 714 pid = self.lastEvent.get_pid() 715 tid = self.lastEvent.get_tid() 716 return pid, tid 717 718 # Get the process and thread from the prefix or the last event. 719 def get_process_and_thread_from_prefix(self): 720 pid, tid = self.get_process_and_thread_ids_from_prefix() 721 process = self.get_process(pid) 722 thread = self.get_thread(tid) 723 return process, thread 724 725 # Get the process object. 726 def get_process(self, pid = None): 727 if pid is None: 728 if self.lastEvent is None: 729 raise CmdError("no current process set") 730 process = self.lastEvent.get_process() 731 elif self.lastEvent is not None and pid == self.lastEvent.get_pid(): 732 process = self.lastEvent.get_process() 733 else: 734 try: 735 process = self.debug.system.get_process(pid) 736 except KeyError: 737 raise CmdError("process not found (%d)" % pid) 738 return process 739 740 # Get the thread object. 741 def get_thread(self, tid = None): 742 if tid is None: 743 if self.lastEvent is None: 744 raise CmdError("no current process set") 745 thread = self.lastEvent.get_thread() 746 elif self.lastEvent is not None and tid == self.lastEvent.get_tid(): 747 thread = self.lastEvent.get_thread() 748 else: 749 try: 750 thread = self.debug.system.get_thread(tid) 751 except KeyError: 752 raise CmdError("thread not found (%d)" % tid) 753 return thread 754 755 # Read the process memory. 756 def read_memory(self, address, size, pid = None): 757 process = self.get_process(pid) 758 try: 759 data = process.peek(address, size) 760 except WindowsError: 761 orig_address = HexOutput.integer(address) 762 next_address = HexOutput.integer(address + size) 763 msg = "error reading process %d, from %s to %s (%d bytes)" 764 msg = msg % (pid, orig_address, next_address, size) 765 raise CmdError(msg) 766 return data 767 768 # Write the process memory. 769 def write_memory(self, address, data, pid = None): 770 process = self.get_process(pid) 771 try: 772 process.write(address, data) 773 except WindowsError: 774 size = len(data) 775 orig_address = HexOutput.integer(address) 776 next_address = HexOutput.integer(address + size) 777 msg = "error reading process %d, from %s to %s (%d bytes)" 778 msg = msg % (pid, orig_address, next_address, size) 779 raise CmdError(msg) 780 781 # Change a register value. 782 def change_register(self, register, value, tid = None): 783 784 # Get the thread. 785 if tid is None: 786 if self.lastEvent is None: 787 raise CmdError("no current process set") 788 thread = self.lastEvent.get_thread() 789 else: 790 try: 791 thread = self.debug.system.get_thread(tid) 792 except KeyError: 793 raise CmdError("thread not found (%d)" % tid) 794 795 # Convert the value to integer type. 796 try: 797 value = self.input_integer(value) 798 except ValueError: 799 pid = thread.get_pid() 800 value = self.input_address(value, pid, tid) 801 802 # Suspend the thread. 803 # The finally clause ensures the thread is resumed before returning. 804 thread.suspend() 805 try: 806 807 # Get the current context. 808 ctx = thread.get_context() 809 810 # Register name matching is case insensitive. 811 register = register.lower() 812 813 # Integer 32 bits registers. 814 if register in self.register_names: 815 register = register.title() # eax -> Eax 816 817 # Segment (16 bit) registers. 818 if register in self.segment_names: 819 register = 'Seg%s' % register.title() # cs -> SegCs 820 value = value & 0x0000FFFF 821 822 # Integer 16 bits registers. 823 if register in self.register_alias_16: 824 register = self.register_alias_16[register] 825 previous = ctx.get(register) & 0xFFFF0000 826 value = (value & 0x0000FFFF) | previous 827 828 # Integer 8 bits registers (low part). 829 if register in self.register_alias_8_low: 830 register = self.register_alias_8_low[register] 831 previous = ctx.get(register) % 0xFFFFFF00 832 value = (value & 0x000000FF) | previous 833 834 # Integer 8 bits registers (high part). 835 if register in self.register_alias_8_high: 836 register = self.register_alias_8_high[register] 837 previous = ctx.get(register) % 0xFFFF00FF 838 value = ((value & 0x000000FF) << 8) | previous 839 840 # Set the new context. 841 ctx.__setitem__(register, value) 842 thread.set_context(ctx) 843 844 # Resume the thread. 845 finally: 846 thread.resume() 847 848 # Very crude way to find data within the process memory. 849 # TODO: Perhaps pfind.py can be integrated here instead. 850 def find_in_memory(self, query, process): 851 for mbi in process.get_memory_map(): 852 if mbi.State != win32.MEM_COMMIT or mbi.Protect & win32.PAGE_GUARD: 853 continue 854 address = mbi.BaseAddress 855 size = mbi.RegionSize 856 try: 857 data = process.read(address, size) 858 except WindowsError: 859 msg = "*** Warning: read error at address %s" 860 msg = msg % HexDump.address(address) 861 print(msg) 862 width = min(len(query), 16) 863 p = data.find(query) 864 while p >= 0: 865 q = p + len(query) 866 d = data[ p : min(q, p + width) ] 867 h = HexDump.hexline(d, width = width) 868 a = HexDump.address(address + p) 869 print("%s: %s" % (a, h)) 870 p = data.find(query, q) 871 872 # Kill a process. 873 def kill_process(self, pid): 874 process = self.debug.system.get_process(pid) 875 try: 876 process.kill() 877 if self.debug.is_debugee(pid): 878 self.debug.detach(pid) 879 print("Killed process (%d)" % pid) 880 except Exception: 881 print("Error trying to kill process (%d)" % pid) 882 883 # Kill a thread. 884 def kill_thread(self, tid): 885 thread = self.debug.system.get_thread(tid) 886 try: 887 thread.kill() 888 process = thread.get_process() 889 pid = process.get_pid() 890 if self.debug.is_debugee(pid) and not process.is_alive(): 891 self.debug.detach(pid) 892 print("Killed thread (%d)" % tid) 893 except Exception: 894 print("Error trying to kill thread (%d)" % tid) 895 896#------------------------------------------------------------------------------ 897# Command prompt input 898 899 # Prompt the user for commands. 900 def prompt_user(self): 901 while not self.debuggerExit: 902 try: 903 self.cmdloop() 904 break 905 except CmdError: 906 e = sys.exc_info()[1] 907 print("*** Error: %s" % str(e)) 908 except Exception: 909 traceback.print_exc() 910## self.debuggerExit = True 911 912 # Prompt the user for a YES/NO kind of question. 913 def ask_user(self, msg, prompt = "Are you sure? (y/N): "): 914 print(msg) 915 answer = raw_input(prompt) 916 answer = answer.strip()[:1].lower() 917 return answer == 'y' 918 919 # Autocomplete the given command when not ambiguous. 920 # Convert it to lowercase (so commands are seen as case insensitive). 921 def autocomplete(self, cmd): 922 cmd = cmd.lower() 923 completed = self.completenames(cmd) 924 if len(completed) == 1: 925 cmd = completed[0] 926 return cmd 927 928 # Get the help text for the given list of command methods. 929 # Note it's NOT a list of commands, but a list of actual method names. 930 # Each line of text is stripped and all lines are sorted. 931 # Repeated text lines are removed. 932 # Returns a single, possibly multiline, string. 933 def get_help(self, commands): 934 msg = set() 935 for name in commands: 936 if name != 'do_help': 937 try: 938 doc = getattr(self, name).__doc__.split('\n') 939 except Exception: 940 return ( "No help available when Python" 941 " is run with the -OO switch." ) 942 for x in doc: 943 x = x.strip() 944 if x: 945 msg.add(' %s' % x) 946 msg = list(msg) 947 msg.sort() 948 msg = '\n'.join(msg) 949 return msg 950 951 # Parse the prefix and remove it from the command line. 952 def split_prefix(self, line): 953 prefix = None 954 if line.startswith('~'): 955 pos = line.find(' ') 956 if pos == 1: 957 pos = line.find(' ', pos + 1) 958 if not pos < 0: 959 prefix = line[ 1 : pos ].strip() 960 line = line[ pos : ].strip() 961 return prefix, line 962 963#------------------------------------------------------------------------------ 964# Cmd() hacks 965 966 # Header for help page. 967 doc_header = 'Available commands (type help * or help <command>)' 968 969## # Read and write directly to stdin and stdout. 970## # This prevents the use of raw_input and print. 971## use_rawinput = False 972 973 @property 974 def prompt(self): 975 if self.lastEvent: 976 pid = self.lastEvent.get_pid() 977 tid = self.lastEvent.get_tid() 978 if self.debug.is_debugee(pid): 979## return '~%d(%d)> ' % (tid, pid) 980 return '%d:%d> ' % (pid, tid) 981 return '> ' 982 983 # Return a sorted list of method names. 984 # Only returns the methods that implement commands. 985 def get_names(self): 986 names = Cmd.get_names(self) 987 names = [ x for x in set(names) if x.startswith('do_') ] 988 names.sort() 989 return names 990 991 # Automatically autocomplete commands, even if Tab wasn't pressed. 992 # The prefix is removed from the line and stored in self.cmdprefix. 993 # Also implement the commands that consist of a symbol character. 994 def parseline(self, line): 995 self.cmdprefix, line = self.split_prefix(line) 996 line = line.strip() 997 if line: 998 if line[0] == '.': 999 line = 'plugin ' + line[1:] 1000 elif line[0] == '#': 1001 line = 'python ' + line[1:] 1002 cmd, arg, line = Cmd.parseline(self, line) 1003 if cmd: 1004 cmd = self.autocomplete(cmd) 1005 return cmd, arg, line 1006 1007## # Don't repeat the last executed command. 1008## def emptyline(self): 1009## pass 1010 1011 # Reset the defaults for some commands. 1012 def preloop(self): 1013 self.default_disasm_target = 'eip' 1014 self.default_display_target = 'eip' 1015 self.last_display_command = self.do_db 1016 1017 # Put the prefix back in the command line. 1018 def get_lastcmd(self): 1019 return self.__lastcmd 1020 def set_lastcmd(self, lastcmd): 1021 if self.cmdprefix: 1022 lastcmd = '~%s %s' % (self.cmdprefix, lastcmd) 1023 self.__lastcmd = lastcmd 1024 lastcmd = property(get_lastcmd, set_lastcmd) 1025 1026 # Quit the command prompt if the debuggerExit flag is on. 1027 def postcmd(self, stop, line): 1028 return stop or self.debuggerExit 1029 1030#------------------------------------------------------------------------------ 1031# Commands 1032 1033 # Each command contains a docstring with it's help text. 1034 # The help text consist of independent text lines, 1035 # where each line shows a command and it's parameters. 1036 # Each command method has the help message for itself and all it's aliases. 1037 # Only the docstring for the "help" command is shown as-is. 1038 1039 # NOTE: Command methods MUST be all lowercase! 1040 1041 # Extended help command. 1042 def do_help(self, arg): 1043 """ 1044 ? - show the list of available commands 1045 ? * - show help for all commands 1046 ? <command> [command...] - show help for the given command(s) 1047 help - show the list of available commands 1048 help * - show help for all commands 1049 help <command> [command...] - show help for the given command(s) 1050 """ 1051 if not arg: 1052 Cmd.do_help(self, arg) 1053 elif arg in ('?', 'help'): 1054 # An easter egg :) 1055 print(" Help! I need somebody...") 1056 print(" Help! Not just anybody...") 1057 print(" Help! You know, I need someone...") 1058 print(" Heeelp!") 1059 else: 1060 if arg == '*': 1061 commands = self.get_names() 1062 commands = [ x for x in commands if x.startswith('do_') ] 1063 else: 1064 commands = set() 1065 for x in arg.split(' '): 1066 x = x.strip() 1067 if x: 1068 for n in self.completenames(x): 1069 commands.add( 'do_%s' % n ) 1070 commands = list(commands) 1071 commands.sort() 1072 print(self.get_help(commands)) 1073 1074 def do_shell(self, arg): 1075 """ 1076 ! - spawn a system shell 1077 shell - spawn a system shell 1078 ! <command> [arguments...] - execute a single shell command 1079 shell <command> [arguments...] - execute a single shell command 1080 """ 1081 if self.cmdprefix: 1082 raise CmdError("prefix not allowed") 1083 1084 # Try to use the environment to locate cmd.exe. 1085 # If not found, it's usually OK to just use the filename, 1086 # since cmd.exe is one of those "magic" programs that 1087 # can be automatically found by CreateProcess. 1088 shell = os.getenv('ComSpec', 'cmd.exe') 1089 1090 # When given a command, run it and return. 1091 # When no command is given, spawn a shell. 1092 if arg: 1093 arg = '%s /c %s' % (shell, arg) 1094 else: 1095 arg = shell 1096 process = self.debug.system.start_process(arg, bConsole = True) 1097 process.wait() 1098 1099 # This hack fixes a bug in Python, the interpreter console is closing the 1100 # stdin pipe when calling the exit() function (Ctrl+Z seems to work fine). 1101 class _PythonExit(object): 1102 def __repr__(self): 1103 return "Use exit() or Ctrl-Z plus Return to exit" 1104 def __call__(self): 1105 raise SystemExit() 1106 _python_exit = _PythonExit() 1107 1108 # Spawns a Python shell with some handy local variables and the winappdbg 1109 # module already imported. Also the console banner is improved. 1110 def _spawn_python_shell(self, arg): 1111 import winappdbg 1112 banner = ('Python %s on %s\nType "help", "copyright", ' 1113 '"credits" or "license" for more information.\n') 1114 platform = winappdbg.version.lower() 1115 platform = 'WinAppDbg %s' % platform 1116 banner = banner % (sys.version, platform) 1117 local = {} 1118 local.update(__builtins__) 1119 local.update({ 1120 '__name__' : '__console__', 1121 '__doc__' : None, 1122 'exit' : self._python_exit, 1123 'self' : self, 1124 'arg' : arg, 1125 'winappdbg' : winappdbg, 1126 }) 1127 try: 1128 code.interact(banner=banner, local=local) 1129 except SystemExit: 1130 # We need to catch it so it doesn't kill our program. 1131 pass 1132 1133 def do_python(self, arg): 1134 """ 1135 # - spawn a python interpreter 1136 python - spawn a python interpreter 1137 # <statement> - execute a single python statement 1138 python <statement> - execute a single python statement 1139 """ 1140 if self.cmdprefix: 1141 raise CmdError("prefix not allowed") 1142 1143 # When given a Python statement, execute it directly. 1144 if arg: 1145 try: 1146 compat.exec_(arg, globals(), locals()) 1147 except Exception: 1148 traceback.print_exc() 1149 1150 # When no statement is given, spawn a Python interpreter. 1151 else: 1152 try: 1153 self._spawn_python_shell(arg) 1154 except Exception: 1155 e = sys.exc_info()[1] 1156 raise CmdError( 1157 "unhandled exception when running Python console: %s" % e) 1158 1159 # The plugins interface is quite simple. 1160 # 1161 # Just place a .py file with the plugin name in the "plugins" folder, 1162 # for example "do_example.py" would implement the "example" command. 1163 # 1164 # The plugin must have a function named "do", which implements the 1165 # command functionality exactly like the do_* methods of Cmd instances. 1166 # 1167 # The docstring for the "do" function will be parsed exactly like 1168 # one of the debugger's commands - that is, each line is treated 1169 # independently. 1170 # 1171 def do_plugin(self, arg): 1172 """ 1173 [~prefix] .<name> [arguments] - run a plugin command 1174 [~prefix] plugin <name> [arguments] - run a plugin command 1175 """ 1176 pos = arg.find(' ') 1177 if pos < 0: 1178 name = arg 1179 arg = '' 1180 else: 1181 name = arg[:pos] 1182 arg = arg[pos:].strip() 1183 if not name: 1184 raise CmdError("missing plugin name") 1185 for c in name: 1186 if c not in self.valid_plugin_name_chars: 1187 raise CmdError("invalid plugin name: %r" % name) 1188 name = 'winappdbg.plugins.do_%s' % name 1189 try: 1190 plugin = __import__(name) 1191 components = name.split('.') 1192 for comp in components[1:]: 1193 plugin = getattr(plugin, comp) 1194 reload(plugin) 1195 except ImportError: 1196 raise CmdError("plugin not found: %s" % name) 1197 try: 1198 return plugin.do(self, arg) 1199 except CmdError: 1200 raise 1201 except Exception: 1202 e = sys.exc_info()[1] 1203## traceback.print_exc(e) # XXX DEBUG 1204 raise CmdError("unhandled exception in plugin: %s" % e) 1205 1206 def do_quit(self, arg): 1207 """ 1208 quit - close the debugging session 1209 q - close the debugging session 1210 """ 1211 if self.cmdprefix: 1212 raise CmdError("prefix not allowed") 1213 if arg: 1214 raise CmdError("too many arguments") 1215 if self.confirm_quit: 1216 count = self.debug.get_debugee_count() 1217 if count > 0: 1218 if count == 1: 1219 msg = "There's a program still running." 1220 else: 1221 msg = "There are %s programs still running." % count 1222 if not self.ask_user(msg): 1223 return False 1224 self.debuggerExit = True 1225 return True 1226 1227 do_q = do_quit 1228 1229 def do_attach(self, arg): 1230 """ 1231 attach <target> [target...] - attach to the given process(es) 1232 """ 1233 if self.cmdprefix: 1234 raise CmdError("prefix not allowed") 1235 targets = self.input_process_list( self.split_tokens(arg, 1) ) 1236 if not targets: 1237 print("Error: missing parameters") 1238 else: 1239 debug = self.debug 1240 for pid in targets: 1241 try: 1242 debug.attach(pid) 1243 print("Attached to process (%d)" % pid) 1244 except Exception: 1245 print("Error: can't attach to process (%d)" % pid) 1246 1247 def do_detach(self, arg): 1248 """ 1249 [~process] detach - detach from the current process 1250 detach - detach from the current process 1251 detach <target> [target...] - detach from the given process(es) 1252 """ 1253 debug = self.debug 1254 token_list = self.split_tokens(arg) 1255 if self.cmdprefix: 1256 token_list.insert(0, self.cmdprefix) 1257 targets = self.input_process_list(token_list) 1258 if not targets: 1259 if self.lastEvent is None: 1260 raise CmdError("no current process set") 1261 targets = [ self.lastEvent.get_pid() ] 1262 for pid in targets: 1263 try: 1264 debug.detach(pid) 1265 print("Detached from process (%d)" % pid) 1266 except Exception: 1267 print("Error: can't detach from process (%d)" % pid) 1268 1269 def do_windowed(self, arg): 1270 """ 1271 windowed <target> [arguments...] - run a windowed program for debugging 1272 """ 1273 if self.cmdprefix: 1274 raise CmdError("prefix not allowed") 1275 cmdline = self.input_command_line(arg) 1276 try: 1277 process = self.debug.execl(arg, 1278 bConsole = False, 1279 bFollow = self.options.follow) 1280 print("Spawned process (%d)" % process.get_pid()) 1281 except Exception: 1282 raise CmdError("can't execute") 1283 self.set_fake_last_event(process) 1284 1285 def do_console(self, arg): 1286 """ 1287 console <target> [arguments...] - run a console program for debugging 1288 """ 1289 if self.cmdprefix: 1290 raise CmdError("prefix not allowed") 1291 cmdline = self.input_command_line(arg) 1292 try: 1293 process = self.debug.execl(arg, 1294 bConsole = True, 1295 bFollow = self.options.follow) 1296 print("Spawned process (%d)" % process.get_pid()) 1297 except Exception: 1298 raise CmdError("can't execute") 1299 self.set_fake_last_event(process) 1300 1301 def do_continue(self, arg): 1302 """ 1303 continue - continue execution 1304 g - continue execution 1305 go - continue execution 1306 """ 1307 if self.cmdprefix: 1308 raise CmdError("prefix not allowed") 1309 if arg: 1310 raise CmdError("too many arguments") 1311 if self.debug.get_debugee_count() > 0: 1312 return True 1313 1314 do_g = do_continue 1315 do_go = do_continue 1316 1317 def do_gh(self, arg): 1318 """ 1319 gh - go with exception handled 1320 """ 1321 if self.cmdprefix: 1322 raise CmdError("prefix not allowed") 1323 if arg: 1324 raise CmdError("too many arguments") 1325 if self.lastEvent: 1326 self.lastEvent.continueStatus = win32.DBG_EXCEPTION_HANDLED 1327 return self.do_go(arg) 1328 1329 def do_gn(self, arg): 1330 """ 1331 gn - go with exception not handled 1332 """ 1333 if self.cmdprefix: 1334 raise CmdError("prefix not allowed") 1335 if arg: 1336 raise CmdError("too many arguments") 1337 if self.lastEvent: 1338 self.lastEvent.continueStatus = win32.DBG_EXCEPTION_NOT_HANDLED 1339 return self.do_go(arg) 1340 1341 def do_refresh(self, arg): 1342 """ 1343 refresh - refresh the list of running processes and threads 1344 [~process] refresh - refresh the list of running threads 1345 """ 1346 if arg: 1347 raise CmdError("too many arguments") 1348 if self.cmdprefix: 1349 process = self.get_process_from_prefix() 1350 process.scan() 1351 else: 1352 self.debug.system.scan() 1353 1354 def do_processlist(self, arg): 1355 """ 1356 pl - show the processes being debugged 1357 processlist - show the processes being debugged 1358 """ 1359 if self.cmdprefix: 1360 raise CmdError("prefix not allowed") 1361 if arg: 1362 raise CmdError("too many arguments") 1363 system = self.debug.system 1364 pid_list = self.debug.get_debugee_pids() 1365 if pid_list: 1366 print("Process ID File name") 1367 for pid in pid_list: 1368 if pid == 0: 1369 filename = "System Idle Process" 1370 elif pid == 4: 1371 filename = "System" 1372 else: 1373 filename = system.get_process(pid).get_filename() 1374 filename = PathOperations.pathname_to_filename(filename) 1375 print("%-12d %s" % (pid, filename)) 1376 1377 do_pl = do_processlist 1378 1379 def do_threadlist(self, arg): 1380 """ 1381 tl - show the threads being debugged 1382 threadlist - show the threads being debugged 1383 """ 1384 if arg: 1385 raise CmdError("too many arguments") 1386 if self.cmdprefix: 1387 process = self.get_process_from_prefix() 1388 for thread in process.iter_threads(): 1389 tid = thread.get_tid() 1390 name = thread.get_name() 1391 print("%-12d %s" % (tid, name)) 1392 else: 1393 system = self.debug.system 1394 pid_list = self.debug.get_debugee_pids() 1395 if pid_list: 1396 print("Thread ID Thread name") 1397 for pid in pid_list: 1398 process = system.get_process(pid) 1399 for thread in process.iter_threads(): 1400 tid = thread.get_tid() 1401 name = thread.get_name() 1402 print("%-12d %s" % (tid, name)) 1403 1404 do_tl = do_threadlist 1405 1406 def do_kill(self, arg): 1407 """ 1408 [~process] kill - kill a process 1409 [~thread] kill - kill a thread 1410 kill - kill the current process 1411 kill * - kill all debugged processes 1412 kill <processes and/or threads...> - kill the given processes and threads 1413 """ 1414 if arg: 1415 if arg == '*': 1416 target_pids = self.debug.get_debugee_pids() 1417 target_tids = list() 1418 else: 1419 target_pids = set() 1420 target_tids = set() 1421 if self.cmdprefix: 1422 pid, tid = self.get_process_and_thread_ids_from_prefix() 1423 if tid is None: 1424 target_tids.add(tid) 1425 else: 1426 target_pids.add(pid) 1427 for token in self.split_tokens(arg): 1428 try: 1429 pid = self.input_process(token) 1430 target_pids.add(pid) 1431 except CmdError: 1432 try: 1433 tid = self.input_process(token) 1434 target_pids.add(pid) 1435 except CmdError: 1436 msg = "unknown process or thread (%s)" % token 1437 raise CmdError(msg) 1438 target_pids = list(target_pids) 1439 target_tids = list(target_tids) 1440 target_pids.sort() 1441 target_tids.sort() 1442 msg = "You are about to kill %d processes and %d threads." 1443 msg = msg % ( len(target_pids), len(target_tids) ) 1444 if self.ask_user(msg): 1445 for pid in target_pids: 1446 self.kill_process(pid) 1447 for tid in target_tids: 1448 self.kill_thread(tid) 1449 else: 1450 if self.cmdprefix: 1451 pid, tid = self.get_process_and_thread_ids_from_prefix() 1452 if tid is None: 1453 if self.lastEvent is not None and pid == self.lastEvent.get_pid(): 1454 msg = "You are about to kill the current process." 1455 else: 1456 msg = "You are about to kill process %d." % pid 1457 if self.ask_user(msg): 1458 self.kill_process(pid) 1459 else: 1460 if self.lastEvent is not None and tid == self.lastEvent.get_tid(): 1461 msg = "You are about to kill the current thread." 1462 else: 1463 msg = "You are about to kill thread %d." % tid 1464 if self.ask_user(msg): 1465 self.kill_thread(tid) 1466 else: 1467 if self.lastEvent is None: 1468 raise CmdError("no current process set") 1469 pid = self.lastEvent.get_pid() 1470 if self.ask_user("You are about to kill the current process."): 1471 self.kill_process(pid) 1472 1473 # TODO: create hidden threads using undocumented API calls. 1474 def do_modload(self, arg): 1475 """ 1476 [~process] modload <filename.dll> - load a DLL module 1477 """ 1478 filename = self.split_tokens(arg, 1, 1)[0] 1479 process = self.get_process_from_prefix() 1480 try: 1481 process.inject_dll(filename, bWait=False) 1482 except RuntimeError: 1483 print("Can't inject module: %r" % filename) 1484 1485 # TODO: modunload 1486 1487 def do_stack(self, arg): 1488 """ 1489 [~thread] k - show the stack trace 1490 [~thread] stack - show the stack trace 1491 """ 1492 if arg: # XXX TODO add depth parameter 1493 raise CmdError("too many arguments") 1494 pid, tid = self.get_process_and_thread_ids_from_prefix() 1495 process = self.get_process(pid) 1496 thread = process.get_thread(tid) 1497 try: 1498 stack_trace = thread.get_stack_trace_with_labels() 1499 if stack_trace: 1500 print(CrashDump.dump_stack_trace_with_labels(stack_trace),) 1501 else: 1502 print("No stack trace available for thread (%d)" % tid) 1503 except WindowsError: 1504 print("Can't get stack trace for thread (%d)" % tid) 1505 1506 do_k = do_stack 1507 1508 def do_break(self, arg): 1509 """ 1510 break - force a debug break in all debugees 1511 break <process> [process...] - force a debug break 1512 """ 1513 debug = self.debug 1514 system = debug.system 1515 targets = self.input_process_list( self.split_tokens(arg) ) 1516 if not targets: 1517 targets = debug.get_debugee_pids() 1518 targets.sort() 1519 if self.lastEvent: 1520 current = self.lastEvent.get_pid() 1521 else: 1522 current = None 1523 for pid in targets: 1524 if pid != current and debug.is_debugee(pid): 1525 process = system.get_process(pid) 1526 try: 1527 process.debug_break() 1528 except WindowsError: 1529 print("Can't force a debug break on process (%d)") 1530 1531 def do_step(self, arg): 1532 """ 1533 p - step on the current assembly instruction 1534 next - step on the current assembly instruction 1535 step - step on the current assembly instruction 1536 """ 1537 if self.cmdprefix: 1538 raise CmdError("prefix not allowed") 1539 if self.lastEvent is None: 1540 raise CmdError("no current process set") 1541 if arg: # XXX this check is to be removed 1542 raise CmdError("too many arguments") 1543 pid = self.lastEvent.get_pid() 1544 thread = self.lastEvent.get_thread() 1545 pc = thread.get_pc() 1546 code = thread.disassemble(pc, 16)[0] 1547 size = code[1] 1548 opcode = code[2].lower() 1549 if ' ' in opcode: 1550 opcode = opcode[ : opcode.find(' ') ] 1551 if opcode in self.jump_instructions or opcode in ('int', 'ret', 'retn'): 1552 return self.do_trace(arg) 1553 address = pc + size 1554## print(hex(pc), hex(address), size # XXX DEBUG 1555 self.debug.stalk_at(pid, address) 1556 return True 1557 1558 do_p = do_step 1559 do_next = do_step 1560 1561 def do_trace(self, arg): 1562 """ 1563 t - trace at the current assembly instruction 1564 trace - trace at the current assembly instruction 1565 """ 1566 if arg: # XXX this check is to be removed 1567 raise CmdError("too many arguments") 1568 if self.lastEvent is None: 1569 raise CmdError("no current thread set") 1570 self.lastEvent.get_thread().set_tf() 1571 return True 1572 1573 do_t = do_trace 1574 1575 def do_bp(self, arg): 1576 """ 1577 [~process] bp <address> - set a code breakpoint 1578 """ 1579 pid = self.get_process_id_from_prefix() 1580 if not self.debug.is_debugee(pid): 1581 raise CmdError("target process is not being debugged") 1582 process = self.get_process(pid) 1583 token_list = self.split_tokens(arg, 1, 1) 1584 try: 1585 address = self.input_address(token_list[0], pid) 1586 deferred = False 1587 except Exception: 1588 address = token_list[0] 1589 deferred = True 1590 if not address: 1591 address = token_list[0] 1592 deferred = True 1593 self.debug.break_at(pid, address) 1594 if deferred: 1595 print("Deferred breakpoint set at %s" % address) 1596 else: 1597 print("Breakpoint set at %s" % address) 1598 1599 def do_ba(self, arg): 1600 """ 1601 [~thread] ba <a|w|e> <1|2|4|8> <address> - set hardware breakpoint 1602 """ 1603 debug = self.debug 1604 thread = self.get_thread_from_prefix() 1605 pid = thread.get_pid() 1606 tid = thread.get_tid() 1607 if not debug.is_debugee(pid): 1608 raise CmdError("target thread is not being debugged") 1609 token_list = self.split_tokens(arg, 3, 3) 1610 access = token_list[0].lower() 1611 size = token_list[1] 1612 address = token_list[2] 1613 if access == 'a': 1614 access = debug.BP_BREAK_ON_ACCESS 1615 elif access == 'w': 1616 access = debug.BP_BREAK_ON_WRITE 1617 elif access == 'e': 1618 access = debug.BP_BREAK_ON_EXECUTION 1619 else: 1620 raise CmdError("bad access type: %s" % token_list[0]) 1621 if size == '1': 1622 size = debug.BP_WATCH_BYTE 1623 elif size == '2': 1624 size = debug.BP_WATCH_WORD 1625 elif size == '4': 1626 size = debug.BP_WATCH_DWORD 1627 elif size == '8': 1628 size = debug.BP_WATCH_QWORD 1629 else: 1630 raise CmdError("bad breakpoint size: %s" % size) 1631 thread = self.get_thread_from_prefix() 1632 tid = thread.get_tid() 1633 pid = thread.get_pid() 1634 if not debug.is_debugee(pid): 1635 raise CmdError("target process is not being debugged") 1636 address = self.input_address(address, pid) 1637 if debug.has_hardware_breakpoint(tid, address): 1638 debug.erase_hardware_breakpoint(tid, address) 1639 debug.define_hardware_breakpoint(tid, address, access, size) 1640 debug.enable_hardware_breakpoint(tid, address) 1641 1642 def do_bm(self, arg): 1643 """ 1644 [~process] bm <address-address> - set memory breakpoint 1645 """ 1646 pid = self.get_process_id_from_prefix() 1647 if not self.debug.is_debugee(pid): 1648 raise CmdError("target process is not being debugged") 1649 process = self.get_process(pid) 1650 token_list = self.split_tokens(arg, 1, 2) 1651 address, size = self.input_address_range(token_list[0], pid) 1652 self.debug.watch_buffer(pid, address, size) 1653 1654 def do_bl(self, arg): 1655 """ 1656 bl - list the breakpoints for the current process 1657 bl * - list the breakpoints for all processes 1658 [~process] bl - list the breakpoints for the given process 1659 bl <process> [process...] - list the breakpoints for each given process 1660 """ 1661 debug = self.debug 1662 if arg == '*': 1663 if self.cmdprefix: 1664 raise CmdError("prefix not supported") 1665 breakpoints = debug.get_debugee_pids() 1666 else: 1667 targets = self.input_process_list( self.split_tokens(arg) ) 1668 if self.cmdprefix: 1669 targets.insert(0, self.input_process(self.cmdprefix)) 1670 if not targets: 1671 if self.lastEvent is None: 1672 raise CmdError("no current process is set") 1673 targets = [ self.lastEvent.get_pid() ] 1674 for pid in targets: 1675 bplist = debug.get_process_code_breakpoints(pid) 1676 printed_process_banner = False 1677 if bplist: 1678 if not printed_process_banner: 1679 print("Process %d:" % pid) 1680 printed_process_banner = True 1681 for bp in bplist: 1682 address = repr(bp)[1:-1].replace('remote address ','') 1683 print(" %s" % address) 1684 dbplist = debug.get_process_deferred_code_breakpoints(pid) 1685 if dbplist: 1686 if not printed_process_banner: 1687 print("Process %d:" % pid) 1688 printed_process_banner = True 1689 for (label, action, oneshot) in dbplist: 1690 if oneshot: 1691 address = " Deferred unconditional one-shot" \ 1692 " code breakpoint at %s" 1693 else: 1694 address = " Deferred unconditional" \ 1695 " code breakpoint at %s" 1696 address = address % label 1697 print(" %s" % address) 1698 bplist = debug.get_process_page_breakpoints(pid) 1699 if bplist: 1700 if not printed_process_banner: 1701 print("Process %d:" % pid) 1702 printed_process_banner = True 1703 for bp in bplist: 1704 address = repr(bp)[1:-1].replace('remote address ','') 1705 print(" %s" % address) 1706 for tid in debug.system.get_process(pid).iter_thread_ids(): 1707 bplist = debug.get_thread_hardware_breakpoints(tid) 1708 if bplist: 1709 print("Thread %d:" % tid) 1710 for bp in bplist: 1711 address = repr(bp)[1:-1].replace('remote address ','') 1712 print(" %s" % address) 1713 1714 def do_bo(self, arg): 1715 """ 1716 [~process] bo <address> - make a code breakpoint one-shot 1717 [~thread] bo <address> - make a hardware breakpoint one-shot 1718 [~process] bo <address-address> - make a memory breakpoint one-shot 1719 [~process] bo <address> <size> - make a memory breakpoint one-shot 1720 """ 1721 token_list = self.split_tokens(arg, 1, 2) 1722 pid, tid, address, size = self.input_breakpoint(token_list) 1723 debug = self.debug 1724 found = False 1725 if size is None: 1726 if tid is not None: 1727 if debug.has_hardware_breakpoint(tid, address): 1728 debug.enable_one_shot_hardware_breakpoint(tid, address) 1729 found = True 1730 if pid is not None: 1731 if debug.has_code_breakpoint(pid, address): 1732 debug.enable_one_shot_code_breakpoint(pid, address) 1733 found = True 1734 else: 1735 if debug.has_page_breakpoint(pid, address): 1736 debug.enable_one_shot_page_breakpoint(pid, address) 1737 found = True 1738 if not found: 1739 print("Error: breakpoint not found.") 1740 1741 def do_be(self, arg): 1742 """ 1743 [~process] be <address> - enable a code breakpoint 1744 [~thread] be <address> - enable a hardware breakpoint 1745 [~process] be <address-address> - enable a memory breakpoint 1746 [~process] be <address> <size> - enable a memory breakpoint 1747 """ 1748 token_list = self.split_tokens(arg, 1, 2) 1749 pid, tid, address, size = self.input_breakpoint(token_list) 1750 debug = self.debug 1751 found = False 1752 if size is None: 1753 if tid is not None: 1754 if debug.has_hardware_breakpoint(tid, address): 1755 debug.enable_hardware_breakpoint(tid, address) 1756 found = True 1757 if pid is not None: 1758 if debug.has_code_breakpoint(pid, address): 1759 debug.enable_code_breakpoint(pid, address) 1760 found = True 1761 else: 1762 if debug.has_page_breakpoint(pid, address): 1763 debug.enable_page_breakpoint(pid, address) 1764 found = True 1765 if not found: 1766 print("Error: breakpoint not found.") 1767 1768 def do_bd(self, arg): 1769 """ 1770 [~process] bd <address> - disable a code breakpoint 1771 [~thread] bd <address> - disable a hardware breakpoint 1772 [~process] bd <address-address> - disable a memory breakpoint 1773 [~process] bd <address> <size> - disable a memory breakpoint 1774 """ 1775 token_list = self.split_tokens(arg, 1, 2) 1776 pid, tid, address, size = self.input_breakpoint(token_list) 1777 debug = self.debug 1778 found = False 1779 if size is None: 1780 if tid is not None: 1781 if debug.has_hardware_breakpoint(tid, address): 1782 debug.disable_hardware_breakpoint(tid, address) 1783 found = True 1784 if pid is not None: 1785 if debug.has_code_breakpoint(pid, address): 1786 debug.disable_code_breakpoint(pid, address) 1787 found = True 1788 else: 1789 if debug.has_page_breakpoint(pid, address): 1790 debug.disable_page_breakpoint(pid, address) 1791 found = True 1792 if not found: 1793 print("Error: breakpoint not found.") 1794 1795 def do_bc(self, arg): 1796 """ 1797 [~process] bc <address> - clear a code breakpoint 1798 [~thread] bc <address> - clear a hardware breakpoint 1799 [~process] bc <address-address> - clear a memory breakpoint 1800 [~process] bc <address> <size> - clear a memory breakpoint 1801 """ 1802 token_list = self.split_tokens(arg, 1, 2) 1803 pid, tid, address, size = self.input_breakpoint(token_list) 1804 debug = self.debug 1805 found = False 1806 if size is None: 1807 if tid is not None: 1808 if debug.has_hardware_breakpoint(tid, address): 1809 debug.dont_watch_variable(tid, address) 1810 found = True 1811 if pid is not None: 1812 if debug.has_code_breakpoint(pid, address): 1813 debug.dont_break_at(pid, address) 1814 found = True 1815 else: 1816 if debug.has_page_breakpoint(pid, address): 1817 debug.dont_watch_buffer(pid, address, size) 1818 found = True 1819 if not found: 1820 print("Error: breakpoint not found.") 1821 1822 def do_disassemble(self, arg): 1823 """ 1824 [~thread] u [register] - show code disassembly 1825 [~process] u [address] - show code disassembly 1826 [~thread] disassemble [register] - show code disassembly 1827 [~process] disassemble [address] - show code disassembly 1828 """ 1829 if not arg: 1830 arg = self.default_disasm_target 1831 token_list = self.split_tokens(arg, 1, 1) 1832 pid, tid = self.get_process_and_thread_ids_from_prefix() 1833 process = self.get_process(pid) 1834 address = self.input_address(token_list[0], pid, tid) 1835 try: 1836 code = process.disassemble(address, 15*8)[:8] 1837 except Exception: 1838 msg = "can't disassemble address %s" 1839 msg = msg % HexDump.address(address) 1840 raise CmdError(msg) 1841 if code: 1842 label = process.get_label_at_address(address) 1843 last_code = code[-1] 1844 next_address = last_code[0] + last_code[1] 1845 next_address = HexOutput.integer(next_address) 1846 self.default_disasm_target = next_address 1847 print("%s:" % label) 1848## print(CrashDump.dump_code(code)) 1849 for line in code: 1850 print(CrashDump.dump_code_line(line, bShowDump = False)) 1851 1852 do_u = do_disassemble 1853 1854 def do_search(self, arg): 1855 """ 1856 [~process] s [address-address] <search string> 1857 [~process] search [address-address] <search string> 1858 """ 1859 token_list = self.split_tokens(arg, 1, 3) 1860 pid, tid = self.get_process_and_thread_ids_from_prefix() 1861 process = self.get_process(pid) 1862 if len(token_list) == 1: 1863 pattern = token_list[0] 1864 minAddr = None 1865 maxAddr = None 1866 else: 1867 pattern = token_list[-1] 1868 addr, size = self.input_address_range(token_list[:-1], pid, tid) 1869 minAddr = addr 1870 maxAddr = addr + size 1871 iter = process.search_bytes(pattern) 1872 if process.get_bits() == 32: 1873 addr_width = 8 1874 else: 1875 addr_width = 16 1876 # TODO: need a prettier output here! 1877 for addr in iter: 1878 print(HexDump.address(addr, addr_width)) 1879 1880 do_s = do_search 1881 1882 def do_searchhex(self, arg): 1883 """ 1884 [~process] sh [address-address] <hexadecimal pattern> 1885 [~process] searchhex [address-address] <hexadecimal pattern> 1886 """ 1887 token_list = self.split_tokens(arg, 1, 3) 1888 pid, tid = self.get_process_and_thread_ids_from_prefix() 1889 process = self.get_process(pid) 1890 if len(token_list) == 1: 1891 pattern = token_list[0] 1892 minAddr = None 1893 maxAddr = None 1894 else: 1895 pattern = token_list[-1] 1896 addr, size = self.input_address_range(token_list[:-1], pid, tid) 1897 minAddr = addr 1898 maxAddr = addr + size 1899 iter = process.search_hexa(pattern) 1900 if process.get_bits() == 32: 1901 addr_width = 8 1902 else: 1903 addr_width = 16 1904 for addr, bytes in iter: 1905 print(HexDump.hexblock(bytes, addr, addr_width),) 1906 1907 do_sh = do_searchhex 1908 1909## def do_strings(self, arg): 1910## """ 1911## [~process] strings - extract ASCII strings from memory 1912## """ 1913## if arg: 1914## raise CmdError("too many arguments") 1915## pid, tid = self.get_process_and_thread_ids_from_prefix() 1916## process = self.get_process(pid) 1917## for addr, size, data in process.strings(): 1918## print("%s: %r" % (HexDump.address(addr), data) 1919 1920 def do_d(self, arg): 1921 """ 1922 [~thread] d <register> - show memory contents 1923 [~thread] d <register-register> - show memory contents 1924 [~thread] d <register> <size> - show memory contents 1925 [~process] d <address> - show memory contents 1926 [~process] d <address-address> - show memory contents 1927 [~process] d <address> <size> - show memory contents 1928 """ 1929 return self.last_display_command(arg) 1930 1931 def do_db(self, arg): 1932 """ 1933 [~thread] db <register> - show memory contents as bytes 1934 [~thread] db <register-register> - show memory contents as bytes 1935 [~thread] db <register> <size> - show memory contents as bytes 1936 [~process] db <address> - show memory contents as bytes 1937 [~process] db <address-address> - show memory contents as bytes 1938 [~process] db <address> <size> - show memory contents as bytes 1939 """ 1940 self.print_memory_display(arg, HexDump.hexblock) 1941 self.last_display_command = self.do_db 1942 1943 def do_dw(self, arg): 1944 """ 1945 [~thread] dw <register> - show memory contents as words 1946 [~thread] dw <register-register> - show memory contents as words 1947 [~thread] dw <register> <size> - show memory contents as words 1948 [~process] dw <address> - show memory contents as words 1949 [~process] dw <address-address> - show memory contents as words 1950 [~process] dw <address> <size> - show memory contents as words 1951 """ 1952 self.print_memory_display(arg, HexDump.hexblock_word) 1953 self.last_display_command = self.do_dw 1954 1955 def do_dd(self, arg): 1956 """ 1957 [~thread] dd <register> - show memory contents as dwords 1958 [~thread] dd <register-register> - show memory contents as dwords 1959 [~thread] dd <register> <size> - show memory contents as dwords 1960 [~process] dd <address> - show memory contents as dwords 1961 [~process] dd <address-address> - show memory contents as dwords 1962 [~process] dd <address> <size> - show memory contents as dwords 1963 """ 1964 self.print_memory_display(arg, HexDump.hexblock_dword) 1965 self.last_display_command = self.do_dd 1966 1967 def do_dq(self, arg): 1968 """ 1969 [~thread] dq <register> - show memory contents as qwords 1970 [~thread] dq <register-register> - show memory contents as qwords 1971 [~thread] dq <register> <size> - show memory contents as qwords 1972 [~process] dq <address> - show memory contents as qwords 1973 [~process] dq <address-address> - show memory contents as qwords 1974 [~process] dq <address> <size> - show memory contents as qwords 1975 """ 1976 self.print_memory_display(arg, HexDump.hexblock_qword) 1977 self.last_display_command = self.do_dq 1978 1979 # XXX TODO 1980 # Change the way the default is used with ds and du 1981 1982 def do_ds(self, arg): 1983 """ 1984 [~thread] ds <register> - show memory contents as ANSI string 1985 [~process] ds <address> - show memory contents as ANSI string 1986 """ 1987 if not arg: 1988 arg = self.default_display_target 1989 token_list = self.split_tokens(arg, 1, 1) 1990 pid, tid, address, size = self.input_display(token_list, 256) 1991 process = self.get_process(pid) 1992 data = process.peek_string(address, False, size) 1993 if data: 1994 print(repr(data)) 1995 self.last_display_command = self.do_ds 1996 1997 def do_du(self, arg): 1998 """ 1999 [~thread] du <register> - show memory contents as Unicode string 2000 [~process] du <address> - show memory contents as Unicode string 2001 """ 2002 if not arg: 2003 arg = self.default_display_target 2004 token_list = self.split_tokens(arg, 1, 2) 2005 pid, tid, address, size = self.input_display(token_list, 256) 2006 process = self.get_process(pid) 2007 data = process.peek_string(address, True, size) 2008 if data: 2009 print(repr(data)) 2010 self.last_display_command = self.do_du 2011 2012 def do_register(self, arg): 2013 """ 2014 [~thread] r - print(the value of all registers 2015 [~thread] r <register> - print(the value of a register 2016 [~thread] r <register>=<value> - change the value of a register 2017 [~thread] register - print(the value of all registers 2018 [~thread] register <register> - print(the value of a register 2019 [~thread] register <register>=<value> - change the value of a register 2020 """ 2021 arg = arg.strip() 2022 if not arg: 2023 self.print_current_location() 2024 else: 2025 equ = arg.find('=') 2026 if equ >= 0: 2027 register = arg[:equ].strip() 2028 value = arg[equ+1:].strip() 2029 if not value: 2030 value = '0' 2031 self.change_register(register, value) 2032 else: 2033 value = self.input_register(arg) 2034 if value is None: 2035 raise CmdError("unknown register: %s" % arg) 2036 try: 2037 label = None 2038 thread = self.get_thread_from_prefix() 2039 process = thread.get_process() 2040 module = process.get_module_at_address(value) 2041 if module: 2042 label = module.get_label_at_address(value) 2043 except RuntimeError: 2044 label = None 2045 reg = arg.upper() 2046 val = HexDump.address(value) 2047 if label: 2048 print("%s: %s (%s)" % (reg, val, label)) 2049 else: 2050 print("%s: %s" % (reg, val)) 2051 2052 do_r = do_register 2053 2054 def do_eb(self, arg): 2055 """ 2056 [~process] eb <address> <data> - write the data to the specified address 2057 """ 2058 # TODO 2059 # data parameter should be optional, use a child Cmd here 2060 pid = self.get_process_id_from_prefix() 2061 token_list = self.split_tokens(arg, 2) 2062 address = self.input_address(token_list[0], pid) 2063 data = HexInput.hexadecimal(' '.join(token_list[1:])) 2064 self.write_memory(address, data, pid) 2065 2066 # XXX TODO 2067 # add ew, ed and eq here 2068 2069 def do_find(self, arg): 2070 """ 2071 [~process] f <string> - find the string in the process memory 2072 [~process] find <string> - find the string in the process memory 2073 """ 2074 if not arg: 2075 raise CmdError("missing parameter: string") 2076 process = self.get_process_from_prefix() 2077 self.find_in_memory(arg, process) 2078 2079 do_f = do_find 2080 2081 def do_memory(self, arg): 2082 """ 2083 [~process] m - show the process memory map 2084 [~process] memory - show the process memory map 2085 """ 2086 if arg: # TODO: take min and max addresses 2087 raise CmdError("too many arguments") 2088 process = self.get_process_from_prefix() 2089 try: 2090 memoryMap = process.get_memory_map() 2091 mappedFilenames = process.get_mapped_filenames() 2092 print('') 2093 print(CrashDump.dump_memory_map(memoryMap, mappedFilenames)) 2094 except WindowsError: 2095 msg = "can't get memory information for process (%d)" 2096 raise CmdError(msg % process.get_pid()) 2097 2098 do_m = do_memory 2099 2100#------------------------------------------------------------------------------ 2101# Event handling 2102 2103# TODO 2104# * add configurable stop/don't stop behavior on events and exceptions 2105 2106 # Stop for all events, unless stated otherwise. 2107 def event(self, event): 2108 self.print_event(event) 2109 self.prompt_user() 2110 2111 # Stop for all exceptions, unless stated otherwise. 2112 def exception(self, event): 2113 self.print_exception(event) 2114 self.prompt_user() 2115 2116 # Stop for breakpoint exceptions. 2117 def breakpoint(self, event): 2118 if hasattr(event, 'breakpoint') and event.breakpoint: 2119 self.print_breakpoint_location(event) 2120 else: 2121 self.print_exception(event) 2122 self.prompt_user() 2123 2124 # Stop for WOW64 breakpoint exceptions. 2125 def wow64_breakpoint(self, event): 2126 self.print_exception(event) 2127 self.prompt_user() 2128 2129 # Stop for single step exceptions. 2130 def single_step(self, event): 2131 if event.debug.is_tracing(event.get_tid()): 2132 self.print_breakpoint_location(event) 2133 else: 2134 self.print_exception(event) 2135 self.prompt_user() 2136 2137 # Don't stop for C++ exceptions. 2138 def ms_vc_exception(self, event): 2139 self.print_exception(event) 2140 event.continueStatus = win32.DBG_CONTINUE 2141 2142 # Don't stop for process start. 2143 def create_process(self, event): 2144 self.print_process_start(event) 2145 self.print_thread_start(event) 2146 self.print_module_load(event) 2147 2148 # Don't stop for process exit. 2149 def exit_process(self, event): 2150 self.print_process_end(event) 2151 2152 # Don't stop for thread creation. 2153 def create_thread(self, event): 2154 self.print_thread_start(event) 2155 2156 # Don't stop for thread exit. 2157 def exit_thread(self, event): 2158 self.print_thread_end(event) 2159 2160 # Don't stop for DLL load. 2161 def load_dll(self, event): 2162 self.print_module_load(event) 2163 2164 # Don't stop for DLL unload. 2165 def unload_dll(self, event): 2166 self.print_module_unload(event) 2167 2168 # Don't stop for debug strings. 2169 def output_string(self, event): 2170 self.print_debug_string(event) 2171 2172#------------------------------------------------------------------------------ 2173# History file 2174 2175 def load_history(self): 2176 global readline 2177 if readline is None: 2178 try: 2179 import readline 2180 except ImportError: 2181 return 2182 if self.history_file_full_path is None: 2183 folder = os.environ.get('USERPROFILE', '') 2184 if not folder: 2185 folder = os.environ.get('HOME', '') 2186 if not folder: 2187 folder = os.path.split(sys.argv[0])[1] 2188 if not folder: 2189 folder = os.path.curdir 2190 self.history_file_full_path = os.path.join(folder, 2191 self.history_file) 2192 try: 2193 if os.path.exists(self.history_file_full_path): 2194 readline.read_history_file(self.history_file_full_path) 2195 except IOError: 2196 e = sys.exc_info()[1] 2197 warnings.warn("Cannot load history file, reason: %s" % str(e)) 2198 2199 def save_history(self): 2200 if self.history_file_full_path is not None: 2201 global readline 2202 if readline is None: 2203 try: 2204 import readline 2205 except ImportError: 2206 return 2207 try: 2208 readline.write_history_file(self.history_file_full_path) 2209 except IOError: 2210 e = sys.exc_info()[1] 2211 warnings.warn("Cannot save history file, reason: %s" % str(e)) 2212 2213#------------------------------------------------------------------------------ 2214# Main loop 2215 2216 # Debugging loop. 2217 def loop(self): 2218 self.debuggerExit = False 2219 debug = self.debug 2220 2221 # Stop on the initial event, if any. 2222 if self.lastEvent is not None: 2223 self.cmdqueue.append('r') 2224 self.prompt_user() 2225 2226 # Loop until the debugger is told to quit. 2227 while not self.debuggerExit: 2228 2229 try: 2230 2231 # If for some reason the last event wasn't continued, 2232 # continue it here. This won't be done more than once 2233 # for a given Event instance, though. 2234 try: 2235 debug.cont() 2236 # On error, show the command prompt. 2237 except Exception: 2238 traceback.print_exc() 2239 self.prompt_user() 2240 2241 # While debugees are attached, handle debug events. 2242 # Some debug events may cause the command prompt to be shown. 2243 if self.debug.get_debugee_count() > 0: 2244 try: 2245 2246 # Get the next debug event. 2247 debug.wait() 2248 2249 # Dispatch the debug event. 2250 try: 2251 debug.dispatch() 2252 2253 # Continue the debug event. 2254 finally: 2255 debug.cont() 2256 2257 # On error, show the command prompt. 2258 except Exception: 2259 traceback.print_exc() 2260 self.prompt_user() 2261 2262 # While no debugees are attached, show the command prompt. 2263 else: 2264 self.prompt_user() 2265 2266 # When the user presses Ctrl-C send a debug break to all debugees. 2267 except KeyboardInterrupt: 2268 success = False 2269 try: 2270 print("*** User requested debug break") 2271 system = debug.system 2272 for pid in debug.get_debugee_pids(): 2273 try: 2274 system.get_process(pid).debug_break() 2275 success = True 2276 except: 2277 traceback.print_exc() 2278 except: 2279 traceback.print_exc() 2280 if not success: 2281 raise # This should never happen! 2282