You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
278 lines
9.3 KiB
278 lines
9.3 KiB
#!/usr/bin/env 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
|
|
#----------------------------------------------------------------------
|
|
|
|
from __future__ import print_function
|
|
|
|
import platform
|
|
import os
|
|
import re
|
|
import sys
|
|
import subprocess
|
|
|
|
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 = subprocess.check_output("xcode-select --print-path", shell=True)
|
|
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 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(str, file=result)
|
|
if show_usage:
|
|
print(create_memfind_options().format_help(), file=result)
|
|
|
|
|
|
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('error: no data specified to search for', file=result)
|
|
return
|
|
|
|
if not target:
|
|
print('error: invalid target', file=result)
|
|
return
|
|
process = target.process
|
|
if not process:
|
|
print('error: invalid process', file=result)
|
|
return
|
|
|
|
error = lldb.SBError()
|
|
bytes = process.ReadMemory(start_addr, options.size, error)
|
|
if error.Success():
|
|
num_matches = 0
|
|
print("Searching memory range [%#x - %#x) for" % (
|
|
start_addr, end_addr), end=' ', file=result)
|
|
for byte in options.data:
|
|
print('%2.2x' % ord(byte), end=' ', file=result)
|
|
print(file=result)
|
|
|
|
match_index = string.find(bytes, options.data)
|
|
while match_index != -1:
|
|
num_matches = num_matches + 1
|
|
print('%#x: %#x + %u' % (start_addr +
|
|
match_index, start_addr, match_index), file=result)
|
|
match_index = string.find(bytes, options.data, match_index + 1)
|
|
|
|
if num_matches == 0:
|
|
print("error: no matches found", file=result)
|
|
else:
|
|
print('error: %s' % (error.GetCString()), file=result)
|
|
|
|
|
|
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')
|