Skip to content

cxiao/rust_string_slicer

Repository files navigation

Rust String Slicer

A Binary Ninja plugin to help find the addresses and lengths of strings in Rust binaries.

A screenshot of Binary Ninja with several structs of type "&str" defined, each of which contains the address and length of a string.

Usage

This plugin provides two new commands:

  • Plugins > Rust String Slicer > Recover String Slices from Readonly Data
  • Plugins > Rust String Slicer > Recover String Slices from Code

A screenshot of two entries in the Binary Ninja menu, both under Plugins > Rust String Slicer: Recover String Slices from Readonly Data, and Recover String Slices from Code

You can view the list of recovered strings and their addresses in the Log window.

The Binary Ninja log window, showing log messages under the "rust_string_slicer.binja_plugin.actions" log category. The messages include both new definitions of the string slice type at certain locations ('Defined new &str at 0x1401c6b38" )and the addresses and lengths of the recovered strings themselves ('Recovered string at addr 0x1401c6a09, len 0xb: 'src\main.rs')

The plugin will also create a new &str type, for any strings defined in read-only data sections that are made up of a pointer to string data + the length of that string data. &str is a Rust primitive type called a "string slice", and is the type used in Rust for string literals (docs).

You can view all created strings of this type by examining cross-references to the &str type.

The Binary Ninja Types and Cross References window, showing references to the &str type, which has fields char* address and int64_t length. Cross references include variables with names like str"C:\Users\User.cargo\registry\src" and str"Impossible: must only have 0 to 8 input bytes in last chunk, with no invalid lengths"

How does this work?

The original motivation for this plugin was to recreate the string slicing functionality in the tech preview of the official IDA Rust Analysis Plugin from Hex-Rays. That plugin is able to find the lengths of Rust strings, which are not null terminated, via some heuristics for finding the string length data.

The heuristics we use are the following:

  • Enumerate all addresses in the binary's read-only data section which have cross-references.
  • Treating each of those addresses as the beginning of a potential string literal.
  • Follow those cross references to see if there is some information around the site of that cross reference about string length.

The Recover String Slices from Readonly Data command uses cross references which point to a data section. For each of these cross references, try to interpret the cross referenced address as a structure like the following, with some heuristics to determine whether the found length_of_string_literal is reasonable:

QWORD address_of_candidate_string_literal
QWORD length_of_string_literal

The Recover String Slices from Code command uses cross references which point to a code section. For each of these cross references, check to see if the subsequent instructions contain a move of an immediate value to a memory location; the immediate value is then taken as the string length. For example, for x64 binaries, look for instructions like this:

lea rax, <address_of_candidate_string_literal>
mov [rsp+<string_var>.<string_pointer_field>], rax
mov [rsp+<string_var>.<string_length_field>], 15

Development

Setting up a development environment

To set up a development environment, including setting up a Python virtual environment:

python -m venv .venv && . .venv/bin/activate
python -m pip install -r requirements.txt
python -m pip install -r dev-requirements.txt
python $PATH_TO_BINARY_NINJA_INSTALLATION/scripts/install_api.py

For formatting, linting, and running unit tests locally, install Nox, then:

nox

You can also invoke each task separately; see noxfile.py for more details on available tasks:

nox -s format
nox -s lint
nox -s test

Linting and unit testing (both against multiple Python versions) are also set up in CI on GitHub Actions.

Testing local versions of the plugin

To test the plugin locally in your own Binary Ninja installation during development, create a symbolic link between your development folder, and the Binary Ninja user plugins folder, so that your development folder is loaded by Binary Ninja on startup as a plugin.

  • MacOS:

    ln -s `pwd` ~/Library/Application\ Support/Binary\ Ninja/plugins/rust_string_slicer
  • Linux:

    ln -s --relative . ~/.binaryninja/plugins/rust_string_slicer
  • Windows (Powershell):

    New-Item -ItemType Junction -Value $(Get-Location) -Path "$env:APPDATA\Binary Ninja\plugins\rust_string_slicer

You should then change the values of the following Python settings in Binary Ninja to point to inside your development folder's virtual environment:

  • python.binaryOverride: Set this to the path of the Python interpreter inside your development virtual environment, e.g. $DEVELOPMENT_FOLDER/rust_string_slicer/.venv/bin/python/
  • python.virtualenv: Set this to the path of the site-packages directory inside your development virtual environment, e.g. $DEVELOPMENT_FOLDER/rust_string_slicer/.venv/lib/python3.11/site-packages

About

A Binary Ninja plugin to help find the addresses and lengths of strings in Rust binaries.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •  

Languages