Skip to main content

Generic Unicorn tracer for side-channels and fault injection

Project description

Join the chat at https://gitter.im/Ledger-Donjon/rainbow

Rainbow

It makes unicorn traces.

Using Unicorn as a basis, Rainbow aims to provide an easy scripting interface to loosely emulate embedded binaries, trace them to perform side-channels, and simulate fault injections.

This is to allow quick and easy testing of physical attack resistance of code snippets, in order to help developers have a first evaluation of the resistance of their code.

An introduction is available here.

A blogpost demonstrating how to turn this tool into an automatic fault injection test pipeline is here, with the corresponding Rust code here.

Installation

You will need Python 3.7 at least.

  • pip install .

If Unicorn or Capstone fails to install somehow:

For the side-channel examples, you need to the latest Lascar, the following command installs the necessary packages.

  • pip install .[examples]

If you wish to use another version of Python, you can drop an issue and we will look into it.

Running the examples

Some examples will use Lascar's side-channel attacks and try to display traces using a custom plotter (visplot) built on top of Vispy. If you want to run those, you will need Vispy and pyqt5 for the instruction trace + execution trace viewer.

In the ./examples/ folder, you will find:

Example output

See the x64_pimp_my_xor example for a debug trace.

In the comment part of each line (after the semicolon), the memory access that was performed is written in a simplified way: [address] <- value for a load or value -> [address] for a store. Right after, if any register was modified during this instruction, its new value is shown.

At a branch instruction, if the destination is a known function, its name is shown together with the return address and the function's address.

Basic usage

Grab a device or generic emulator like so:

from rainbow.devices import rainbow_stm32f215

e = rainbow_stm32f215()

Load a binary:

e.load('file', typ='.elf')
e.setup()

File type is guessed on the extension when possible (.elf, .hex).

Starting the emulation is done like so:

e.start(start_address, stop_address, count=number_of_instructions)

Just like with unicorn. The underlying Unicorn instance is always available as e.emu.

To enable printing as code gets executed, simply use the Print flag.

from rainbow import Print
import colorama

colorama.init()  # Only do this once to enable colors

e = rainbow_stm32f215(print=Print.Code | Print.Functions)  # see other values of the flag

Side-Channel simulation

Rainbow only produces an execution trace, without applying any processing (such as adding noise) on the values. This is left as some post-processing, so that the user can apply its own leakage model and simulate various conditions from the same traces. Also, not introducing any noise allows testing in a worst-case scenario, which can yield important results.

To perform the analysis, one can use Lascar. You can find some scripts in the examples folder here which already use it.

To setup tracing (to produce an execution trace) use the trace_config option to the emulator. The following piece of code sets up tracing of register using the Hamming weight leakage model.

from rainbow import TraceConfig, HammingWeight

e = rainbow_stm32f215(trace_config=TraceConfig(register=HammingWeight()))
e.load('file', typ='.elf')
e.setup()

e.start(start_address)

print(e.trace)
# [{"type": "code", "register": 7}, {"type": "code": "register": 5}]

If you setup tracing for mem_address, then the e.trace list will have dictionaries like {"type": "mem_read", "address": 1234} or {"type": "mem_write", "address": 1234} with the value of the address entry passed through the leakage model. Tracing for mem_value does the same, but traces memory values read or written and produces entries like {"type": "mem_read", "value": 1234}. Note that these approaches can be combined, resulting in the dictionary having both an address and value entries.

If you setup tracing for code, dissasembled instructions will be available in the trace with dictionaries like {"type": "code", "instruction": " 404 ldm.w r0, {r4, r5, r6, r7}"}. Note that this tracing option combined with register tracing produces a dictionary with both instruction and register entries.

Application examples

In the case of hardware wallets for example, one could check that:

  • The PIN verification procedure does not allow to use a bad password even with a controlled instruction skip
  • The scalar multiplication procedure does not leak any information on the used scalar
  • a purely software AES is protected against basic DPA attacks without using lab testing equipment (oscilloscope, current/EM probes, ...)

Rainbow and Lascar allow testing implemented countermeasures were correctly coded and the compiler did not interfere. It cannot, however, verify against hardware-related leaks such as some sequence of operations that somehow cancels out random masks on a bus or hidden register.

Bonus applications

Whiteboxed encryption primitives could also be broken using this tool, instead of e.g. Intel Pin or Valgrind to trace execution. Unicorn has several advantages in this regard:

  • Can be used on a different platform than that of the target binary
  • Allows easy manipulation of the state (for example redefining an external call to rand() in Python)

Disadvantages:

  • Some reverse engineering necessary !

As a whitebox example (available in examples/OAES, below is the result of the variance of SECCON 2016's OAES encryption function, which has a heavy control flow obfuscation. One can clearly see the 10 rounds of the AES despite this obfuscation:

OAES Variance

Supported archs

Embedded devices:

  • STM32F215
  • STM32l431

Generic emulators:

  • ARM
  • ARM Cortex M
  • x86
  • x86_64
  • M68K

File formats:

  • ELF
  • Intel Hex file
  • PE

Project details


Release history Release notifications | RSS feed

This version

2.0

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

donjon_rainbow-2.0.tar.gz (13.0 MB view details)

Uploaded Source

Built Distribution

donjon_rainbow-2.0-py3-none-any.whl (51.3 kB view details)

Uploaded Python 3

File details

Details for the file donjon_rainbow-2.0.tar.gz.

File metadata

  • Download URL: donjon_rainbow-2.0.tar.gz
  • Upload date:
  • Size: 13.0 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.1.0 CPython/3.12.3

File hashes

Hashes for donjon_rainbow-2.0.tar.gz
Algorithm Hash digest
SHA256 1f6aeb0ae26028bebd4f769527f50ead90b544ceec7a6e4b31ce620f5057bcea
MD5 22ada3f3b237935b8644b1f9a07592b1
BLAKE2b-256 d2ab3cd0c6ed3937c23650b122ef26cc1ba433c3f0d2a3d9ec9d5fdb5cf4ef05

See more details on using hashes here.

File details

Details for the file donjon_rainbow-2.0-py3-none-any.whl.

File metadata

  • Download URL: donjon_rainbow-2.0-py3-none-any.whl
  • Upload date:
  • Size: 51.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.1.0 CPython/3.12.3

File hashes

Hashes for donjon_rainbow-2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d641946d966e672b38e3a43c1b0b9bcd05c76572dd2c356382afde0d4670439b
MD5 aeb4eec3f0eb3c81cb4727c4967f8b10
BLAKE2b-256 0ce45c3e3604a1197103ae67bafa926d6c30550b96ed70fe630f834b5763086b

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page