Clarify coverage wording and add ROM analysis guide
This commit is contained in:
524
docs/LARGE_ROM_ANALYSIS_WORKFLOW.md
Normal file
524
docs/LARGE_ROM_ANALYSIS_WORKFLOW.md
Normal file
@@ -0,0 +1,524 @@
|
||||
# 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.**
|
||||
Reference in New Issue
Block a user