3.6 KiB
Embedded Systems Reverse Engineering
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 (0–255) 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
- Find the literal pool entry containing
D0 07 00 00in HxD - Replace with
88 13 00 00
Before: D0 07 00 00 (2000)
After: 88 13 00 00 (5000)
Conversion and Flash
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
-
Why can't 2000 be encoded as a movs immediate? What is the maximum value movs can hold? The
movs Rd, #imm8instruction is a 16-bit Thumb encoding that has only 8 bits for the immediate value, giving a range of 0–255. The value 2000 (0x7D0) is far beyond this range. Even the 32-bit Thumb-2movwinstruction can only encode 0–65535, 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. -
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 =
0x000003E8hex. 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). -
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
0x7D0from the same pool address (using its ownldr 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 allldrinstructions whose PC-relative offset resolves to the same pool address. -
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.