mirror of
https://github.com/mytechnotalent/Embedded-Hacking.git
synced 2026-05-19 22:38:05 +02:00
142 lines
5.9 KiB
Markdown
142 lines
5.9 KiB
Markdown
# Embedded Systems Reverse Engineering
|
|
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
|
|
|
|
## Week 10
|
|
Conditionals in Embedded Systems: Debugging and Hacking Static & Dynamic Conditionals w/ SG90 Servo Motor PWM Basics
|
|
|
|
### Non-Credit Practice Exercise 3: Reverse the Servo Direction
|
|
|
|
#### Objective
|
|
Find the branch targets for case '1' and case '2' in the `0x0020_dynamic-conditionals` binary using GDB, identify where each case loads its angle values from the literal pool, and swap the angle pairs so that pressing '1' now does what '2' originally did (180°→0°) and pressing '2' does what '1' originally did (0°→180°).
|
|
|
|
#### Prerequisites
|
|
- Completed Week 10 tutorial (GDB and hex editor sections)
|
|
- `0x0020_dynamic-conditionals.elf` and `0x0020_dynamic-conditionals.bin` available in your build directory
|
|
- GDB (`arm-none-eabi-gdb`) and OpenOCD installed
|
|
- A hex editor (HxD, ImHex, or similar)
|
|
- Python installed (for UF2 conversion)
|
|
- Raspberry Pi Pico 2 with SG90 servo motor connected on GPIO 6
|
|
|
|
#### Task Description
|
|
Currently, case '1' calls `servo_set_angle(0.0f)` then `servo_set_angle(180.0f)`, while case '2' calls `servo_set_angle(180.0f)` then `servo_set_angle(0.0f)`. To reverse the servo direction, you will swap the literal pool angle values between the two cases. This means patching case '1' to load 180.0f first and 0.0f second, and case '2' to load 0.0f first and 180.0f second.
|
|
|
|
#### 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\0x0020_dynamic-conditionals.elf
|
|
```
|
|
|
|
**Connect to target:**
|
|
|
|
```gdb
|
|
(gdb) target remote :3333
|
|
(gdb) monitor reset halt
|
|
```
|
|
|
|
##### Step 2: Map Both Case Branch Targets
|
|
|
|
Disassemble main and trace both paths:
|
|
|
|
```gdb
|
|
(gdb) disassemble 0x10000234,+250
|
|
```
|
|
|
|
Identify:
|
|
- Case '1' branch target → first `ldr r0` (loads 0.0f), `bl servo_set_angle`, second `ldr r0` (loads 180.0f), `bl servo_set_angle`
|
|
- Case '2' branch target → first `ldr r0` (loads 180.0f), `bl servo_set_angle`, second `ldr r0` (loads 0.0f), `bl servo_set_angle`
|
|
|
|
##### Step 3: Find All Four Literal Pool Entries
|
|
|
|
Examine the literal pool entries for each angle load:
|
|
|
|
```gdb
|
|
(gdb) x/wx <case1_angle1_literal>
|
|
(gdb) x/wx <case1_angle2_literal>
|
|
(gdb) x/wx <case2_angle1_literal>
|
|
(gdb) x/wx <case2_angle2_literal>
|
|
```
|
|
|
|
Record which addresses contain `0x00000000` (0.0f) and which contain `0x43340000` (180.0f).
|
|
|
|
##### Step 4: Calculate All File Offsets
|
|
|
|
```
|
|
file_offset = literal_pool_address - 0x10000000
|
|
```
|
|
|
|
You need four offsets — two for case '1' angles and two for case '2' angles.
|
|
|
|
##### Step 5: Plan the Swap
|
|
|
|
| Location | Original | New |
|
|
| -------------- | ---------------- | ---------------- |
|
|
| Case 1 Angle 1 | `00 00 00 00` (0.0f) | `00 00 34 43` (180.0f) |
|
|
| Case 1 Angle 2 | `00 00 34 43` (180.0f) | `00 00 00 00` (0.0f) |
|
|
| Case 2 Angle 1 | `00 00 34 43` (180.0f) | `00 00 00 00` (0.0f) |
|
|
| Case 2 Angle 2 | `00 00 00 00` (0.0f) | `00 00 34 43` (180.0f) |
|
|
|
|
###### Question 1: Could the compiler share a single literal pool entry for all references to `0x43340000`? How would that affect your patching plan?
|
|
|
|
##### Step 6: Patch with HxD
|
|
|
|
1. In HxD, open `C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0020_dynamic-conditionals\build\0x0020_dynamic-conditionals.bin`
|
|
2. For each of the four literal pool entries, navigate to its file offset and swap the values as planned
|
|
3. Be methodical — patch one at a time and verify each before moving to the next
|
|
|
|
##### Step 7: Save and Convert
|
|
|
|
1. Click **File** → **Save As** → `0x0020_dynamic-conditionals-h.bin`
|
|
|
|
```powershell
|
|
cd C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0020_dynamic-conditionals
|
|
python ..\uf2conv.py build\0x0020_dynamic-conditionals-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2
|
|
```
|
|
|
|
##### Step 8: Flash and Verify
|
|
|
|
1. Hold BOOTSEL and plug in your Pico 2
|
|
2. Drag and drop `hacked.uf2` onto the RPI-RP2 drive
|
|
|
|
**Check the behavior:**
|
|
- Press '1' → servo sweeps from **180° to 0°** (was 0° to 180°)
|
|
- Press '2' → servo sweeps from **0° to 180°** (was 180° to 0°)
|
|
- The text output still says "one" and "two" — only the physical behavior changed
|
|
|
|
#### Expected Output
|
|
|
|
After completing this exercise, you should be able to:
|
|
- Trace multiple branch paths to their literal pool references
|
|
- Understand how data (angles) and code (branches) are separate concerns
|
|
- Swap data values to reverse physical behavior while keeping code structure intact
|
|
- Recognize shared vs. separate literal pool entries
|
|
|
|
#### Questions for Reflection
|
|
|
|
###### Question 1: The terminal still prints "one" and "two" with the original meanings, but the servo does the opposite. Why is this a security concern in real embedded systems?
|
|
|
|
###### Question 2: Instead of swapping literal pool values, could you swap the branch targets themselves? What are the pros and cons of each approach?
|
|
|
|
###### Question 3: If the literal pool entries are shared between cases (one `0x43340000` word referenced by both), how would your patch strategy change?
|
|
|
|
###### Question 4: What tool could you use to confirm the swapped behavior without physical hardware — just by reading the patched disassembly?
|
|
|
|
#### Tips and Hints
|
|
- Use `x/4wx <literal_pool_start>` to dump the entire literal pool at once and see all angle values together
|
|
- If the compiler shares a single literal pool entry for 180.0f across both cases, swapping it would affect both — you may need to create a duplicate entry
|
|
- The simplest approach: if each case has its own literal pool entries, just swap the 4-byte values at each offset
|
|
- Verify by disassembling the patched binary in GDB to confirm the `ldr` instructions now reference the swapped values
|