3.7 KiB
Embedded Systems Reverse Engineering
Week 6
Static Variables in Embedded Systems: Debugging and Hacking Static Variables w/ GPIO Input Basics
Non-Credit Practice Exercise 3 Solution: Make the Overflow Happen Faster
Answers
Patch Details
| Item | Original | Patched |
|---|---|---|
| Instruction | adds r3, #0x1 | adds r3, #0xa |
| Address | 0x1000027c | 0x1000027c |
| Hex bytes | 01 33 | 0A 33 |
| Increment value | 1 | 10 |
| File offset | 0x27c | 0x27c |
Instruction Encoding
Thumb adds rD, #imm8:
Byte 0: immediate value (0x01 → 0x0A)
Byte 1: opcode + register (0x33 = adds r3)
Serial Output After Patch
regular_fav_num: 42
static_fav_num: 42
regular_fav_num: 42
static_fav_num: 52
regular_fav_num: 42
static_fav_num: 62
...
regular_fav_num: 42
static_fav_num: 252
regular_fav_num: 42
static_fav_num: 6 ← Overflow! 252 + 10 = 262 mod 256 = 6
Reflection Answers
-
The overflow now wraps to 6 instead of 0. Explain why, using the modular arithmetic of a
uint8_t(range 0-255). Auint8_tstores values modulo 256. Starting at 42 and incrementing by 10: the sequence passes through 42, 52, 62, ..., 242, 252. The next value is 252 + 10 = 262. Sinceuint8_tcan only hold 0–255:262 \bmod 256 = 6. The wrap value is non-zero because the increment (10) does not evenly divide into 256. With increment 1, the value hits exactly 255, and255 + 1 = 256 \bmod 256 = 0. With increment 10, it skips from 252 directly to 262, bypassing 0 and landing on 6. -
What is the maximum value you could change the increment to while still using
adds r3, #imm8? What would happen if you needed an increment larger than 255? The maximum is 255 (0xFF). Theadds rD, #imm8Thumb encoding has an 8-bit immediate field, so valid values are 0–255. For an increment larger than 255, you would need to: (a) use a 32-bit Thumb-2adds.winstruction which supports a wider range of modified immediates, (b) use amovs+addstwo-instruction sequence to load a larger value, or (c) load the value from a literal pool withldrthen use a register-registeradd. Each approach requires different instruction sizes, so patching in a hex editor becomes more complex—you may need to shift code or use NOP padding. -
If you changed the increment to 128 (
0x80), how many iterations would it take to wrap, and what value would it wrap to? Starting at 42, incrementing by 128: 42 → 170 → 42 → 170 → ... Wait, let's compute:42 + 128 = 170(first iteration),170 + 128 = 298 \bmod 256 = 42(second iteration). It wraps after 2 iterations back to 42. The variable alternates between 42 and 170 forever because2 \times 128 = 256, and42 + 256 = 42 \bmod 256. The value never reaches 0—it cycles between exactly two values. -
Could you achieve the same speedup by changing the
strb(store byte) tostrh(store halfword)? Why or why not? No. Changingstrbtostrhwould store 16 bits instead of 8, which means the variable would be treated as auint16_t(range 0–65535). This would actually slow down overflow—it would take 65,535 − 42 = 65,493 iterations to wrap instead of 213. Additionally,strhwrites 2 bytes to RAM, potentially corrupting the adjacent byte at0x200005a9. The proper way to speed up overflow is to increase the increment value, not change the storage width. Thestrbtruncation to 8 bits is what enforces theuint8_tmodular arithmetic.