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
13from __future__ import print_function
14
15import platform
16import os
17import re
18import sys
19
20try:
21    # Just try for LLDB in case PYTHONPATH is already correctly setup
22    import lldb
23except ImportError:
24    lldb_python_dirs = list()
25    # lldb is not in the PYTHONPATH, try some defaults for the current platform
26    platform_system = platform.system()
27    if platform_system == 'Darwin':
28        # On Darwin, try the currently selected Xcode directory
29        xcode_dir = commands.getoutput("xcode-select --print-path")
30        if xcode_dir:
31            lldb_python_dirs.append(
32                os.path.realpath(
33                    xcode_dir +
34                    '/../SharedFrameworks/LLDB.framework/Resources/Python'))
35            lldb_python_dirs.append(
36                xcode_dir + '/Library/PrivateFrameworks/LLDB.framework/Resources/Python')
37        lldb_python_dirs.append(
38            '/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python')
39    success = False
40    for lldb_python_dir in lldb_python_dirs:
41        if os.path.exists(lldb_python_dir):
42            if not (sys.path.__contains__(lldb_python_dir)):
43                sys.path.append(lldb_python_dir)
44                try:
45                    import lldb
46                except ImportError:
47                    pass
48                else:
49                    print('imported lldb from: "%s"' % (lldb_python_dir))
50                    success = True
51                    break
52    if not success:
53        print("error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly")
54        sys.exit(1)
55
56import commands
57import optparse
58import shlex
59import string
60import struct
61import time
62
63
64def append_data_callback(option, opt_str, value, parser):
65    if opt_str == "--uint8":
66        int8 = int(value, 0)
67        parser.values.data += struct.pack('1B', int8)
68    if opt_str == "--uint16":
69        int16 = int(value, 0)
70        parser.values.data += struct.pack('1H', int16)
71    if opt_str == "--uint32":
72        int32 = int(value, 0)
73        parser.values.data += struct.pack('1I', int32)
74    if opt_str == "--uint64":
75        int64 = int(value, 0)
76        parser.values.data += struct.pack('1Q', int64)
77    if opt_str == "--int8":
78        int8 = int(value, 0)
79        parser.values.data += struct.pack('1b', int8)
80    if opt_str == "--int16":
81        int16 = int(value, 0)
82        parser.values.data += struct.pack('1h', int16)
83    if opt_str == "--int32":
84        int32 = int(value, 0)
85        parser.values.data += struct.pack('1i', int32)
86    if opt_str == "--int64":
87        int64 = int(value, 0)
88        parser.values.data += struct.pack('1q', int64)
89
90
91def create_memfind_options():
92    usage = "usage: %prog [options] STARTADDR [ENDADDR]"
93    description = '''This command can find data in a specified address range.
94Options are used to specify the data that is to be looked for and the options
95can be specified multiple times to look for longer streams of data.
96'''
97    parser = optparse.OptionParser(
98        description=description,
99        prog='memfind',
100        usage=usage)
101    parser.add_option(
102        '-s',
103        '--size',
104        type='int',
105        metavar='BYTESIZE',
106        dest='size',
107        help='Specify the byte size to search.',
108        default=0)
109    parser.add_option(
110        '--int8',
111        action="callback",
112        callback=append_data_callback,
113        type='string',
114        metavar='INT',
115        dest='data',
116        help='Specify a 8 bit signed integer value to search for in memory.',
117        default='')
118    parser.add_option(
119        '--int16',
120        action="callback",
121        callback=append_data_callback,
122        type='string',
123        metavar='INT',
124        dest='data',
125        help='Specify a 16 bit signed integer value to search for in memory.',
126        default='')
127    parser.add_option(
128        '--int32',
129        action="callback",
130        callback=append_data_callback,
131        type='string',
132        metavar='INT',
133        dest='data',
134        help='Specify a 32 bit signed integer value to search for in memory.',
135        default='')
136    parser.add_option(
137        '--int64',
138        action="callback",
139        callback=append_data_callback,
140        type='string',
141        metavar='INT',
142        dest='data',
143        help='Specify a 64 bit signed integer value to search for in memory.',
144        default='')
145    parser.add_option(
146        '--uint8',
147        action="callback",
148        callback=append_data_callback,
149        type='string',
150        metavar='INT',
151        dest='data',
152        help='Specify a 8 bit unsigned integer value to search for in memory.',
153        default='')
154    parser.add_option(
155        '--uint16',
156        action="callback",
157        callback=append_data_callback,
158        type='string',
159        metavar='INT',
160        dest='data',
161        help='Specify a 16 bit unsigned integer value to search for in memory.',
162        default='')
163    parser.add_option(
164        '--uint32',
165        action="callback",
166        callback=append_data_callback,
167        type='string',
168        metavar='INT',
169        dest='data',
170        help='Specify a 32 bit unsigned integer value to search for in memory.',
171        default='')
172    parser.add_option(
173        '--uint64',
174        action="callback",
175        callback=append_data_callback,
176        type='string',
177        metavar='INT',
178        dest='data',
179        help='Specify a 64 bit unsigned integer value to search for in memory.',
180        default='')
181    return parser
182
183
184def memfind_command(debugger, command, result, dict):
185    # Use the Shell Lexer to properly parse up command options just like a
186    # shell would
187    command_args = shlex.split(command)
188    parser = create_memfind_options()
189    (options, args) = parser.parse_args(command_args)
190    # try:
191    #     (options, args) = parser.parse_args(command_args)
192    # except:
193    #     # if you don't handle exceptions, passing an incorrect argument to the OptionParser will cause LLDB to exit
194    #     # (courtesy of OptParse dealing with argument errors by throwing SystemExit)
195    #     result.SetStatus (lldb.eReturnStatusFailed)
196    #     print >>result, "error: option parsing failed" # returning a string is the same as returning an error whose description is the string
197    #     return
198    memfind(debugger.GetSelectedTarget(), options, args, result)
199
200
201def print_error(str, show_usage, result):
202    print(str, file=result)
203    if show_usage:
204        print(create_memfind_options().format_help(), file=result)
205
206
207def memfind(target, options, args, result):
208    num_args = len(args)
209    start_addr = 0
210    if num_args == 1:
211        if options.size > 0:
212            print_error(
213                "error: --size must be specified if there is no ENDADDR argument",
214                True,
215                result)
216            return
217        start_addr = int(args[0], 0)
218    elif num_args == 2:
219        if options.size != 0:
220            print_error(
221                "error: --size can't be specified with an ENDADDR argument",
222                True,
223                result)
224            return
225        start_addr = int(args[0], 0)
226        end_addr = int(args[1], 0)
227        if start_addr >= end_addr:
228            print_error(
229                "error: inavlid memory range [%#x - %#x)" %
230                (start_addr, end_addr), True, result)
231            return
232        options.size = end_addr - start_addr
233    else:
234        print_error("error: memfind takes 1 or 2 arguments", True, result)
235        return
236
237    if not options.data:
238        print('error: no data specified to search for', file=result)
239        return
240
241    if not target:
242        print('error: invalid target', file=result)
243        return
244    process = target.process
245    if not process:
246        print('error: invalid process', file=result)
247        return
248
249    error = lldb.SBError()
250    bytes = process.ReadMemory(start_addr, options.size, error)
251    if error.Success():
252        num_matches = 0
253        print("Searching memory range [%#x - %#x) for" % (
254            start_addr, end_addr), end=' ', file=result)
255        for byte in options.data:
256            print('%2.2x' % ord(byte), end=' ', file=result)
257        print(file=result)
258
259        match_index = string.find(bytes, options.data)
260        while match_index != -1:
261            num_matches = num_matches + 1
262            print('%#x: %#x + %u' % (start_addr +
263                                               match_index, start_addr, match_index), file=result)
264            match_index = string.find(bytes, options.data, match_index + 1)
265
266        if num_matches == 0:
267            print("error: no matches found", file=result)
268    else:
269        print('error: %s' % (error.GetCString()), file=result)
270
271
272if __name__ == '__main__':
273    print('error: this script is designed to be used within the embedded script interpreter in LLDB')
274elif getattr(lldb, 'debugger', None):
275    memfind_command.__doc__ = create_memfind_options().format_help()
276    lldb.debugger.HandleCommand(
277        'command script add -f memory.memfind_command memfind')
278    print('"memfind" command installed, use the "--help" option for detailed help')
279