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
12import commands
13import platform
14import os
15import re
16import sys
17
18try:
19    # Just try for LLDB in case PYTHONPATH is already correctly setup
20    import lldb
21except 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
49import commands
50import optparse
51import shlex
52import string
53import struct
54import time
55
56def 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
82def create_memfind_options():
83    usage = "usage: %prog [options] STARTADDR [ENDADDR]"
84    description='''This command can find data in a specified address range.
85Options are used to specify the data that is to be looked for and the options
86can 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
100def 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
116def print_error(str, show_usage, result):
117    print >>result, str
118    if show_usage:
119        print >>result, create_memfind_options().format_help()
120
121def 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
176if __name__ == '__main__':
177    print 'error: this script is designed to be used within the embedded script interpreter in LLDB'
178elif 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'
182