mirror of
https://github.com/mytechnotalent/Embedded-Hacking.git
synced 2026-07-06 12:37:54 +02:00
Updated WEEK04
This commit is contained in:
@@ -1,57 +0,0 @@
|
||||
# Embedded Systems Reverse Engineering
|
||||
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
|
||||
|
||||
## Week 10
|
||||
Dynamic Conditionals in Embedded Systems: Debugging and Hacking Dynamic Conditionals w/ SG90 Servo Basics
|
||||
|
||||
### Non-Credit Practice Exercise 1 Solution: Change Servo Angle Range
|
||||
|
||||
#### Answers
|
||||
|
||||
##### IEEE-754 Angle Encodings
|
||||
|
||||
| Angle | IEEE-754 Hex | Little-Endian Bytes | Sign | Exponent | Mantissa |
|
||||
|--------|--------------|--------------------|----- |----------|-----------|
|
||||
| 0.0f | 0x00000000 | 00 00 00 00 | 0 | 0 | 0 |
|
||||
| 45.0f | 0x42340000 | 00 00 34 42 | 0 | 132 | 0x340000 |
|
||||
| 135.0f | 0x43070000 | 00 00 07 43 | 0 | 134 | 0x070000 |
|
||||
| 180.0f | 0x43340000 | 00 00 34 43 | 0 | 134 | 0x340000 |
|
||||
|
||||
##### Patch 1: Maximum Angle 180.0f → 135.0f
|
||||
|
||||
```
|
||||
Before: 00 00 34 43 (180.0f)
|
||||
After: 00 00 07 43 (135.0f)
|
||||
```
|
||||
|
||||
##### Patch 2: Minimum Angle 0.0f → 45.0f
|
||||
|
||||
```
|
||||
Before: 00 00 00 00 (0.0f)
|
||||
After: 00 00 34 42 (45.0f)
|
||||
```
|
||||
|
||||
##### Result
|
||||
|
||||
Servo sweeps from **45° to 135°** instead of 0° to 180°, a 90° range centered at the midpoint.
|
||||
|
||||
##### Conversion and Flash
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
#### Reflection Answers
|
||||
|
||||
1. **Break down the IEEE-754 encoding of 180.0f (0x43340000). What are the sign bit, exponent, and mantissa fields?**
|
||||
`0x43340000` in binary: `0 10000110 01101000000000000000000`. **Sign** = 0 (positive). **Exponent** = `10000110` = 134, biased exponent = 134 - 127 = 7. **Mantissa** = `0x340000` = `01101000...0`, representing 1.01101₂ = 1 + 0.25 + 0.125 + 0.03125 = 1.40625. Value = 1.40625 × 2⁷ = 1.40625 × 128 = **180.0**.
|
||||
|
||||
2. **Why is 0.0f represented as 0x00000000 (all zeros) in IEEE-754? Most floats have a non-zero exponent — what makes zero special?**
|
||||
Zero is a **special case** in IEEE-754. When both the exponent and mantissa are all zeros, the value is defined as ±0.0 (the sign bit distinguishes +0.0 from -0.0). This is by design — the IEEE-754 standard reserves the all-zeros exponent for zero and denormalized numbers. Unlike normal floats that have an implicit leading 1 in the mantissa (1.xxx), zero has no such implicit bit. This special encoding means you can check for zero by testing if all 32 bits are 0, which is efficient for hardware.
|
||||
|
||||
3. **What is the IEEE-754 encoding of 90.0f? Show the sign, exponent, and mantissa calculation.**
|
||||
90.0 = 1.40625 × 2⁶. **Sign** = 0. **Exponent** = 6 + 127 = 133 = `0x85` = `10000101`. **Mantissa**: 90.0 / 64 = 1.40625, fractional part = 0.40625 = 0.25 + 0.125 + 0.03125 = `0110100...0` = `0x340000`. Result: `0 10000101 01101000000000000000000` = `0x42B40000`. Little-endian: `00 00 B4 42`.
|
||||
|
||||
4. **The compiler might use movs r0, #0 instead of loading 0.0f from a literal pool. Why would it choose one approach over the other?**
|
||||
For integer zero, the compiler prefers `movs r0, #0` (a 2-byte Thumb instruction) because it's smaller and faster than a literal pool load. However, for **floating-point** zero used with VFP instructions, the compiler must load it into an FPU register (e.g., `s0`). If the FPU has a `vmov.f32 s0, #0.0` immediate form available, it can encode zero directly. Otherwise, it loads from a literal pool or uses `movs` to set an integer register to 0 and transfers it with `vmov s0, r0`. The choice depends on instruction context — integer vs. FPU register — and optimization level.
|
||||
@@ -1,155 +0,0 @@
|
||||
# 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 1: Change Servo Angle Range
|
||||
|
||||
#### Objective
|
||||
Find the IEEE-754 floating-point value `0x43340000` (180.0f) in the `0x0020_dynamic-conditionals` binary using GDB, calculate the file offset, patch it to `0x43070000` (135.0f) using a hex editor, then find and patch the `0x00000000` (0.0f) value to `0x42340000` (45.0f), and verify on hardware that the servo now sweeps from 45° to 135° instead of 0° to 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
|
||||
The program uses `servo_set_angle(0.0f)` and `servo_set_angle(180.0f)` to sweep the servo across its full range. The float values `0.0` (`0x00000000`) and `180.0` (`0x43340000`) are loaded from the literal pool into registers before the `bl servo_set_angle` calls. You will locate these values in the disassembly, find the corresponding bytes in the `.bin` file, and patch them to `45.0` (`0x42340000`) and `135.0` (`0x43070000`) so the servo sweeps a narrower 90° range centered at 90°.
|
||||
|
||||
#### 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 servo_set_angle Calls
|
||||
|
||||
Disassemble main and look for the `servo_set_angle` calls:
|
||||
|
||||
```gdb
|
||||
(gdb) disassemble 0x10000234,+250
|
||||
```
|
||||
|
||||
Look for instruction patterns like:
|
||||
|
||||
```
|
||||
ldr r0, [pc, #offset] ; load float from literal pool
|
||||
bl servo_set_angle
|
||||
```
|
||||
|
||||
There will be multiple pairs — one loading `0x00000000` (0.0f) and another loading `0x43340000` (180.0f).
|
||||
|
||||
##### Step 3: Examine the Literal Pool Values
|
||||
|
||||
Once you find the `ldr r0, [pc, #offset]` instructions, examine the literal pool entries they reference:
|
||||
|
||||
```gdb
|
||||
(gdb) x/wx <literal_pool_address_for_180>
|
||||
```
|
||||
|
||||
You should see `0x43340000` (180.0f in IEEE-754).
|
||||
|
||||
```gdb
|
||||
(gdb) x/wx <literal_pool_address_for_0>
|
||||
```
|
||||
|
||||
You should see `0x00000000` (0.0f in IEEE-754).
|
||||
|
||||
##### Step 4: Calculate the File Offsets
|
||||
|
||||
```
|
||||
file_offset = literal_pool_address - 0x10000000
|
||||
```
|
||||
|
||||
Note the file offsets for both 4-byte words.
|
||||
|
||||
##### Step 5: Encode the New Values
|
||||
|
||||
**IEEE-754 Encoding:**
|
||||
|
||||
| Angle | IEEE-754 Hex | Little-Endian Bytes |
|
||||
| ----- | -------------- | ------------------- |
|
||||
| 0.0f | `0x00000000` | `00 00 00 00` |
|
||||
| 45.0f | `0x42340000` | `00 00 34 42` |
|
||||
| 135.0f| `0x43070000` | `00 00 07 43` |
|
||||
| 180.0f| `0x43340000` | `00 00 34 43` |
|
||||
|
||||
##### 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 enter the file offset for the 180.0f value
|
||||
3. You should see: `00 00 34 43`
|
||||
4. Replace with: `00 00 07 43` (135.0f)
|
||||
5. Press **Ctrl+G** and enter the file offset for the 0.0f value
|
||||
6. You should see: `00 00 00 00`
|
||||
7. Replace with: `00 00 34 42` (45.0f)
|
||||
|
||||
###### Question 1: How many literal pool entries reference `0x43340000`? Do you need to patch all of them?
|
||||
|
||||
##### 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 servo behavior:**
|
||||
- Press '1' → servo sweeps from **45°** to **135°** (was 0° to 180°)
|
||||
- Press '2' → servo sweeps from **135°** to **45°** (was 180° to 0°)
|
||||
- The total sweep range should be noticeably smaller
|
||||
|
||||
#### Expected Output
|
||||
|
||||
After completing this exercise, you should be able to:
|
||||
- Locate IEEE-754 floating-point values in the literal pool
|
||||
- Understand the IEEE-754 single-precision encoding format
|
||||
- Patch floating-point angle constants in embedded firmware
|
||||
- Calculate file offsets from memory addresses
|
||||
|
||||
#### Questions for Reflection
|
||||
|
||||
###### Question 1: The value `180.0f` is encoded as `0x43340000` in IEEE-754. Break this down: what are the sign bit, exponent, and mantissa fields?
|
||||
|
||||
###### Question 2: Why is `0.0f` stored as `0x00000000` and not some other pattern? What makes zero special in IEEE-754?
|
||||
|
||||
###### Question 3: If you wanted to set the servo to exactly 90° (center), what IEEE-754 hex value would you use? Show the calculation.
|
||||
|
||||
###### Question 4: Could the compiler optimize `0.0f` differently — for example, using `movs r0, #0` instead of a literal pool load? How would that affect your patching strategy?
|
||||
|
||||
#### Tips and Hints
|
||||
- IEEE-754 for 90.0f: sign=0, exponent=127+6=133=0x85, mantissa=0x340000 → `0x42b40000`
|
||||
- There may be multiple references to `0x43340000` in the literal pool — the case '1' and case '2' branches each load both angles
|
||||
- Be careful with `0x00000000` — make sure you are patching a float literal pool entry and not zeroed data
|
||||
- Use `x/4wx <address>` in GDB to view several literal pool words at once
|
||||
@@ -1,64 +0,0 @@
|
||||
# Embedded Systems Reverse Engineering
|
||||
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
|
||||
|
||||
## Week 10
|
||||
Dynamic Conditionals in Embedded Systems: Debugging and Hacking Dynamic Conditionals w/ SG90 Servo Basics
|
||||
|
||||
### Non-Credit Practice Exercise 2 Solution: Add a Third Command
|
||||
|
||||
#### Answers
|
||||
|
||||
##### Patch 1: Compare Byte '2' → '3'
|
||||
|
||||
Change `cmp r4, #0x32` to `cmp r4, #0x33`:
|
||||
|
||||
```
|
||||
Before: 32 2C (cmp r4, #0x32 = '2')
|
||||
After: 33 2C (cmp r4, #0x33 = '3')
|
||||
```
|
||||
|
||||
Only the immediate byte changes: `0x32` → `0x33`.
|
||||
|
||||
##### Patch 2: Case Angle 1 — 180.0f → 90.0f
|
||||
|
||||
```
|
||||
Before: 00 00 34 43 (180.0f)
|
||||
After: 00 00 B4 42 (90.0f)
|
||||
```
|
||||
|
||||
##### Patch 3: Case Angle 2 — 0.0f → 90.0f
|
||||
|
||||
```
|
||||
Before: 00 00 00 00 (0.0f)
|
||||
After: 00 00 B4 42 (90.0f)
|
||||
```
|
||||
|
||||
##### IEEE-754 Reference
|
||||
|
||||
| Angle | Hex | Little-Endian |
|
||||
|--------|-------------|----------------|
|
||||
| 0.0f | 0x00000000 | 00 00 00 00 |
|
||||
| 90.0f | 0x42B40000 | 00 00 B4 42 |
|
||||
| 180.0f | 0x43340000 | 00 00 34 43 |
|
||||
|
||||
##### Behavior After Patch
|
||||
|
||||
| Key | Action |
|
||||
|-----|--------------------------------------|
|
||||
| '1' | Sweep 0° → 180° (unchanged) |
|
||||
| '3' | Move to 90° center (new command) |
|
||||
| '2' | Falls to default — prints "??" |
|
||||
|
||||
#### Reflection Answers
|
||||
|
||||
1. **Why does this exercise repurpose the existing case '2' path instead of adding a completely new branch? What would adding a new branch require?**
|
||||
Adding a new branch would require inserting new instructions into the binary — additional `cmp`, `beq`, angle-loading code, and a `servo_set_angle` call. This would shift all subsequent code addresses, breaking every PC-relative branch, literal pool reference, and function call in the program. In a compiled binary without relocation information, inserting bytes is extremely difficult. Repurposing the existing case '2' path reuses the existing branch structure, angle-loading instructions, and function calls — only the data values change, not the code layout.
|
||||
|
||||
2. **The cmp instruction uses an 8-bit immediate field. What is the range of characters you could compare against? Could you use a non-ASCII value?**
|
||||
The `cmp Rn, #imm8` Thumb instruction has an 8-bit unsigned immediate, giving a range of 0–255 (`0x00`–`0xFF`). This covers all ASCII characters (0–127) plus extended values (128–255). You could compare against any byte value, including non-printable characters (`0x01`–`0x1F`), DEL (`0x7F`), or extended characters (`0x80`–`0xFF`). However, the user needs to be able to type the character via `getchar()` — non-printable characters would require special terminal input (e.g., Ctrl combinations).
|
||||
|
||||
3. **How would you keep BOTH the original '2' command AND add '3' as a new command, using only data patches (no instruction insertion)?**
|
||||
You could repurpose the **default/else** branch path. After the `cmp r4, #0x32` (case '2'), there's typically a branch to a default handler that prints "??". If you change the compare in the default path (or an unused branch) to `cmp r4, #0x33`, and redirect its logic to reuse one of the existing `servo_set_angle` code paths, you could handle both. Alternatively, if the binary has any unreachable code or NOP sleds, you could repurpose that space. The constraint is that you cannot increase the binary size — only modify existing bytes.
|
||||
|
||||
4. **What would happen if you changed the compare value to 0x00 (null)? Could a user ever trigger this case?**
|
||||
A compare against `0x00` would trigger on a null byte. In terminal input via `getchar()`, a null character is not easily typed — most terminals don't send `0x00` on any key press. On some systems, Ctrl+@ or Ctrl+Shift+2 generates a null byte, but this is platform-dependent. In practice, comparing against `0x00` would create an unreachable case — the command would exist in the binary but could never be triggered via normal serial terminal input, effectively making it a dead code path.
|
||||
@@ -1,159 +0,0 @@
|
||||
# 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
|
||||
@@ -1,49 +0,0 @@
|
||||
# Embedded Systems Reverse Engineering
|
||||
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
|
||||
|
||||
## Week 10
|
||||
Dynamic Conditionals in Embedded Systems: Debugging and Hacking Dynamic Conditionals w/ SG90 Servo Basics
|
||||
|
||||
### Non-Credit Practice Exercise 3 Solution: Reverse the Servo Direction
|
||||
|
||||
#### Answers
|
||||
|
||||
##### Four Literal Pool Swaps
|
||||
|
||||
| Patch | Location | Original | Patched |
|
||||
|-------|---------------|-------------------|-------------------|
|
||||
| Case 1 Angle 1 | Literal pool | 00 00 00 00 (0.0f) | 00 00 34 43 (180.0f) |
|
||||
| Case 1 Angle 2 | Literal pool | 00 00 34 43 (180.0f) | 00 00 00 00 (0.0f) |
|
||||
| Case 2 Angle 1 | Literal pool | 00 00 34 43 (180.0f) | 00 00 00 00 (0.0f) |
|
||||
| Case 2 Angle 2 | Literal pool | 00 00 00 00 (0.0f) | 00 00 34 43 (180.0f) |
|
||||
|
||||
##### Behavior After Patch
|
||||
|
||||
| Key | Original | Patched |
|
||||
|-----|---------------------|---------------------|
|
||||
| '1' | 0° → 180° (sweep up) | 180° → 0° (sweep down) |
|
||||
| '2' | 180° → 0° (sweep down) | 0° → 180° (sweep up) |
|
||||
|
||||
The terminal output text ("Moving to 180..." / "Moving to 0...") remains unchanged — it still says the original directions. Only the physical servo behavior is reversed.
|
||||
|
||||
##### GDB Verification
|
||||
|
||||
```gdb
|
||||
(gdb) x/4wx <literal_pool_start>
|
||||
```
|
||||
|
||||
Examine all angle entries in the literal pool to identify which 4-byte words to swap.
|
||||
|
||||
#### Reflection Answers
|
||||
|
||||
1. **After this patch, the serial output still says "Moving to 180" when the servo actually moves to 0. Why is this a security concern? What real-world attack does this mimic?**
|
||||
This is a classic **display spoofing** attack. The user interface (serial output) shows one thing while the hardware does another. In real-world systems, this mimics attacks on SCADA/ICS systems where operator displays show "normal" readings while the physical process is manipulated (similar to Stuxnet, which showed normal centrifuge speeds while actually damaging them). In medical devices, this could display a safe dosage while delivering a different amount. The lesson is that **you cannot trust the display if the firmware has been tampered with** — the display text and the actual behavior are patched independently.
|
||||
|
||||
2. **Instead of swapping the data values, could you achieve the same result by swapping the branch targets (making case '1' jump to case '2' code and vice versa)? What are the trade-offs?**
|
||||
Yes, you could swap the `beq` target addresses so that when the user presses '1', execution jumps to the case '2' code path and vice versa. **Trade-offs:** Swapping branch targets changes the instructions (modifying the offset bytes in `beq`), which is more complex — you need to correctly calculate the new PC-relative offsets. Swapping data values is simpler (just exchange 4-byte float values) and less error-prone. However, swapping branches would also swap the printf messages, so "Moving to 180" would display for the path that actually moves to 180 — keeping the display consistent. The data-swap approach intentionally creates a mismatch between display and behavior.
|
||||
|
||||
3. **If the compiler shares a single literal pool entry for 0x43340000 (180.0f) across both cases, how does swapping that one entry affect the behavior?**
|
||||
If the compiler optimized by sharing a single `0x43340000` literal pool entry for all references to 180.0f, then both case '1' and case '2' load from the same address. Changing that one entry to `0x00000000` (0.0f) would affect **both** cases simultaneously — they would both use 0.0f where they originally used 180.0f. Similarly, if there's only one `0x00000000` entry shared, changing it affects both cases. You would need to verify whether each case uses its own pool entry or shares entries by examining the `ldr` offsets. If shared, you may need to find unused space to create a second copy of the value.
|
||||
|
||||
4. **How would you verify the patch is correct without physical hardware? What GDB commands would you use?**
|
||||
Set breakpoints before each `bl servo_set_angle` call, then examine `r0` (or `s0`) which holds the angle argument. Run through both cases and verify: (1) `b *<case1_servo_call>` → `c` → press '1' → `info float` or `p $s0` — should show 180.0f (was 0.0f). (2) Continue to second call — should show 0.0f (was 180.0f). Repeat for case '2'. You can also examine the literal pool directly: `x/wx <pool_addr>` to verify the bytes were swapped. Additionally, `x/f <pool_addr>` displays the value as a float for quick verification.
|
||||
@@ -1,141 +0,0 @@
|
||||
# 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
|
||||
@@ -1,56 +0,0 @@
|
||||
# Embedded Systems Reverse Engineering
|
||||
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
|
||||
|
||||
## Week 10
|
||||
Dynamic Conditionals in Embedded Systems: Debugging and Hacking Dynamic Conditionals w/ SG90 Servo Basics
|
||||
|
||||
### Non-Credit Practice Exercise 4 Solution: Speed Profile
|
||||
|
||||
#### Answers
|
||||
|
||||
##### Sleep Duration Values
|
||||
|
||||
| Parameter | Original | Case 1 (Fast Snap) | Case 2 (Slow Sweep) |
|
||||
|-------------|---------|--------------------|--------------------|
|
||||
| Duration | 500 ms | 100 ms | 1000 ms |
|
||||
| Hex | 0x1F4 | 0x64 | 0x3E8 |
|
||||
| LE Bytes | F4 01 00 00 | 64 00 00 00 | E8 03 00 00 |
|
||||
|
||||
##### Patch Case 1: 500ms → 100ms (Fast Snap)
|
||||
|
||||
```
|
||||
Before: F4 01 00 00 (500ms)
|
||||
After: 64 00 00 00 (100ms)
|
||||
```
|
||||
|
||||
##### Patch Case 2: 500ms → 1000ms (Slow Sweep)
|
||||
|
||||
```
|
||||
Before: F4 01 00 00 (500ms)
|
||||
After: E8 03 00 00 (1000ms)
|
||||
```
|
||||
|
||||
##### Literal Pool Considerations
|
||||
|
||||
If the compiler shares a single literal pool entry for `0x000001F4` across both cases, you **cannot** patch them independently without additional work. Verify by checking whether case 1 and case 2 `ldr` instructions reference the same pool address. If shared, you need to find unused space in the binary for a second value or repurpose another unused literal pool entry.
|
||||
|
||||
##### Behavior After Patch
|
||||
|
||||
| Key | Original | Patched |
|
||||
|-----|-------------------|----------------------------------|
|
||||
| '1' | 500ms between moves | 100ms — near-instantaneous snap |
|
||||
| '2' | 500ms between moves | 1000ms — slow, deliberate sweep |
|
||||
|
||||
#### Reflection Answers
|
||||
|
||||
1. **Why does 100ms feel like an instant "snap" while 1000ms feels like a smooth sweep? The servo moves the same distance either way.**
|
||||
Human perception of motion depends on the **pause between position updates**, not the motor speed. At 100ms delay, the servo reaches each angle before the next one is set — the positions update so quickly that the motion appears continuous and instant. At 1000ms delay, there's a full second between movements, so you can see the servo pause at each intermediate angle. The SG90 servo physically takes about 200–300ms to traverse its full range at no load, so 100ms is faster than the travel time (the servo is still moving when the next command arrives), creating a snappy feel. At 1000ms, the servo has already completed its move and waits idle before the next command.
|
||||
|
||||
2. **If both cases share the same literal pool entry for 500ms, what strategy would you use to give them different sleep values?**
|
||||
Several approaches: (1) **Find unused literal pool space** — look for entries that are no longer referenced and overwrite one with `0x64` (100ms) while keeping the other for `0x3E8` (1000ms). (2) **Repurpose an existing value** — if another constant in the pool happens to equal your desired value, redirect the `ldr` offset to point there. (3) **Change the `ldr` to a `movs`** — for values ≤ 255 (like 100), replace the 4-byte `ldr r0, [pc, #offset]` with `movs r0, #0x64` (2 bytes) + `nop` (2 bytes) for padding. This works for case 1 (100 fits in 8 bits) but not case 2 (1000 exceeds 255).
|
||||
|
||||
3. **What is the minimum sleep_ms value where the SG90 servo can actually complete a full 0°–180° sweep before the next command?**
|
||||
The SG90 servo has a rated speed of approximately 0.12 seconds per 60° at 4.8V. For a full 180° sweep: 0.12 × (180/60) = 0.12 × 3 = **0.36 seconds (360ms)**. In practice, with load and signal processing overhead, **400–500ms** is a safe minimum for reliable full-range travel. Below this, the servo may not reach the target angle before the next position command arrives, resulting in incomplete movements or jittery behavior. The original 500ms value was chosen to reliably allow full travel.
|
||||
|
||||
4. **What would happen if you set sleep_ms to 0 for both cases? How would the servo physically behave?**
|
||||
With `sleep_ms(0)`, the loop runs at full CPU speed, sending angle commands as fast as the processor can execute. The servo would receive thousands of position updates per second, alternating between two angles. Physically, the servo would **vibrate or oscillate** — it never has time to reach either target angle before being told to go to the other one. The PWM signal would switch so rapidly that the servo's control circuit would see constantly changing targets, producing a buzzing sound and erratic oscillation near the midpoint. This could also overheat the servo motor due to constant direction changes.
|
||||
@@ -1,144 +0,0 @@
|
||||
# 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
|
||||
+313
-338
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user