about summary refs log tree commit diff stats
path: root/gen_trace.py
blob: ec5cb86b6d08c639bda310070e4a4e5f00e1f1a2 (plain) (blame)
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()