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