Greg Clayton | 04bd684 | 2013-07-11 22:38:00 +0000 | [diff] [blame] | 1 | #!/usr/bin/python |
| 2 | |
| 3 | #---------------------------------------------------------------------- |
| 4 | # Be sure to add the python path that points to the LLDB shared library. |
| 5 | # |
| 6 | # # To use this in the embedded python interpreter using "lldb" just |
| 7 | # import it with the full path using the "command script import" |
| 8 | # command |
| 9 | # (lldb) command script import /path/to/cmdtemplate.py |
| 10 | #---------------------------------------------------------------------- |
| 11 | |
| 12 | import commands |
| 13 | import platform |
| 14 | import os |
| 15 | import re |
| 16 | import sys |
| 17 | |
| 18 | try: |
| 19 | # Just try for LLDB in case PYTHONPATH is already correctly setup |
| 20 | import lldb |
| 21 | except ImportError: |
| 22 | lldb_python_dirs = list() |
| 23 | # lldb is not in the PYTHONPATH, try some defaults for the current platform |
| 24 | platform_system = platform.system() |
| 25 | if platform_system == 'Darwin': |
| 26 | # On Darwin, try the currently selected Xcode directory |
| 27 | xcode_dir = commands.getoutput("xcode-select --print-path") |
| 28 | if xcode_dir: |
| 29 | lldb_python_dirs.append(os.path.realpath(xcode_dir + '/../SharedFrameworks/LLDB.framework/Resources/Python')) |
| 30 | lldb_python_dirs.append(xcode_dir + '/Library/PrivateFrameworks/LLDB.framework/Resources/Python') |
| 31 | lldb_python_dirs.append('/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python') |
| 32 | success = False |
| 33 | for lldb_python_dir in lldb_python_dirs: |
| 34 | if os.path.exists(lldb_python_dir): |
| 35 | if not (sys.path.__contains__(lldb_python_dir)): |
| 36 | sys.path.append(lldb_python_dir) |
| 37 | try: |
| 38 | import lldb |
| 39 | except ImportError: |
| 40 | pass |
| 41 | else: |
| 42 | print 'imported lldb from: "%s"' % (lldb_python_dir) |
| 43 | success = True |
| 44 | break |
| 45 | if not success: |
| 46 | print "error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly" |
| 47 | sys.exit(1) |
| 48 | |
| 49 | import commands |
| 50 | import optparse |
| 51 | import shlex |
| 52 | import string |
| 53 | import struct |
| 54 | import time |
| 55 | |
| 56 | def append_data_callback(option, opt_str, value, parser): |
| 57 | if opt_str == "--uint8": |
| 58 | int8 = int(value, 0) |
| 59 | parser.values.data += struct.pack('1B',int8) |
| 60 | if opt_str == "--uint16": |
| 61 | int16 = int(value, 0) |
| 62 | parser.values.data += struct.pack('1H',int16) |
| 63 | if opt_str == "--uint32": |
| 64 | int32 = int(value, 0) |
| 65 | parser.values.data += struct.pack('1I',int32) |
| 66 | if opt_str == "--uint64": |
| 67 | int64 = int(value, 0) |
| 68 | parser.values.data += struct.pack('1Q',int64) |
| 69 | if opt_str == "--int8": |
| 70 | int8 = int(value, 0) |
| 71 | parser.values.data += struct.pack('1b',int8) |
| 72 | if opt_str == "--int16": |
| 73 | int16 = int(value, 0) |
| 74 | parser.values.data += struct.pack('1h',int16) |
| 75 | if opt_str == "--int32": |
| 76 | int32 = int(value, 0) |
| 77 | parser.values.data += struct.pack('1i',int32) |
| 78 | if opt_str == "--int64": |
| 79 | int64 = int(value, 0) |
| 80 | parser.values.data += struct.pack('1q',int64) |
| 81 | |
| 82 | def create_memfind_options(): |
| 83 | usage = "usage: %prog [options] STARTADDR [ENDADDR]" |
| 84 | description='''This command can find data in a specified address range. |
| 85 | Options are used to specify the data that is to be looked for and the options |
| 86 | can be specified multiple times to look for longer streams of data. |
| 87 | ''' |
| 88 | parser = optparse.OptionParser(description=description, prog='memfind',usage=usage) |
| 89 | parser.add_option('-s', '--size', type='int', metavar='BYTESIZE', dest='size', help='Specify the byte size to search.', default=0) |
| 90 | 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='') |
| 91 | 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='') |
| 92 | 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='') |
| 93 | 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='') |
| 94 | 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='') |
| 95 | 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='') |
| 96 | 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='') |
| 97 | 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='') |
| 98 | return parser |
| 99 | |
| 100 | def memfind_command (debugger, command, result, dict): |
| 101 | # Use the Shell Lexer to properly parse up command options just like a |
| 102 | # shell would |
| 103 | command_args = shlex.split(command) |
| 104 | parser = create_memfind_options() |
| 105 | (options, args) = parser.parse_args(command_args) |
| 106 | # try: |
| 107 | # (options, args) = parser.parse_args(command_args) |
| 108 | # except: |
| 109 | # # if you don't handle exceptions, passing an incorrect argument to the OptionParser will cause LLDB to exit |
| 110 | # # (courtesy of OptParse dealing with argument errors by throwing SystemExit) |
| 111 | # result.SetStatus (lldb.eReturnStatusFailed) |
| 112 | # print >>result, "error: option parsing failed" # returning a string is the same as returning an error whose description is the string |
| 113 | # return |
| 114 | memfind (debugger.GetSelectedTarget(), options, args, result) |
| 115 | |
| 116 | def print_error(str, show_usage, result): |
| 117 | print >>result, str |
| 118 | if show_usage: |
| 119 | print >>result, create_memfind_options().format_help() |
| 120 | |
| 121 | def memfind (target, options, args, result): |
| 122 | num_args = len(args) |
| 123 | start_addr = 0 |
| 124 | if num_args == 1: |
| 125 | if options.size > 0: |
| 126 | print_error ("error: --size must be specified if there is no ENDADDR argument", True, result) |
| 127 | return |
| 128 | start_addr = int(args[0], 0) |
| 129 | elif num_args == 2: |
| 130 | if options.size != 0: |
| 131 | print_error ("error: --size can't be specified with an ENDADDR argument", True, result) |
| 132 | return |
| 133 | start_addr = int(args[0], 0) |
| 134 | end_addr = int(args[1], 0) |
| 135 | if start_addr >= end_addr: |
| 136 | print_error ("error: inavlid memory range [%#x - %#x)" % (start_addr, end_addr), True, result) |
| 137 | return |
| 138 | options.size = end_addr - start_addr |
| 139 | else: |
| 140 | print_error ("error: memfind takes 1 or 2 arguments", True, result) |
| 141 | return |
| 142 | |
| 143 | if not options.data: |
| 144 | print >>result, 'error: no data specified to search for' |
| 145 | return |
| 146 | |
| 147 | if not target: |
| 148 | print >>result, 'error: invalid target' |
| 149 | return |
| 150 | process = target.process |
| 151 | if not process: |
| 152 | print >>result, 'error: invalid process' |
| 153 | return |
| 154 | |
| 155 | error = lldb.SBError() |
| 156 | bytes = process.ReadMemory (start_addr, options.size, error) |
| 157 | if error.Success(): |
| 158 | num_matches = 0 |
| 159 | print >>result, "Searching memory range [%#x - %#x) for" % (start_addr, end_addr), |
| 160 | for byte in options.data: |
| 161 | print >>result, '%2.2x' % ord(byte), |
| 162 | print >>result |
| 163 | |
| 164 | match_index = string.find(bytes, options.data) |
| 165 | while match_index != -1: |
| 166 | num_matches = num_matches + 1 |
| 167 | print >>result, '%#x: %#x + %u' % (start_addr + match_index, start_addr, match_index) |
| 168 | match_index = string.find(bytes, options.data, match_index + 1) |
| 169 | |
| 170 | if num_matches == 0: |
| 171 | print >>result, "error: no matches found" |
| 172 | else: |
| 173 | print >>result, 'error: %s' % (error.GetCString()) |
| 174 | |
| 175 | |
| 176 | if __name__ == '__main__': |
| 177 | print 'error: this script is designed to be used within the embedded script interpreter in LLDB' |
| 178 | elif getattr(lldb, 'debugger', None): |
| 179 | memfind_command.__doc__ = create_memfind_options().format_help() |
| 180 | lldb.debugger.HandleCommand('command script add -f memory.memfind_command memfind') |
| 181 | print '"memfind" command installed, use the "--help" option for detailed help' |