mirror of
https://github.com/mytechnotalent/Embedded-Hacking.git
synced 2026-06-30 17:55:32 +02:00
Update WEEK11
This commit is contained in:
+3
-3
@@ -1,4 +1,4 @@
|
||||
# Week 1: Introduction and Overview of Embedded Reverse Engineering: Ethics, Scoping, and Basic Concepts
|
||||
# Week 1: Introduction and Overview of Embedded Reverse Engineering: Ethics, Scoping, and Basic Concepts
|
||||
|
||||
## What You'll Learn This Week
|
||||
|
||||
@@ -265,8 +265,8 @@ Before we start, make sure you have:
|
||||
|
||||
Open a terminal and start OpenOCD:
|
||||
|
||||
```cmd
|
||||
openocd -s "%USERPROFILE%\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000"
|
||||
```powershell
|
||||
openocd -s "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000"
|
||||
```
|
||||
|
||||
### Connecting to Your Pico 2 with GDB
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
# Week 2: Hello, World - Debugging and Hacking Basics: Debugging and Hacking a Basic Program for the Pico 2
|
||||
# Week 2: Hello, World - Debugging and Hacking Basics: Debugging and Hacking a Basic Program for the Pico 2
|
||||
|
||||
## What You'll Learn This Week
|
||||
|
||||
@@ -164,8 +164,8 @@ OpenOCD is the bridge between your computer and the Pico 2's debug interface. It
|
||||
|
||||
**Open Terminal 1 and type:**
|
||||
|
||||
```cmd
|
||||
openocd -s "%USERPROFILE%\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000"
|
||||
```powershell
|
||||
openocd -s "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000"
|
||||
```
|
||||
|
||||
**What this command means:**
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
# Week 3: Embedded System Analysis: Understanding the RP2350 Architecture w/ Comprehensive Firmware Analysis
|
||||
# Week 3: Embedded System Analysis: Understanding the RP2350 Architecture w/ Comprehensive Firmware Analysis
|
||||
|
||||
## What You'll Learn This Week
|
||||
|
||||
@@ -361,8 +361,8 @@ Before we start, make sure you have:
|
||||
|
||||
**Terminal 1 - Start OpenOCD:**
|
||||
|
||||
```cmd
|
||||
openocd -s "%USERPROFILE%\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000"
|
||||
```powershell
|
||||
openocd -s "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000"
|
||||
```
|
||||
|
||||
**Terminal 2 - Start GDB:**
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
# Week 6: Static Variables in Embedded Systems: Debugging and Hacking Static Variables w/ GPIO Input Basics
|
||||
# Week 6: Static Variables in Embedded Systems: Debugging and Hacking Static Variables w/ GPIO Input Basics
|
||||
|
||||
## What You'll Learn This Week
|
||||
|
||||
@@ -430,8 +430,8 @@ This demonstrates unsigned integer overflow!
|
||||
|
||||
**Terminal 1 - Start OpenOCD:**
|
||||
|
||||
```cmd
|
||||
openocd -s "%USERPROFILE%\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000"
|
||||
```powershell
|
||||
openocd -s "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000"
|
||||
```
|
||||
|
||||
**Terminal 2 - Start GDB:**
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
# Week 7: Constants in Embedded Systems: Debugging and Hacking Constants w/ 1602 LCD I2C Basics
|
||||
# Week 7: Constants in Embedded Systems: Debugging and Hacking Constants w/ 1602 LCD I2C Basics
|
||||
|
||||
## What You'll Learn This Week
|
||||
|
||||
@@ -471,8 +471,8 @@ OTHER_FAV_NUM: 1337
|
||||
|
||||
**Terminal 1 - Start OpenOCD:**
|
||||
|
||||
```cmd
|
||||
openocd -s "%USERPROFILE%\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000"
|
||||
```powershell
|
||||
openocd -s "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000"
|
||||
```
|
||||
|
||||
**Terminal 2 - Start GDB:**
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
# Week 9: Operators in Embedded Systems: Debugging and Hacking Operators w/ DHT11 Temperature & Humidity Sensor Single-Wire Protocol Basics.
|
||||
# Week 9: Operators in Embedded Systems: Debugging and Hacking Operators w/ DHT11 Temperature & Humidity Sensor Single-Wire Protocol Basics.
|
||||
|
||||
## What You'll Learn This Week
|
||||
|
||||
@@ -491,8 +491,8 @@ Humidity: 51.0%, Temperature: 23.8 deg C
|
||||
|
||||
Open a terminal and start OpenOCD:
|
||||
|
||||
```cmd
|
||||
openocd -s "%USERPROFILE%\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000"
|
||||
```powershell
|
||||
openocd -s "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000"
|
||||
```
|
||||
|
||||
You should see output indicating OpenOCD connected successfully to your Pico 2 via the Debug Probe.
|
||||
|
||||
+4
-4
@@ -596,8 +596,8 @@ one
|
||||
|
||||
Open a terminal and start OpenOCD:
|
||||
|
||||
```cmd
|
||||
openocd -s "%USERPROFILE%\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000"
|
||||
```powershell
|
||||
openocd -s "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000"
|
||||
```
|
||||
|
||||
You should see output indicating OpenOCD connected successfully to your Pico 2 via the Debug Probe.
|
||||
@@ -1048,8 +1048,8 @@ int main(void) {
|
||||
|
||||
Open a terminal and start OpenOCD:
|
||||
|
||||
```cmd
|
||||
openocd -s "%USERPROFILE%\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000"
|
||||
```powershell
|
||||
openocd -s "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000"
|
||||
```
|
||||
|
||||
You should see output indicating OpenOCD connected successfully to your Pico 2 via the Debug Probe.
|
||||
|
||||
+203
-133
@@ -132,9 +132,9 @@ ptr->led1_pin = 16; // Using arrow with pointer
|
||||
+-----------------------------------------------------------------+
|
||||
| Dot vs Arrow Operator |
|
||||
| |
|
||||
| struct_variable.member ?-- Use with actual struct |
|
||||
| struct_variable.member <-- Use with actual struct |
|
||||
| |
|
||||
| pointer_to_struct->member ?-- Use with pointer to struct |
|
||||
| pointer_to_struct->member <-- Use with pointer to struct |
|
||||
| |
|
||||
| The arrow (->) is shorthand for (*pointer).member |
|
||||
| |
|
||||
@@ -182,11 +182,11 @@ simple_led_ctrl_t leds = {
|
||||
| +----------+ +----------+ |
|
||||
| | Button | | | |
|
||||
| | 1 | --- IR Light Pulses --- | ++ | |
|
||||
| | +---+ | ~~~~~~~~~~~~? | Sensor | |
|
||||
| | | ?? | | | | |
|
||||
| | +---+ | ~~~~~~~~~~~~> | Sensor | |
|
||||
| | | Tx | | | | |
|
||||
| | +---+ | +----+-----+ |
|
||||
| | IR LED | | |
|
||||
| +----------+ ? |
|
||||
| +----------+ v |
|
||||
| GPIO Pin |
|
||||
| (Digital signal) |
|
||||
| |
|
||||
@@ -243,7 +243,7 @@ int add_numbers(int a, int b) {
|
||||
}
|
||||
|
||||
// Function call
|
||||
int result = add_numbers(5, 3); // result = 8
|
||||
int result = add_numbers(5, 3); // result = 8
|
||||
```
|
||||
|
||||
### Function Components
|
||||
@@ -266,9 +266,9 @@ int result = add_numbers(5, 3); // result = 8
|
||||
| | | +-- Function name ||
|
||||
| | +-- Return type (what it gives back) ||
|
||||
| | ||
|
||||
| | if (ir_command == 0x0C) return 1; ?-- Body ||
|
||||
| | if (ir_command == 0x0C) return 1; <-- Body ||
|
||||
| | if (ir_command == 0x18) return 2; ||
|
||||
| | return 0; ?-- Return value ||
|
||||
| | return 0; <-- Return value ||
|
||||
| | } ||
|
||||
| +-------------------------------------------------------------+|
|
||||
| |
|
||||
@@ -310,11 +310,11 @@ leds_all_off(&my_leds); // Pass the ADDRESS of my_leds
|
||||
| Passing Struct by Pointer |
|
||||
| |
|
||||
| main() { |
|
||||
| simple_led_ctrl_t leds; ?-- Struct lives here |
|
||||
| leds_all_off(&leds); ?-- Pass ADDRESS (pointer) |
|
||||
| simple_led_ctrl_t leds; <-- Struct lives here |
|
||||
| leds_all_off(&leds); <-- Pass ADDRESS (pointer) |
|
||||
| } | |
|
||||
| | |
|
||||
| ? |
|
||||
| v |
|
||||
| leds_all_off(simple_led_ctrl_t *leds) { |
|
||||
| gpio_put(leds->led1_pin, false); |
|
||||
| ---- |
|
||||
@@ -339,9 +339,9 @@ When the compiler converts your C code to assembly, it "flattens" struct operati
|
||||
|
||||
**C Code:**
|
||||
```c
|
||||
gpio_init(leds.led1_pin); // leds.led1_pin = 16
|
||||
gpio_init(leds.led2_pin); // leds.led2_pin = 17
|
||||
gpio_init(leds.led3_pin); // leds.led3_pin = 18
|
||||
gpio_init(leds.led1_pin); // leds.led1_pin = 16
|
||||
gpio_init(leds.led2_pin); // leds.led2_pin = 17
|
||||
gpio_init(leds.led3_pin); // leds.led3_pin = 18
|
||||
```
|
||||
|
||||
**Assembly (what the compiler produces):**
|
||||
@@ -368,7 +368,7 @@ bl gpio_init ; call gpio_init(18)
|
||||
| +-------------------------------------------------------------+|
|
||||
| | |
|
||||
| | Compiler transforms |
|
||||
| ? |
|
||||
| v |
|
||||
| Assembly Level (Flattened): |
|
||||
| +-------------------------------------------------------------+|
|
||||
| | movs r0, #16 ; Just the VALUE, no struct reference ||
|
||||
@@ -531,9 +531,9 @@ int main(void) {
|
||||
leds.led3_state = false;
|
||||
|
||||
// Check NEC codes
|
||||
if (key == 0x0C) leds.led1_state = true; // GPIO16
|
||||
if (key == 0x18) leds.led2_state = true; // GPIO17
|
||||
if (key == 0x5E) leds.led3_state = true; // GPIO18
|
||||
if (key == 0x0C) leds.led1_state = true; // GPIO16
|
||||
if (key == 0x18) leds.led2_state = true; // GPIO17
|
||||
if (key == 0x5E) leds.led3_state = true; // GPIO18
|
||||
|
||||
// Apply states
|
||||
gpio_put(leds.led1_pin, leds.led1_state);
|
||||
@@ -594,8 +594,8 @@ int main(void) {
|
||||
|
||||
Open a terminal and start OpenOCD:
|
||||
|
||||
```cmd
|
||||
openocd -s "%USERPROFILE%\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000"
|
||||
```powershell
|
||||
openocd -s "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000"
|
||||
```
|
||||
|
||||
You should see output indicating OpenOCD connected successfully to your Pico 2 via the Debug Probe.
|
||||
@@ -647,68 +647,93 @@ monitor reset halt
|
||||
continue
|
||||
```
|
||||
|
||||
### Step 11: Examine the Struct on the Stack
|
||||
### Step 11: Observe the Flattened Struct
|
||||
|
||||
After stepping into main, the struct is initialized on the stack. Examine it:
|
||||
Instead of allocating the struct on the stack, the compiler completely **flattened and optimized** it out! There is no `sub sp` instruction for the struct.
|
||||
|
||||
Examine the `main` disassembly again:
|
||||
|
||||
```gdb
|
||||
stepi 20
|
||||
x/6xb $sp
|
||||
disassemble 0x10000234,+200
|
||||
```
|
||||
|
||||
You should see the struct layout: `10 11 12 00 00 00` (pins 16, 17, 18 and three false states).
|
||||
Notice how values 16, 17, and 18 are just loaded directly into registers (`movs r0, #16`, etc.) and passed to functions. The struct abstraction is entirely gone.
|
||||
|
||||
### Step 12: Watch GPIO Initialization
|
||||
|
||||
Set a breakpoint on gpio_init and watch each LED pin get initialized:
|
||||
Set a breakpoint on the first `gpio_init` call and watch the LED pin get initialized:
|
||||
|
||||
```gdb
|
||||
break *0x10000260
|
||||
break *0x1000023c
|
||||
continue
|
||||
info registers r0
|
||||
```
|
||||
|
||||
You should see `r0 = 0x10` (16), `0x11` (17), `0x12` (18) for each call.
|
||||
You should see `r0 = 16`. If you set breakpoints on the subsequent calls (`0x1000024c`, `0x10000258`) and continue, you will see `17` and `18`.
|
||||
|
||||
### Step 13: Examine IR Key Processing
|
||||
|
||||
Set a breakpoint after ir_getkey returns:
|
||||
Because `ir_getkey` is polled in a loop and returns `-1` constantly when no key is pressed, breaking at the immediate return (`0x10000274`) will just flood you with `-1`s!
|
||||
|
||||
Instead, set a breakpoint *inside* the `if (key >= 0)` block at `0x10000278`. This is right after the `blt.n` conditional branch that skips over the processing logic when no key is pressed:
|
||||
|
||||
```gdb
|
||||
break *0x10000290
|
||||
break *0x10000278
|
||||
continue
|
||||
```
|
||||
|
||||
Press a button on the remote, then check:
|
||||
Now press a button on the remote. The program will halt. At this point, the key value has been moved into `r4` (from the `subs r4, r0, #0` instruction earlier):
|
||||
|
||||
```gdb
|
||||
info registers r0
|
||||
info registers r4
|
||||
```
|
||||
|
||||
You'll see the NEC code (0x0C, 0x18, or 0x5E).
|
||||
You'll see the decimal value of the NEC code (e.g., 12 for `0x0C`, 24 for `0x18`, or 94 for `0x5E`).
|
||||
|
||||
### Step 14: Watch the Conditional Checks
|
||||
|
||||
Step through the NEC code comparisons:
|
||||
Let's look at the comparisons where the code checks which button was pressed:
|
||||
|
||||
```gdb
|
||||
stepi 10
|
||||
info registers
|
||||
```
|
||||
|
||||
Watch for `cmp r0, #0x0c`, `cmp r0, #0x18`, `cmp r0, #0x5e` instructions.
|
||||
|
||||
### Step 15: Examine gpio_put Arguments
|
||||
|
||||
Before each gpio_put call, check the pin and state:
|
||||
|
||||
```gdb
|
||||
break *0x100002a0
|
||||
break *0x10000280
|
||||
continue
|
||||
info registers r0 r1
|
||||
```
|
||||
|
||||
`r0` = GPIO pin number, `r1` = state (0 or 1).
|
||||
*(Note: If the program doesn't halt immediately, press a button on the remote to trigger the breakpoint!)*
|
||||
|
||||
```gdb
|
||||
x/8i $pc
|
||||
```
|
||||
|
||||
Notice the compiler uses decimal comparisons: `cmp r4, #12` (which is `0x0c`), `cmp r4, #24` (which is `0x18`), and a subtraction trick `sub.w r4, r4, #94` (which is `0x5e`) to determine which button was pressed.
|
||||
|
||||
### Step 15: Observe Inlined GPIO Operations
|
||||
|
||||
The compiler even inlined the `gpio_put` calls! First, let's clear any old breakpoints so we don't accidentally stop somewhere else (like you might have seen if you hit an old breakpoint first!):
|
||||
|
||||
```gdb
|
||||
delete
|
||||
break *0x10000298
|
||||
continue
|
||||
```
|
||||
|
||||
*(Note: Again, press a button on the remote if it doesn't halt immediately!)*
|
||||
|
||||
Now check the registers:
|
||||
|
||||
```gdb
|
||||
info registers r1 r2 r3 r4
|
||||
```
|
||||
|
||||
Depending on which button you pressed, one of the state registers will be `1` (ON) and the others will be `0` (OFF):
|
||||
- `r1` = State for the **Red** LED (GPIO 16)
|
||||
- `r3` = State for the **Green** LED (GPIO 17)
|
||||
- `r4` = State for the **Yellow** LED (GPIO 18)
|
||||
- `r2` = `16` (`0x10`), which is the starting pin number being written to.
|
||||
|
||||
*(Note: Since you are paused here, the physical LEDs haven't updated yet! They will only change after these `mcrr` instructions execute.)*
|
||||
|
||||
Instead of branching to `gpio_put`, it uses direct hardware instructions (disassembled as `mcrr` on the RP2350) to write the states directly to the GPIO hardware.
|
||||
|
||||
### Step 16: Exit GDB
|
||||
|
||||
@@ -788,18 +813,18 @@ At address `0x10000236`:
|
||||
Look for three consecutive calls with values 16, 17, 18:
|
||||
|
||||
```assembly
|
||||
movs r0, #0x10 ; 16 = GPIO16 (led1_pin)
|
||||
bl FUN_xxxxx ; gpio_init
|
||||
1000023a 10 20 movs r0,#0x10
|
||||
1000023c 00 f0 8c f9 bl FUN_10000558 ; gpio_init
|
||||
|
||||
movs r0, #0x11 ; 17 = GPIO17 (led2_pin)
|
||||
bl FUN_xxxxx ; gpio_init
|
||||
1000024a 11 20 movs r0,#0x11
|
||||
1000024c 00 f0 84 f9 bl FUN_10000558 ; gpio_init
|
||||
|
||||
movs r0, #0x12 ; 18 = GPIO18 (led3_pin)
|
||||
bl FUN_xxxxx ; gpio_init
|
||||
10000256 12 20 movs r0,#0x12
|
||||
10000258 00 f0 7e f9 bl FUN_10000558 ; gpio_init
|
||||
```
|
||||
|
||||
This pattern reveals the struct members! Update the function signature:
|
||||
1. Right-click -> **Edit Function Signature**
|
||||
1. Right-click on `FUN_10000558` -> **Edit Function Signature**
|
||||
2. Change to: `void gpio_init(uint gpio)`
|
||||
3. Click **OK**
|
||||
|
||||
@@ -808,19 +833,24 @@ This pattern reveals the struct members! Update the function signature:
|
||||
Look for a function call with GPIO 5:
|
||||
|
||||
```assembly
|
||||
movs r0, #0x5 ; GPIO 5 for IR receiver
|
||||
bl FUN_xxxxx ; ir_init
|
||||
10000262 05 20 movs r0,#0x5
|
||||
10000264 00 f0 38 f8 bl FUN_100002d8 ; ir_init
|
||||
```
|
||||
|
||||
1. Right-click -> **Edit Function Signature**
|
||||
1. Right-click on `FUN_100002d8` -> **Edit Function Signature**
|
||||
2. Change to: `void ir_init(uint pin)`
|
||||
3. Click **OK**
|
||||
|
||||
### Step 26: Resolve printf
|
||||
|
||||
Right after ir_init, look for the "IR receiver on GPIO" string being loaded:
|
||||
Right after ir_init, look for the "IR receiver on GPIO" string being loaded and passed to a function:
|
||||
|
||||
1. Right-click -> **Edit Function Signature**
|
||||
```assembly
|
||||
1000026a 19 48 ldr r0=>s_IR_receiver_on_GPIO_%d...
|
||||
1000026c 03 f0 46 f9 bl FUN_100034fc ; printf
|
||||
```
|
||||
|
||||
1. Right-click on `FUN_100034fc` -> **Edit Function Signature**
|
||||
2. Change to: `int printf(char *format,...)`
|
||||
3. Check the **Varargs** checkbox
|
||||
4. Click **OK**
|
||||
@@ -830,12 +860,12 @@ Right after ir_init, look for the "IR receiver on GPIO" string being loaded:
|
||||
Look for a function that returns a value checked against conditions:
|
||||
|
||||
```assembly
|
||||
bl FUN_xxxxx ; Call ir_getkey
|
||||
cmp r0, #0 ; Check if >= 0
|
||||
blt no_key ; If negative, no key pressed
|
||||
10000270 00 f0 46 f8 bl FUN_10000300 ; Call ir_getkey
|
||||
10000274 04 1e subs r4,r0,#0x0 ; Check if >= 0
|
||||
10000276 1e db blt LAB_100002b6 ; If negative, no key pressed
|
||||
```
|
||||
|
||||
1. Right-click -> **Edit Function Signature**
|
||||
1. Right-click on `FUN_10000300` -> **Edit Function Signature**
|
||||
2. Change to: `int ir_getkey(void)`
|
||||
3. Click **OK**
|
||||
|
||||
@@ -844,11 +874,11 @@ blt no_key ; If negative, no key pressed
|
||||
Look for calls with 10 (0x0A) or 1 (0x01):
|
||||
|
||||
```assembly
|
||||
movs r0, #0x0A ; 10 milliseconds
|
||||
bl FUN_xxxxx ; sleep_ms
|
||||
100002a8 0a 20 movs r0,#0xa
|
||||
100002aa 00 f0 81 fe bl FUN_10000fb0 ; sleep_ms
|
||||
```
|
||||
|
||||
1. Right-click -> **Edit Function Signature**
|
||||
1. Right-click on `FUN_10000fb0` -> **Edit Function Signature**
|
||||
2. Change to: `void sleep_ms(uint ms)`
|
||||
3. Click **OK**
|
||||
|
||||
@@ -861,11 +891,12 @@ bl FUN_xxxxx ; sleep_ms
|
||||
After each `gpio_init`, look for direction setting:
|
||||
|
||||
```assembly
|
||||
mov.w r4, #0x1 ; direction = output (1 = GPIO_OUT)
|
||||
mcrr p0, 0x4, r3, r4 ; Configure GPIO direction register
|
||||
10000240 4f f0 01 04 mov.w r4,#0x1 ; direction = output
|
||||
10000244 10 23 movs r3,#0x10 ; GPIO 16
|
||||
10000246 44 ec 44 30 mcrr p0,0x4,r3,r4,cr4 ; Configure GPIO direction register
|
||||
```
|
||||
|
||||
This is the compiler's version of `gpio_set_dir(pin, GPIO_OUT)`.
|
||||
This is the compiler's heavily optimized version of `gpio_set_dir(pin, GPIO_OUT)`.
|
||||
|
||||
### Step 30: Map the Struct Members
|
||||
|
||||
@@ -894,21 +925,22 @@ Create a mental (or written) map:
|
||||
|
||||
## Part 14: Hacking Structures
|
||||
|
||||
### Step 31: Open the Bytes Editor
|
||||
### Step 31: Enable Instruction Patching
|
||||
|
||||
1. Click **Window** -> **Bytes**
|
||||
2. Click the pencil icon to enable editing
|
||||
We will use Ghidra's **Patch Instruction** feature to modify the assembly directly instead of editing raw bytes.
|
||||
|
||||
### Step 32: Swap LED Pin Assignments
|
||||
|
||||
We'll swap the red and green LED pins to reverse their behavior!
|
||||
We'll swap the red and green LED pins to reverse their behavior! Because the compiler fully flattened the struct, modifying the `gpio_init` pins won't actually change the main loop's behavior (since all three pins are initialized anyway). We must patch the hardcoded pins inside the **main loop** itself!
|
||||
|
||||
**Find the gpio_init calls:**
|
||||
**Find and patch the `movs` calls in the loop:**
|
||||
|
||||
1. Locate where `0x10` (16) is loaded for led1_pin
|
||||
2. Change `0x10` to `0x11` (swap red to green's pin)
|
||||
3. Locate where `0x11` (17) is loaded for led2_pin
|
||||
4. Change `0x11` to `0x10` (swap green to red's pin)
|
||||
1. Navigate to `10000296` where the red LED pin is loaded: `movs r2,#0x10`
|
||||
2. Right-click the instruction -> **Patch Instruction** (or press Ctrl+Shift+G)
|
||||
3. Change `#0x10` to `#0x11` (swap red to green's pin) and press **Enter**
|
||||
4. Navigate to `1000029c` where the green LED pin is loaded: `movs r2,#0x11`
|
||||
5. Right-click the instruction -> **Patch Instruction**
|
||||
6. Change `#0x11` to `#0x10` (swap green to red's pin) and press **Enter**
|
||||
|
||||
**Before:**
|
||||
```
|
||||
@@ -925,7 +957,7 @@ LED 2 (0x18) -> GPIO 16 -> Red LED (SWAPPED!)
|
||||
### Step 33: Export and Flash
|
||||
|
||||
1. Click **File** -> **Export Program**
|
||||
2. Set **Format** to **Binary**
|
||||
2. Set **Format** to **Raw Bytes**
|
||||
3. Name: `0x0023_structures-h.bin`
|
||||
4. Click **OK**
|
||||
|
||||
@@ -959,14 +991,14 @@ python ..\uf2conv.py build\0x0023_structures-h.bin --base 0x10000000 --family 0x
|
||||
| +-----------------+ +-----------------+ |
|
||||
| | Terminal Log | | Physical LEDs | |
|
||||
| +-----------------+ +-----------------+ |
|
||||
| | NEC: 0x0C | ?------- | GREEN LED on | ?-- Mismatch! |
|
||||
| | NEC: 0x0C | +------- | GREEN LED on | <-- Mismatch! |
|
||||
| | (expects RED) | | (not red!) | |
|
||||
| +-----------------+ +-----------------+ |
|
||||
| | NEC: 0x18 | ?------- | RED LED on | ?-- Mismatch! |
|
||||
| | NEC: 0x18 | +------- | RED LED on | <-- Mismatch! |
|
||||
| | (expects GREEN) | | (not green!) | |
|
||||
| +-----------------+ +-----------------+ |
|
||||
| |
|
||||
| The OPERATOR sees correct logs but WRONG physical behavior! |
|
||||
| The OPERATOR sees correct logs but WRONG physical behavior! |
|
||||
| |
|
||||
+-----------------------------------------------------------------+
|
||||
```
|
||||
@@ -1050,21 +1082,21 @@ int process_ir_led_command(int ir_command, simple_led_ctrl_t *leds, uint8_t blin
|
||||
| |
|
||||
| main() |
|
||||
| | |
|
||||
| +--? process_ir_led_command(key, &leds, 3) |
|
||||
| +--> process_ir_led_command(key, &leds, 3) |
|
||||
| | |
|
||||
| +--? leds_all_off(&leds) |
|
||||
| | +--? gpio_put() * 3 |
|
||||
| +--> leds_all_off(&leds) |
|
||||
| | +--> gpio_put() * 3 |
|
||||
| | |
|
||||
| +--? ir_to_led_number(ir_command) |
|
||||
| | +--? returns 1, 2, or 3 |
|
||||
| +--> ir_to_led_number(ir_command) |
|
||||
| | +--> returns 1, 2, or 3 |
|
||||
| | |
|
||||
| +--? get_led_pin(&leds, led_num) |
|
||||
| | +--? returns GPIO pin number |
|
||||
| +--> get_led_pin(&leds, led_num) |
|
||||
| | +--> returns GPIO pin number |
|
||||
| | |
|
||||
| +--? blink_led(pin, 3, 50) |
|
||||
| | +--? gpio_put() + sleep_ms() in loop |
|
||||
| +--> blink_led(pin, 3, 50) |
|
||||
| | +--> gpio_put() + sleep_ms() in loop |
|
||||
| | |
|
||||
| +--? gpio_put(pin, true) |
|
||||
| +--> gpio_put(pin, true) |
|
||||
| |
|
||||
+-----------------------------------------------------------------+
|
||||
```
|
||||
@@ -1086,8 +1118,8 @@ int process_ir_led_command(int ir_command, simple_led_ctrl_t *leds, uint8_t blin
|
||||
|
||||
Open a terminal and start OpenOCD:
|
||||
|
||||
```cmd
|
||||
openocd -s "%USERPROFILE%\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000"
|
||||
```powershell
|
||||
openocd -s "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000"
|
||||
```
|
||||
|
||||
You should see output indicating OpenOCD connected successfully to your Pico 2 via the Debug Probe.
|
||||
@@ -1126,75 +1158,116 @@ disassemble 0x10000234,+300
|
||||
|
||||
You'll see multiple function prologues (push) and epilogues (pop) for the helper functions.
|
||||
|
||||
### Step 43: Set Breakpoints on Key Functions
|
||||
### Step 43: Discover the Missing Functions
|
||||
|
||||
Set breakpoints on the helper functions:
|
||||
The C code relies heavily on helper functions (`process_ir_led_command`, `leds_all_off`, `ir_to_led_number`, `blink_led`). But if you scroll through the disassembly, you'll notice a distinct lack of function prologues, epilogues, or `bl` calls to anything other than `sleep_ms` or `printf`!
|
||||
|
||||
The compiler has aggressively **inlined** every single helper function into the main loop.
|
||||
|
||||
Let's trace exactly how the compiler flattened this logic. First, set a breakpoint right when a key press is detected:
|
||||
|
||||
```gdb
|
||||
break *0x10000234
|
||||
break *0x10000280
|
||||
break *0x100002a0
|
||||
```
|
||||
|
||||
Reset and run:
|
||||
|
||||
```gdb
|
||||
monitor reset halt
|
||||
break *0x10000284
|
||||
continue
|
||||
```
|
||||
|
||||
### Step 44: Trace the Function Call Chain
|
||||
*(Press a button on your remote to trigger the breakpoint!)*
|
||||
|
||||
When you press a remote button, step through the calls:
|
||||
### Step 44: Examine leds_all_off (Inlined)
|
||||
|
||||
In the C code, `process_ir_led_command` starts by calling `leds_all_off(&leds)`.
|
||||
|
||||
Let's look at the next few instructions starting from our breakpoint:
|
||||
```gdb
|
||||
stepi 50
|
||||
info registers
|
||||
x/8i $pc
|
||||
```
|
||||
|
||||
Watch the call chain: `process_ir_led_command` -> `leds_all_off` -> `ir_to_led_number` -> `get_led_pin` -> `blink_led`.
|
||||
You'll see:
|
||||
```assembly
|
||||
0x10000284 <main+80>: mov r1, r4
|
||||
0x10000286 <main+82>: ldr r0, [pc, #156]
|
||||
0x10000288 <main+84>: bl 0x10003554 <__wrap_printf>
|
||||
0x1000028c <main+88>: movs r5, #16
|
||||
0x1000028e <main+90>: mcrr 0, 4, r5, r6, cr0
|
||||
0x10000292 <main+94>: movs r3, #17
|
||||
0x10000294 <main+96>: mcrr 0, 4, r3, r6, cr0
|
||||
0x10000298 <main+100>: movs r2, #18
|
||||
```
|
||||
Because `r6` was set to `0` earlier, the compiler is just directly writing `0` to pins 16, 17, and 18 using `mcrr`. It bypassed the function call entirely!
|
||||
|
||||
### Step 45: Examine ir_to_led_number
|
||||
### Step 45: Examine ir_to_led_number (Inlined)
|
||||
|
||||
When the comparison function runs, check the return value:
|
||||
Next, the C code calls `ir_to_led_number` and `get_led_pin`. Let's see how the compiler handled that by inspecting further down:
|
||||
|
||||
```gdb
|
||||
info registers r0
|
||||
x/4i 0x1000029e
|
||||
```
|
||||
|
||||
For button "1", you should see `r0 = 1`. For button "2", `r0 = 2`.
|
||||
```assembly
|
||||
0x1000029e <main+106>: cmp r4, #12
|
||||
0x100002a0 <main+108>: beq.n 0x100002c6 <main+146>
|
||||
0x100002a2 <main+110>: cmp r4, #24
|
||||
0x100002a4 <main+112>: beq.n 0x1000030a <main+214>
|
||||
```
|
||||
Instead of a separate function, it simply compares the button code in `r4` (`12`, `24`, `94`) and branches straight to the correct blinking logic!
|
||||
|
||||
### Step 46: Watch the blink_led Loop
|
||||
|
||||
Set a breakpoint inside blink_led and watch it execute 3 times:
|
||||
Let's set a breakpoint where the blinking loop begins for Button 1 (which handles the Red LED on pin 16).
|
||||
|
||||
```gdb
|
||||
break *0x100002c0
|
||||
break *0x100002c6
|
||||
continue
|
||||
info registers r0 r1
|
||||
```
|
||||
|
||||
`r0` = pin number, `r1` = state (alternates 0 and 1).
|
||||
Once halted, inspect the setup:
|
||||
```gdb
|
||||
x/6i $pc
|
||||
```
|
||||
|
||||
### Step 47: Examine Pointer Dereference
|
||||
```assembly
|
||||
0x100002c6 <main+146>: mov.w r8, #1 @ led_num = 1
|
||||
0x100002ca <main+150>: movs r4, #3 @ blink_count = 3
|
||||
0x100002cc <main+152>: mcrr 0, 4, r5, r7, cr0 @ Turn LED ON (r7=1)
|
||||
0x100002d0 <main+156>: movs r0, #50 @ Delay 50ms
|
||||
0x100002d2 <main+158>: bl 0x10001008 <sleep_ms>
|
||||
0x100002d6 <main+162>: mcrr 0, 4, r5, r6, cr0 @ Turn LED OFF (r6=0)
|
||||
```
|
||||
The compiler placed the blink count in `r4` and handles the toggling with `mcrr` instructions surrounding `sleep_ms`.
|
||||
|
||||
Watch how the struct pointer is used to get LED pins:
|
||||
### Step 47: The Struct Pointer is a Lie
|
||||
|
||||
If you were trying to find the `leds` struct pointer to examine the pins using `x/6xb`, you'd be looking forever. Because the compiler realized the struct is never passed to any external non-inlined functions, it **never bothered creating it in memory**. It simply kept track of the pin numbers (16, 17, 18) directly in registers during compilation!
|
||||
|
||||
### Step 48: Observe the Loop Condition
|
||||
|
||||
Continue execution until you hit the end of the blink loop:
|
||||
|
||||
```gdb
|
||||
x/6xb $r0
|
||||
break *0x100002e0
|
||||
continue
|
||||
```
|
||||
|
||||
This shows the struct contents when `leds` pointer is in r0.
|
||||
|
||||
### Step 48: Watch Return Values
|
||||
|
||||
After function calls, check return values in r0:
|
||||
At this point, GDB is halted **right before** it performs the subtraction. If you check `r3` now, it will contain random leftover garbage (like `0x400b0000`) because the instruction hasn't run yet! Check `r4` (the current blink count):
|
||||
|
||||
```gdb
|
||||
stepi 20
|
||||
info registers r0
|
||||
info registers r4
|
||||
```
|
||||
|
||||
Now, step forward two instructions to let it actually do the math:
|
||||
|
||||
```gdb
|
||||
stepi 2
|
||||
info registers r3 r4
|
||||
```
|
||||
|
||||
```assembly
|
||||
0x100002e0 <main+172>: subs r3, r4, #1
|
||||
0x100002e2 <main+174>: ands.w r4, r3, #255
|
||||
0x100002e6 <main+178>: bne.n 0x100002cc <main+152>
|
||||
```
|
||||
You will now see `r3` become `2`, and `r4` become `2`! It successfully subtracted 1 from the blink count and stored it back. Since it hasn't hit 0 yet, the `bne.n` instruction will branch back to the start of the blink (`0x100002cc`) to flash the LED again!
|
||||
|
||||
### Step 49: Exit GDB
|
||||
|
||||
When done exploring:
|
||||
@@ -1491,7 +1564,4 @@ This is just the beginning:
|
||||
|
||||
**Congratulations on completing this course! You now have the curiosity, persistence, and skills that embedded systems engineers and security researchers thrive on. Keep experimenting, documenting, and sharing your work. The world needs more builders and defenders like you!**
|
||||
|
||||
Happy hacking! ?
|
||||
|
||||
|
||||
|
||||
Happy hacking! :)
|
||||
|
||||
Reference in New Issue
Block a user