525 lines
9.6 KiB
Markdown
525 lines
9.6 KiB
Markdown
|
|
# Large ADSP-219x ROM Analysis Workflow
|
||
|
|
|
||
|
|
This guide describes a practical workflow for reverse engineering a
|
||
|
|
large raw ADSP-219x ROM image in radare2 using the `adsp219x`
|
||
|
|
architecture plugin from this repository.
|
||
|
|
|
||
|
|
It is written for the common case where you have a large blob, for
|
||
|
|
example a 1 MB ROM dump, and you do not yet know:
|
||
|
|
|
||
|
|
- where code starts and where data starts
|
||
|
|
- how many independent code regions exist
|
||
|
|
- where the main loops and dispatchers are
|
||
|
|
- which PM words are instructions and which are tables
|
||
|
|
|
||
|
|
The workflow below is intentionally conservative. For raw DSP ROMs,
|
||
|
|
manual validation beats aggressive auto-analysis.
|
||
|
|
|
||
|
|
## Assumptions
|
||
|
|
|
||
|
|
- The ROM is for an ADSP-219x family DSP.
|
||
|
|
- Instructions are 24-bit words.
|
||
|
|
- The dump is raw program memory, not a richly annotated executable.
|
||
|
|
- You already have a working radare2 installation and the
|
||
|
|
`adsp219x` plugin is installed or loadable with `-L`.
|
||
|
|
|
||
|
|
## Load The ROM
|
||
|
|
|
||
|
|
For an installed plugin:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
r2 -a adsp219x -b 24 firmware.bin
|
||
|
|
```
|
||
|
|
|
||
|
|
For a local plugin build that is not installed:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
r2 -a adsp219x -L ./r2plugin/asm_adsp219x.so -b 24 firmware.bin
|
||
|
|
```
|
||
|
|
|
||
|
|
Why the flags matter:
|
||
|
|
|
||
|
|
- `-a adsp219x`: forces the custom architecture plugin
|
||
|
|
- `-b 24`: tells radare2 to treat the code as 24-bit ADSP-219x words
|
||
|
|
|
||
|
|
## ROM Format Sanity Check
|
||
|
|
|
||
|
|
Before analyzing flow, check whether the byte layout looks correct.
|
||
|
|
|
||
|
|
ADSP-219x code is usually stored either as:
|
||
|
|
|
||
|
|
- packed 24-bit words: `aa bb cc dd ee ff ...`
|
||
|
|
- padded 32-bit words with a leading zero byte
|
||
|
|
|
||
|
|
If your disassembly looks completely implausible, confirm the ROM
|
||
|
|
format first.
|
||
|
|
|
||
|
|
Helpful first commands:
|
||
|
|
|
||
|
|
```text
|
||
|
|
px 64
|
||
|
|
s 0
|
||
|
|
pd 16
|
||
|
|
```
|
||
|
|
|
||
|
|
If the first few decoded instructions look impossible but the hex dump
|
||
|
|
shows a repeating `00 xx xx xx` structure, you may be looking at a
|
||
|
|
32-bit padded dump and need to strip padding before analysis.
|
||
|
|
|
||
|
|
## High-Level Strategy
|
||
|
|
|
||
|
|
For a large ROM, do not start with `aaa`.
|
||
|
|
|
||
|
|
A better sequence is:
|
||
|
|
|
||
|
|
1. validate the entry region manually
|
||
|
|
2. mark only plausible functions
|
||
|
|
3. follow explicit control flow
|
||
|
|
4. distinguish code from tables
|
||
|
|
5. expand analysis gradually
|
||
|
|
6. use graph views only after local validation
|
||
|
|
|
||
|
|
`aaa` on a large raw DSP ROM often creates a false sense of structure
|
||
|
|
by treating valid-looking data words as code.
|
||
|
|
|
||
|
|
## Workflow Diagram
|
||
|
|
|
||
|
|
```mermaid
|
||
|
|
flowchart TD
|
||
|
|
A[Load ROM in r2] --> B[Check raw bytes and first instructions]
|
||
|
|
B --> C{Plausible code at reset area?}
|
||
|
|
C -- no --> D[Verify ROM packing, offset, endianness, dump source]
|
||
|
|
D --> B
|
||
|
|
C -- yes --> E[Create first function manually]
|
||
|
|
E --> F[Inspect linear disassembly and graph]
|
||
|
|
F --> G{Looks like real control flow?}
|
||
|
|
G -- no --> H[Do not mark as function, treat as possible data]
|
||
|
|
G -- yes --> I[Follow jumps, calls, DO UNTIL loops]
|
||
|
|
I --> J[Name regions and comment findings]
|
||
|
|
J --> K[Search for more entry points and dispatchers]
|
||
|
|
K --> L[Only then expand with broader analysis]
|
||
|
|
L --> M[Separate code islands from PM data tables]
|
||
|
|
```
|
||
|
|
|
||
|
|
## Step 1: Inspect The Reset Region Manually
|
||
|
|
|
||
|
|
Start at address zero unless you have evidence of a different boot
|
||
|
|
mapping.
|
||
|
|
|
||
|
|
```text
|
||
|
|
s 0
|
||
|
|
pd 20
|
||
|
|
pd 64
|
||
|
|
```
|
||
|
|
|
||
|
|
Look for signs of real code:
|
||
|
|
|
||
|
|
- immediate register loads
|
||
|
|
- `JUMP` or `CALL`
|
||
|
|
- `DO ... UNTIL`
|
||
|
|
- initialization of `I`, `M`, `L`, `CNTR`, status registers
|
||
|
|
- branches to other regions
|
||
|
|
|
||
|
|
Red flags that suggest you are not in code:
|
||
|
|
|
||
|
|
- long stretches of decodeable but nonsensical instructions
|
||
|
|
- no branches, no calls, no returns
|
||
|
|
- bizarre register moves with no purpose
|
||
|
|
- disassembly that looks random but hex bytes look regular
|
||
|
|
|
||
|
|
## Step 2: Mark The First Function Manually
|
||
|
|
|
||
|
|
Once the entry region looks plausible:
|
||
|
|
|
||
|
|
```text
|
||
|
|
s 0
|
||
|
|
af
|
||
|
|
pdf
|
||
|
|
agf
|
||
|
|
```
|
||
|
|
|
||
|
|
Meaning:
|
||
|
|
|
||
|
|
- `af`: define a function at the current address
|
||
|
|
- `pdf`: print the current function linearly
|
||
|
|
- `agf`: show the control-flow graph of the current function
|
||
|
|
|
||
|
|
This is a better first move than `aaa`, because it forces you to
|
||
|
|
validate one region before scaling out.
|
||
|
|
|
||
|
|
## Step 3: Use Graphs Locally, Not Globally
|
||
|
|
|
||
|
|
For a large ROM, graphs are most useful once you already believe the
|
||
|
|
current region is code.
|
||
|
|
|
||
|
|
Useful commands:
|
||
|
|
|
||
|
|
```text
|
||
|
|
agf
|
||
|
|
VV
|
||
|
|
afb
|
||
|
|
```
|
||
|
|
|
||
|
|
- `agf`: static graph of current function
|
||
|
|
- `VV`: visual graph mode
|
||
|
|
- `afb`: list/show basic blocks
|
||
|
|
|
||
|
|
If `agf` produces a clean graph with obvious branches and loop edges,
|
||
|
|
the region is probably code.
|
||
|
|
|
||
|
|
If `agf` is trivial, chaotic, or makes no semantic sense, you may be
|
||
|
|
looking at data or a bad function boundary.
|
||
|
|
|
||
|
|
## Step 4: Expand By Following Explicit Flow
|
||
|
|
|
||
|
|
After validating the first function, expand manually through explicit
|
||
|
|
targets:
|
||
|
|
|
||
|
|
```text
|
||
|
|
pdf
|
||
|
|
axt
|
||
|
|
afl
|
||
|
|
```
|
||
|
|
|
||
|
|
Then jump to interesting destinations:
|
||
|
|
|
||
|
|
```text
|
||
|
|
s <target>
|
||
|
|
af
|
||
|
|
pdf
|
||
|
|
agf
|
||
|
|
```
|
||
|
|
|
||
|
|
Prioritize:
|
||
|
|
|
||
|
|
- call targets
|
||
|
|
- jump targets
|
||
|
|
- loop bodies
|
||
|
|
- indirect branch setup code
|
||
|
|
|
||
|
|
For ADSP-219x specifically, also watch for:
|
||
|
|
|
||
|
|
- `DO ... UNTIL`
|
||
|
|
- tight MAC loops
|
||
|
|
- register setup for DAGs
|
||
|
|
- memory access kernels
|
||
|
|
|
||
|
|
## Step 5: Search For ADSP-219x-Specific Patterns
|
||
|
|
|
||
|
|
Search directly for common control-flow patterns:
|
||
|
|
|
||
|
|
```text
|
||
|
|
/a JUMP
|
||
|
|
/a CALL
|
||
|
|
/a DO
|
||
|
|
/a RTS
|
||
|
|
/a RTI
|
||
|
|
```
|
||
|
|
|
||
|
|
Also inspect likely DSP kernel patterns:
|
||
|
|
|
||
|
|
```text
|
||
|
|
/a MR
|
||
|
|
/a MX0
|
||
|
|
/a MY0
|
||
|
|
/a IO(
|
||
|
|
```
|
||
|
|
|
||
|
|
These searches are not perfect, but they help locate:
|
||
|
|
|
||
|
|
- processing loops
|
||
|
|
- dispatch logic
|
||
|
|
- hardware initialization
|
||
|
|
- coefficient loads
|
||
|
|
|
||
|
|
## Step 6: Distinguish Code From Data
|
||
|
|
|
||
|
|
In a large ADSP-219x ROM, many PM words are data, not instructions.
|
||
|
|
You must actively separate them.
|
||
|
|
|
||
|
|
### Heuristic: likely code
|
||
|
|
|
||
|
|
A region is likely code if it has:
|
||
|
|
|
||
|
|
- incoming xrefs from jumps or calls
|
||
|
|
- meaningful block structure
|
||
|
|
- function-like boundaries
|
||
|
|
- setup followed by control flow
|
||
|
|
- loops, branches, and exits
|
||
|
|
|
||
|
|
Check with:
|
||
|
|
|
||
|
|
```text
|
||
|
|
axt @ <addr>
|
||
|
|
pdf
|
||
|
|
agf
|
||
|
|
```
|
||
|
|
|
||
|
|
### Heuristic: likely data
|
||
|
|
|
||
|
|
A region is likely data if it has:
|
||
|
|
|
||
|
|
- regular numeric patterns in hex
|
||
|
|
- no meaningful control flow
|
||
|
|
- no incoming code xrefs
|
||
|
|
- no returns or loop structure
|
||
|
|
- many decodeable instructions that make no programmatic sense
|
||
|
|
|
||
|
|
Check with both disassembly and raw bytes:
|
||
|
|
|
||
|
|
```text
|
||
|
|
pd 32
|
||
|
|
px 64
|
||
|
|
```
|
||
|
|
|
||
|
|
If the hex dump looks more plausible than the disassembly, it is often
|
||
|
|
a table.
|
||
|
|
|
||
|
|
### ADSP-219x-specific data patterns
|
||
|
|
|
||
|
|
Common PM data in DSP firmware:
|
||
|
|
|
||
|
|
- FIR coefficients
|
||
|
|
- IIR coefficients
|
||
|
|
- FFT sine/cosine tables
|
||
|
|
- lookup tables
|
||
|
|
- packed constants
|
||
|
|
- boot configuration words
|
||
|
|
|
||
|
|
These often decode into valid-looking instructions by accident.
|
||
|
|
|
||
|
|
## Step 7: Delay Global Auto-Analysis
|
||
|
|
|
||
|
|
Only after you have mapped a few real code islands should you broaden
|
||
|
|
analysis:
|
||
|
|
|
||
|
|
```text
|
||
|
|
aa
|
||
|
|
afl
|
||
|
|
```
|
||
|
|
|
||
|
|
Prefer `aa` first.
|
||
|
|
|
||
|
|
Use `aaa` only when:
|
||
|
|
|
||
|
|
- the ROM format is confirmed
|
||
|
|
- the entry region is valid
|
||
|
|
- you already understand where major code regions are
|
||
|
|
- the plugin behaves consistently on this image
|
||
|
|
|
||
|
|
Why this matters:
|
||
|
|
|
||
|
|
- `aa` is less aggressive
|
||
|
|
- `aaa` can create junk functions in large raw ROMs
|
||
|
|
- false positives are expensive to clean up mentally
|
||
|
|
|
||
|
|
## Step 8: Name What You Understand
|
||
|
|
|
||
|
|
As soon as a region is understood, name and annotate it.
|
||
|
|
|
||
|
|
Useful commands:
|
||
|
|
|
||
|
|
```text
|
||
|
|
afn entry_init
|
||
|
|
afn main_loop
|
||
|
|
afn io_dispatch
|
||
|
|
CCu probable coefficient table
|
||
|
|
CCu hardware init and watchdog setup
|
||
|
|
```
|
||
|
|
|
||
|
|
Naming reduces rework and makes graph navigation much easier.
|
||
|
|
|
||
|
|
## Step 9: Build A Region Map
|
||
|
|
|
||
|
|
For a 1 MB ROM, keep a rough map as you go:
|
||
|
|
|
||
|
|
- boot/reset code
|
||
|
|
- hardware init
|
||
|
|
- interrupt vector area
|
||
|
|
- main loop
|
||
|
|
- DSP kernels
|
||
|
|
- dispatch tables
|
||
|
|
- PM data tables
|
||
|
|
- obvious unused or padding regions
|
||
|
|
|
||
|
|
This can live in:
|
||
|
|
|
||
|
|
- r2 comments
|
||
|
|
- a notebook
|
||
|
|
- a separate markdown file
|
||
|
|
|
||
|
|
The important thing is to stop treating the ROM as one continuous
|
||
|
|
thing. Large firmware becomes manageable once you divide it into
|
||
|
|
regions.
|
||
|
|
|
||
|
|
## Step 10: Revisit Ambiguous Areas Later
|
||
|
|
|
||
|
|
Do not force a conclusion too early.
|
||
|
|
|
||
|
|
When a region is ambiguous:
|
||
|
|
|
||
|
|
- leave it unnamed or mark it as tentative
|
||
|
|
- inspect surrounding xrefs first
|
||
|
|
- compare with neighboring validated code
|
||
|
|
- revisit it after understanding more of the firmware
|
||
|
|
|
||
|
|
Good reverse engineering on large DSP ROMs is iterative.
|
||
|
|
|
||
|
|
## Recommended First 15 Minutes
|
||
|
|
|
||
|
|
If you want a concrete first-pass routine for a 1 MB ROM:
|
||
|
|
|
||
|
|
### 1. Open the ROM
|
||
|
|
|
||
|
|
```bash
|
||
|
|
r2 -a adsp219x -b 24 firmware.bin
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. Check the first bytes and first instructions
|
||
|
|
|
||
|
|
```text
|
||
|
|
s 0
|
||
|
|
px 64
|
||
|
|
pd 32
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. If plausible, define the first function
|
||
|
|
|
||
|
|
```text
|
||
|
|
af
|
||
|
|
pdf
|
||
|
|
agf
|
||
|
|
```
|
||
|
|
|
||
|
|
### 4. Enter visual graph mode
|
||
|
|
|
||
|
|
```text
|
||
|
|
VV
|
||
|
|
```
|
||
|
|
|
||
|
|
### 5. Follow obvious branch targets manually
|
||
|
|
|
||
|
|
```text
|
||
|
|
s <target>
|
||
|
|
af
|
||
|
|
pdf
|
||
|
|
agf
|
||
|
|
```
|
||
|
|
|
||
|
|
### 6. Search for loops and calls
|
||
|
|
|
||
|
|
```text
|
||
|
|
/a DO
|
||
|
|
/a CALL
|
||
|
|
/a JUMP
|
||
|
|
```
|
||
|
|
|
||
|
|
### 7. Compare suspicious regions with hex
|
||
|
|
|
||
|
|
```text
|
||
|
|
pd 32
|
||
|
|
px 64
|
||
|
|
```
|
||
|
|
|
||
|
|
### 8. Only then widen analysis
|
||
|
|
|
||
|
|
```text
|
||
|
|
aa
|
||
|
|
afl
|
||
|
|
```
|
||
|
|
|
||
|
|
## When To Suspect A Bad Decode
|
||
|
|
|
||
|
|
Pause and reassess if you see:
|
||
|
|
|
||
|
|
- no meaningful flow anywhere near the entry region
|
||
|
|
- every region looks equally nonsensical
|
||
|
|
- branch targets never lead to reasonable code
|
||
|
|
- graph mode shows nonsense everywhere
|
||
|
|
- the ROM appears to decode but nothing behaves like firmware
|
||
|
|
|
||
|
|
Then check:
|
||
|
|
|
||
|
|
- ROM packing
|
||
|
|
- dump alignment
|
||
|
|
- whether the image is compressed or encrypted
|
||
|
|
- whether the base offset is wrong
|
||
|
|
- whether the file contains headers before the actual code
|
||
|
|
|
||
|
|
## Practical Command Cheat Sheet
|
||
|
|
|
||
|
|
Open:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
r2 -a adsp219x -b 24 firmware.bin
|
||
|
|
```
|
||
|
|
|
||
|
|
Start region:
|
||
|
|
|
||
|
|
```text
|
||
|
|
s 0
|
||
|
|
pd 20
|
||
|
|
pd 64
|
||
|
|
px 64
|
||
|
|
```
|
||
|
|
|
||
|
|
Define and inspect function:
|
||
|
|
|
||
|
|
```text
|
||
|
|
af
|
||
|
|
pdf
|
||
|
|
agf
|
||
|
|
```
|
||
|
|
|
||
|
|
Visual graph:
|
||
|
|
|
||
|
|
```text
|
||
|
|
VV
|
||
|
|
```
|
||
|
|
|
||
|
|
Search:
|
||
|
|
|
||
|
|
```text
|
||
|
|
/a JUMP
|
||
|
|
/a CALL
|
||
|
|
/a DO
|
||
|
|
/a RTS
|
||
|
|
```
|
||
|
|
|
||
|
|
Cross-references:
|
||
|
|
|
||
|
|
```text
|
||
|
|
axt
|
||
|
|
axt @ <addr>
|
||
|
|
```
|
||
|
|
|
||
|
|
Broader analysis:
|
||
|
|
|
||
|
|
```text
|
||
|
|
aa
|
||
|
|
afl
|
||
|
|
```
|
||
|
|
|
||
|
|
Naming:
|
||
|
|
|
||
|
|
```text
|
||
|
|
afn main_loop
|
||
|
|
CCu probable PM coefficient table
|
||
|
|
```
|
||
|
|
|
||
|
|
## Summary
|
||
|
|
|
||
|
|
For a large ADSP-219x ROM:
|
||
|
|
|
||
|
|
- do not trust auto-analysis first
|
||
|
|
- validate the entry region manually
|
||
|
|
- graph only locally at first
|
||
|
|
- follow explicit flow edges
|
||
|
|
- use both disassembly and hex dumps
|
||
|
|
- treat PM as mixed code and data
|
||
|
|
- expand analysis gradually
|
||
|
|
|
||
|
|
The core rule is simple:
|
||
|
|
|
||
|
|
**Local confidence first, global analysis later.**
|