# Embedded Systems Reverse Engineering [Repository](https://github.com/mytechnotalent/Embedded-Hacking) ## Week 5 Integers and Floats in Embedded Systems: Debugging and Hacking Integers and Floats w/ Intermediate GPIO Output Assembler Analysis ### Non-Credit Practice Exercise 2: Patch the Float Binary — Changing 42.5 to 99.0 #### Objective Calculate the IEEE 754 double-precision encoding of `99.0`, patch the float binary in Ghidra to change the printed value from `42.5` to `99.0`, export the patched binary, convert it to UF2 format, and flash it to the Pico 2 to verify the change. #### Prerequisites - Completed Exercise 1 (float binary imported and analyzed in Ghidra) - Understanding of IEEE 754 double-precision encoding from Week 5 Part 2.7 - Python installed for UF2 conversion - `uf2conv.py` script available in your project directory - Raspberry Pi Pico 2 connected via USB - Serial monitor software (PuTTY, minicom, or screen) #### Task Description You will convert `99.0` to its IEEE 754 double-precision encoding by hand (integer-to-binary conversion, normalization, field extraction), determine which register words need to change, patch the data constant in Ghidra, and verify on hardware that the serial output now prints `99.000000`. #### Step-by-Step Instructions ##### Step 1: Convert 99 to Binary Use repeated division by 2: | Division | Quotient | Remainder | |----------|----------|-----------| | 99 ÷ 2 | 49 | **1** | | 49 ÷ 2 | 24 | **1** | | 24 ÷ 2 | 12 | **0** | | 12 ÷ 2 | 6 | **0** | | 6 ÷ 2 | 3 | **0** | | 3 ÷ 2 | 1 | **1** | | 1 ÷ 2 | 0 | **1** | Read remainders bottom-to-top: $99_{10} = 1100011_2$ ##### Step 2: Handle the Fractional Part The fractional part of `99.0` is `.0` — exactly zero. There are no fractional bits. $$99.0_{10} = 1100011.0_2$$ ##### Step 3: Normalize to IEEE 754 Form Move the binary point so there is exactly one `1` before it: $$1100011.0_2 = 1.100011_2 \times 2^6$$ We shifted the binary point 6 places left, so the exponent is **6**. ##### Step 4: Extract the IEEE 754 Fields 1. **Sign:** `0` (positive) 2. **Exponent:** $6 + 1023 = 1029 = 10000000101_2$ 3. **Mantissa:** `100011` followed by 46 zeros (everything after the `1.`, padded to 52 bits) 4. **Full double:** `0x4058C00000000000` Split into register words: - **r3 (high word):** `0x4058C000` - **r2 (low word):** `0x00000000` ##### Step 5: Determine What to Patch Compare old vs. new: | Register | Old Value | New Value | Changed? | | -------- | ------------ | ------------ | -------- | | `r2` | `0x00000000` | `0x00000000` | No | | `r3` | `0x40454000` | `0x4058C000` | **Yes** | Since `r2` stays all zeros, we only need to patch the high word in `r3`. ##### Step 6: Locate the Data Constant in Ghidra Open your Ghidra project from Exercise 1. In the Listing view, find the data constant that loads into `r3`: ``` DAT_1000024c 10000248 00 40 45 40 undefined4 40454000h ``` This is the 32-bit constant `0x40454000` — the high word of the double `42.5`. ##### Step 7: Patch the Constant 1. Click on **Window** → **Bytes** to open the Bytes Editor 2. Click the **Pencil Icon** to enable editing 3. Navigate to the data at `DAT_1000024c` 4. The bytes in memory (little-endian) read: `00 40 45 40` 5. Change them to: `00 C0 58 40` (which is `0x4058C000` in little-endian) 6. Press **Enter** **Verify** in the Listing view — the data should now show `4058C000h`. ##### Step 8: Verify the Patch in the Decompile Window The decompiled code should now reference the new constant. The value loaded into `r3` should be `0x4058C000`. ##### Step 9: Export the Patched Binary 1. Click **File** → **Export Program** 2. Set **Format** to **Raw Bytes** 3. Navigate to your build directory 4. Name the file: `0x000e_floating-point-data-type-h.bin` 5. Click **OK** ##### Step 10: Convert to UF2 Format Open a terminal and navigate to your project directory: ```powershell cd Embedded-Hacking-main\0x000e_floating-point-data-type ``` Run the conversion: ```powershell python ..\uf2conv.py build\0x000e_floating-point-data-type-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2 ``` ##### Step 11: Flash and Verify 1. Hold **BOOTSEL** and plug in your Pico 2 2. Drag and drop `hacked.uf2` onto the RPI-RP2 drive 3. Open your serial monitor **Expected output:** ``` fav_num: 99.000000 fav_num: 99.000000 fav_num: 99.000000 ... ``` 🎉 **Success!** The value changed from `42.5` to `99.0`! #### Expected Output After completing this exercise, you should: - See `fav_num: 99.000000` printing instead of `fav_num: 42.500000` - Have a patched binary file (`0x000e_floating-point-data-type-h.bin`) - Have a UF2 file (`hacked.uf2`) - Understand the complete IEEE 754 encoding and patching workflow for floats #### Questions for Reflection ###### Question 1: Why did we only need to patch r3 (the high word) and not r2 (the low word)? ###### Question 2: What would the high word be if we wanted to patch the value to `-99.0` instead? (Hint: which bit controls the sign?) ###### Question 3: Walk through the encoding of `100.0` as a double. What are the high and low words? ###### Question 4: Why do we need the `--family 0xe48bff59` flag when converting to UF2? #### Tips and Hints - Always verify your encoding with Python: `import struct; struct.pack('>d', 99.0).hex()` - Little-endian means the bytes in memory are reversed: `0x4058C000` is stored as `00 C0 58 40` - If you patch the wrong bytes, use **File** → **Undo** in Ghidra (or re-import the original binary) - The bias for double-precision is always 1023; for single-precision it's 127 - A clean fractional part (like `.0` or `.5`) means the low word (`r2`) is all zeros #### Next Steps - Proceed to Exercise 3 to analyze the double-precision binary - Try patching to different values: `3.14`, `100.0`, `255.0` - Compare how many register words change for clean vs. messy fractions #### Additional Challenge Patch the float to `3.14` instead of `99.0`. Since `3.14` has a repeating binary fraction, the low word will **not** be zero. This means you need to modify the assembly to load a non-zero value into `r2` as well. Can you figure out where `r2` gets its zero value (`movs r4, #0x0`) and change it?