mirror of
https://github.com/mytechnotalent/Embedded-Hacking.git
synced 2026-04-01 17:10:20 +02:00
188 lines
6.7 KiB
Markdown
188 lines
6.7 KiB
Markdown
# Embedded Systems Reverse Engineering
|
|
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
|
|
|
|
## Week 6
|
|
Static Variables in Embedded Systems: Debugging and Hacking Static Variables w/ GPIO Input Basics
|
|
|
|
### Non-Credit Practice Exercise 2: Reverse Engineer gpio_set_pulls with GDB
|
|
|
|
#### Objective
|
|
Use GDB to disassemble the `gpio_set_pulls` function, trace its register writes to identify the PADS_BANK0 hardware register it modifies, and document how the Pico 2 configures internal pull-up and pull-down resistors at the hardware level.
|
|
|
|
#### Prerequisites
|
|
- Completed Week 6 tutorial (GDB section)
|
|
- `0x0014_static-variables.elf` binary available in your build directory
|
|
- GDB (`arm-none-eabi-gdb`) and OpenOCD installed
|
|
- Understanding of GPIO pull-up resistors from Week 6 Part 2
|
|
- Understanding of function inlining from Week 6 Part 3
|
|
|
|
#### Task Description
|
|
You will use GDB to locate the `gpio_set_pulls` function (remember: `gpio_pull_up` is inlined and becomes `gpio_set_pulls`), disassemble it, step through it instruction by instruction, and identify the PADS_BANK0 register address it writes to. You will document the bit fields being set and explain how the hardware implements pull-up and pull-down resistors.
|
|
|
|
#### Step-by-Step Instructions
|
|
|
|
##### Step 1: Start the Debug Session
|
|
|
|
**Terminal 1 - Start OpenOCD:**
|
|
|
|
```powershell
|
|
openocd ^
|
|
-s "C:\Users\flare-vm\.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:**
|
|
|
|
```powershell
|
|
arm-none-eabi-gdb build\0x0014_static-variables.elf
|
|
```
|
|
|
|
**Connect to target:**
|
|
|
|
```gdb
|
|
(gdb) target remote :3333
|
|
(gdb) monitor reset halt
|
|
```
|
|
|
|
##### Step 2: Find Where gpio_set_pulls is Called
|
|
|
|
From the tutorial, we know `gpio_set_pulls` is called at `0x1000024e` with arguments for GPIO 15:
|
|
|
|
```gdb
|
|
(gdb) x/5i 0x10000240
|
|
```
|
|
|
|
You should see:
|
|
```
|
|
0x10000240: movs r0, #15 ; GPIO pin 15
|
|
0x10000242: mov.w r3, #0 ; GPIO_IN (direction)
|
|
0x10000246: mcrr 0, 4, r0, r3, cr4 ; gpio_set_dir via coprocessor
|
|
0x1000024a: movs r2, #0 ; down = false
|
|
0x1000024c: movs r1, #1 ; up = true
|
|
0x1000024e: bl 0x100002d8 ; gpio_set_pulls
|
|
```
|
|
|
|
##### Step 3: Disassemble gpio_set_pulls
|
|
|
|
Now examine the function itself:
|
|
|
|
```gdb
|
|
(gdb) x/30i 0x100002d8
|
|
```
|
|
|
|
Study the disassembly carefully. Look for:
|
|
- Address calculations (base address of PADS_BANK0)
|
|
- Register loads and stores
|
|
- Bit manipulation instructions (AND, OR, BIC)
|
|
- The write to the hardware register
|
|
|
|
##### Step 4: Set a Breakpoint at gpio_set_pulls
|
|
|
|
```gdb
|
|
(gdb) b *0x100002d8
|
|
(gdb) monitor reset halt
|
|
(gdb) c
|
|
```
|
|
|
|
When the breakpoint hits, examine the arguments:
|
|
|
|
```gdb
|
|
(gdb) info registers r0 r1 r2
|
|
```
|
|
|
|
You should see:
|
|
- `r0 = 15` (GPIO pin)
|
|
- `r1 = 1` (pull-up enable)
|
|
- `r2 = 0` (pull-down disable)
|
|
|
|
##### Step 5: Step Through the Function
|
|
|
|
Use `si` (step instruction) to execute one instruction at a time:
|
|
|
|
```gdb
|
|
(gdb) si
|
|
(gdb) info registers
|
|
```
|
|
|
|
Repeat, watching the registers change. Pay attention to:
|
|
|
|
1. **Address calculation**: The function computes the PADS_BANK0 register address for GPIO 15. The base address is `0x40038000`, and each GPIO pad has a 4-byte register. GPIO 0 starts at offset `0x04`, so GPIO 15 is at:
|
|
|
|
$$0x40038000 + 0x04 + (15 \times 4) = 0x40038000 + 0x04 + 0x3C = 0x40038040$$
|
|
|
|
2. **Read the current register value**: The function reads the existing pad configuration
|
|
3. **Modify the pull bits**: It sets or clears the pull-up (bit 3) and pull-down (bit 2) bits
|
|
4. **Write back**: It stores the modified value
|
|
|
|
##### Step 6: Examine the PADS_BANK0 Register
|
|
|
|
After the function completes, examine the result:
|
|
|
|
```gdb
|
|
(gdb) x/1wx 0x40038040
|
|
```
|
|
|
|
Document the value you see. The relevant bits are:
|
|
|
|
| Bit | Name | Value | Meaning |
|
|
| --- | -------- | ----- | ----------------------- |
|
|
| 3 | PUE | 1 | Pull-up enable |
|
|
| 2 | PDE | 0 | Pull-down enable |
|
|
| 1 | SCHMITT | 1 | Schmitt trigger enabled |
|
|
| 0 | SLEWFAST | 0 | Slow slew rate |
|
|
|
|
##### Step 7: Compare with gpio_set_pulls for the LED Pin
|
|
|
|
Continue execution until gpio_set_pulls is called again (if it is). Or, examine the pad register for GPIO 16 (the LED pin):
|
|
|
|
```gdb
|
|
(gdb) x/1wx 0x40038044
|
|
```
|
|
|
|
Compare the values. The LED pin (output) should NOT have pull-up enabled.
|
|
|
|
##### Step 8: Document Your Findings
|
|
|
|
Create a table documenting the function's behavior:
|
|
|
|
| Step | Instruction(s) | Register Changes |
|
|
| ------------------------ | ------------------ | --------------------------- |
|
|
| Load PADS base address | `ldr rX, =...` | rX = `0x40038000` |
|
|
| Calculate pad offset | `adds`, `lsls` | offset = `0x04 + pin * 4` |
|
|
| Read current pad config | `ldr rX, [addr]` | rX = current register value |
|
|
| Clear pull bits | `bic rX, rX, #0xC` | Clear bits 2 and 3 |
|
|
| Set pull-up bit | `orr rX, rX, #0x8` | Set bit 3 (PUE) |
|
|
| Write back | `str rX, [addr]` | Updated pad register |
|
|
|
|
#### Expected Output
|
|
|
|
After completing this exercise, you should be able to:
|
|
- Disassemble and trace through a hardware configuration function
|
|
- Identify PADS_BANK0 register addresses for any GPIO pin
|
|
- Understand how pull-up and pull-down resistors are controlled at the register level
|
|
- Explain why `gpio_pull_up(pin)` becomes `gpio_set_pulls(pin, true, false)` in the binary
|
|
|
|
#### Questions for Reflection
|
|
|
|
###### Question 1: Why does the function read-modify-write the register instead of just writing a new value? What would happen if it just wrote without reading first?
|
|
|
|
###### Question 2: The pad register for GPIO 15 is at offset `0x40` from the PADS base. How is this offset calculated? What would the offset be for GPIO 0?
|
|
|
|
###### Question 3: What would happen if you enabled BOTH pull-up and pull-down at the same time (bits 2 and 3 both set)?
|
|
|
|
###### Question 4: The compiler inlines `gpio_pull_up` into `gpio_set_pulls`. What does this tell you about the compiler's optimization level? How does inlining affect binary analysis?
|
|
|
|
#### Tips and Hints
|
|
- PADS_BANK0 base address on RP2350 is `0x40038000`
|
|
- Each GPIO has a 4-byte pad control register starting at offset `0x04`
|
|
- The `bic` instruction clears bits (Bit Clear); `orr` sets bits (OR)
|
|
- Use `x/1wx` to examine a 32-bit word; use `x/1tb` to see individual bits
|
|
- The RP2350 datasheet section on PADS_BANK0 has the full register bit map
|
|
|
|
#### Next Steps
|
|
- Proceed to Exercise 3 to reverse engineer the `eor` (XOR) instruction in the GPIO logic
|
|
- Try enabling pull-down instead of pull-up by modifying the `gpio_set_pulls` arguments in GDB: `set $r1 = 0` and `set $r2 = 1` before the function call
|
|
- Examine the PADS registers for all GPIOs to see which pins have pull-ups enabled after boot
|