Refactor E and S

This commit is contained in:
Kevin Thomas
2026-03-19 15:01:07 -04:00
parent f524f5b86b
commit 1784a107ae
81 changed files with 2986 additions and 247 deletions
+66
View File
@@ -0,0 +1,66 @@
# Embedded Systems Reverse Engineering
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
## Week 9
Operators in Embedded Systems: Debugging and Hacking Operators w/ DHT11 Basics
### Non-Credit Practice Exercise 1 Solution: Change the Sleep Duration
#### Answers
##### Sleep Duration Values
| Parameter | Original | Patched |
|---------------|-------------|-------------|
| Duration (ms) | 2000 | 5000 |
| Hex | 0x000007D0 | 0x00001388 |
| Little-endian | D0 07 00 00 | 88 13 00 00 |
##### Why a Literal Pool Is Needed
The value 2000 exceeds the 8-bit immediate range of `movs` (0255) and the 16-bit range is also impractical for a single-instruction load. The compiler stores `0x000007D0` in a **literal pool** near the function code and loads it with a `ldr r0, [pc, #offset]` instruction that reads the 32-bit word from the pool into `r0` before the `bl sleep_ms` call.
##### Patch Procedure
1. Find the literal pool entry containing `D0 07 00 00` in HxD
2. Replace with `88 13 00 00`
```
Before: D0 07 00 00 (2000)
After: 88 13 00 00 (5000)
```
##### Conversion and Flash
```powershell
cd C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x001a_operators
python ..\uf2conv.py build\0x001a_operators-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2
```
##### Serial Output After Patch
```
arithmetic_operator: 50
increment_operator: 5
relational_operator: 0
logical_operator: 0
bitwise_operator: 12
assignment_operator: 11
Humidity: 51.0%, Temperature: 24.0°C
```
Output repeats every **5 seconds** instead of 2 seconds.
#### Reflection Answers
1. **Why can't 2000 be encoded as a movs immediate? What is the maximum value movs can hold?**
The `movs Rd, #imm8` instruction is a 16-bit Thumb encoding that has only 8 bits for the immediate value, giving a range of 0255. The value 2000 (`0x7D0`) is far beyond this range. Even the 32-bit Thumb-2 `movw` instruction can only encode 065535, which could handle 2000, but the compiler chose a literal pool approach. The literal pool is a general-purpose solution that works for any 32-bit value, including addresses and large constants.
2. **If you wanted to change the sleep to exactly 1 second (1000ms), what 4 bytes would you write in little-endian? Show your work.**
1000 decimal = `0x000003E8` hex. In little-endian byte order (LSB first): `E8 03 00 00`. Breakdown: byte 0 = `0xE8` (LSB), byte 1 = `0x03`, byte 2 = `0x00`, byte 3 = `0x00` (MSB).
3. **Could other code in the program reference the same literal pool entry containing 0x7D0? What would happen if it did?**
Yes, the compiler may share literal pool entries to save space. If another instruction also loads `0x7D0` from the same pool address (using its own `ldr rN, [pc, #offset]`), then patching that pool entry would change the value for ALL instructions that reference it. This is a risk with literal pool patching — you might unintentionally modify other parts of the program. To check, search for all `ldr` instructions whose PC-relative offset resolves to the same pool address.
4. **What would happen if you set sleep_ms to 0? Would the program crash or just run extremely fast?**
The program would not crash — `sleep_ms(0)` is a valid call that returns immediately. The loop would run as fast as the processor can execute it, printing operator values and reading the DHT11 sensor with no delay between iterations. The serial output would flood extremely quickly. However, the DHT11 sensor has a minimum sampling interval of about 1 second; reading it more frequently may return stale data or read errors ("DHT11 read failed"), but the program itself would continue running.
+1 -1
View File
@@ -4,7 +4,7 @@
## Week 9
Operators in Embedded Systems: Debugging and Hacking Operators w/ DHT11 Temperature & Humidity Sensor Single-Wire Protocol Basics
### Exercise 1: Change the Sleep Duration
### Non-Credit Practice Exercise 1: Change the Sleep Duration
#### Objective
Find the `sleep_ms(2000)` call in the `0x001a_operators` binary using GDB, identify the immediate value `0x7d0` (2000) being loaded into `r0`, calculate the file offset, patch it to `0x1388` (5000) using a hex editor, and verify on hardware that the serial output now prints every 5 seconds instead of every 2 seconds.
+66
View File
@@ -0,0 +1,66 @@
# Embedded Systems Reverse Engineering
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
## Week 9
Operators in Embedded Systems: Debugging and Hacking Operators w/ DHT11 Basics
### Non-Credit Practice Exercise 2 Solution: Invert the Temperature Reading
#### Answers
##### IEEE-754 Sign Bit Flip
| Value | IEEE-754 Hex | Little-Endian Bytes | Sign Bit |
|---------|--------------|--------------------|---------|
| +0.1f | 0x3DCCCCCD | CD CC CC 3D | 0 |
| -0.1f | 0xBDCCCCCD | CD CC CC BD | 1 |
Only the **last byte** changes in little-endian: `3D``BD`. This flips bit 31 (the IEEE-754 sign bit) from 0 to 1, negating the value.
##### Patch at File Offset 0x42C
```
Offset 0x42C:
Before: CD CC CC 3D (+0.1f)
After: CD CC CC BD (-0.1f)
```
Only 1 byte changes: offset `0x42F` from `0x3D` to `0xBD`.
##### How the Temperature Changes
The DHT11 returns raw integer and decimal parts (e.g., integer=24, decimal=0). The firmware computes:
```
temperature = integer_part + (decimal_part × 0.1f)
```
With `vfma.f32 s15, s13, s11`: result = s15 + (s13 × s11) = integer + (decimal × 0.1f)
After patching to -0.1f: result = integer + (decimal × -0.1f)
For a reading of 24.5°C: original = 24 + (5 × 0.1) = 24.5°C, patched = 24 + (5 × -0.1) = 23.5°C
For a reading of 24.0°C: integer=24, decimal=0, so 24 + (0 × -0.1) = 24.0°C (unchanged when decimal is 0).
##### Serial Output After Patch
```
Humidity: 51.0%, Temperature: 23.5°C
```
(Temperature decimal contribution is inverted; effect depends on the decimal component.)
#### Reflection Answers
1. **Why does changing the byte from 0x3D to 0xBD negate the float? What specific bit is being flipped?**
In IEEE-754 single-precision format, bit 31 is the **sign bit**: 0 = positive, 1 = negative. The byte `0x3D` in binary is `0011 1101` and `0xBD` is `1011 1101` — only bit 7 of that byte differs, which corresponds to bit 31 of the 32-bit float (since it's the MSB of the last byte in little-endian storage). The exponent and mantissa bits remain identical, so the magnitude stays exactly `0.1` — only the sign changes.
2. **Why does patching one constant affect both humidity AND temperature? They use different vfma instructions (at 0x410 and 0x414) — so why are both affected?**
Both `vfma` instructions at `0x410` (humidity) and `0x414` (temperature) load the scaling constant from the **same literal pool entry** at offset `0x42C`. The compiler recognized that both computations use the same `0.1f` value and stored it only once to save space. Both `vldr s11, [pc, #offset]` instructions resolve to address `0x1000042C`. So patching that single 4-byte value changes the scaling factor for both humidity and temperature simultaneously.
3. **What is the IEEE-754 encoding of 0.5f? If the raw sensor decimal reading was 8, what would the computed value be with 0.5f instead of 0.1f?**
0.5f in IEEE-754: sign=0, exponent=126 (`0x7E`), mantissa=0. Hex = `0x3F000000`. Little-endian bytes: `00 00 00 3F`. With a raw decimal reading of 8: `8 × 0.5 = 4.0`. So if the integer part was 24, the result would be `24 + 4.0 = 28.0°C` instead of `24 + 0.8 = 24.8°C`.
4. **Could you achieve the same inversion by patching the vfma instruction instead of the constant? What instruction change would work?**
Yes. You could change `vfma.f32` (fused multiply-add: d = d + a×b) to `vfms.f32` (fused multiply-subtract: d = d - a×b). This would compute `temperature = integer - (decimal × 0.1f)` instead of `integer + (decimal × 0.1f)`, achieving the same sign inversion on the decimal contribution. The instruction encoding difference between `vfma` and `vfms` is typically a single bit in the opcode. However, this approach is more complex than simply flipping the sign bit of the constant.
+1 -1
View File
@@ -4,7 +4,7 @@
## Week 9
Operators in Embedded Systems: Debugging and Hacking Operators w/ DHT11 Temperature & Humidity Sensor Single-Wire Protocol Basics
### Exercise 2: Invert the Temperature Reading
### Non-Credit Practice Exercise 2: Invert the Temperature Reading
#### Objective
Using GDB to locate the IEEE-754 scaling constant `0.1f` at file offset `0x42C`, patch it to `-0.1f` using a hex editor, and verify on hardware that the serial output now displays negative temperature values.
+74
View File
@@ -0,0 +1,74 @@
# Embedded Systems Reverse Engineering
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
## Week 9
Operators in Embedded Systems: Debugging and Hacking Operators w/ DHT11 Basics
### Non-Credit Practice Exercise 3 Solution: Add a Fixed Temperature Offset
#### Answers
##### Two Patches Required
This exercise requires **two** simultaneous patches to add a fixed +10°C offset to every temperature reading.
##### Patch 1: Instruction at Offset 0x414
Change `vfma.f32` to `vadd.f32`:
| Item | Original | Patched |
|------------|------------------------------|------------------------------|
| Instruction | vfma.f32 s15, s13, s11 | vadd.f32 s15, s15, s11 |
| Encoding | e6 ee a5 7a | b4 ee a5 7a |
| Operation | s15 = s15 + (s13 × s11) | s15 = s15 + s11 |
```
Offset 0x414:
Before: E6 EE A5 7A (vfma.f32)
After: B4 EE A5 7A (vadd.f32)
```
Only the first two bytes change: `E6 EE``B4 EE`.
##### Patch 2: Constant at Offset 0x42C
Change the constant from 0.1f to 10.0f:
| Value | IEEE-754 Hex | Little-Endian Bytes |
|--------|--------------|--------------------|
| 0.1f | 0x3DCCCCCD | CD CC CC 3D |
| 10.0f | 0x41200000 | 00 00 20 41 |
```
Offset 0x42C:
Before: CD CC CC 3D (0.1f)
After: 00 00 20 41 (10.0f)
```
##### Why Both Patches Are Needed
- **Original:** `vfma.f32` computes `temp = integer + (decimal × 0.1f)` — fractional scaling
- **After both patches:** `vadd.f32` computes `temp = integer + 10.0f` — fixed offset addition
- **If only constant changed:** `vfma.f32` would compute `temp = integer + (decimal × 10.0f)` — amplified decimal, not a fixed offset
##### Serial Output After Patch
```
Humidity: 51.0%, Temperature: 34.0°C
```
(Original 24.0°C + 10.0°C offset = 34.0°C)
#### Reflection Answers
1. **Why are both patches needed? What would happen if you only changed the constant to 10.0f but left vfma unchanged?**
If you only changed `0.1f` to `10.0f` but left `vfma.f32`, the computation would be `temp = integer + (decimal × 10.0f)`. For a reading of 24.5°C (integer=24, decimal=5): result = 24 + (5 × 10.0) = 74.0°C — wildly incorrect. The `vfma` instruction multiplies two operands and adds, so the constant serves as a multiplier for the decimal part. By changing to `vadd.f32`, we eliminate the multiplication entirely and just add the constant directly to the integer, giving `24 + 10.0 = 34.0°C`.
2. **The humidity vfma instruction at 0x410 was NOT changed. Both vfma instructions (0x410 and 0x414) load the same 0.1f constant from 0x42C. With the constant now 10.0f, what happens to the humidity computation?**
The humidity `vfma` at `0x410` now computes `hum = integer + (decimal × 10.0f)`. If the humidity decimal part is 0 (e.g., raw humidity = 51.0%), then `51 + (0 × 10.0) = 51.0%` — unchanged. But if the decimal part is non-zero (e.g., raw = 51.3%, decimal=3), the result would be `51 + (3 × 10.0) = 81.0%` — grossly incorrect. The DHT11 sensor's humidity decimal is often 0, so you might not notice the bug immediately, but it's a latent defect.
3. **If you wanted to add a 10°C offset to temperature WITHOUT affecting humidity, what additional patch(es) would you need?**
You would need to ensure humidity still uses the original `0.1f` scaling. Options: (1) Also change the humidity `vfma` at `0x410` to `vadd.f32` and create a separate literal pool entry with `0.1f` for it — but this requires finding free space. (2) More practically, place a second copy of `0.1f` (`CD CC CC 3D`) in unused space in the binary, and redirect the humidity `vldr` instruction's PC-relative offset to point to that new location instead of `0x42C`. (3) Alternatively, NOP out the humidity `vfma` entirely if the decimal contribution is negligible.
4. **Why do only the first 2 bytes differ between vfma and vadd? What do the last 2 bytes encode?**
In the ARM VFPv4 encoding, the first two bytes (`E6 EE` vs `B4 EE`) contain the **opcode** that distinguishes the operation type (fused multiply-add vs addition). The last two bytes (`A5 7A`) encode the **operand registers**: the source and destination VFP registers (s15, s13, s11). Since both instructions operate on the same registers, the operand encoding is identical. Only the operation code changes — this is a characteristic of the ARM instruction set where opcode and operand fields are cleanly separated.
+1 -1
View File
@@ -4,7 +4,7 @@
## Week 9
Operators in Embedded Systems: Debugging and Hacking Operators w/ DHT11 Temperature & Humidity Sensor Single-Wire Protocol Basics
### Exercise 3: Add a Fixed Temperature Offset
### Non-Credit Practice Exercise 3: Add a Fixed Temperature Offset
#### Objective
Patch both the `vfma.f32` instruction at file offset `0x414` and the scaling constant at `0x42C` to replace the multiply-add with a simple add of `10.0f`, causing every temperature reading to be increased by exactly 10°C, and verify on hardware.
+72
View File
@@ -0,0 +1,72 @@
# Embedded Systems Reverse Engineering
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
## Week 9
Operators in Embedded Systems: Debugging and Hacking Operators w/ DHT11 Basics
### Non-Credit Practice Exercise 4 Solution: Find All printf Format Strings
#### Answers
##### Complete String Catalog
| # | String | Type | Used By |
|---|-----------------------------------------------------|---------------|-------------|
| 1 | `"arithmetic_operator: %d\r\n"` | Format string | printf #1 |
| 2 | `"increment_operator: %d\r\n"` | Format string | printf #2 |
| 3 | `"relational_operator: %d\r\n"` | Format string | printf #3 |
| 4 | `"logical_operator: %d\r\n"` | Format string | printf #4 |
| 5 | `"bitwise_operator: %d\r\n"` | Format string | printf #5 |
| 6 | `"assignment_operator: %d\r\n"` | Format string | printf #6 |
| 7 | `"Humidity: %.1f%%, Temperature: %.1f°C\r\n"` | Format string | printf #7 |
| 8 | `"DHT11 read failed\r\n"` | Plain string | puts |
##### Format Specifier Analysis
| Specifier | Meaning | Used In |
|-----------|------------------------------------------|---------------|
| `%d` | Signed decimal integer | Strings 16 |
| `%.1f` | Float with 1 decimal place | String 7 |
| `%%` | Literal percent sign (escaped) | String 7 |
| `\r\n` | Carriage return + line feed (0x0D 0x0A) | All strings |
| `°C` | UTF-8 degree symbol + C (0xC2 0xB0 0x43)| String 7 |
##### Expected Operator Output Values
| Operator | Expression | Value | Explanation |
|-----------------------|------------------------|-------|------------------------------------|
| arithmetic_operator | 5 × 10 | 50 | Multiplication |
| increment_operator | x++ (x=5) | 5 | Post-increment returns old value |
| relational_operator | 6 > 10 | 0 | False |
| logical_operator | (6>10) && (10>6) | 0 | Short-circuit: first operand false |
| bitwise_operator | 6 << 1 | 12 | Left shift = multiply by 2 |
| assignment_operator | 6 + 5 | 11 | Addition assignment |
##### GDB Search Commands
```gdb
(gdb) x/20s 0x10003e00
(gdb) x/50s 0x10003d00
```
##### Special Byte Sequences in Strings
| Sequence | Bytes | Meaning |
|----------|------------|---------------------|
| `\r\n` | 0x0D 0x0A | CRLF line ending |
| `%%` | 0x25 0x25 | Literal % character |
| `°` | 0xC2 0xB0 | UTF-8 degree symbol |
#### Reflection Answers
1. **The humidity/temperature format string contains %%. What would happen if you patched one of the % characters to a different character (e.g., changed %% to %,)?**
The `%%` escape produces a literal `%` in the output. If you change it to `%,` (bytes `25 2C`), `printf` would interpret `%,` as the start of a format specifier where `,` is the conversion character. Since `,` is not a valid `printf` conversion specifier, the behavior is **undefined** — most implementations would either print garbage, skip it, or consume the next argument from the stack incorrectly. This could corrupt the remaining output or even crash if `printf` tries to read a non-existent argument.
2. **If you changed "arithmetic_operator" to "hacked_operator__" (same length) in the binary, what would the serial output look like? Would the computed value change?**
The serial output would show `hacked_operator__: 50` instead of `arithmetic_operator: 50`. The **computed value (50) would not change** — it's determined by the actual multiplication instruction in the code, not by the format string. The format string is just a label for display purposes. The `%d` specifier still reads the same `r1` register value (50) passed as the second argument to `printf`.
3. **What happens if you make a format string 1 byte longer (e.g., add a character)? Where would the extra byte be stored?**
The extra byte would overwrite the **null terminator** (`0x00`) of the current string, and the byte after that is the first byte of the next consecutive string in `.rodata`. This effectively merges the two strings: `printf` would continue reading past the intended end into the next string's data until it finds another `0x00`. The output would include garbage characters from the adjacent string. If the adjacent data happens to contain `%` followed by a valid specifier, `printf` might try to read additional arguments from the stack, potentially causing a crash or information leak.
4. **The "DHT11 read failed" message uses puts instead of printf. Why would the compiler choose puts over printf for this particular string?**
The compiler (with optimizations enabled) recognizes that `printf("DHT11 read failed\r\n")` has **no format specifiers** — it's a plain string with no `%d`, `%s`, `%f`, etc. Since no formatting is needed, the compiler optimizes it to `puts("DHT11 read failed")` (which automatically appends a newline). This is a common GCC optimization (`-fprintf-return-value`) because `puts` is simpler and faster than `printf` — it doesn't need to parse the format string looking for specifiers. The `\r\n` may be handled slightly differently depending on the implementation, but the key insight is that the compiler automatically selects the more efficient function.
+1 -1
View File
@@ -4,7 +4,7 @@
## Week 9
Operators in Embedded Systems: Debugging and Hacking Operators w/ DHT11 Temperature & Humidity Sensor Single-Wire Protocol Basics
### Exercise 4: Find All printf Format Strings
### Non-Credit Practice Exercise 4: Find All printf Format Strings
#### Objective
Systematically search through the `0x001a_operators` binary using GDB and a hex editor to locate every `printf` format string, catalog their addresses, file offsets, contents, and purposes, and gain experience identifying data structures in compiled binaries.