patat | ||
---|---|---|
|
- NOT a 0day.
- OpenXC is working as-designed
- This is a chance to reverse a soft target
- ... and to do it with
r2
If you haven't yet: please build and install r2 from git now : git clone https://github.com/radare/radare2; cd radare2 ; ./sys/install.sh. And also
pip install r2pipe
.
Download the target binary file at https://bit.ly/2ZtEJSH This is a 'FORDBOARD' vi-firmware device; here's the datasheet for a LPC1768 https://bit.ly/2XHemb5
- An intermediate
r2
course - A fast-forward through the 'hard parts' of RE
- An examination of how to use
r2
once you know the hard-won information - A very long exposition on the value of xrefs
If you haven't yet: please build and install r2 from git now : git clone https://github.com/radare/radare2; cd radare2 ; ./sys/install.sh. And also
pip install r2pipe
.
Download the target binary file at https://bit.ly/2ZtEJSH This is a 'FORDBOARD' vi-firmware device; here's the datasheet for a LPC1768 https://bit.ly/2XHemb5
I won't be teaching you (sorry):
- how to understand ARM and/or ARM Thumb mode
- how to reverse engineer code, in general
- how to build and install r2 from git
- r2 basics: seeking, flags, printing hex, printing disassembly
If you still want to stick in the workshop and join us anyways we can BOTH (you and I) do our best to accommodate.
If you haven't yet: please build and install r2 from git now : git clone https://github.com/radare/radare2; cd radare2 ; ./sys/install.sh. And also
pip install r2pipe
.
Download the target binary file at https://bit.ly/2ZtEJSH This is a 'FORDBOARD' vi-firmware device; here's the datasheet for a LPC1768 https://bit.ly/2XHemb5
- Rapid Review:
.dbc
, OpenXC,r2
- Load raw firmware (FW) into
r2
- (eventually) Pretty-print data structures from the FW
If you haven't yet: please build and install r2 from git now : git clone https://github.com/radare/radare2; cd radare2 ; ./sys/install.sh. And also
pip install r2pipe
.
Download the target binary file at https://bit.ly/2ZtEJSH This is a 'FORDBOARD' vi-firmware device; here's the datasheet for a LPC1768 https://bit.ly/2XHemb5
Those of you who are ready: What is the CPU arch of this target binary?
CAN messages commonly pack signals into bitfield locations of a frame ‘type’ (Arbitration ID) Encoding time-varying signals by changing those bitfield contents over time
DBC files describe the mapping of signals to ArbId and bitfield (over simplied)
If you haven't yet: please build and install r2 from git now : git clone https://github.com/radare/radare2; cd radare2 ; ./sys/install.sh. And also
pip install r2pipe
.
Download the target binary file at https://bit.ly/2ZtEJSH This is a 'FORDBOARD' vi-firmware device; here's the datasheet for a LPC1768 https://bit.ly/2XHemb5
A platform for consuming vehicle data from CAN. Provides Reference Applications, and also reference HW.
An actively developed platform:
- OpenXC now supports iOS app development along with Android and Python. http://openxcplatform.com/iOS/getting-started.html
- OpenXC has integrations for Bug Labs Dweet and Freeboard products. https://openxc.dweet.io/.
If you haven't yet: please build and install r2 from git now : git clone https://github.com/radare/radare2; cd radare2 ; ./sys/install.sh. And also
pip install r2pipe
.
Download the target binary file at https://bit.ly/2ZtEJSH This is a 'FORDBOARD' vi-firmware device; here's the datasheet for a LPC1768 https://bit.ly/2XHemb5
- Moves messages in MessagePack (like JSON)
- i.e. the firmware contains a mapping from CAN signal to (descriptive) JSON
- so there are strings describing bitfield locations
If you haven't yet: please build and install r2 from git now : git clone https://github.com/radare/radare2; cd radare2 ; ./sys/install.sh. And also
pip install r2pipe
.
Download the target binary file at https://bit.ly/2ZtEJSH This is a 'FORDBOARD' vi-firmware device; here's the datasheet for a LPC1768 https://bit.ly/2XHemb5
Reverse engineering tool suite
- Our main focus in on the r2 tool of the suite
$ r2 ~/src/binary-samples/elf-Linux-ARMv7-ls
[0x0000c268]> aa
[x] Analyze all flags starting with sym. and entry0 (aa)
[0x0000c268]> s main
[0x00009eb8]> ax~main | head -n 4
main 0x9eb8 -> DATA -> 0xacfc case.default.0xab10+492
main+32 0x9ed8 -> CALL -> 0x14d78 entry1.init+35344
main+36 0x9edc -> DATA -> 0xadf4 case.default.0xab10+740
main+44 0x9ee4 -> CALL -> 0x9d7c sym.imp.setlocale
[0x00009eb8]> q
$ r2 -n vi-firmware-FORDBOARD.bin
[0x00000000]> aa
[x] Analyze all flags starting with sym. and entry0 (aa)
[0x00000000]> s main
Cannot seek to unknown address 'main'
[0x00000000]> ?E looks like you're going to reverse some FW. would you like some help?
.--. .-----------------------------------------------------------------------.
| _| | |
| O O < looks like you're going to reverse some FW. would you like some help? |
| | | | |
|| | / `-----------------------------------------------------------------------'
|`-'|
`---'
[0x00000000]>
? -- help postfix
s -- seek
ax -- list xrefs
axd p -- create data reference to <p>
aa -- recursively analyze exports/syms and entrypoints
aap -- find functions by searching for preludes
~ -- grep postfix
@ -- temporary seek postfix
~? -- count lines postfix
* -- postfix, echo in r2 commands
` -- subshell
( -- macros
@@ -- iterator
. -- source output as r2 commands
e -- set config
f -- set a flag (aka named address)
om -- setup a mapping of a file into virtual memory
?e -- echo
?v -- echo hex value
t -- list defined types
tk -- define type details
to -- load type defines from .
pf -- print formatted
pf.t -- define named format
px -- print (canonical) hexdump
pxw -- print word-wise hexdump
pxr -- print telescoping word references
pv4 -- print quadword value
pD n -- disassemble <n> bytes
- OpenXC translates CAN signals into API messages for connected apps
- The OpenXC FW contains the details to decode CAN contents into signals (this is not unique)
- The OpenXC API messages are in Message Pack (like JSON)...
- So, the CAN signal decoding has descriptive strings
If you haven't yet: please build and install r2 from git now : git clone https://github.com/radare/radare2; cd radare2 ; ./sys/install.sh. And also
pip install r2pipe
.
Download the target binary file at https://bit.ly/2ZtEJSH This is a 'FORDBOARD' vi-firmware device; here's the datasheet for a LPC1768 https://bit.ly/2XHemb5
Open XC device firmwares embedded JSON CAN signal descriptions:
"0x49": {
"name": "decoder_test_message",
"bus": "unfiltered_bus",
"signals": {
"TirePressure": {
"generic_name": "tire_pressure_front_left",
"decoder": "tirePressureDecoder",
"bit_position": 56,
"bit_size": 8
...
Into the firmware as structures
struct CanSignal {
struct CanMessageDefinition* message;
const char* genericName;
uint8_t bitPosition;
uint8_t bitSize;
float factor;
...
- "The Impacts of JSON on Reversing Your Firmware" -- SANS Webcast
- "OpenXC Reversing" -- bsmt's blog, http://bsmt.me/posts/openxc-reversing/
If you haven't yet: please build and install r2 from git now : git clone https://github.com/radare/radare2; cd radare2 ; ./sys/install.sh. And also
pip install r2pipe
.
Download the target binary file at https://bit.ly/2ZtEJSH This is a 'FORDBOARD' vi-firmware device; here's the datasheet for a LPC1768 https://bit.ly/2XHemb5
Many, many; but, in particular: @devttys0's classes and blog, http://www.devttys0.com/ -- covers a multiple cases of loading raw FW blobs
If you haven't yet: please build and install r2 from git now : git clone https://github.com/radare/radare2; cd radare2 ; ./sys/install.sh. And also
pip install r2pipe
.
Download the target binary file at https://bit.ly/2ZtEJSH This is a 'FORDBOARD' vi-firmware device; here's the datasheet for a LPC1768 https://bit.ly/2XHemb5
"Reverse Engineering Embedded ARM Devices" -- @trufae (pancake) https://www.youtube.com/watch?v=oXSx0Qo2Upk
If you haven't yet: please build and install r2 from git now : git clone https://github.com/radare/radare2; cd radare2 ; ./sys/install.sh. And also
pip install r2pipe
.
Download the target binary file at https://bit.ly/2ZtEJSH This is a 'FORDBOARD' vi-firmware device; here's the datasheet for a LPC1768 https://bit.ly/2XHemb5
vi-firmware project on github: https://github.com/openxc/vi-firmware/
- This is what we meant by soft target
- We can look at the firmware 'blob' and confirm things in the source and/or intermediates
- Or we can pretend we only have the blob (we'll do this)
If you haven't yet: please build and install r2 from git now : git clone https://github.com/radare/radare2; cd radare2 ; ./sys/install.sh. And also
pip install r2pipe
.
Download the target binary file at https://bit.ly/2ZtEJSH This is a 'FORDBOARD' vi-firmware device; here's the datasheet for a LPC1768 https://bit.ly/2XHemb5
- Load raw firmware (FW) into
r2
- (eventually) Pretty-print data structures from the FW
Following a general process of reversing a FW blob
- Get FW (skipped -- we'll use the open source build)
- Learn architecture
- Learn load address
- Load FW into r2 at load address
- Identify 'loader loops' and target addresses
- Load FW copies into r2 at target addresses
- Analyze (to get xrefs)
- (eventually) Pretty-print data structures from the FW
If you haven't yet: please build and install r2 from git now : git clone https://github.com/radare/radare2; cd radare2 ; ./sys/install.sh. And also
pip install r2pipe
.
Download the target binary file at https://bit.ly/2ZtEJSH This is a 'FORDBOARD' vi-firmware device; here's the datasheet for a LPC1768 https://bit.ly/2XHemb5
Different ways:
r2
way 1:p=i
to summarize the invalid instructions in an image; change arch to find minimumr2
way 2:pxAv
to print categories of instructions- RTFM way: read the datasheet
- tools way 1: https://github.com/airbus-seclab/cpu_rec
- tools way 2: binwalk arch test
- dumb way: strings (e.g. build settings strings)
- eyeball way (seriously, there are some archs you can recognize by looking at the hex)
- also don't forget this is an open source project, you can use the source.
$ python3 ~/src/cpu_rec/cpu_rec.py lib/vi-firmware-FORDBOARD.bin
lib/vi-firmware-FORDBOARD.bin
full(0x27f00) ARMhf
chunk(0x1e000;60) ARMhf
- we knew from before this is ARM
- ARM can have different instruction coding; fixed-width 32bit and variable width THUMB, so-called '16 bit'
- block size should be small enough to 'see' the entire binary:
b $s / X
e asm.bits
matters alot ;e asm.cpu
less so
[0x00000000]> b $s / 3
[0x00000000]> p=i
0x00000000 00 00ff |''''''''''''''''''''''''''''''''''|
0x0000d500 01 00ff |..................................................|
0x0001aa00 02 00ff |.__________________________________________________|
[0x00000000]> e asm.arch=arm
[0x00000000]> p=i
0x00000000 00 00ff |''''''''''''''''''''''''''''''''''|
0x0000d500 01 00ff |..................................................|
0x0001aa00 02 00ff |.__________________________________________________|
[0x00000000]> e asm.bits=16
[0x00000000]> p=i
0x00000000 00 0054 |''''____________|
0x0000d500 01 0016 |....|______________________
0x0001aa00 02 0086 |.__________________________|
[0x00000000]> e asm.cpu=cortex
[0x00000000]> p=i
0x00000000 00 0055 |''''____________|
0x0000d500 01 0018 |....|_____________________
0x0001aa00 02 0086 |.__________________________|
[0x00000000]>
Different ways:
- Datasheet way 1: where is the executable non-volatile storage? Where does it map to?
- Datasheet way 2: where is the interrupt vector table (IVT) in memory? Do you see one in the bin?
- Guessing way 1: Do you see something that could be an IVT? Do the vectors point somewhere useful? Could they point somewhere useful by changing them all by a constant offset?
- Guessing way 2: Look for loader loops -- more on this later (we need it for .data and .rodata) setup
- Point-n-pray way: https://github.com/sgayou/rbasefind
- Also (don't forget): you can check the source code for the project -- kinda cheating though
Use one or more of the ways to get the load address of the bin.
Datasheet way 1: where is the executable non-volatile storage? Where does it map to?
Datasheet way 2: where is the interrupt vector table (IVT) in memory? Do you see one in the bin?
Guessing way 1: Do you see something that could be an IVT? Do the vectors point somewhere useful? Could they point somewhere useful by changing them all by a constant offset?
Guessing way 2: Look for loader loops -- more on this later (we need it for .data and .rodata) setup
Point-n-pray way: https://github.com/sgayou/rbasefind
Also (don't forget): you can check the source code for the project -- kinda cheating though
- lots of 0x00013XXX
- could be offset 0x13000
- rounder numbers are better though: 0x10000
$ r2 -n vi-firmware-FORDBOARD.bin
-- Well, it looks like its working.
[0x00000000]> pxw
0x00000000 0x10008000 0x00013821 0x0001381f 0x0001381f ....!8...8...8..
0x00000010 0x0001381f 0x0001381f 0x0001381f 0x00000000 .8...8...8......
0x00000020 0x00000000 0x00000000 0x00000000 0x0001381f .............8..
0x00000030 0x0001381f 0x00000000 0x0001381f 0x00019261 .8.......8..a...
0x00000040 0x0001381f 0x0001381f 0x0001aa1d 0x0001381f .8...8.......8..
0x00000050 0x0001381f 0x0001381f 0x00019429 0x0001381f .8...8..)....8..
0x00000060 0x0001381f 0x0001381f 0x0001381f 0x0001381f .8...8...8...8..
0x00000070 0x0001381f 0x0001381f 0x0001381f 0x0001381f .8...8...8...8..
0x00000080 0x0001381f 0x0001381f 0x0001381f 0x0001381f .8...8...8...8..
0x00000090 0x00019259 0x0001381f 0x0001381f 0x0001381f Y....8...8...8..
0x000000a0 0x0001a2ff 0x00018b55 0x0001381f 0x0001381f ....U....8...8..
0x000000b0 0x0001381f 0x0001381f 0x0001381f 0x0001381f .8...8...8...8..
0x000000c0 0x0001381f 0x0001381f 0x00019253 0x4c05b510 .8...8..S......L
0x000000d0 0xb9337823 0xb1134b04 0xf3af4804 0x23018000 #x3..K...H.....#
0x000000e0 0xbd107023 0x10005100 0x00000000 0x00032bd0 #p...Q.......+..
0x000000f0 0x4b06b508 0x4806b11b 0xf3af4906 0x48068000 ...K...H.I.....H
[0x00000000]>
- Works here; but it will take a long time
$ ~/src/rbasefind/target/debug/rbasefind vi-firmware-FORDBOARD.bin
Located 355 strings
Located 26728 pointers
Scanning with 8 threads...
0x00010000: 304
0x00011000: 20
0x0ffe3000: 16
0x0ffe2000: 16
0x00012000: 11
0x0000f000: 11
0x0ffe4000: 8
0xf7fd2000: 4
0xf7fd1000: 4
0xeffe2000: 4
1d [bengardiner:/mnt … entations/r2 OpenXC Workshop/lib] 7m33s $
We know the FW is ARM, THUMB and should be loaded at 0x10000
r2 -m 0x10000 -a arm -b 16 vi-firmware-FORDBOARD.bin
Eventually you'll need to script the setup; a file script is needed. This is equivalent to the above. Here's my stage1.r2
:
e asm.arch = arm
e asm.cpu = cortex
e asm.bits = 16
e bin.baddr = 0x00010000
f flash = 0x00010000
e io.va = true
on vi-firmware-FORDBOARD.bin flash r-x
?e ===
You can download my stage1.r2 here: https://tinyurl.com/yxonhpjt
We can search the flash section for functions by matching function preludes with aap
.
[0x10002d80]> aa?~ aap
| aap find and analyze function preludes
ARM uses multi-instruction loads of 'pointers' so emulation is required to pick up xrefs of code reffering to data. Use aae
.
[0x00000000]> aa?~ aae
| aae [len] ([addr]) analyze references with ESIL (optionally to address)
You can download my stage1.r2 here: https://tinyurl.com/yxonhpjt
download my stage1.r2 here: https://tinyurl.com/yxonhpjt if you haven't made your own
- We know there is an array of CAN signals in memory somewhere
- This array presumably makes references to strings that describe CAN signals. e.g. 'tire', 'speed', 'etc'
But only code making data references to the strings right now.
$ r2 -i stage1.r2 --
3
===
-- Thank you for using radare2. Have a nice night!
[0x00000000]> aap
[>] Scanning r-x 0x10000 - 0x37f00 done
Analyzed 459 functions based on preludes
[0x00000000]> aae
[0x00000000]> f~str~tire
0x000301e9 13 str.tire_pressure
0x00030242 25 str.tire_pressure_front_right
0x0003025c 23 str.tire_pressure_rear_left
0x00030274 24 str.tire_pressure_rear_right
0x0003028d 24 str.tire_pressure_front_left
[0x00000000]> axt @@= `f~str~tire`
fcn.00014f4c 0x14fce [DATA] ldr r0, str.tire_pressure
fcn.00014f4c 0x14fce [DATA] ldr r0, str.tire_pressure
fcn.00014f4c 0x14f98 [DATA] ldr r1, str.tire_pressure_front_right
fcn.00014f4c 0x14f98 [DATA] ldr r1, str.tire_pressure_front_right
fcn.00014f4c 0x14fa8 [DATA] ldr r1, str.tire_pressure_rear_left
fcn.00014f4c 0x14fa8 [DATA] ldr r1, str.tire_pressure_rear_left
fcn.00014f4c 0x14fb8 [DATA] ldr r1, str.tire_pressure_rear_right
fcn.00014f4c 0x14fb8 [DATA] ldr r1, str.tire_pressure_rear_right
fcn.00014f4c 0x14f86 [DATA] ldr r1, str.tire_pressure_front_left
fcn.00014f4c 0x14f86 [DATA] ldr r1, str.tire_pressure_front_left
- We have the right base address; but there are no data xrefs to strings.
- This is a bad sign; string cross-references are pretty important for disassembly
- The reason for this situation: the code references 'copies' of the strings in RAM
- The binary's 'writeable' data will live in RAM
- The initial values of these are copied from non-volatile (e.g. flash) to RAM
- e.g. C programs get this as part of the C common runtime libs; i.e. the compiler does it for the programmer
Different ways:
- Datasheet way: starting at the reset vector, follow function calls until you see a function that has 1 or two large copying of arrays followsd by a large zeroing of a region of memory
- NB: ARM Thumb mode calls are a branch/jump to address+1 (i.e. any odd address is a THUMB mode call to the that address-1)
- Guessing way: scan the binary for a function that has 1 or two large copying etc. -- start at the beginning and end of the binary.
- Also: this is an OSS project, you can use the source
The FW copies
0x____
to0x________
for a length of0x____
The loader loop will look kinda like this:
0x... push {r3, lr}
0x... ...
┌─> 0x... ldr r2, [...]
╎ 0x... ldr r1, [...]
╎ 0x... adds r0, r3, r2
╎ 0x... cmp r0, r1
┌──< 0x... bhs 0x...
│╎ 0x... ldr r1, [...]
│╎ 0x... ldr r1, [r3, r1]
│╎ 0x... str r1, [r3, r2]
│╎ 0x... adds r3, 4
│└─< 0x... b 0x...
└──> 0x... ldr r3, [...]
┌─> 0x... ldr r2, [...]
╎ 0x... cmp r3, r2
┌──< 0x... bhs 0x...
│╎ 0x... movs r2, 0
│╎ 0x... str r2, [r3], 4
│└─< 0x... b 0x...
└──> 0x... ...
The FW copies
0x32ebc
to0x100000c8
for a length of0x4558
0x00013820 08b5 push {r3, lr}
0x00013822 07f039f9 bl 0x1aa98 ;[1]
0x00013826 0c4b ldr r3, [0x00013858] ; [0x13858:4]=0xe000ed00
0x00013828 0c4a ldr r2, [0x0001385c] ; [0x1385c:4]=0x10000 flash
0x0001382a 9a60 str r2, [r3, 8]
0x0001382c 0023 movs r3, 0
┌─> 0x0001382e 0c4a ldr r2, [0x00013860] ; [0x13860:4]=0x100000c8
╎ 0x00013830 0c49 ldr r1, [0x00013864] ; [0x13864:4]=0x10004620
╎ 0x00013832 9818 adds r0, r3, r2
╎ 0x00013834 8842 cmp r0, r1
┌──< 0x00013836 04d2 bhs 0x13842
│╎ 0x00013838 0b49 ldr r1, [0x00013868] ; [0x13868:4]=0x32ebc
│╎ 0x0001383a 5958 ldr r1, [r3, r1]
│╎ 0x0001383c 9950 str r1, [r3, r2]
│╎ 0x0001383e 0433 adds r3, 4
│└─< 0x00013840 f5e7 b 0x1382e
└──> 0x00013842 0a4b ldr r3, [0x0001386c] ; [0x1386c:4]=0x10005100
┌─> 0x00013844 0a4a ldr r2, [0x00013870] ; [0x13870:4]=0x1000674c
╎ 0x00013846 9342 cmp r3, r2
┌──< 0x00013848 03d2 bhs 0x13852
│╎ 0x0001384a 0022 movs r2, 0
│╎ 0x0001384c 43f8042b str r2, [r3], 4
│└─< 0x00013850 f8e7 b 0x13844
└──> 0x00013852 00f0bdff bl 0x147d0 ;[2]
We add mappings to r2 using the file mappings om
command.
[...]> om?~ om
| om fd vaddr [size] [paddr] [rwx] [name] create new io map
The FW copies
0x32ebc
to0x100000c8
for a length of0x4558
download the stage1.r2 here: https://tinyurl.com/yxonhpjt
- Add a 'flag' at
0x100000c8
calleddata
with commandf
- map the copy with
om
- auto-analyze based on function preludes with
aap
- get some code-to-data xrefs with
aae
NB:
The FW copies
0x32ebc
to0x100000c8
for a length of0x4558
Here's the changes to the project file (just the tail). My stage2.r2
$ tail stage2.r2
f data = 0x100000c8
om 3 data 0x4558 `?v 0x32ebc - flash` mrw- DATA
aap
aae
?e ===
Let's fast-forward a bit. There is an array of CAN signals in this firmware. It starts at 0x10002D80
. Each element in the array will be an instance of this structure.
You could figure all this out without the source code; it would take alot of grinding in reversing the firmware. But it is totally possible.
struct CanSignal {
struct CanMessageDefinition* message;
const char* genericName;
uint8_t bitPosition;
uint8_t bitSize;
float factor;
...
So...
- the first word will be a pointer to structure (some data address).
- the second word will be a pointer to a string
Ideally we would have:
- xrefs (remember? they are important) for both code ref'ing {code, data} and data ref'ing data
- data in the dissasembly -- otherwise we could mistake data for code
We can investigate by using:
ax
and~
to grep for things -- alternative isaxt@
to ask about references TO an addresspD 8
to check how r2 is dissassembling the location of theCanSignal
array.pxr 8
to 'telescope': what do references to references to references (etc.) look like.
Ideally:
xrefs for both code ref'ing {code, data} and data ref'ing data
data in the dissasembly -- otherwise we could mistake data for code
[0x00000000]> f CanSignals_start = 0x10002D80
[0x00000000]> s CanSignals_start
[0x10002d80]> pD 8
;-- CanSignals_start:
0x10002d80 cc2c cmp r4, 0xcc ; 204
0x10002d82 0010 asrs r0, r0, 0x20
0x10002d84 9404 lsls r4, r2, 0x12
0x10002d86 0300 movs r3, r0
[0x10002d80]> ax~0x10002d80
[0x10002d80]> ax~CanSignals_start
So: NOT GREAT...
Craig Heffner (@devttys0) has a simple but incredibly useful plugin for grooming all data as 32bit offsets called codatify. This results in highlighting potentially useful references while exploring new firmwares.
In r2 we can emulate this plugin by formatting each address with Cd
and creating data xrefs with axd
-- iterating over the target addresses using the @@
modifier.
Iterating over the address to assign each as a dword with Cd
works fine, but creating a data xref at each address requires evaluating a command and using the evaluation as an argument to create data xref.
Creating data xrefs with axd
is where the iterate command falls apart. We must create a script to drive r2
; we can use lots of language bindings, but we'll choose to use python.
import r2pipe
import sys
r2p=r2pipe.open() # open without arguments only for #!pipe
for a in range(int(sys.argv[1], 0), int(sys.argv[2], 0), 4):
r2p.cmd('s 0x%x' % a)
pointer = r2p.cmdj('pfj p4')
r2p.cmd('Cd 4')
r2p.cmd('axd 0x%x' % pointer[0].get('value'))
available here: https://tinyurl.com/y5du6fu6.
This is how r2pipe scripts get called from radare:
#!pipe python ...
Create a codatify script of your owm or download from here: https://tinyurl.com/y5du6fu6
- iterate over all 4 byte words in a range given in
argv[1]
toargv[2]
- mark each as data with
Cd
- get the value of those bytes as a word. e.g.
pvw
,px
, etc - make a data xref based on that pointer value with
axd
Update Your Project File
- Groom the data 'copy' you just added by calling that script. Add this to your project file
#!pipe python codatafy.py `?v data` `?v data+0x4558`
- move the analysis to after the grooming
NB: you need to install: pip install r2pipe
Here's my stage3.r2
(the tail of it):
$ tail stage3.r2
f data = 0x100000c8
om 3 data 0x4558 `?v 0x32ebc - flash` mrw- DATA
#!pipe python codatafy.py `?v data` `?v data+0x4558`
aap
aae
?e ===
You can download my stage3.r2
here: https://tinyurl.com/y6fr67by
Ideally:
xrefs for both code ref'ing {code, data} and data ref'ing data
data in the dissasembly -- otherwise we could mistake data for code
$ r2 -i stage3.r2 --
3
[>] Scanning r-x 0x10000 - 0x37f00 done
Analyzed 459 functions based on preludes
===
-- For a full list of commands see `strings /dev/urandom`
[0x1000461c]> f CanSignals_start = 0x10002D80
[0x1000461c]> s CanSignals_start
[0x10002d80]> pD 8
;-- CanSignals_start:
; DATA XREF from fcn.00015064 (0x1506a)
; DATA XREF from fcn.00015088 (0x150c0)
0x10002d80 .dword 0x10002ccc
;-- str.signal1:
; DATA XREF from fcn.00015088 (+0x1e0)
0x10002d84 .string "signal1" ; len=7
[0x10002d80]> ax~0x10002d80
fcn.00015064+6 0x1506a -> DATA -> 0x10002d80 CanSignals_start
fcn.00015088+56 0x150c0 -> DATA -> 0x10002d80 CanSignals_start
CanSignals_start 0x10002d80 -> DATA -> 0x10002ccc str.Specified_string...
You can download my
stage3.r2
here: https://tinyurl.com/y6fr67by
So... BETTER :)
Even 'telescoping' works pretty well.
[0x10002d80]> pxrq 16
WARNING: r_bin_get_section_at: assertion 'o' failed (line 809)
WARNING: r_bin_get_section_at: assertion 'o' failed (line 809)
WARNING: r_bin_get_section_at: assertion 'o' failed (line 809)
WARNING: r_bin_get_section_at: assertion 'o' failed (line 809)
0x10002d80 0x10002ccc .,.. @CanSignals_start R 0x1000328c --> R 0x7a120
0x10002d84 0x00030494 .... @str.signal1 fcn.00029448 R X 'ldr r3, [r6, 0x14]' (sig..
0x10002d88 0x00000c34 4... 0
0x10002d8c 0x3f800000 ...? 0
[0x10002d80]> pxw 16
0x10002d80 0x10002ccc 0x00030494 0x00000c34 0x3f800000 .,......4......?
Open XC device firmwares embedded JSON CAN signal descriptions:
"0x49": {
"name": "decoder_test_message",
"bus": "unfiltered_bus",
"signals": {
"TirePressure": {
"generic_name": "tire_pressure_front_left",
"decoder": "tirePressureDecoder",
"bit_position": 56,
"bit_size": 8
...
Into the firmware as structures
struct CanSignal {
struct CanMessageDefinition* message;
const char* genericName;
uint8_t bitPosition;
uint8_t bitSize;
float factor;
...
Extract CAN Signals From the Firmware
Print the CAN signal information in human-readable form.
Radare2 knows about types and can pretty-print based on possibly-compound type information
$ r2 -i stage3.r2 --
3
[>] Scanning r-x 0x10000 - 0x37f00 done
Analyzed 459 functions based on preludes
===
-- I am Pentium of Borg. Division is futile. You will be approximated.
[0x1000461c]> t?
Usage: t # cparse types commands
| t List all loaded types
| tj List all loaded types as json
| t <type> Show type in 'pf' syntax
| t* List types info in r2 commands
| t- <name> Delete types by its name
| t-* Remove all types
| ta <type> Mark immediate as a type offset
| tc[type.name] List all/given types in C output format
| te[?] List all loaded enums
| td[?] <string> Load types from string
| tf List all loaded functions signatures
| tk <sdb-query> Perform sdb query
| tl[?] Show/Link type to an address
| tn[?] [-][addr] manage noreturn function attributes and marks
| to - Open cfg.editor to load types
| to <path> Load types from C header file
| toe[type.name] Open cfg.editor to edit types
| tos <path> Load types from parsed Sdb database
| tp <type> [addr|varname] cast data at <address> to <type> and print it
| tpx <type> <hexpairs> Show value for type with specified byte sequence
| ts[?] Print loaded struct types
| tu[?] Print loaded union types
| tx[f?] Type xrefs
| tt[?] List all loaded typedefs
Radare2 can parse c struct definitions and use it's in-built definitions of basic types.
You could create a structure definition by doing some long RE. But we'll fast forward a bit and try to use the header file from the Open XC project. canutil.h
Of course, trying to load the structures directly from the openxc source tree won't work.
[0x1000461c]> to /home/bengardiner/src/vi-firmware/src/can/canutil.h
#include "/usr/include/stdint.h"
In file included from /home/bengardiner/src/vi-firmware/src/can/canutil.h:4:
/usr/include/stdint.h:26: error: include file '/usr/include/bits/libc-header-sta...
In file included from /home/bengardiner/src/vi-firmware/src/can/canutil.h:4:
/usr/include/stdint.h:27: error: include file '/usr/include/bits/types.h' not fo...
In file included from /home/bengardiner/src/vi-firmware/src/can/canutil.h:4:
/usr/include/stdint.h:28: error: include file '/usr/include/bits/wchar.h' not fo...
In file included from /home/bengardiner/src/vi-firmware/src/can/canutil.h:4:
/usr/include/stdint.h:29: error: include file '/usr/include/bits/wordsize.h' not...
In file included from /home/bengardiner/src/vi-firmware/src/can/canutil.h:4:
/usr/include/stdint.h:31: error: declaration expected
But we can prepare the structures a little and then we'll have some structure definitions loaded for pretty printing.
- Define bool
- Stub typedef function pointers
- Remove const on e.g. const char *
- Use struct instances instead of typedefs
- Manually correct the packing/alignment that results
You can download mine here: https://tinyurl.com/y6kpcdn6
After which, radare2 is happy to give us some nice print formats from structs
$ r2 -i stage3.r2 --
3
[>] Scanning r-x 0x10000 - 0x37f00 done
Analyzed 459 functions based on preludes
===
-- Insert coin to continue ...
[0x1000461c]> to ./canutil.h
[0x1000461c]> t CanSignal
pf pzbbffff?ddpbdppdf message genericName bitPosition bitSize factor offset minValue
maxValue (FrequencyClock)frequencyClock sendSame forceSendChanged states stateCount
writable decoder encoder received lastValue
Final wrinkle: since we were analyzing a ARM THUMB binary, we set the e asm.bits=16
but this has an undesirable side-effect: all pointers in the pretty-printing are set to 16-bits wide. We can fix this by setting e asm.bits=32
while pretty-printing.
[0x1000461c]> s CanSignals_start
[0x10002d80]> e asm.bits=32
[0x10002d80]> .t CanSignal
message : 0x10002d80 = 0x10002ccc
genericName : 0x10002d84 = "\x94\x04\x03"
bitPosition : 0x10002d88 = 0x34
bitSize : 0x10002d89 = 0x0c
factor : 0x10002d8a = 0
offset : 0x10002d8e = 2.27795078e-41
minValue : 0x10002d92 = 0
maxValue : 0x10002d96 = 0
frequencyClock :
struct<FrequencyClock>
frequency : 0x10002d9a = 0
lastTick : 0x10002d9e = (qword)0x0000000000000000
timeFunction : 0x10002da6 = 0x00010000
sendSame : 0x10002daa = 0
forceSendChanged : 0x10002dae = 0
states : 0x10002db2 = 0x00000000
stateCount : 0x10002db6 = 0x00
writable : 0x10002db7 = 0
decoder : 0x10002dbb = 0x00000000
encoder : 0x10002dbf = 0x00000000
received : 0x10002dc3 = 2945024
lastValue : 0x10002dc7 = 3.83654982e-37
[0x1000461c]> s CanSignals_start
[0x10002d80]> e asm.bits=32
[0x10002d80]> .t CanSignal
message : 0x10002d80 = 0x10002ccc
genericName : 0x10002d84 = "\x94\x04\x03"
bitPosition : 0x10002d88 = 0x34
bitSize : 0x10002d89 = 0x0c
factor : 0x10002d8a = 0
offset : 0x10002d8e = 2.27795078e-41
minValue : 0x10002d92 = 0
maxValue : 0x10002d96 = 0
frequencyClock :
struct<FrequencyClock>
frequency : 0x10002d9a = 0
lastTick : 0x10002d9e = (qword)0x0000000000000000
timeFunction : 0x10002da6 = 0x00010000
sendSame : 0x10002daa = 0
forceSendChanged : 0x10002dae = 0
states : 0x10002db2 = 0x00000000
stateCount : 0x10002db6 = 0x00
writable : 0x10002db7 = 0
decoder : 0x10002dbb = 0x00000000
encoder : 0x10002dbf = 0x00000000
received : 0x10002dc3 = 2945024
lastValue : 0x10002dc7 = 3.83654982e-37
Which is OK; but not GREAT.
- no clear arbitration id
- lots of 'noise' fields
We can define our own pretty printing formats by taking the output of t CanSignal
as a starting point.
- download the
canutil.h
(or perform the .h grooming yourself) https://tinyurl.com/y6kpcdn6 - update your project file
- add a flag for start of can signals at
0x10002D80
- load the structures from
canutil.h
withto
- set pointer width to 4 bytes
e asm.bits =
- add a flag for start of can signals at
- starting from
t CanSignal
output, create your own prettier printing. Look atpf??
. - Remove 'noise' fields by deleting them from the named list and replacing their print encodings with
:
until only these fields remain:- name / bitPosition / bitSize / factor / offset
- Name it
JustCanSignal
withpf.myformat ...
For reference:
pf pzbbffff?ddpbdppdf message genericName bitPosition bitSize factor offset minValue
maxValue (FrequencyClock)frequencyClock sendSame forceSendChanged states stateCount
writable decoder encoder received lastValue
$ tail stage4.r2
aap
aae
f CanSignals_start = 0x10002D80
to canutil.h
e asm.bits=32
pf.JustCanSignal *x*zN1N1..ff::::::.::::: (CanMessageDefinition)message genericName
bitPosition bitSize factor offset
?e ===
Download it here: https://tinyurl.com/y2kngz5l
Here it is in action:
$ r2 -i stage4.r2 --
3
[>] Scanning r-x 0x10000 - 0x37f00 done
Analyzed 459 functions based on preludes
===
-- Starting bitcoin miner in background...
[0x1000461c]> s CanSignals_start
[0x10002d80]> pf.JustCanSignal
(CanMessageDefinition)message : (*0x10002ccc)0x10002d80 = 0x1000328c
genericName : (*0x30494)0x10002d84 = "signal1"
bitPosition : 0x10002d88 = 52
bitSize : 0x10002d89 = 12
factor : 0x10002d8c = 1
offset : 0x10002d90 = 0
[X] name
[X] bitPosition
[X] bitSize
[X] factor
[X] offset
[ ] arbitration ID
The first element of the CanSignal
structure is a pointer to a CanMessageDefinition
[0x10002d80]> pvw 4
0x10002ccc
[0x10002d80]> s `pvw 4`
[0x10002ccc]> t CanMessageDefinition
pf pdE?d[8]b bus id (CanMessageFormat)format (FrequencyClock)frequencyClock
forceSendChanged lastValue
[0x10002ccc]> .t CanMessageDefinition
Cannot find format for struct `CanMessageFormat`
bus : 0x10002ccc = 0x1000328c
id : 0x10002cd0 = 66
format : 0x10002cd4 = format (enum CanMessageFormat) = 0x0 ; STANDARD
frequencyClock :
struct<FrequencyClock>
frequency : 0x10002cd5 = 0
lastTick : 0x10002cd9 = (qword)0x0000000000000000
timeFunction : 0x10002ce1 = 0x01000000
forceSendChanged : 0x10002ce5 = 0
lastValue : 0x10002ce9 = [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c ]
[0x10002ccc]>
We really only want the id
field; we also would (ideally) like to print id
right next to the bit-positions, scale & offset. So we add a struct dereference to it:
[0x10002ccc]> s-
[0x10002d80]> pf.JustCanMessageArbId :x.::[8]: id
[0x10002d80]> pf *?*zN1N1..ff::::::.::::: (JustCanMessageArbId)message genericName
bitPosition bitSize factor offset
...
What happened?
There is a limitation in r2 (for now -- they've fixed all the previous ones). The format printer format string can have either a pointer to a structure OR a pointer to a string; but not both.
[0x10002d80]> pf *x*zN1N1..ff::::::.::::: (CanMessageDefinition)message genericName
bitPosition bitSize factor offset
(CanMessageDefinition)message : (*0x10002ccc)0x10002d80 = 0x1000328c
genericName : (*0x30494)0x10002d84 = "signal1"
bitPosition : 0x10002d88 = 52
bitSize : 0x10002d89 = 12
factor : 0x10002d8c = 1
offset : 0x10002d90 = 0
[0x10002d80]> pf *?*zN1N1..ff::::::.::::: (JustCanMessageArbId)message genericName
bitPosition bitSize factor offset
[0x10002d80]> pf *?xN1N1..ff::::::.::::: (JustCanMessageArbId)message genericName
bitPosition bitSize factor offset
message : (*0x10002ccc)
struct<JustCanMessageArbId>
id : 0x10002cd0 = 0x00000042
genericName : 0x10002d84 = 0x00030494
bitPosition : 0x10002d88 = 52
bitSize : 0x10002d89 = 12
factor : 0x10002d8c = 1
offset : 0x10002d90 = 0
The workaround is to split up that format statement and 'print twice.'
[0x10002d80]> pf *? (JustCanMessageArbId)message
message : (*0x10002ccc)
struct<JustCanMessageArbId>
id : 0x10002cd0 = 0x00000042
[0x10002d80]> pf *zN1N1..ff::::::.::::: genericName bitPosition bitSize factor
offset @+4
genericName : (*0x30494)0x10002d84 = "signal1"
bitPosition : 0x10002d88 = 52
bitSize : 0x10002d89 = 12
factor : 0x10002d8c = 1
offset : 0x10002d90 = 0
Update your project file with format statements for:
- the pointer to the message id
- the remainder of the structure
- define a macro
(name, command1, command2)
and run the macro to print the first CanSignals entry.
For reference:
- prints just the id in a CanMessageDefinition:
pf.JustCanMessageArbId :x.::[8]: id
- prints the remaining fields of interest in the array:
pf.JustRestOfCanSignal *zN1N1..ff::::::.::::: genericName bitPosition bitSize fact or offset
- the two step printing is
pf *? (JustCanMessageArbId)message
thenpf ? (JustRestOfCanSignal)signal @+4
$ tail stage5.r2
e asm.bits=32
pf.JustCanSignal *x*zN1N1..ff::::::.::::: (CanMessageDefinition)message genericNam
e bitPosition bitSize factor offset
pf.JustCanMessageArbId :x.::[8]: id
pf.JustRestOfCanSignal *zN1N1..ff::::::.::::: genericName bitPosition bitSize fact
or offset
(printSignal, pf *? (JustCanMessageArbId)message, pf ? (JustRestOfCanSignal)signal
@+4)
?e ===
You can download it here: https://tinyurl.com/y4fnkpnr
$ r2 -i stage5.r2 --
[...]
[0x1000461c]> s CanSignals_start
[0x10002d80]> .(printSignal)
message : (*0x10002ccc)
struct<JustCanMessageArbId>
id : 0x10002cd0 = 0x00000042
signal :
struct<JustRestOfCanSignal>
genericName : (*0x30494)0x10002d84 = "signal1"
bitPosition : 0x10002d88 = 52
bitSize : 0x10002d89 = 12
factor : 0x10002d8c = 1
offset : 0x10002d90 = 0
[0x10002d80]> .(printSignal) @@s:CanSignals_start 0x1000328c 0x44
message : (*0x10002ccc)
struct<JustCanMessageArbId>
id : 0x10002cd0 = 0x00000042
signal :
struct<JustRestOfCanSignal>
genericName : (*0x30494)0x10002d84 = "signal1"
bitPosition : 0x10002d88 = 52
bitSize : 0x10002d89 = 12
factor : 0x10002d8c = 1
offset : 0x10002d90 = 0
message : (*0x10002cf0)
struct<JustCanMessageArbId>
id : 0x10002cf4 = 0x00000049
signal :
struct<JustRestOfCanSignal>
genericName : (*0x3028d)0x10002dc8 = "tire_pressure_front_left"
bitPosition : 0x10002dcc = 56
bitSize : 0x10002dcd = 8
factor : 0x10002dd0 = 1
offset : 0x10002dd4 = 0
[...]
- My Previous Employer: Irdeto
- Ford (in general). OpenXC people & Eric Marsman (in particular).
Our r2 Commands
? -- help postfix
s -- seek
ax -- list xrefs
axd p -- create data reference to <p>
aa -- recursively analyze exports/syms and entrypoints
aap -- find functions by searching for preludes
~ -- grep postfix
@ -- temporary seek postfix
~? -- count lines postfix
* -- postfix, echo in r2 commands
` -- subshell
( -- macros
@@ -- iterator
. -- source output as r2 commands
e -- set config
f -- set a flag (aka named address)
om -- setup a mapping of a file into virtual memory
?e -- echo
?v -- echo hex value
t -- list defined types
tk -- define type details
to -- load type defines from .
pf -- print formatted
pf.t -- define named format
px -- print (canonical) hexdump
pxw -- print word-wise hexdump
pxr -- print telescoping word references
pv4 -- print quadword value
pD n -- disassemble <n> bytes