Triton Dynamic Binary Analysis Library

repository·master·Indexed 26 days ago

https://github.com/jonathansalwan/triton

A dynamic binary analysis library for building program analysis tools, automating reverse engineering, software verification, and code emulation. It supports multiple ISAs including x86, x86-64, ARM32, AArch64, and RISC-V. Triton provides a Python API for dynamic symbolic execution, allowing users to symbolize registers, execute instructions, solve constraints via SMT solvers, and lift path predicates to LLVM IR.

Tokens
2.4K
Snippets
8
Records
12
Agent score
38%

What's inside Triton

  1. Explore tools built with Triton

    master

    Triton is used as a foundation for several specialized security and reverse-engineering tools. If you are looking for specific implementations of concolic execution, deobfuscation, or binary analysis, consider these projects:

    • Exrop: Automatic ROPChain Generation.
    • Pimp: An R2 plugin for concolic execution and total control.
    • Ponce: An IDA 2016 plugin for one-click symbolic execution.
    • QSynthesis: A greybox synthesizer for deobfuscating assembly instructions.
    • TritonDSE: A Dynamic Symbolic Execution (DSE) library with loading and exploration capabilities.
    • Titan: A VMProtect devirtualizer.
  2. Install Triton from source on Linux and MacOS

    master

    To build from source, clone the repository and use cmake. To enable full functionality including LLVM and Bitwuzla support, use the specific CMake flags provided below.

    # Standard build
    $ git clone https://github.com/JonathanSalwan/Triton
    $ cd Triton
    $ mkdir build ; cd build
    $ cmake ..
    $ make -j3
    $ sudo make install
    
    # Build with LLVM and Bitwuzla support
    $ cmake -DLLVM_INTERFACE=ON -DCMAKE_PREFIX_PATH=$(llvm-config --prefix) -DBITWUZLA_INTERFACE=ON ..
  3. Speed up Triton emulation using GDB fulldump

    master

    Emulating a large binary from the entry point can be extremely slow (e.g., taking hours for millions of instructions). To speed up analysis, you can skip the initial unpacking/initialization phase by using a fulldump.

    1. Use a hardware watchpoint or breakpoint in GDB to stop the execution at an interesting point (e.g., right before a getchar call or a specific function).
    2. Use the fulldump command (provided by the Triton GDB integration) to save the entire register state and memory segments at that specific moment.
    3. Initialize Triton using this dump file to start emulation directly from the captured state, significantly reducing execution time.
    # 1. Set a breakpoint at the target address
    $ gdb ./MarsAnalytica
    gdb-peda$ b *0x4030a4
    
    # 2. Run until the breakpoint is hit
    gdb-peda$ c
    Breakpoint 2, 0x00000000004030a4 in ?? ()
    
    # 3. Create the fulldump
    gdb-peda$ fulldump
    Full dump saved into fulldump.dump
  4. Install Triton using vcpkg

    master

    Triton is available via the vcpkg dependency manager.

    $ git clone https://github.com/Microsoft/vcpkg.git
    $ cd vcpkg
    $ ./bootstrap-vcpkg.sh  # ./bootstrap-vcpkg.bat for Windows
    $ ./vcpkg integrate install
    $ ./vcpkg install triton
  5. Quick start with Triton Python API

    master

    Triton allows you to perform dynamic symbolic execution by creating a TritonContext for a specific architecture. You can set concrete register values, symbolize registers, execute instructions, and solve constraints using an SMT solver interface.

    from triton import *
    
    # Create the Triton context with a defined architecture
    ctx = TritonContext(ARCH.X86_64)
    
    # Define concrete values (optional)
    ctx.setConcreteRegisterValue(ctx.registers.rip, 0x40000)
    
    # Symbolize data (optional)
    ctx.symbolizeRegister(ctx.registers.rax, 'my_rax')
    
    # Execute instructions
    ctx.processing(Instruction(b"\x48\x35\x34\x12\x00\x00")) # xor rax, 0x1234
    ctx.processing(Instruction(b"\x48\x89\xc1")) # mov rcx, rax
    
    # Get the symbolic expression
    rcx_expr = ctx.getSymbolicRegister(ctx.registers.rcx)
    print(rcx_expr)
    
    # Solve constraint
    ctx.getModel(rcx_expr.getAst() == 0xdead)
  6. Install Triton on Windows

    master

    On Windows, you can use cmake to generate a Visual Studio solution or use setup.py to build a debug version of triton.pyd. Alternatively, precompiled binaries are available via AppVeyor artefacts (requires Visual C++ Redistributable for Visual Studio 2012).

    # Using CMake for Visual Studio
    > git clone https://github.com/JonathanSalwan/Triton.git
    > cd Triton
    > mkdir build
    > cd build
    > cmake -G "Visual Studio 14 2015 Win64" \
      -DBOOST_ROOT="C:/Users/jonathan/Works/Tools/boost_1_61_0" \
      -DPYTHON_INCLUDE_DIRS="C:/Python36/include" \
      -DPYTHON_LIBRARIES="C:/Python36/libs/python36.lib" \
      -DZ3_INCLUDE_DIRS="C:/Users/jonathan/Works/Tools/z3-4.6.0-x64-win/include" \
      -DZ3_LIBRARIES="C:/Users/jonathan/Works/Tools/z3-4.6.0-x64-win/bin/libz3.lib" \
      -DCAPSTONE_INCLUDE_DIRS="C:/Users/jonathan/Works/Tools/capstone-5.0.1-win64/include" \
      -DCAPSTONE_LIBRARIES="C:/Users/jonathan/Works/Tools/capstone-5.0.1-win64/capstone.lib" ..
  7. Unpack a packed binary using GDB memory dump

    master

    If a binary is packed (e.g., it shows no strings or has dynamic jumps in IDA), you can unpack it by dumping the process memory while it is running and waiting for input.

    1. Run the binary in gdb.
    2. When the binary reaches the input prompt (e.g., Citizen Access ID:), interrupt it with Ctrl-C.
    3. Identify the memory maps using cat /proc/<PID>/maps (where <PID> is the process ID obtained via getpid).
    4. Use the dump memory command in GDB to save the specific memory range to a file.
  8. Symbolize user input and solve constraints in emulation

    master

    When performing binary emulation, you can hijack specific libc routines (like getchar) to symbolize their return values. This allows you to treat user input as symbolic variables.

    To optimize the analysis of large binaries or virtual machines:

    1. Symbolize Input: During emulation, when a target function like getchar is called, symbolize its return value.
    2. Filter Instructions: Generate a sub-trace by printing only instructions that contain symbolic variables. This significantly reduces the instruction count by focusing only on code paths linked to the input.
    3. Constraint Solving: Use Triton's symbolic engine to apply constraints to flags (e.g., setting zf to 1 for conditional jumps) to explore specific execution paths.
  9. Fix Python library detection on MacOS M1

    master

    If you encounter Could NOT find PythonLibs errors on MacOS M1, manually specify the Python paths in your cmake command. You can find the required paths using sysconfig.get_paths() in Python.

    cmake -DCMAKE_INSTALL_PREFIX=/opt/homebrew/ \
          -DPYTHON_EXECUTABLE=/opt/homebrew/bin/python3 \
          -DPYTHON_LIBRARIES=/opt/homebrew/Cellar/python@3.10/3.10.8/Frameworks/Python.framework/Versions/3.10/lib/libpython3.10.dylib \
          -DPYTHON_INCLUDE_DIRS=/opt/homebrew/opt/python@3.10/Frameworks/Python.framework/Versions/3.10/include/python3.10/ \
          ..
  10. Lift path predicates to LLVM IR

    master

    You can reconstruct the logic of a binary by lifting the path predicate generated during emulation into LLVM Intermediate Representation (IR). This is useful for recovering the underlying mathematical constraints or source-like logic of a program.

    Use ctx.getPathPredicate() to retrieve the current path predicate and ctx.liftToLLVM() to convert it into an LLVM module.

    def lifting2llvm(ctx):
       predicate = ctx.getPathPredicate()
       M = ctx.liftToLLVM(predicate, fname="mars_analytica", optimize=True)
       print(M)
       return