Updated WEEK05

This commit is contained in:
Kevin Thomas
2026-02-21 09:55:14 -05:00
parent c1626d1c96
commit a17767e099
6 changed files with 2370 additions and 3 deletions
+271
View File
@@ -0,0 +1,271 @@
# 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
### Exercise 1: Analyze the Float Binary in Ghidra
#### Objective
Import and analyze the `0x000e_floating-point-data-type.bin` binary in Ghidra to understand how the compiler handles floating-point variables, discover float-to-double promotion, and decode the IEEE 754 double-precision encoding of `42.5` from two 32-bit registers.
#### Prerequisites
- Ghidra installed and configured
- `0x000e_floating-point-data-type.bin` binary available in your build directory
- Understanding of IEEE 754 encoding from Week 5 Part 2
- Basic Ghidra navigation skills from Weeks 3 and 4
#### Task Description
You will import the float binary into Ghidra, configure it for ARM Cortex-M33, resolve function names, discover that the compiler promotes `float` to `double` when passing to `printf`, and manually decode the 64-bit double `0x4045400000000000` field by field to confirm it represents `42.5`.
#### Step-by-Step Instructions
##### Step 1: Start Ghidra and Create New Project
```bash
ghidraRun
```
1. Click **File****New Project**
2. Select **Non-Shared Project**
3. Click **Next**
4. Enter Project Name: `week05-ex01-floating-point`
5. Choose a project directory
6. Click **Finish**
##### Step 2: Import the Binary
1. Navigate to your file explorer
2. Find `Embedded-Hacking/0x000e_floating-point-data-type/build/0x000e_floating-point-data-type.bin`
3. **Drag and drop** the `.bin` file into Ghidra's project window
##### Step 3: Configure Import Settings
When the import dialog appears:
1. Click the three dots (**…**) next to **Language**
2. Search for: `Cortex`
3. Select: **ARM Cortex 32 little endian default**
4. Click **OK**
Now click **Options…** button:
1. Change **Block Name** to: `.text`
2. Change **Base Address** to: `10000000` (XIP flash base)
3. Click **OK**
Then click **OK** on the main import dialog.
##### Step 4: Analyze the Binary
1. Double-click the imported file in the project window
2. When prompted "Analyze now?" click **Yes**
3. Leave all default analysis options selected
4. Click **Analyze**
5. Wait for analysis to complete (watch bottom-right progress bar)
##### Step 5: Locate and Rename the Main Function
Look at the **Symbol Tree** panel on the left. Expand **Functions**.
From previous weeks, we know the boot sequence leads to `main()`:
1. Click on `FUN_10000234`
2. Right-click → **Edit Function Signature**
3. Change to: `int main(void)`
4. Click **OK**
##### Step 6: Resolve stdio_init_all and printf
**Rename stdio_init_all:**
1. Click on `FUN_10002f5c` in the decompile window
2. Right-click → **Edit Function Signature**
3. Change to: `bool stdio_init_all(void)`
4. Click **OK**
**Rename printf:**
1. Click on `FUN_100030ec`
2. Right-click → **Edit Function Signature**
3. Change to: `int __wrap_printf(char *format,...)`
4. Check the **Varargs** checkbox
5. Click **OK**
##### Step 7: Observe the Decompiled Code
After resolving, the decompiled `main` should look like:
```c
int main(void)
{
undefined4 uVar1;
undefined4 extraout_r1;
undefined4 uVar2;
undefined4 extraout_r1_00;
stdio_init_all();
uVar1 = DAT_1000024c;
uVar2 = extraout_r1;
do {
__wrap_printf(DAT_10000250,uVar2,0,uVar1);
uVar2 = extraout_r1_00;
} while( true );
}
```
**Critical observation:** Where is `float fav_num = 42.5`? The compiler optimized it into constants!
##### Step 8: Identify the Register Pair
Look at the **Listing** window (assembly view) for `main`:
```assembly
1000023a 00 24 movs r4, #0x0
1000023c 03 4d ldr r5, [DAT_1000024c] = 40454000h
```
Two values are being passed to `printf`:
- `r2 = 0x00000000` (low 32 bits)
- `r3 = 0x40454000` (high 32 bits)
Together they form a 64-bit double: `0x4045400000000000`
**Why a double?** The C standard requires that `float` arguments to variadic functions like `printf` are **promoted to `double`**. So even though our variable is declared as `float fav_num = 42.5`, `printf` always receives a 64-bit double.
##### Step 9: Write Out the Binary Layout
Convert both registers to binary:
```
r3 (high 32 bits): 0x40454000 = 0100 0000 0100 0101 0100 0000 0000 0000
r2 (low 32 bits): 0x00000000 = 0000 0000 0000 0000 0000 0000 0000 0000
```
Map the 64-bit IEEE 754 fields:
```
Bit 63 (sign): 0
Bits 6252 (exponent): 10000000100
Bits 510 (mantissa): 0101010000000000...0000
```
##### Step 10: Decode the Sign Bit
Bit 63 of the double = bit 31 of r3:
```
r3 = 0x40454000 = 0100 0000 0100 0101 0100 0000 0000 0000
^
bit 31 = 0 → Positive number
```
IEEE 754 sign rule: `0` = Positive, `1` = Negative.
##### Step 11: Decode the Exponent
Extract bits 3020 from r3:
```
0x40454000: 0 10000000100 01010100000000000000
^ ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^
sign exponent mantissa (top 20)
```
Exponent bits: `10000000100` = 2¹⁰ + 2² = 1024 + 4 = **1028**
Subtract the double-precision bias (1023):
$$\text{real exponent} = 1028 - 1023 = \mathbf{5}$$
##### Step 12: Decode the Mantissa
High 20 bits of mantissa (from r3 bits 190):
```
0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
```
Low 32 bits of mantissa (from r2):
```
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
```
With the implied leading `1`:
```
1.010101 00000...
```
##### Step 13: Reconstruct the Final Value
$$1.010101_2 \times 2^5 = 101010.1_2$$
Convert to decimal:
| Bit | Power | Value |
|-----|-------|-------|
| 1 | 2⁵ | 32 |
| 0 | 2⁴ | 0 |
| 1 | 2³ | 8 |
| 0 | 2² | 0 |
| 1 | 2¹ | 2 |
| 0 | 2⁰ | 0 |
| 1 | 2⁻¹ | 0.5 |
$$32 + 8 + 2 + 0.5 = \mathbf{42.5} ✓$$
##### Step 14: Find the Format String
In the Listing view, click on the data reference to locate:
```
s_fav_num:_%f_100034a8 ds "fav_num: %f\r\n"
```
Note that the format specifier is `%f`, confirming this is a floating-point print call.
##### Step 15: Document Your Findings
Create a table of your observations:
| Item | Value | Notes |
| ------------------------- | ------------------ | ---------------------------------- |
| Main function address | `0x10000234` | Entry point of program |
| Float value (original) | `42.5` | Declared as `float` |
| Double hex encoding | `0x4045400000000000` | Promoted to double for printf |
| r3 (high word) | `0x40454000` | Contains sign + exponent + mantissa top bits |
| r2 (low word) | `0x00000000` | All zeros — clean fractional part |
| Exponent (stored) | 1028 | Biased value |
| Exponent (real) | 5 | After subtracting bias 1023 |
| Format string | `"fav_num: %f\r\n"` | Located at `0x100034a8` |
| Float-to-double promotion | Yes | C standard for variadic functions |
#### Expected Output
After completing this exercise, you should be able to:
- Import and configure ARM binaries in Ghidra for float analysis
- Explain why `printf` receives a `double` even when the variable is a `float`
- Identify the register pair `r2:r3` that holds a 64-bit double
- Manually decode an IEEE 754 double from hex to decimal
- Locate format strings in the binary
#### Questions for Reflection
###### Question 1: Why does the compiler promote a `float` to a `double` when passing it to `printf`?
###### Question 2: The low word (`r2`) is `0x00000000`. What does this tell you about the fractional part of `42.5`?
###### Question 3: What is the purpose of the exponent bias (1023) in IEEE 754 double-precision?
###### Question 4: If the sign bit (bit 63) were `1` instead of `0`, what value would the double represent?
#### Tips and Hints
- The **Listing** window shows raw assembly; the **Decompile** window shows reconstructed C
- Double-click on a `DAT_` reference to jump to the data constant
- Use Python to verify: `import struct; struct.pack('>d', 42.5).hex()` gives `4045400000000000`
- Remember: r3 = high 32 bits (sign + exponent + top mantissa), r2 = low 32 bits (bottom mantissa)
- The bias for doubles is always 1023; for floats it's 127
#### Next Steps
- Proceed to Exercise 2 to patch this float value in Ghidra
- Try computing the IEEE 754 encoding of other values like `3.14` or `100.0` by hand
- Compare the 32-bit float encoding `0x422A0000` with the 64-bit double encoding `0x4045400000000000` — both represent `42.5`
#### Additional Challenge
Find the data constant `DAT_1000024c` in the Listing view. What raw bytes are stored there? Remember that ARM is little-endian — the bytes in memory are in reverse order. Write out the byte order as it appears in memory vs. as a 32-bit value.