mirror of
https://github.com/mytechnotalent/Embedded-Hacking.git
synced 2026-05-20 14:54:50 +02:00
160 lines
5.9 KiB
Markdown
160 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 2: Add a Third Command
|
|
|
|
#### Objective
|
|
Find the comparison instruction `cmp r4, #0x32` ('2') in the `0x0020_dynamic-conditionals` binary using GDB, locate the branch target for case '2', and modify existing code so that pressing '3' (0x33) moves the servo to the center position (90°) by patching one of the existing comparisons and its corresponding angle value to `0x42b40000` (90.0f).
|
|
|
|
#### 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
|
|
The program has two active commands: '1' (0x31) moves the servo 0°→180° and '2' (0x32) moves it 180°→0°. Adding a completely new code path would require rewriting branch offsets throughout the binary. Instead, you will repurpose the '2' command by changing its comparison value from `0x32` ('2') to `0x33` ('3') and patching both of its angle values to `0x42b40000` (90.0f), so pressing '3' moves the servo to center and holds it there.
|
|
|
|
#### 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 the Comparison Instructions
|
|
|
|
Disassemble main and locate both `cmp` instructions:
|
|
|
|
```gdb
|
|
(gdb) disassemble 0x10000234,+250
|
|
```
|
|
|
|
Look for:
|
|
|
|
```
|
|
cmp r4, #0x31 ; compare with '1'
|
|
beq <target1>
|
|
cmp r4, #0x32 ; compare with '2'
|
|
beq <target2>
|
|
```
|
|
|
|
Note the address of the `cmp r4, #0x32` instruction.
|
|
|
|
##### Step 3: Examine the Comparison Byte
|
|
|
|
The `cmp r4, #0x32` instruction encodes `0x32` as an immediate. Examine the instruction bytes:
|
|
|
|
```gdb
|
|
(gdb) x/2bx <address_of_cmp_0x32>
|
|
```
|
|
|
|
You should see a byte containing `0x32`.
|
|
|
|
##### Step 4: Find the Angle Values for Case '2'
|
|
|
|
Follow the `beq` target for case '2' and look for the literal pool loads:
|
|
|
|
```gdb
|
|
(gdb) x/wx <literal_pool_for_case2_angle1>
|
|
(gdb) x/wx <literal_pool_for_case2_angle2>
|
|
```
|
|
|
|
Case '2' loads `0x43340000` (180.0f) first, then `0x00000000` (0.0f).
|
|
|
|
##### Step 5: Calculate the File Offsets
|
|
|
|
```
|
|
file_offset = address - 0x10000000
|
|
```
|
|
|
|
Note offsets for:
|
|
1. The `0x32` byte in the `cmp` instruction
|
|
2. Both angle literal pool entries for case '2'
|
|
|
|
##### Step 6: Encode the New Values
|
|
|
|
| Patch Target | Original | New | Purpose |
|
|
| ---------------- | ---------------- | ---------------- | ------------------------- |
|
|
| Compare byte | `32` | `33` | Match '3' instead of '2' |
|
|
| Angle 1 (180.0f) | `00 00 34 43` | `00 00 b4 42` | 90.0f center position |
|
|
| Angle 2 (0.0f) | `00 00 00 00` | `00 00 b4 42` | 90.0f center position |
|
|
|
|
##### Step 7: 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 compare byte offset
|
|
3. Change `32` to `33`
|
|
4. Go to the first angle offset for case '2' and replace `00 00 34 43` with `00 00 b4 42`
|
|
5. Go to the second angle offset for case '2' and replace `00 00 00 00` with `00 00 b4 42`
|
|
|
|
###### Question 1: Why do we set both angle values to 90.0f instead of just one?
|
|
|
|
##### Step 8: 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 9: 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 0° to 180° (unchanged)
|
|
- Press '3' → servo moves to **90° center** and stays
|
|
- Press '2' → now falls through to default "??" (no longer mapped)
|
|
|
|
#### Expected Output
|
|
|
|
After completing this exercise, you should be able to:
|
|
- Locate and patch comparison immediate values in ARM Thumb instructions
|
|
- Repurpose existing code paths instead of adding new ones
|
|
- Understand how `cmp` immediates are encoded in the instruction stream
|
|
- Combine multiple patches (comparison + data) for a single behavior change
|
|
|
|
#### Questions for Reflection
|
|
|
|
###### Question 1: Why is it easier to repurpose an existing command than to add a truly new third code path in the binary?
|
|
|
|
###### Question 2: The `cmp` instruction uses an 8-bit immediate field. What range of ASCII characters could you use as command keys?
|
|
|
|
###### Question 3: After your patch, pressing '2' now triggers the default "??" branch. Could you keep both '2' AND '3' working? What would that require?
|
|
|
|
###### Question 4: What would happen if you changed the comparison to `0x00`? Could a user ever trigger that command via `getchar()`?
|
|
|
|
#### Tips and Hints
|
|
- The `cmp rN, #imm8` instruction in Thumb has the immediate in the lower byte of the instruction word
|
|
- IEEE-754 for 90.0f: `0x42b40000` → little-endian `00 00 b4 42`
|
|
- Be careful not to confuse literal pool entries between case '1' and case '2' — trace the branch targets carefully
|
|
- The `getchar()` function reads one byte from UART, so any byte value 0x00-0xFF is theoretically possible
|