Files
adsp219x-re/docs/LARGE_ROM_ANALYSIS_WORKFLOW.md

525 lines
9.6 KiB
Markdown
Raw Normal View History

# 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.**