Files
Embedded-Hacking/WEEK10/WEEK10-04.md
T
2026-03-19 15:01:07 -04:00

145 lines
5.8 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 4: Speed Profile
#### Objective
Find both `sleep_ms(500)` calls in the `0x0020_dynamic-conditionals` binary using GDB, identify the literal pool values `0x1f4` (500) loaded into `r0` before each `bl sleep_ms`, calculate the file offsets, and patch case '1' to use `0x64` (100ms) for fast movement and case '2' to use `0x3e8` (1000ms) for slow movement, then verify on hardware that the two keys produce visibly different servo speeds.
#### 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
Both case '1' and case '2' call `sleep_ms(500)` between their two `servo_set_angle` calls. The value `500` (`0x1F4`) is loaded from the literal pool into `r0` before each `bl sleep_ms`. You will find both `sleep_ms` literal pool entries — one in case '1' and one in case '2' — and patch them to different values: `100` (`0x64`) for fast snapping and `1000` (`0x3E8`) for slow sweeping, creating distinct speed profiles per key.
#### 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: Find Both sleep_ms Calls
Disassemble main and locate the `sleep_ms` calls:
```gdb
(gdb) disassemble 0x10000234,+250
```
Look for the pattern repeated in both cases:
```
ldr r0, [pc, #offset] ; load delay value
bl sleep_ms
```
Each case has at least one `sleep_ms` call. Identify which belongs to case '1' and which to case '2' by tracing the branch targets.
##### Step 3: Examine the Literal Pool Entries
For each `sleep_ms` call, examine the referenced literal pool entry:
```gdb
(gdb) x/wx <case1_sleep_literal>
(gdb) x/wx <case2_sleep_literal>
```
Both should show `0x000001f4` (500).
##### Step 4: Calculate the File Offsets
```
file_offset = literal_pool_address - 0x10000000
```
Note the file offsets for both 4-byte sleep values.
##### Step 5: Encode the New Values
| Case | Original | New | Speed |
| ------ | ----------------- | ----------------- | -------- |
| Case 1 | `F4 01 00 00` (500ms) | `64 00 00 00` (100ms) | Fast snap |
| Case 2 | `F4 01 00 00` (500ms) | `E8 03 00 00` (1000ms) | Slow sweep |
###### Question 1: Each case calls `sleep_ms` twice (once between the first and second `servo_set_angle`). Do both share the same literal pool entry, or does each have its own?
##### 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. Press **Ctrl+G** and go to the case '1' sleep value offset
3. Replace `F4 01 00 00` with `64 00 00 00` (100ms)
4. Press **Ctrl+G** and go to the case '2' sleep value offset
5. Replace `F4 01 00 00` with `E8 03 00 00` (1000ms)
##### 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 snaps **fast** (100ms between angles) — almost instant jump
- Press '2' → servo moves **slow** (1000ms between angles) — takes a full second at each position
- The speed difference should be very obvious visually and audibly
#### Expected Output
After completing this exercise, you should be able to:
- Distinguish between multiple literal pool entries for the same value
- Trace which code path references which literal pool entry
- Patch timing constants independently per branch
- Understand how sleep duration affects perceived motor behavior
#### Questions for Reflection
###### Question 1: Why does 100ms make the servo appear to "snap" while 1000ms makes it appear to "sweep"? Is the servo actually moving faster, or is it about the pause between commands?
###### Question 2: If the compiler uses a single shared literal pool entry for all `sleep_ms(500)` calls, what alternative patching strategy would you need to create different speeds per case?
###### Question 3: What is the minimum `sleep_ms` value that would still allow the servo to physically reach its target angle before the next command? How would you determine this experimentally?
###### Question 4: Could you set the sleep to `0` (`00 00 00 00`)? What would happen to the servo behavior?
#### Tips and Hints
- `100` decimal = `0x64`, fits in one byte: `64 00 00 00` in little-endian
- `1000` decimal = `0x3E8`: `E8 03 00 00` in little-endian
- If both `sleep_ms` calls share one literal pool word, you cannot give them different values by patching data alone — you would need to patch one `ldr` instruction to point to a different pool entry or use a `movs` immediate
- The SG90 servo takes about 200-300ms to traverse its full range, so 100ms will cause it to not quite reach the endpoint before the next command fires