# 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 3: Analyze the Double Binary in Ghidra

#### Objective
Import and analyze the `0x0011_double-floating-point-data-type.bin` binary in Ghidra to understand how doubles differ from floats at the binary level, observe that **both** register words carry non-zero data when the fractional part is complex, and decode the IEEE 754 double-precision encoding of `42.52525` from two 32-bit registers.

#### Prerequisites
- Completed Exercises 1 and 2 (float analysis and patching)
- Understanding of IEEE 754 double-precision format from Week 5 Part 3
- Understanding of register pairs (`r2:r3`) from Exercise 1
- Basic Ghidra navigation skills

#### Task Description
You will import the double binary into Ghidra, resolve function names, identify that `42.52525` requires non-zero data in **both** r2 and r3 (unlike `42.5` which had r2 = 0), and decode the full 64-bit double `0x4045433B645A1CAC` field by field to confirm it represents `42.52525`.

#### Step-by-Step Instructions

##### Step 1: Flash the Original Binary

Before analysis, verify the program works:

1. Hold **BOOTSEL** and plug in your Pico 2
2. Flash `0x0011_double-floating-point-data-type.uf2` to the RPI-RP2 drive
3. Open your serial monitor

**Expected output:**
```
fav_num: 42.525250
fav_num: 42.525250
fav_num: 42.525250
...
```

##### Step 2: Create a New Ghidra Project

1. Launch Ghidra: `ghidraRun`
2. Click **File** → **New Project**
3. Select **Non-Shared Project**
4. Project Name: `week05-ex03-double-analysis`
5. Click **Finish**

##### Step 3: Import and Configure the Binary

1. Drag and drop `0x0011_double-floating-point-data-type.bin` into Ghidra
2. Set Language: **ARM Cortex 32 little endian default**
3. Click **Options…**
   - Block Name: `.text`
   - Base Address: `10000000`
4. Click **OK** on all dialogs
5. Double-click the file and click **Yes** to analyze

##### Step 4: Locate and Rename Functions

Identify the main function and standard library calls:

**Rename main:**
1. Click on `FUN_10000238`
2. Right-click → **Edit Function Signature**
3. Change to: `int main(void)`
4. Click **OK**

**Rename stdio_init_all:**
1. Click on `FUN_10002f64`
2. Right-click → **Edit Function Signature**
3. Change to: `bool stdio_init_all(void)`
4. Click **OK**

**Rename printf:**
1. Click on `FUN_100030f4`
2. Right-click → **Edit Function Signature**
3. Change to: `int printf(char *format,...)`
4. Check the **Varargs** checkbox
5. Click **OK**

##### Step 5: Observe the Decompiled Code

After renaming, the decompiled `main` should look like:

```c
int main(void)
{
  undefined4 uVar1;
  undefined4 uVar2;
  undefined4 extraout_r1;
  undefined4 uVar3;
  undefined4 extraout_r1_00;
  
  uVar2 = DAT_10000258;
  uVar1 = DAT_10000254;
  stdio_init_all();
  uVar3 = extraout_r1;
  do {
    printf(DAT_10000250,uVar3,uVar1,uVar2);
    uVar3 = extraout_r1_00;
  } while( true );
}
```

**Critical observation:** There are now **two** non-zero data constants — `DAT_10000254` and `DAT_10000258`. This is the key difference from the float exercise!

##### Step 6: Examine the Assembly Listing

Click on the **Listing** window and find the main function assembly:

```assembly
10000238    push    {r3,r4,r5,lr}
1000023a    adr     r5, [0x10000254]
1000023c    ldrd    r4, r5, [r5, #0x0]    = 645A1CACh / 4045433Bh
10000240    bl      stdio_init_all
```

**Key instruction: `ldrd r4, r5, [r5, #0x0]`**

This is a **load register double** instruction — it loads two consecutive 32-bit words in a single instruction:
- `r4` gets the low word: `0x645A1CAC`
- `r5` gets the high word: `0x4045433B`

Later in the loop:
```assembly
10000244    mov     r2, r4    ; r2 = 0x645A1CAC (low)
10000246    mov     r3, r5    ; r3 = 0x4045433B (high)
```

##### Step 7: Identify the Register Pair

| Register | Value        | Role         |
| -------- | ------------ | ------------ |
| `r2`     | `0x645A1CAC` | Low 32 bits  |
| `r3`     | `0x4045433B` | High 32 bits |

Together: `0x4045433B645A1CAC`

**Compare to the float exercise:**
- Float `42.5`: r2 = `0x00000000`, r3 = `0x40454000` — low word is all zeros
- Double `42.52525`: r2 = `0x645A1CAC`, r3 = `0x4045433B` — **both words are non-zero!**

This happens because `42.52525` has a repeating binary fraction that needs all 52 mantissa bits.

##### Step 8: Decode the Sign Bit

Convert r3 to binary and check bit 31:

```
r3 = 0x4045433B = 0100 0000 0100 0101 0100 0011 0011 1011
                   ^
                   bit 31 = 0  →  Positive number ✓
```

##### Step 9: Decode the Exponent

Extract bits 30–20 from r3:

```
0x4045433B:  0  10000000100  01010100001100111011
             ^  ^^^^^^^^^^^  ^^^^^^^^^^^^^^^^^^^^
           sign  exponent      mantissa (top 20)
```

Exponent bits: `10000000100` = 2¹⁰ + 2² = 1024 + 4 = **1028**

$$\text{real exponent} = 1028 - 1023 = \mathbf{5}$$

##### Step 10: Decode the Mantissa

**High 20 bits** (r3 bits 19–0):
```
0 1 0 1 0 1 0 0 0 0 1 1 0 0 1 1 1 0 1 1
```

**Low 32 bits** (all of r2 = `0x645A1CAC`):
```
0 1 1 0 0 1 0 0 0 1 0 1 1 0 1 0 0 0 0 1 1 1 0 0 1 0 1 0 1 1 0 0
```

With the implied leading `1`:
```
1.0101010000110011101101100100010110100001110010101100
```

##### Step 11: Reconstruct the Integer Part

$$1.0101010000110011..._2 \times 2^5$$

Shift the binary point 5 places right → `101010.1000011001110110...`

Integer part `101010`:

| Bit | Power | Value |
|-----|-------|-------|
| 1   | 2⁵    | 32    |
| 0   | 2⁴    | 0     |
| 1   | 2³    | 8     |
| 0   | 2²    | 0     |
| 1   | 2¹    | 2     |
| 0   | 2⁰    | 0     |

$$32 + 8 + 2 = \mathbf{42}$$

##### Step 12: Approximate the Fractional Part

The first few fractional bits `.10000110011...`:

| Bit  | Power  | Decimal       |
|------|--------|---------------|
| 1    | 2⁻¹   | 0.5           |
| 0    | 2⁻²   | 0             |
| 0    | 2⁻³   | 0             |
| 0    | 2⁻⁴   | 0             |
| 0    | 2⁻⁵   | 0             |
| 1    | 2⁻⁶   | 0.015625      |
| 1    | 2⁻⁷   | 0.0078125     |
| 0    | 2⁻⁸   | 0             |
| 0    | 2⁻⁹   | 0             |
| 1    | 2⁻¹⁰  | 0.0009765625  |
| 1    | 2⁻¹¹  | 0.00048828125 |

First 11 bits sum ≈ 0.5249. The remaining 36 fractional bits refine this to ≈ 0.52525.

$$42 + 0.52525 = \mathbf{42.52525} ✓$$

##### Step 13: Find the Format String

In the Listing view, locate:

```
s_fav_num:_%lf_100034b0    ds    "fav_num: %lf\r\n"
```

Note the `%lf` format specifier — this program explicitly uses `double`, unlike the float program which used `%f`.

##### Step 14: Document Your Findings

| Item                      | Float (`42.5`)       | Double (`42.52525`)      |
| ------------------------- | -------------------- | ------------------------ |
| r2 (low word)             | `0x00000000`         | `0x645A1CAC`             |
| r3 (high word)            | `0x40454000`         | `0x4045433B`             |
| Low word is zero?         | Yes                  | **No**                   |
| Words to patch            | 1 (r3 only)          | **2 (both r2 and r3)**   |
| Format specifier          | `%f`                 | `%lf`                    |
| Assembly load instruction | `movs` + `ldr`       | `ldrd` (load double)     |

#### Expected Output

After completing this exercise, you should understand:
- How the compiler loads a 64-bit double using `ldrd` (load register double)
- Why `42.52525` requires non-zero data in both registers while `42.5` does not
- How to decode a complex IEEE 754 double with a repeating binary fraction
- The differences between float and double handling at the assembly level

#### Questions for Reflection

###### Question 1: Why does `42.5` have a zero low word but `42.52525` does not?

###### Question 2: The assembly uses `ldrd r4, r5, [r5, #0x0]` instead of two separate `ldr` instructions. What is the advantage?

###### Question 3: Both the float and double programs have the same exponent (stored as 1028, real exponent 5). Why?

###### Question 4: If you were patching this double, how many data constants would you need to modify compared to the float exercise?

#### Tips and Hints
- Use Python to verify: `import struct; struct.pack('>d', 42.52525).hex()` gives `4045433b645a1cac`
- The `ldrd` instruction always loads the lower-addressed word into the first register
- A repeating binary fraction (like `0.52525`) can never be represented exactly — double precision uses the closest 52-bit approximation
- Compare data addresses: the float binary has one `DAT_` constant; the double binary has two consecutive ones

#### Next Steps
- Proceed to Exercise 4 to patch both register words
- Compare the mantissa of `42.5` (clean: `010101 000...`) vs. `42.52525` (complex: `0101010000110011...`)
- Think about what values would have a zero low word (hint: powers of 2, halves, quarters)

#### Additional Challenge
Using the 52-bit mantissa `0101010000110011101101100100010110100001110010101100`, manually sum the first 20 fractional bits to see how close you get to `0.52525`. How many bits of precision does it take to get within 0.001 of the true value?
