mirror of
https://github.com/mytechnotalent/Embedded-Hacking.git
synced 2026-05-19 06:18:03 +02:00
145 lines
5.8 KiB
Markdown
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
|