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 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]
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
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:
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!