mirror of
https://github.com/mytechnotalent/Embedded-Hacking.git
synced 2026-05-26 01:07:53 +02:00
Refactor E and S
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
# Embedded Systems Reverse Engineering
|
||||
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
|
||||
|
||||
## Week 11
|
||||
Functions in Embedded Systems: Debugging and Hacking Functions w/ IR Remote and Multi-LED Control
|
||||
|
||||
### Non-Credit Practice Exercise 1 Solution: Add a Fourth LED
|
||||
|
||||
#### Answers
|
||||
|
||||
##### Struct Layout (simple_led_ctrl_t)
|
||||
|
||||
| Offset | Field | Size | Original Value | Hex |
|
||||
|--------|------------|--------|----------------|------|
|
||||
| 0 | led1_pin | 1 byte | GPIO 16 | 0x10 |
|
||||
| 1 | led2_pin | 1 byte | GPIO 17 | 0x11 |
|
||||
| 2 | led3_pin | 1 byte | GPIO 18 | 0x12 |
|
||||
| 3 | led1_state | 1 byte | false (0) | 0x00 |
|
||||
| 4 | led2_state | 1 byte | false (0) | 0x00 |
|
||||
| 5 | led3_state | 1 byte | false (0) | 0x00 |
|
||||
|
||||
Total struct size: **6 bytes** (3 pin bytes + 3 state bytes).
|
||||
|
||||
##### Assembly Initialization Pattern
|
||||
|
||||
```asm
|
||||
movs r0, #0x10 ; led1_pin = 16
|
||||
strb r0, [r4, #0] ; struct offset 0
|
||||
movs r0, #0x11 ; led2_pin = 17
|
||||
strb r0, [r4, #1] ; struct offset 1
|
||||
movs r0, #0x12 ; led3_pin = 18
|
||||
strb r0, [r4, #2] ; struct offset 2
|
||||
```
|
||||
|
||||
##### Patch: Add GPIO 19 at Struct Offset 3
|
||||
|
||||
Writing `0x13` to offset 3 **overwrites led1_state**:
|
||||
|
||||
| Offset | Before | After | Impact |
|
||||
|--------|-------------|-------------|----------------------|
|
||||
| 3 | 0x00 (led1_state = false) | 0x13 (led4_pin = GPIO 19) | led1_state corrupted |
|
||||
|
||||
##### GDB Verification
|
||||
|
||||
```gdb
|
||||
(gdb) b *0x10000280
|
||||
(gdb) c
|
||||
(gdb) x/8bx <struct_address>
|
||||
```
|
||||
|
||||
Before patch: `10 11 12 00 00 00`
|
||||
After patch: `10 11 12 13 00 00`
|
||||
|
||||
#### Reflection Answers
|
||||
|
||||
1. **The original struct has 6 members (3 pins + 3 states) in 6 bytes. If you add a fourth pin at offset 3, you overwrite led1_state. What is the practical impact on LED 1 behavior?**
|
||||
The byte `0x13` (decimal 19) is written to offset 3, which the program reads as `led1_state`. Since `bool` in C treats any non-zero value as `true`, `led1_state` would be interpreted as `true` (on) immediately after the struct is initialized. LED 1 would appear to be in the "on" state from the start, regardless of whether the user pressed button 1. The `leds_all_off` function may reset it to 0, but every time the struct is re-initialized on the stack (each loop iteration), the corrupted state returns. The fourth LED at GPIO 19 would need additional `gpio_init` and `gpio_set_dir` calls to actually function — just writing the pin number into the struct doesn't configure the GPIO hardware.
|
||||
|
||||
2. **How would you verify the exact struct layout and offsets using GDB's memory examination commands?**
|
||||
Set a breakpoint after struct initialization (`b *0x10000280`), then `x/6bx <struct_base>` to see all 6 bytes. Verify: offsets 0–2 should show `10 11 12` (pin values), offsets 3–5 should show `00 00 00` (state values). Use `x/1bx <struct_base+N>` for individual fields. To find the struct base, examine `r4` at the breakpoint since the `strb r0, [r4, #N]` instructions use r4 as the base. You can also use `p/x $r4` to get the base address, then `x/6bx $r4` for the complete layout.
|
||||
|
||||
3. **If the get_led_pin function uses a bounds check (e.g., if led_num > 3 return 0), what additional patch would you need?**
|
||||
You would need to find the comparison instruction in `get_led_pin` (at approximately `0x100002a0`) — likely a `cmp rN, #3` followed by a conditional branch. Patch the immediate from `#3` to `#4` so the bounds check allows led_num = 4. For example, if the check is `cmp r1, #3; bhi default`, change `03` to `04` in the `cmp` instruction's immediate byte. Without this patch, passing led_num=4 would fail the bounds check and return 0 (no pin), so the fourth LED would never be addressed.
|
||||
|
||||
4. **Could you extend the struct without overwriting existing fields by finding free space elsewhere in the binary? What challenges would that introduce?**
|
||||
You could find unused space (padding, NOP sleds, or unused data) and place the extended struct there. However, this introduces major challenges: (1) Every instruction that references the original struct address via `r4` would need to be redirected to the new location. (2) All `strb`/`ldrb` offsets would need updating. (3) Stack-allocated structs are recreated each loop iteration — you'd need to change the stack frame size (`sub sp, sp, #N`). (4) Functions that receive the struct pointer as an argument would need their call sites updated. In practice, relocating a struct in a compiled binary is extremely complex and error-prone — overwriting adjacent fields is the pragmatic (if destructive) approach.
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
## Week 11
|
||||
Structures and Functions in Embedded Systems: Debugging and Hacking w/ IR Remote Control and NEC Protocol Basics
|
||||
|
||||
### Exercise 1: Add a Fourth LED
|
||||
### Non-Credit Practice Exercise 1: Add a Fourth LED
|
||||
|
||||
#### Objective
|
||||
Find the struct initialization pattern in the `0x0026_functions` binary using GDB where `led1_pin` (0x10), `led2_pin` (0x11), and `led3_pin` (0x12) are stored, locate an unused byte in the struct memory region, and patch it to include a fourth LED on GPIO 19 (0x13) by extending the struct data and modifying the `ir_to_led_number` function to handle a fourth button mapping.
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
# Embedded Systems Reverse Engineering
|
||||
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
|
||||
|
||||
## Week 11
|
||||
Functions in Embedded Systems: Debugging and Hacking Functions w/ IR Remote and Multi-LED Control
|
||||
|
||||
### Non-Credit Practice Exercise 2 Solution: Change Blink Count
|
||||
|
||||
#### Answers
|
||||
|
||||
##### Blink Count Parameter
|
||||
|
||||
| Parameter | Original | Patched |
|
||||
|-------------|---------|---------|
|
||||
| Blink count | 3 | 5 |
|
||||
| Hex | 0x03 | 0x05 |
|
||||
| Register | r1 | r1 |
|
||||
| Instruction | movs r1, #3 | movs r1, #5 |
|
||||
|
||||
##### Assembly Context (blink_led Call)
|
||||
|
||||
```asm
|
||||
movs r0, <pin> ; r0 = GPIO pin number
|
||||
movs r1, #3 ; r1 = blink count ← PATCH THIS
|
||||
movs r2, #0x32 ; r2 = delay (50ms)
|
||||
bl blink_led ; blink_led(pin, 3, 50)
|
||||
```
|
||||
|
||||
##### Patch
|
||||
|
||||
The immediate byte in `movs r1, #3` is the first byte of the 2-byte Thumb instruction:
|
||||
|
||||
```
|
||||
Before: 03 21 (movs r1, #3)
|
||||
After: 05 21 (movs r1, #5)
|
||||
```
|
||||
|
||||
File offset = instruction address - 0x10000000.
|
||||
|
||||
##### Behavior After Patch
|
||||
|
||||
| Button | LED | Original | Patched |
|
||||
|--------|--------|-------------------|---------------------|
|
||||
| 1 | Red | Blinks 3×, stays on | Blinks 5×, stays on |
|
||||
| 2 | Green | Blinks 3×, stays on | Blinks 5×, stays on |
|
||||
| 3 | Yellow | Blinks 3×, stays on | Blinks 5×, stays on |
|
||||
|
||||
Total blink time at 50ms delay: 5 × (50 + 50) = **500ms** (was 300ms).
|
||||
|
||||
#### Reflection Answers
|
||||
|
||||
1. **movs rN, #imm8 can encode values 0–255. What is the maximum blink count with a single byte patch?**
|
||||
The maximum is **255** (`0xFF`). The `movs Rd, #imm8` Thumb instruction uses a full 8-bit immediate field, giving an unsigned range of 0–255. Setting the blink count to 255 would make each LED blink 255 times per button press — at 50ms on + 50ms off per blink, that's 255 × 100ms = **25.5 seconds** of blinking before the LED stays on. A count of 0 would skip the blink loop entirely (LED turns on immediately with no blinking).
|
||||
|
||||
2. **Why is blink count in r1 and not r0? What does r0 hold at this point?**
|
||||
The ARM calling convention (AAPCS) passes the first four function arguments in registers `r0`, `r1`, `r2`, `r3` in order. The `blink_led` function signature is `blink_led(uint8_t pin, uint8_t count, uint32_t delay_ms)`. So `r0` = pin (the GPIO number of the LED to blink), `r1` = count (how many times to blink), and `r2` = delay_ms (the delay in milliseconds between on/off transitions). The blink count is the second parameter, hence `r1`.
|
||||
|
||||
3. **If you wanted a blink count larger than 255 (e.g., 1000), what instruction sequence would the compiler generate instead of movs?**
|
||||
For values exceeding 255, the compiler would use a 32-bit Thumb-2 `movw r1, #imm16` instruction, which can encode 0–65535. For example, `movw r1, #1000` would be 4 bytes: `40 F2 E8 31` (encoding `movw r1, #0x3E8`). For values exceeding 65535, the compiler would add `movt r1, #imm16` to set the upper 16 bits, or use a literal pool load (`ldr r1, [pc, #offset]`). The function parameter type (`uint8_t`) would still truncate to 0–255, so a count of 1000 would wrap to 232 (1000 mod 256) unless the function uses a wider type internally.
|
||||
|
||||
4. **Is there one shared movs r1, #3 instruction for all three LEDs, or does each blink_led call have its own? How can you tell?**
|
||||
Each `blink_led` call likely has its **own** `movs r1, #3` instruction. The compiler generates separate parameter setup sequences for each `bl blink_led` call site — the `movs r0, <pin>` instruction before each call loads a different GPIO pin. You can verify by disassembling the full range (`disassemble 0x10000234,+300`) and counting how many `movs r1, #3` instructions appear before `bl blink_led` calls. If there are three separate call sites with three separate `movs r1, #3` instructions, you need to **patch all three** to change the blink count for every LED. If only one is patched, only that LED's blink count changes.
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
## Week 11
|
||||
Structures and Functions in Embedded Systems: Debugging and Hacking w/ IR Remote Control and NEC Protocol Basics
|
||||
|
||||
### Exercise 2: Change Blink Count
|
||||
### Non-Credit Practice Exercise 2: Change Blink Count
|
||||
|
||||
#### Objective
|
||||
Find the `blink_led(pin, 3, 50)` call in the `0x0026_functions` binary using GDB, identify the immediate value `#3` being loaded into `r1` (the blink count parameter), calculate the file offset, and patch it to `#5` so that each LED blinks 5 times instead of 3 when activated by the IR remote.
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
# Embedded Systems Reverse Engineering
|
||||
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
|
||||
|
||||
## Week 11
|
||||
Functions in Embedded Systems: Debugging and Hacking Functions w/ IR Remote and Multi-LED Control
|
||||
|
||||
### Non-Credit Practice Exercise 3 Solution: Swap All Three LEDs
|
||||
|
||||
#### Answers
|
||||
|
||||
##### GPIO Rotation Patch
|
||||
|
||||
| Struct Member | Original | Patched | Effect |
|
||||
|--------------|-----------------|-----------------|---------------------|
|
||||
| led1_pin | 0x10 (GPIO 16 Red) | 0x11 (GPIO 17 Green) | Button 1 → Green |
|
||||
| led2_pin | 0x11 (GPIO 17 Green) | 0x12 (GPIO 18 Yellow) | Button 2 → Yellow |
|
||||
| led3_pin | 0x12 (GPIO 18 Yellow) | 0x10 (GPIO 16 Red) | Button 3 → Red |
|
||||
|
||||
##### Assembly Patches
|
||||
|
||||
Three single-byte patches in `movs` immediate fields:
|
||||
|
||||
```
|
||||
Patch 1 (led1_pin): 10 → 11
|
||||
Before: 10 20 (movs r0, #0x10)
|
||||
After: 11 20 (movs r0, #0x11)
|
||||
|
||||
Patch 2 (led2_pin): 11 → 12
|
||||
Before: 11 20 (movs r0, #0x11)
|
||||
After: 12 20 (movs r0, #0x12)
|
||||
|
||||
Patch 3 (led3_pin): 12 → 10
|
||||
Before: 12 20 (movs r0, #0x12)
|
||||
After: 10 20 (movs r0, #0x10)
|
||||
```
|
||||
|
||||
##### GDB Verification
|
||||
|
||||
```gdb
|
||||
(gdb) b *0x10000280
|
||||
(gdb) c
|
||||
(gdb) x/6bx <struct_address>
|
||||
```
|
||||
|
||||
Before patch: `10 11 12 00 00 00`
|
||||
After patch: `11 12 10 00 00 00`
|
||||
|
||||
##### Behavior After Patch
|
||||
|
||||
| Button (IR) | NEC Code | Terminal Log | Actual LED |
|
||||
|------------|----------|---------------------------|-----------|
|
||||
| Button 1 | 0x0C | "LED 1 activated on GPIO 16" | Green (GPIO 17) |
|
||||
| Button 2 | 0x18 | "LED 2 activated on GPIO 17" | Yellow (GPIO 18) |
|
||||
| Button 3 | 0x5E | "LED 3 activated on GPIO 18" | Red (GPIO 16) |
|
||||
|
||||
The terminal logs are **desynchronized** from actual behavior.
|
||||
|
||||
#### Reflection Answers
|
||||
|
||||
1. **Terminal log still says "LED 1 activated on GPIO 16" even though GPIO 17 (Green) is actually blinking. Why don't the logs update automatically?**
|
||||
The `printf` format strings and their arguments are separate from the struct pin assignments. The log message "LED 1 activated on GPIO 16" is generated from hardcoded format strings or from reading the **original** pin value before our patch takes effect. The GPIO number in the log comes from a different code path — likely a format string like `"LED %d activated on GPIO %d\r\n"` where the GPIO value was loaded from the struct at a different point or is computed independently. Since we only patched the `movs` instructions that store pin values into the struct, the logging code still uses whatever values it computes independently.
|
||||
|
||||
2. **If the struct initialization used ldr from a literal pool instead of movs immediates, how would the patching differ?**
|
||||
With literal pool loads, the pin values would be stored as 32-bit words in a data area near the function code. You would need to: (1) find the `ldr r0, [pc, #offset]` instruction, (2) calculate the PC-relative offset to locate the literal pool entry, (3) navigate to the pool address in the hex editor, and (4) modify the 4-byte value there. For example, GPIO 16 would be `10 00 00 00` (little-endian) in the pool. This is more work than patching a 1-byte `movs` immediate, and you'd need to verify no other code shares the same pool entry. The `movs` approach is simpler because the value is encoded directly in the instruction.
|
||||
|
||||
3. **Could you achieve the same LED rotation by patching gpio_init/gpio_put calls instead of the struct initialization? Which approach is cleaner?**
|
||||
Patching `gpio_init` and `gpio_put` calls would require finding every call site that references each GPIO pin and modifying the pin argument. This is scattered throughout multiple functions (`process_ir_led_command`, `blink_led`, `leds_all_off`). The struct initialization approach is **far cleaner** — three adjacent `movs` instructions in one location control the entire mapping. By patching the struct data at its source, every function that reads from the struct automatically gets the new values. This demonstrates the power of data-driven design: changing the data at one point affects all code that uses it.
|
||||
|
||||
4. **In a real attack, why is log desynchronization (display says one thing, hardware does another) dangerous for forensic analysis?**
|
||||
Log desynchronization is dangerous because forensic investigators rely on logs to reconstruct what happened. If logs show "LED 1 on GPIO 16" but the hardware actually activated GPIO 17, investigators would draw incorrect conclusions about which physical device was controlled. In industrial systems, this could mask sabotage — operators see "normal" readings while equipment is being misused. In security systems, tampered firmware could log "door locked" while actually unlocking it. The logs become actively misleading, not just incomplete. This is a form of **anti-forensics** that makes post-incident analysis unreliable and can delay or prevent discovery of the actual attack.
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
## Week 11
|
||||
Structures and Functions in Embedded Systems: Debugging and Hacking w/ IR Remote Control and NEC Protocol Basics
|
||||
|
||||
### Exercise 3: Swap All Three LEDs
|
||||
### Non-Credit Practice Exercise 3: Swap All Three LEDs
|
||||
|
||||
#### Objective
|
||||
Find the struct initialization instructions where `led1_pin` = 0x10 (GPIO 16, Red), `led2_pin` = 0x11 (GPIO 17, Green), and `led3_pin` = 0x12 (GPIO 18, Yellow) are written in the `0x0026_functions` binary using GDB, calculate the file offsets, and rotate the GPIO values so that button 1→Green (0x11), button 2→Yellow (0x12), and button 3→Red (0x10), then verify on hardware that the LED mapping has shifted.
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
# Embedded Systems Reverse Engineering
|
||||
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
|
||||
|
||||
## Week 11
|
||||
Functions in Embedded Systems: Debugging and Hacking Functions w/ IR Remote and Multi-LED Control
|
||||
|
||||
### Non-Credit Practice Exercise 4 Solution: Change Blink Speed
|
||||
|
||||
#### Answers
|
||||
|
||||
##### Delay Parameter
|
||||
|
||||
| Parameter | Original | Patched |
|
||||
|----------|-------------|--------------|
|
||||
| Delay | 50ms | 25ms |
|
||||
| Hex | 0x32 | 0x19 |
|
||||
| Register | r2 | r2 |
|
||||
| Instruction | movs r2, #0x32 | movs r2, #0x19 |
|
||||
|
||||
##### Assembly Context
|
||||
|
||||
```asm
|
||||
movs r0, <pin> ; r0 = GPIO pin
|
||||
movs r1, #3 ; r1 = blink count
|
||||
movs r2, #0x32 ; r2 = delay 50ms ← PATCH THIS
|
||||
bl blink_led ; blink_led(pin, 3, 50)
|
||||
```
|
||||
|
||||
##### Patch
|
||||
|
||||
```
|
||||
Before: 32 22 (movs r2, #0x32 = 50ms)
|
||||
After: 19 22 (movs r2, #0x19 = 25ms)
|
||||
```
|
||||
|
||||
**Warning:** The byte `0x32` is also ASCII '2'. Verify you're patching the correct `movs r2` instruction by checking surrounding bytes — `movs r1, #3` (`03 21`) should appear immediately before, and `bl blink_led` immediately after.
|
||||
|
||||
##### Timing Comparison
|
||||
|
||||
| Metric | Original (50ms) | Patched (25ms) |
|
||||
|-------------------|-----------------|----------------|
|
||||
| On-time per blink | 50ms | 25ms |
|
||||
| Off-time per blink| 50ms | 25ms |
|
||||
| One blink cycle | 100ms | 50ms |
|
||||
| 3 blinks total | 300ms | 150ms |
|
||||
| Perceived speed | Normal | 2× faster |
|
||||
|
||||
#### Reflection Answers
|
||||
|
||||
1. **If delay = 1ms (0x01), would you still see the LED blink, or would it appear constantly on?**
|
||||
At 1ms on/off (2ms per cycle, 500Hz flicker), the LED would appear **constantly on** to the human eye. Human flicker fusion threshold is approximately 60Hz — anything above that appears as a steady light. At 500Hz, the LED is switching far too fast for the eye to perceive individual blinks. The LED would look like it's at roughly 50% brightness (since it's on half the time) compared to being fully on. The 3 blinks would complete in just 6ms total, appearing as a brief flash rather than distinct blinks.
|
||||
|
||||
2. **0x32 appears as both the delay value (50ms) and potentially ASCII '2'. How would you systematically find ALL occurrences of 0x32 and determine which to patch?**
|
||||
Search the binary for all `0x32` bytes, then examine the **context** of each occurrence: (1) Check the byte following `0x32` — if it's `0x22`, this is `movs r2, #0x32` (the delay parameter). If it's `0x2C`, it's `cmp r4, #0x32` (comparing against ASCII '2'). (2) Examine surrounding instructions: the delay `0x32` will be preceded by `movs r1, #3` (blink count) and followed by `bl blink_led`. A comparison `0x32` will be near `beq`/`bne` branches. (3) Use GDB to disassemble the region (`x/10i <addr-4>`) and read the instruction mnemonic. (4) Cross-reference with the function structure — delay patches are in `blink_led` call setup, comparisons are in `ir_to_led_number` or similar dispatcher functions.
|
||||
|
||||
3. **For a delay of 500ms (0x1F4), the value won't fit in a movs immediate (max 255). How would the compiler handle it?**
|
||||
For 500 (`0x1F4`), the compiler would use either: (1) A 32-bit `movw r2, #0x1F4` Thumb-2 instruction (4 bytes), which can encode any 16-bit immediate (0–65535). (2) A literal pool load: `ldr r2, [pc, #offset]` that reads `0x000001F4` from a nearby data word. The `movw` approach is preferred for values 256–65535 because it's a single instruction with no data dependency. For values exceeding 65535, a literal pool or `movw`+`movt` pair would be necessary.
|
||||
|
||||
4. **The blink function uses the delay for both on-time and off-time (symmetrical blink). Could you make the LED stay on longer than off by patching only one instruction?**
|
||||
Not with a single patch to the `movs r2` instruction, because `blink_led` uses the same delay parameter for both the on-phase and off-phase `sleep_ms` calls internally. To create asymmetric blink timing, you would need to patch **inside** the `blink_led` function itself — find the two `sleep_ms` calls within the blink loop and modify their delay arguments independently. For example, find the `ldr`/`movs` that sets up `r0` before each `bl sleep_ms` inside `blink_led`, and patch one to a different value. This would require disassembling `blink_led` to locate both `sleep_ms` call sites.
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
## Week 11
|
||||
Structures and Functions in Embedded Systems: Debugging and Hacking w/ IR Remote Control and NEC Protocol Basics
|
||||
|
||||
### Exercise 4: Change Blink Speed
|
||||
### Non-Credit Practice Exercise 4: Change Blink Speed
|
||||
|
||||
#### Objective
|
||||
Find the `blink_led(pin, 3, 50)` call in the `0x0026_functions` binary using GDB, identify the immediate value `0x32` (50) being loaded into `r2` (the delay parameter), calculate the file offset, and patch it to `0x19` (25) so that each LED blinks at double speed when activated by the IR remote.
|
||||
|
||||
Reference in New Issue
Block a user