Round 62: the code loads byte-perfect and six bytes change 60 ms later; the memset is not the culprit

Sampling the overlay every 50 ms from MENU: before it, crc 169b5c51 matching 262/2568; at +0.00 s, crc 60234c72 matching 2568/2568, which is the app's own CRC; at +0.06 s, crc 4f23f6f3 matching 2562/2568; and every later sample identical. So the load is byte-perfect and the loader's own check would pass at that instant, and exactly six bytes -- the pointer 0x0801D640 plus 28 0a -- change sixty milliseconds later and never change again.

That retires round 59's reading: a memset zeroing the whole 4 KiB would change thousands of bytes, not six, so the hundred-a-second memset is not what stops the app. The damage is a six-byte structure write.

Together with the offset tracking code_size (code_size - 124 for both the 2744 and 2568 builds), something on the load path that knows code_size writes a six-byte structure at overlay + code_size - 124 just after the copy and before the app is entered. Next: watch those bytes again but ignore the memset, reporting only the first hit whose fill byte is not zero.
This commit is contained in:
mckero committed 2026-10-02 13:42:02 +08:00
1 parent 244c51c2e1
commit 27e84ca3da
2 files changed
+50

No files matched your search

+26
View File
@@ -2429,3 +2429,29 @@ overlay exactly once. Whatever the routine is called, nothing the loader writes
Next: identify 0x080175fe and 0x080047ca against the image itself rather than against a fetched file -- for
instance by looking at what each calls and returns -- or fetch the sources for the exact build being run.
**Round 62: the code loads byte-perfect and exactly six bytes change sixty milliseconds later. The memset is not the culprit.**
Sampling the overlay every fifty milliseconds from the instant MENU is pressed:
before MENU crc 169b5c51 matching 262/2568
+0.00 s crc 60234c72 matching 2568/2568 EXACT
+0.06 s crc 4f23f6f3 matching 2562/2568
... every later sample identical
60234c72 is the app's own CRC, so at that instant the overlay holds the code byte for byte and the loader's own
verification would pass. Sixty milliseconds later exactly six bytes have changed -- the four-byte pointer
0x0801D640, which appears once in the image, plus 28 0a -- and nothing changes after that.
That retires round 59's reading. A memset that zeroes the whole 4 KiB overlay would change thousands of bytes,
not six, so the hundred-a-second memset is not what stops the app. It is a separate routine doing something
else, and the damage is a six-byte structure write.
The two measurements together are what make this sharp: the offset that ends up wrong tracks the app's size
(code_size - 124 for both the 2744-byte and the 2568-byte build), and the write is a pointer plus a small
value. Something on the load path, which knows code_size, writes a six-byte structure at overlay + code_size
- 124 just after the copy and before the app is entered.
Next: watch those four bytes again, but ignore the memset and report only the first hit whose fill byte is not
zero -- and if that is still the memset, watch a byte of the six that the memset does not clear, so the budget
is not spent on it.
+24
View File
@@ -2123,3 +2123,27 @@ PC=0x0801ae7a(`memset` 内部,入口 0x0801ae70) SP=0x20003c50
**下一步**:**对着镜像本身、而不是对着取来的文件,去认 `0x080175fe` 与 `0x080047ca`** ✓ ——
比如看它们各自调用与返回了什么 ✓ —— **或者去取「正在跑的那个构建」的源码** ✓。
**第 62 轮:代码是逐字节完美载入的,而恰好 6 个字节在六十毫秒后改变。那个 `memset` 不是元凶。**
从按下 MENU 的那一刻起,每五十毫秒给覆盖区采一次样:
```
before MENU crc 169b5c51 匹配 262/2568
+0.00 s crc 60234c72 匹配 2568/2568 EXACT
+0.06 s crc 4f23f6f3 匹配 2562/2568
... 此后每一次采样都相同
```
`60234c72` **就是应用自己的 CRC** ✓ —— 所以**在那一刻覆盖区里逐字节就是那段代码 ✓,加载器自己的校验会通过** ✓。
**六十毫秒之后恰好 6 个字节变了** ✗ —— 那个在镜像里只出现一次的 4 字节指针 `0x0801D640` ✓,加上 `28 0a` ✓ —— **而此后什么都不再变** ✓。
**这一条推翻了第 59 轮的读法** ✗:**把整片 4 KiB 覆盖区清零的 `memset` 会改掉成千上万个字节 ✓,而不是 6 个 ✓** ——
**所以那个每秒一百次的 `memset` 并不是让应用停下来原因** ✓。**它是另一个例程在做别的事,而真正的损伤是一次 6 字节的结构写入** ✓。
**把这两条测量放在一起,图景才锋利起来** ✓:**出错位置随应用大小移动(2744 与 2568 两个构建都是 `code_size − 124`)** ✓,
**而那次写入是一个指针加一个小值** ✓。**是加载路径上某个知道 `code_size` 的东西 ✓,在拷贝之后、应用被进入之前 ✓,
在 `覆盖区 + code_size − 124` 处写下一个 6 字节的结构** ✓。
**下一步**:**再看那四个字节一次,但把 `memset` 忽略掉 ✓,只报第一个「填充字节不为零」的命中** ✓ ——
**若那仍然是 `memset`,就改看那 6 个字节里 `memset` 不会清掉的一个字节 ✓,这样观察预算就不会被它吃光** ✓。