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.
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.
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 r2svdThis 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]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.r2The 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:
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 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.
@)The @ in the script indicates a temporary seek, allowing
the plugin to define comments or attributes without permanently changing
the current offset.
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]>
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:
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:
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!