1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
import argparse
import lldb
import lldb_target
def record_trace(binary: str,
args: list[str] = [],
func_name: str | None = 'main') -> list[int]:
"""
:param binary: The binary file to execute.
:param args: Arguments to the program. Should *not* include the
executable's location as the usual first argument.
:param func_name: Only record trace of a specific function.
"""
# Set up LLDB target
target = lldb_target.LLDBConcreteTarget(binary, args)
# Skip to first instruction in `main`
if func_name is not None:
result = lldb.SBCommandReturnObject()
break_at_func = f'b -b {func_name} -s {target.module.GetFileSpec().GetFilename()}'
target.interpreter.HandleCommand(break_at_func, result)
target.run()
# Run until main function is exited
trace = []
while not target.is_exited():
thread = target.process.GetThreadAtIndex(0)
# Break if the traced function is exited
if func_name is not None:
func_names = [thread.GetFrameAtIndex(i).GetFunctionName() \
for i in range(0, thread.GetNumFrames())]
if func_name not in func_names:
break
trace.append(target.read_register('pc'))
thread.StepInstruction(False)
return trace
def parse_args():
prog = argparse.ArgumentParser()
prog.add_argument('binary',
help='The executable to trace.')
prog.add_argument('-o', '--output',
default='breakpoints',
type=str,
help='File to which the recorded trace is written.')
prog.add_argument('--args',
default=[],
nargs='+',
help='Arguments to the executable.')
return prog.parse_args()
def main():
args = parse_args()
trace = record_trace(args.binary, args.args)
with open(args.output, 'w') as file:
for addr in trace:
print(hex(addr), file=file)
print(f'Generated a trace of {len(trace)} instructions.')
if __name__ == '__main__':
main()
|