{ "cells": [ { "cell_type": "markdown", "id": "79738fb2-444b-424c-8a8c-17c483aa422c", "metadata": { "tags": [] }, "source": [ "# Loaders\n", "\n", "Loaders are the objects enabling loading a given binary intro TritonDSE memory so that it can get symbolically executed.\n", "In essence, they map the program in memory and to initialize registers.\n", "\n", "## Linux ELF loader\n", "\n", "TritonDSE primarly supports userland Linux programs and provides a basic ELF file loader based on LIEF.\n", "It will only loads the main binary but not other shared libraries." ] }, { "cell_type": "code", "execution_count": 4, "id": "d76e7165-8551-485a-8bca-971dade9a07e", "metadata": { "tags": [] }, "outputs": [ { "data": { "text/plain": [ "[Map(start=4194304, size=2036, perm=, name='seg2'),\n", " Map(start=6295056, size=576, perm=, name='seg3'),\n", " Map(start=251662336, size=4096, perm=, name='[extern]'),\n", " Map(start=1879048192, size=2147483648, perm=, name='[stack]')]" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from tritondse import ProcessState, Program\n", "\n", "p = Program(\"crackme_xor\")\n", "\n", "ps = ProcessState.from_loader(p)\n", "\n", "list(ps.memory.get_maps())" ] }, { "cell_type": "markdown", "id": "82a9fef3-4243-4870-bb17-5521b97c0479", "metadata": {}, "source": [ "The loader maps the ``LOAD`` segment of the ELF file and creates two additional segments for imported functions and for the stack." ] }, { "cell_type": "markdown", "id": "f053deb8-d9ee-4a1e-b0ac-77d2140d6e82", "metadata": {}, "source": [ "## CLE Loader\n", "\n", "To enable supporting more file format and shared libraries TritonDSE uses the infamous [cle project](https://github.com/angr/cle)\n", "from the angr symbolic executor." ] }, { "cell_type": "code", "execution_count": 5, "id": "82ad1fdf-a62c-46f4-96e9-b4125b37d1bb", "metadata": { "tags": [] }, "outputs": [ { "data": { "text/plain": [ "[Map(start=0, size=8192, perm=, name='[fs]'),\n", " Map(start=4194304, size=2036, perm=, name='seg-crackme_xor'),\n", " Map(start=6295056, size=496, perm=, name='seg-crackme_xor'),\n", " Map(start=6295552, size=80, perm=, name='seg-crackme_xor'),\n", " Map(start=7340032, size=152376, perm=, name='seg-libc.so.6'),\n", " Map(start=7495680, size=1395900, perm=, name='seg-libc.so.6'),\n", " Map(start=8892416, size=338734, perm=, name='seg-libc.so.6'),\n", " Map(start=9234640, size=14128, perm=, name='seg-libc.so.6'),\n", " Map(start=9248768, size=61264, perm=, name='seg-libc.so.6'),\n", " Map(start=9437184, size=3464, perm=, name='seg-ld-linux-x86-64.so.2'),\n", " Map(start=9441280, size=151249, perm=, name='seg-ld-linux-x86-64.so.2'),\n", " Map(start=9592832, size=39932, perm=, name='seg-ld-linux-x86-64.so.2'),\n", " Map(start=9636320, size=5664, perm=, name='seg-ld-linux-x86-64.so.2'),\n", " Map(start=9641984, size=4824, perm=, name='seg-ld-linux-x86-64.so.2'),\n", " Map(start=251662336, size=4096, perm=, name='[extern]'),\n", " Map(start=1879048192, size=2147483649, perm=, name='[stack]')]" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from tritondse import ProcessState, CleLoader\n", "\n", "p = CleLoader(\"crackme_xor\")\n", "\n", "ps = ProcessState.from_loader(p)\n", "\n", "list(ps.memory.get_maps())" ] }, { "cell_type": "markdown", "id": "9fd30be3-19cd-4944-9638-9cf4bb240f1f", "metadata": {}, "source": [ "As expected it resolved all the required shared libraries and loaded them in the ``ProcessState`` memory." ] }, { "cell_type": "raw", "id": "f0abb663-e14c-4e9b-9761-3c387c855c0c", "metadata": {}, "source": [ "
\n", "

Warning

\n", "

Using CLE requires emulating binaries using the same architecture than the host.

\n", "
" ] }, { "cell_type": "raw", "id": "8b87d13b-fd5e-438d-8169-00813000df73", "metadata": {}, "source": [ "
\n", "

Warning

\n", "

While CLE can theoretically load MachO or PE binary they have not been loaded.

\n", "
" ] }, { "cell_type": "markdown", "id": "817b376e-2bce-463b-a8b1-0112d8a34b2a", "metadata": { "tags": [] }, "source": [ "## Firmware Loading\n", "\n", "Performing symbolic execution on low-level firmware requires a specific loader.\n", "TritonDSE provides the ``MonolithicLoader`` that enables loading monolithic firmware by defining the memory segments manually.\n", "\n", "The following example shows how to load a small firmware:" ] }, { "cell_type": "code", "execution_count": null, "id": "10bfa085-40d2-4b3b-8077-12f2142b9d04", "metadata": {}, "outputs": [], "source": [ "from tritondse import Architecture, MonolithicLoader, LoadableSegment\n", "\n", "BASE_ADDRESS= 0x8000000\n", "ENTRY_POINT = 0x81dc46e\n", "STACK_ADDR = 0x1000000\n", "STACK_SIZE = 1024*6\n", "\n", "raw_f = Path(\"./bugged_json_parser.bin\").read_bytes()\n", "\n", "ldr = MonolithicLoader(Architecture.ARM32,\n", " cpustate = {\"pc\": ENTRY_POINT, \n", " \"sp\": STACK_ADDR+STACK_SIZE},\n", " set_thumb=True,\n", " maps = [LoadableSegment(BASE_ADDRESS, len(raw_f), Perm.R|Perm.X, raw_f, name=\"bugged_json_parser\"),\n", " LoadableSegment(STACK_ADDR, STACK_SIZE, Perm.R|Perm.W, name=\"[stack]\")])\n" ] }, { "cell_type": "markdown", "id": "65da0631-89c5-4c05-919f-41bc8bc421dc", "metadata": {}, "source": [ "In this example we define two memory segments, one for the firmware itself, and one for an arbitrary stack. We also adjust `pc` and `sp` to point respectively to the entry point\n", "and the base of the stack." ] }, { "cell_type": "markdown", "id": "4a213cfd-196b-4940-bc56-17ce195c5362", "metadata": {}, "source": [ "## Writing a Loader\n", "\n", "If none of the available loaders are available for the program to emulate, one can define its own loader.\n", "It has to inherit ``Loader`` and have to implement all methods of this class. The class to inherit have\n", "the following interface:" ] }, { "cell_type": "code", "execution_count": null, "id": "c530fc08-975b-4bdd-ba83-7631ded089e5", "metadata": {}, "outputs": [], "source": [ "class Loader(object):\n", " def __init__(self, path: str):\n", " self.bin_path = Path(path)\n", "\n", " @property\n", " def name(self) -> str:\n", " raise NotImplementedError()\n", "\n", " @property\n", " def entry_point(self) -> Addr:\n", " raise NotImplementedError()\n", "\n", " @property\n", " def architecture(self) -> Architecture:\n", " raise NotImplementedError()\n", "\n", " @property\n", " def arch_mode(self) -> Optional[ArchMode]:\n", " return None\n", "\n", " @property\n", " def platform(self) -> Optional[Platform]:\n", " return None\n", "\n", " def memory_segments(self) -> Generator[LoadableSegment, None, None]:\n", " raise NotImplementedError()\n", "\n", " @property\n", " def cpustate(self) -> Dict[str, int]:\n", " return {}\n", "\n", "\n", " def imported_functions_relocations(self) -> Generator[Tuple[str, Addr], None, None]:\n", " yield from ()\n", "\n", " def imported_variable_symbols_relocations(self) -> Generator[Tuple[str, Addr], None, None]:\n", " yield from ()\n", "\n", " def find_function_addr(self, name: str) -> Optional[Addr]:\n", " return None" ] }, { "cell_type": "markdown", "id": "4a23451f-9347-4681-ae0e-a48bbcc0d3b8", "metadata": {}, "source": [ "Function ``find_function_addr`` is used to attach a callback using the name of the function. As such, the loader\n", "has to provide a function to resolve a function name to its address." ] } ], "metadata": { "kernelspec": { "display_name": "pastis-env", "language": "python", "name": "pastis-env" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.9" } }, "nbformat": 4, "nbformat_minor": 5 }