| #!/usr/bin/python |
| |
| #---------------------------------------------------------------------- |
| # Be sure to add the python path that points to the LLDB shared library. |
| # |
| # # To use this in the embedded python interpreter using "lldb" just |
| # import it with the full path using the "command script import" |
| # command |
| # (lldb) command script import /path/to/cmdtemplate.py |
| #---------------------------------------------------------------------- |
| |
| import commands |
| import platform |
| import os |
| import re |
| import sys |
| |
| try: |
| # Just try for LLDB in case PYTHONPATH is already correctly setup |
| import lldb |
| except ImportError: |
| lldb_python_dirs = list() |
| # lldb is not in the PYTHONPATH, try some defaults for the current platform |
| platform_system = platform.system() |
| if platform_system == 'Darwin': |
| # On Darwin, try the currently selected Xcode directory |
| xcode_dir = commands.getoutput("xcode-select --print-path") |
| if xcode_dir: |
| lldb_python_dirs.append( |
| os.path.realpath( |
| xcode_dir + |
| '/../SharedFrameworks/LLDB.framework/Resources/Python')) |
| lldb_python_dirs.append( |
| xcode_dir + '/Library/PrivateFrameworks/LLDB.framework/Resources/Python') |
| lldb_python_dirs.append( |
| '/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python') |
| success = False |
| for lldb_python_dir in lldb_python_dirs: |
| if os.path.exists(lldb_python_dir): |
| if not (sys.path.__contains__(lldb_python_dir)): |
| sys.path.append(lldb_python_dir) |
| try: |
| import lldb |
| except ImportError: |
| pass |
| else: |
| print 'imported lldb from: "%s"' % (lldb_python_dir) |
| success = True |
| break |
| if not success: |
| print "error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly" |
| sys.exit(1) |
| |
| import commands |
| import optparse |
| import shlex |
| import string |
| import struct |
| import time |
| |
| |
| def append_data_callback(option, opt_str, value, parser): |
| if opt_str == "--uint8": |
| int8 = int(value, 0) |
| parser.values.data += struct.pack('1B', int8) |
| if opt_str == "--uint16": |
| int16 = int(value, 0) |
| parser.values.data += struct.pack('1H', int16) |
| if opt_str == "--uint32": |
| int32 = int(value, 0) |
| parser.values.data += struct.pack('1I', int32) |
| if opt_str == "--uint64": |
| int64 = int(value, 0) |
| parser.values.data += struct.pack('1Q', int64) |
| if opt_str == "--int8": |
| int8 = int(value, 0) |
| parser.values.data += struct.pack('1b', int8) |
| if opt_str == "--int16": |
| int16 = int(value, 0) |
| parser.values.data += struct.pack('1h', int16) |
| if opt_str == "--int32": |
| int32 = int(value, 0) |
| parser.values.data += struct.pack('1i', int32) |
| if opt_str == "--int64": |
| int64 = int(value, 0) |
| parser.values.data += struct.pack('1q', int64) |
| |
| |
| def create_memfind_options(): |
| usage = "usage: %prog [options] STARTADDR [ENDADDR]" |
| description = '''This command can find data in a specified address range. |
| Options are used to specify the data that is to be looked for and the options |
| can be specified multiple times to look for longer streams of data. |
| ''' |
| parser = optparse.OptionParser( |
| description=description, |
| prog='memfind', |
| usage=usage) |
| parser.add_option( |
| '-s', |
| '--size', |
| type='int', |
| metavar='BYTESIZE', |
| dest='size', |
| help='Specify the byte size to search.', |
| default=0) |
| parser.add_option( |
| '--int8', |
| action="callback", |
| callback=append_data_callback, |
| type='string', |
| metavar='INT', |
| dest='data', |
| help='Specify a 8 bit signed integer value to search for in memory.', |
| default='') |
| parser.add_option( |
| '--int16', |
| action="callback", |
| callback=append_data_callback, |
| type='string', |
| metavar='INT', |
| dest='data', |
| help='Specify a 16 bit signed integer value to search for in memory.', |
| default='') |
| parser.add_option( |
| '--int32', |
| action="callback", |
| callback=append_data_callback, |
| type='string', |
| metavar='INT', |
| dest='data', |
| help='Specify a 32 bit signed integer value to search for in memory.', |
| default='') |
| parser.add_option( |
| '--int64', |
| action="callback", |
| callback=append_data_callback, |
| type='string', |
| metavar='INT', |
| dest='data', |
| help='Specify a 64 bit signed integer value to search for in memory.', |
| default='') |
| parser.add_option( |
| '--uint8', |
| action="callback", |
| callback=append_data_callback, |
| type='string', |
| metavar='INT', |
| dest='data', |
| help='Specify a 8 bit unsigned integer value to search for in memory.', |
| default='') |
| parser.add_option( |
| '--uint16', |
| action="callback", |
| callback=append_data_callback, |
| type='string', |
| metavar='INT', |
| dest='data', |
| help='Specify a 16 bit unsigned integer value to search for in memory.', |
| default='') |
| parser.add_option( |
| '--uint32', |
| action="callback", |
| callback=append_data_callback, |
| type='string', |
| metavar='INT', |
| dest='data', |
| help='Specify a 32 bit unsigned integer value to search for in memory.', |
| default='') |
| parser.add_option( |
| '--uint64', |
| action="callback", |
| callback=append_data_callback, |
| type='string', |
| metavar='INT', |
| dest='data', |
| help='Specify a 64 bit unsigned integer value to search for in memory.', |
| default='') |
| return parser |
| |
| |
| def memfind_command(debugger, command, result, dict): |
| # Use the Shell Lexer to properly parse up command options just like a |
| # shell would |
| command_args = shlex.split(command) |
| parser = create_memfind_options() |
| (options, args) = parser.parse_args(command_args) |
| # try: |
| # (options, args) = parser.parse_args(command_args) |
| # except: |
| # # if you don't handle exceptions, passing an incorrect argument to the OptionParser will cause LLDB to exit |
| # # (courtesy of OptParse dealing with argument errors by throwing SystemExit) |
| # result.SetStatus (lldb.eReturnStatusFailed) |
| # print >>result, "error: option parsing failed" # returning a string is the same as returning an error whose description is the string |
| # return |
| memfind(debugger.GetSelectedTarget(), options, args, result) |
| |
| |
| def print_error(str, show_usage, result): |
| print >>result, str |
| if show_usage: |
| print >>result, create_memfind_options().format_help() |
| |
| |
| def memfind(target, options, args, result): |
| num_args = len(args) |
| start_addr = 0 |
| if num_args == 1: |
| if options.size > 0: |
| print_error( |
| "error: --size must be specified if there is no ENDADDR argument", |
| True, |
| result) |
| return |
| start_addr = int(args[0], 0) |
| elif num_args == 2: |
| if options.size != 0: |
| print_error( |
| "error: --size can't be specified with an ENDADDR argument", |
| True, |
| result) |
| return |
| start_addr = int(args[0], 0) |
| end_addr = int(args[1], 0) |
| if start_addr >= end_addr: |
| print_error( |
| "error: inavlid memory range [%#x - %#x)" % |
| (start_addr, end_addr), True, result) |
| return |
| options.size = end_addr - start_addr |
| else: |
| print_error("error: memfind takes 1 or 2 arguments", True, result) |
| return |
| |
| if not options.data: |
| print >>result, 'error: no data specified to search for' |
| return |
| |
| if not target: |
| print >>result, 'error: invalid target' |
| return |
| process = target.process |
| if not process: |
| print >>result, 'error: invalid process' |
| return |
| |
| error = lldb.SBError() |
| bytes = process.ReadMemory(start_addr, options.size, error) |
| if error.Success(): |
| num_matches = 0 |
| print >>result, "Searching memory range [%#x - %#x) for" % ( |
| start_addr, end_addr), |
| for byte in options.data: |
| print >>result, '%2.2x' % ord(byte), |
| print >>result |
| |
| match_index = string.find(bytes, options.data) |
| while match_index != -1: |
| num_matches = num_matches + 1 |
| print >>result, '%#x: %#x + %u' % (start_addr + |
| match_index, start_addr, match_index) |
| match_index = string.find(bytes, options.data, match_index + 1) |
| |
| if num_matches == 0: |
| print >>result, "error: no matches found" |
| else: |
| print >>result, 'error: %s' % (error.GetCString()) |
| |
| |
| if __name__ == '__main__': |
| print 'error: this script is designed to be used within the embedded script interpreter in LLDB' |
| elif getattr(lldb, 'debugger', None): |
| memfind_command.__doc__ = create_memfind_options().format_help() |
| lldb.debugger.HandleCommand( |
| 'command script add -f memory.memfind_command memfind') |
| print '"memfind" command installed, use the "--help" option for detailed help' |