/advent

Day 08: SVD Files (OK)

Welcome back to the Advent Of Radare!

As we progress through the Radare2 Advent Calendar, today’s topic delves into leveraging SVD files within Radare2 using the r2svd plugin.

SVD (System View Description) files are XML-based representations of microcontroller peripherals and their memory mappings, commonly provided by microcontroller manufacturers (like for example ARM Cortex-M-based SoCs). These files contain detailed information with name, description and addresses of every bit about peripherals, registers, and fields, which can be incredibly useful for reverse engineering embedded firmware to understand where serial ports are located, where analog and digital pins are used, etc.

Today, we will explore how to to load those files into Radare2, interpret the results, and understand the process.

What’s an the SVD?

As we briefly explained above, SVD files help reverse engineers understand the layout of peripherals and registers, saving hours of manual analysis. By loading an SVD file into Radare2:

An SVD file describes a microcontroller’s memory-mapped peripherals, detailing:

There are different places we can look at to find those files, but sadly it’s not unified and many of them don’t follow the standard structure, so it’s probable that we may find some inconsistencies when loading them.

Luckily this situation is improving over time and with the help of the community they are easier to spot and use.

Installing The Plugin

In radare2 r2svd is a plugin available through the Radare2 Package Manager (r2pm). Follow these steps to set it up:

Ensure the r2pm package database is updated by running r2pm -U and then run the following line to install the package:

r2pm -ci r2svd

This installs the plugin in Radare2’s binary directory (also known as R2PM_BINDIR (see r2pm -H for more details))

To ensure the plugin is correctly installed, run the tool without any arguments thru r2pm to displays the plugin’s help message.

$ r2pm -r r2svd
Usage: .!r2svd [mcu] [svd]

Running R2SVD

This tool is designed to be executed with an svd fie as argument, for this example we will be taking this project as example:

Use the .! command in Radare2 to invoke external commands. To load an SVD file (e.g., STM8S003F3.svd), you can execute:

[0x00000000]> .!r2svd ./control/STM8S003F3.svd

Alternatively, you can use the following shell command to generate a script to be loaded later into r2 without depending on having r2svd installed.

r2pm -r r2svd control/STM8S003F3.svd > script.r2

Understanding the Output

The r2svd command processes the SVD file, extracting relevant information about peripherals, registers, and bitfields. The generated output defines flags and comments for memory-mapped devices, making the reverse engineering process more structured. Here’s an example:

$ r2pm -r r2svd control/STM8S003F3.svd
'@0x53e0'CC ADC1
'f peripheral.ADC1 4 0x53e0
'@0x53e0'Cr 1 pfb 8b DBH
'f reg.ADC1.DB0RL 4 0x53e1
...

Breaking Down the Script:

Single Quote

The single quote tells the r2 command interpreter to ignore all the special characters and do not evaluate them. This hint calls a command interally using the r_core_cmd_call api instead of r_core_cmd.

Calling commands is faster and safer, because it won’t permit command execution and skipping all the command syntax parsing will reduce the time needed to load the script, for some cases this is really noticeable.

Flags

Flags are defined using the f command, which names memory locations for peripherals and registers:

'f peripheral.ADC1 4 0x53e0
'f reg.ADC1.DB0RL 4 0x53e1

This makes navigation and reference within Radare2 straightforward, as you can seek directly to a named flag.

Temporal Seek (@)

The @ in the script indicates a temporary seek, allowing the plugin to define comments or attributes without permanently changing the current offset.

Formatted Bitfields (pfb)

The pfb command describes the bitfield layout for specific addresses, providing visual representation of individual fields:

'@0x53e0'Cr 1 pfb 8b DBH

Use the following help command in Radare2 to learn about the pfb syntax:

[0x00000000]> pfb?
Usage: pfb  print formatted bitfields
| pfb [fmt] [fnames]   print formatted bitfield in ascii art
| pfbc [fmt] [fnames]  same as pfb, but using C syntax
| pfbj [fmt] [fnames]  same as pfb but in json output
| pfbq [fmt] [fnames]  same as pfb, but quieter oneliner
| pfbd [fmt] [fnames]  same as pfb, but for debugging reasons
Examples:
|  pfb 3b4b foo bar    2 bitfields, first of 3 bits and second of 4
|  pfb 3b+4b foo bar   same as above, the + sign is ignored
|  pfb 3b..4b foo bar  same as above, but separated by 2 unused bits
|  pfb 3b2.4b foo bar  same as above, you can use digits and dot
[0x00000000]>

This is how that will look after binding the pfb command to an address with the Cr command like it’s shown below:

[0x00005400]> pd 1
;-- reg.ADC1.CSR:
0x00005400 ; (Cr 1 pfb 4b1b1b1b1b CH AWDIE EOCIE AWD EOC)
00000000     0x00000000
\__/VVVV
  | |||`-----      EOC =    0o     0   0x00 @ 7 + 1
  | ||`------      AWD =    0o     0   0x00 @ 6 + 1
  | |`-------    EOCIE =    0o     0   0x00 @ 5 + 1
  | `--------    AWDIE =    0o     0   0x00 @ 4 + 1
  `----------       CH =    0o     0   0x00 @ 0 + 4 ; ADC1.CSR: ADC control/status register
[0x00005400]>

Memory Maps

One of the main problems when analyzing firmwares is to identify where the NAND, RAM, ROM and PERIPHERALS are mapped, and create a proper memory layout for them. Some of this information is available in the SVD files, other can be extracted by analysing the code inside the boot code of the dumped image.

Let’s get some insights with r2svd and we will cover the memory layout in detail in a future blog post!

If you are curious about that, feel free to checkout the challenge scripts and the help message and usage for the om commands.

Let’s run grep and filter the output to find out where the peripherals are mapped.

$ r2pm -r r2svd control/STM8S003F3.svd | grep peripheral
'f peripheral.ADC1 4 0x53e0
'f peripheral.AWU 4 0x50f0
'f peripheral.BEEP 4 0x50f3
'f peripheral.CLK 4 0x50c0
'f peripheral.CPU 4 0x7f00
'f peripheral.DM 4 0x7f90
'f peripheral.FLASH 4 0x505a
'f peripheral.I2C 4 0x5210
'f peripheral.ITC 4 0x50a0
'f peripheral.IWDG 4 0x50e0
'f peripheral.OPT 4 0x4800
'f peripheral.PORTA 4 0x5000
'f peripheral.PORTB 4 0x5005
'f peripheral.PORTC 4 0x500a
'f peripheral.PORTD 4 0x500f
'f peripheral.PORTE 4 0x5014
'f peripheral.PORTF 4 0x5019
'f peripheral.SPI 4 0x5200
'f peripheral.SWIM 4 0x7f80
'f peripheral.TIM1 4 0x5250
'f peripheral.TIM2 4 0x5300
'f peripheral.TIM4 4 0x5340
'f peripheral.UART1 4 0x5230
'f peripheral.WWDG 4 0x50d1

As we can see the range seems to go from 0x5000 and 0x8000.

Is this all the info we can extract from the SVD file? totally not! r2svd can be improved to retrive architecture configuration details, if you are interested in contributing to radare2, this is also another good pick, because the architecture, regitser bit size and endian information can be exposed into r2 with '-e commands, or we can also expose all this details that are probably not relevant for radare2, but for the analyst by generating some '?e/'echo commands printing the details about fpu/revision/icache/.. that will be shown on startup time if needed.

$ head -n 30 control/STM8S003F3.svd
<?xml version="1.0" encoding="utf-8"?>
<device xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" schemaVersion="1.1" xs:noNamespaceSchemaLocation="CMSIS-SVD.xsd">
  <vendor>STMicroelectronics</vendor>
  <name>STM8S003F3</name>
  <version>1.0</version>
  <series>STM8S/STM8AF, low density</series>
  <description>Mainstream Value line 8-bit MCU with 8 Kbytes Flash, 16 MHz CPU, integrated EEPROM </description>
  <cpu>
    <name>other</name>
    <revision>r1p0</revision>
    <endian>big</endian>
    <mpuPresent>false</mpuPresent>
    <fpuPresent>false</fpuPresent>
    <fpuDP>false</fpuDP>
    <dspPresent>false</dspPresent>
    <icachePresent>false</icachePresent>
    <dcachePresent>false</dcachePresent>
    <itcmPresent>false</itcmPresent>
    <dtcmPresent>false</dtcmPresent>
    <vtorPresent>false</vtorPresent>
    <nvicPrioBits>4</nvicPrioBits>
  </cpu>
  <size>8</size>
  <width>8</width>
  <addressUnitBits>8</addressUnitBits>
  <access>read-write</access>
...

If you want to contribute to r2svd here there are some links:

Challenge

The challenge for today will be to pick your favourite IOT device, a lightbulb, smartplug, or even a cheap smartwatch. pull the firmware files from the iOS/Android app or their support website and load it into radare2.

You may want to do some basic research to find out the cpu model used there and the svd file and load all the metadata to get a comprehensive disassembly.

Reference Links:

Final words

The r2svd plugin is a powerful addition to Radare2 for embedded systems analysis, automating the tedious task of manually mapping peripherals and registers. By combining SVD files with Radare2’s extensive capabilities, reverse engineers can streamline their workflows and focus on deeper analysis. Whether you’re working on firmware reverse engineering or debugging, r2svd is a tool worth mastering.

Start exploring, and happy reversing!