Files
Embedded-Hacking/WEEK04/WEEK04-04.md
T
Kevin Thomas 29073bd383 Added WEEK04
2026-01-31 14:07:15 -05:00

346 lines
9.5 KiB
Markdown

# Embedded Systems Reverse Engineering
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
## Week 4
Variables in Embedded Systems: Debugging and Hacking Variables w/ GPIO Output Basics
### Exercise 4: Patch GPIO Binary to Change LED Pin
#### Objective
Patch the `0x0008_uninitialized-variables.bin` binary to change which LED blinks, modify the printed value, and change the blink timing, then verify all changes work correctly on hardware.
#### Prerequisites
- Completed Exercise 3 (GPIO binary analyzed in Ghidra)
- Understanding of how GPIO pin numbers are encoded
- Knowledge of hexadecimal-to-decimal conversion
- Pico 2 with ability to test multiple GPIO pins
#### Task Description
You will locate all instances where GPIO 16 is used, patch them to GPIO 17 (changing from red LED to green LED), modify the printed value from 0 to 66, and adjust the blink timing from 500ms to 100ms for faster blinking.
#### Step-by-Step Instructions
##### Step 1: Plan Your Patches
Before we start patching, let's identify what needs to change:
| Current Value | New Value | Description | Hex Conversion |
| ------------- | --------- | --------------------- | ------------------- |
| GPIO 16 | GPIO 17 | Change LED pin | 0x10 → 0x11 |
| age = 0 | age = 66 | Change printed value | 0x00 → 0x42 |
| 500ms | 100ms | Change blink timing | 0x1f4 → 0x64 |
**Verify the hex conversions:**
- 17 decimal = 0x11 hex ✓
- 66 decimal = 0x42 hex ✓
- 100 decimal = 0x64 hex ✓
##### Step 2: Open the GPIO Project in Ghidra
1. Launch Ghidra and open `week04-ex03-gpio-analysis`
2. Double-click the binary to open the CodeBrowser
3. Navigate to the `main` function
**Review the decompiled code:**
```c
int main(void)
{
stdio_init_all();
gpio_init(0x10);
gpio_set_dir(0x10, 1);
while (true) {
printf("age: %d\r\n", 0);
gpio_put(0x10, 1);
sleep_ms(0x1f4);
gpio_put(0x10, 0);
sleep_ms(0x1f4);
}
}
```
##### Step 3: Find and Patch gpio_init Parameter
Look at the **Listing** window (assembly view) for the main function.
**Find the gpio_init call:**
```assembly
1000023a 10 20 movs r0, #0x10
1000023c xx xx bl gpio_init
```
**Patch instruction:**
1. Right-click on `movs r0, #0x10`
2. Select **Patch Instruction**
3. Change `#0x10` to `#0x11`
4. Press **Enter**
**Result:**
```assembly
1000023a 11 20 movs r0, #0x11
```
The instruction bytes change from `10 20` to `11 20`.
##### Step 4: Find and Patch gpio_set_dir Parameter
Continue down the assembly listing:
```assembly
10000240 10 23 movs r3, #0x10
10000242 01 22 movs r2, #1
10000244 xx xx bl gpio_set_dir
```
**Patch the r3 load:**
1. Right-click on `movs r3, #0x10`
2. Select **Patch Instruction**
3. Change to `#0x11`
4. Press **Enter**
**Why r3 instead of r0?** The SDK might pass GPIO pin as the first parameter differently, or this could be due to register allocation. Trust the analysis!
##### Step 5: Find All gpio_put Calls
Inside the while loop, there are two `gpio_put` calls:
**First gpio_put (LED ON):**
```assembly
10000252 10 24 movs r4, #0x10
10000254 01 25 movs r5, #1
10000256 xx xx bl gpio_put
```
**Patch:**
1. Right-click on `movs r4, #0x10`
2. Change to `#0x11`
**Second gpio_put (LED OFF):**
```assembly
1000025e 10 24 movs r4, #0x10
10000260 00 25 movs r5, #0
10000262 xx xx bl gpio_put
```
**Patch:**
1. Right-click on `movs r4, #0x10`
2. Change to `#0x11`
**Note:** The exact addresses will vary, but the pattern is consistent.
##### Step 6: Patch the Printed Value
Find the printf call in the loop:
```assembly
1000024a 00 21 movs r1, #0x0
1000024c xx xx ldr r0, [pc, #offset]
1000024e xx xx bl printf
```
**Patch the value:**
1. Right-click on `movs r1, #0x0`
2. Select **Patch Instruction**
3. Change to `#0x42` (66 in decimal)
4. Press **Enter**
**Result:**
```assembly
1000024a 42 21 movs r1, #0x42
```
##### Step 7: Patch the Sleep Timing (First)
Find the first `sleep_ms(0x1f4)` call:
```assembly
10000258 f4 21 movs r1, #0xf4
1000025a 01 20 movs r0, #1
1000025c 00 04 lsls r0, r0, #16
1000025e 08 44 add r0, r1
10000260 xx xx bl sleep_ms
```
**Wait, this looks complex!** The value 0x1f4 (500) is being constructed:
- Load 1 into r0
- Shift left 16 bits: 1 << 16 = 0x10000
- Load 0xf4 into r1
- Add them: 0x10000 + 0xf4 = 0x1f4
**Alternative pattern (simpler):**
```assembly
10000xxx f4 20 movs r0, #0xf4
10000xxx 01 20 movs r1, #0x01
10000xxx ...
```
**For 100ms (0x64):**
Simply find where 0x1f4 is loaded and change it to 0x64.
**If the instruction is:**
```assembly
movs r0, #0x1f4
```
**Change to:**
```assembly
movs r0, #0x64
```
**Note:** The exact encoding depends on the instruction. For immediate values > 255, Thumb-2 uses different encodings.
##### Step 8: Handle Large Immediate Values
If `sleep_ms(500)` uses a multi-instruction sequence to load 0x1f4, you have two options:
**Option A: Patch both instructions**
If it's loading 0x1f4 as (0x100 + 0xf4):
1. Find where 0xf4 is loaded
2. Change it to 0x64
3. Find where 0x1 is loaded for the high byte
4. Change it to 0x0
**Option B: Simplify to single instruction**
Since 0x64 (100) fits in an 8-bit immediate, you can replace the multi-instruction sequence with:
```assembly
movs r0, #0x64
nop
nop
```
##### Step 9: Verify All Patches
Check the **Decompile** window to confirm changes:
```c
int main(void)
{
stdio_init_all();
gpio_init(0x11); // Changed from 0x10!
gpio_set_dir(0x11, 1);
while (true) {
printf("age: %d\r\n", 0x42); // Changed from 0!
gpio_put(0x11, 1); // Changed from 0x10!
sleep_ms(0x64); // Changed from 0x1f4!
gpio_put(0x11, 0); // Changed from 0x10!
sleep_ms(0x64); // Changed from 0x1f4!
}
}
```
Perfect! All changes are reflected.
##### Step 10: Export the Patched Binary
1. Click **File****Export Program**
2. Set Format: **Binary**
3. Navigate to: `Embedded-Hacking/0x0008_uninitialized-variables/build/`
4. Filename: `0x0008_uninitialized-variables-h.bin`
5. Click **OK**
##### Step 11: Convert to UF2
Open PowerShell and navigate to the project:
```powershell
cd C:\path\to\Embedded-Hacking\0x0008_uninitialized-variables
```
**Run conversion:**
```powershell
python ..\uf2conv.py build\0x0008_uninitialized-variables-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2
```
**Expected output:**
```
Converting to uf2, output size: 61952, start address: 0x10000000
Wrote 61952 bytes to build\hacked.uf2
```
##### Step 12: Flash and Test
**Enter bootloader:**
1. Hold **BOOTSEL** button
2. Plug in USB
3. Release BOOTSEL
**Flash:**
1. Drag `build\hacked.uf2` to RPI-RP2 drive
2. Pico reboots automatically
**Test with serial monitor:**
```
age: 66
age: 66
age: 66
...
```
**Hardware verification:**
- ✅ GREEN LED (GPIO 17) should be blinking
- ✅ RED LED (GPIO 16) should be off
- ✅ Blink rate should be much faster (10 Hz instead of 1 Hz)
- ✅ Serial output shows 66 instead of 0
🎉 **Triple success!** You've patched three different aspects of the program!
#### Expected Output
After completing this exercise, you should:
- See `age: 66` printing continuously
- Observe the green LED (GPIO 17) blinking rapidly
- Understand how to find and patch all instances of a value
- Know how to handle different immediate value encoding schemes
#### Questions for Reflection
###### Question 1: Why did we need to patch GPIO 16 in multiple places (gpio_init, gpio_set_dir, gpio_put)?
###### Question 2: What would happen if you forgot to patch one of the gpio_put calls?
###### Question 3: How does the instruction encoding differ for immediate values less than 256 vs. greater than 255?
###### Question 4: If you wanted to make the LED blink at exactly 5 Hz, what sleep_ms value would you use?
#### Tips and Hints
- Use Ghidra's **Search****For Scalars** to find all instances of a hex value
- Right-click in Listing → **Highlight****Highlight Instruction** helps track your patches
- Make notes of addresses you've patched to avoid confusion
- Test incrementally - patch one thing at a time if you're unsure
- Keep the original UF2 to revert if needed
#### Next Steps
- Try patching to use GPIO 18 (blue LED) instead
- Change the printf format string to display in hexadecimal
- Experiment with different timing patterns (e.g., 200ms on, 800ms off)
#### Additional Challenge
**Advanced Multi-LED Pattern:**
Patch the binary to create an alternating pattern:
- GPIO 16 (red) blinks for 100ms
- GPIO 17 (green) blinks for 100ms
- GPIO 18 (blue) blinks for 100ms
- Repeat
This requires:
1. Adding new gpio_init and gpio_set_dir calls for GPIO 18
2. Restructuring the while loop to have three gpio_put sequences
3. Finding space in the binary or replacing existing code
**Hint:** You might need to NOP out some instructions and carefully insert new ones. This is advanced patching!
#### Verification Checklist
Before moving on, confirm:
- [ ] GPIO 17 LED blinks (not GPIO 16)
- [ ] Blink rate is approximately 10 Hz (100ms on/off)
- [ ] Serial output shows "age: 66"
- [ ] You can explain each patch you made
- [ ] You understand why each patch was necessary
- [ ] You successfully converted and flashed the UF2
**Congratulations!** You've completed all Week 4 exercises and mastered variable analysis, binary patching, and GPIO manipulation!