feat(VM): cd 槽位表可静态导出 + 槽位语义还原(符号求值)
tdc_program 新增 symbolic_assignments(): 对小程序做线性符号求值, 把字节码还原成
`r6 = c.navigator.userAgent` 这样的可读表达式(覆盖字面串/BIND_THIS/MAKE_(REG_)PROP_REF/
GET_GLOBAL_PROP/NEW_FUNC/CALL_*/STORE_REG/LOAD_REG_IMM/REVERSE_TOP2/DUP/算术)。
tdc_layout 新增:
* slot_value_expr(): 精确规则 —— 采集器模块把值存进某寄存器, `get` 闭包只返回它
(caps 的父寄存器就是取值寄存器); 含"模块里唯一闭包"的兜底
* slot_semantics(): 合并"工厂赋值链 + require 委派 + 内部闭包"三条线索
* 输出改成叶子语义 + 真机明文并排核对
实测确认(线上构建 + 同会话解出的真机 collect):
* 清单模块 = registry[7], 构造方式是 `r6[r6.length] = require(N)` 追加
-> **require 顺序即 cd 槽位顺序**(37 个 require + 1 个字面量槽 = 38, 与真机完全吻合)
* 语义已精确命中: cd[3] <- c.navigator.userAgent, cd[5] <- c.navigator.languages
* 仍有若干槽的语义标签未对齐(取值寄存器规则需再收口), 已在账本记录
This commit is contained in:
@@ -175,6 +175,110 @@ class Program:
|
||||
out.append((mid, tgt))
|
||||
return out
|
||||
|
||||
# ---------------------------------------------------------- 轻量符号求值
|
||||
def symbolic_assignments(self, entry: int, limit: int = 30) -> List[Tuple[str, str]]:
|
||||
"""对一个小函数(如采集器模块)做**线性符号求值**, 还原 `rN = <表达式>`。
|
||||
|
||||
只覆盖这类小模块实际用到的形状: 字面串 / BIND_THIS / MAKE_(REG_)PROP_REF /
|
||||
GET_PROP / GET_GLOBAL_PROP / NEW_FUNC / CALL_FUNC / CALL_METHOD / STORE_REG /
|
||||
LOAD_REG_IMM / REVERSE_TOP2 / DUP / 算术。
|
||||
|
||||
目的: 把"这一槽取的是什么值"变成可读路径, 例如 `c.navigator.userAgent`。
|
||||
说明: 遇到分支时按 pc 顺序近似处理, 结果用于**定位语义**, 不作为执行依据。
|
||||
"""
|
||||
regs: Dict[int, str] = {}
|
||||
out: List[Tuple[str, str]] = []
|
||||
st: List[Tuple[str, str]] = [] # ("reg", n) | ("sym", text)
|
||||
|
||||
def top() -> Tuple[str, str]:
|
||||
return st[-1] if st else ("sym", "?")
|
||||
|
||||
def popn(n: int) -> List[Tuple[str, str]]:
|
||||
r = st[-n:] if n else []
|
||||
del st[len(st) - n:]
|
||||
return r
|
||||
|
||||
for inst in self.function_code(entry):
|
||||
n, ops = inst["name"], inst["operands"] or []
|
||||
if n == "PUSH_IMM_ARRAY":
|
||||
st.append(("reg", ops[0]))
|
||||
elif n == "PUSH_EMPTY_STR":
|
||||
st.append(("sym", ""))
|
||||
elif n == "PUSH_STR_CHAR":
|
||||
k, v = top()
|
||||
st[-1] = ("sym", v + chr(ops[0] & 0xFF))
|
||||
elif n == "PUSH_IMM":
|
||||
st.append(("sym", str(ops[0])))
|
||||
elif n == "PUSH_UNDEFINED":
|
||||
st.append(("sym", "undefined"))
|
||||
elif n == "PUSH_NULL":
|
||||
st.append(("sym", "null"))
|
||||
elif n == "PUSH_TRUE":
|
||||
st.append(("sym", "true"))
|
||||
elif n == "PUSH_FALSE":
|
||||
st.append(("sym", "false"))
|
||||
elif n == "LOAD_REG_IMM":
|
||||
st.append(("sym", regs.get(ops[0], f"r{ops[0]}")))
|
||||
elif n == "LOAD_REG_INDIRECT":
|
||||
k, v = st.pop() if st else ("sym", "?")
|
||||
st.append(("sym", regs.get(int(v), f"r{v}") if str(v).isdigit() else v))
|
||||
elif n == "BIND_THIS":
|
||||
k, v = st.pop() if st else ("sym", "?")
|
||||
st.append(("sym", f"c.{v}"))
|
||||
elif n == "MAKE_PROP_REF":
|
||||
key = top()[1]
|
||||
popn(1)
|
||||
base = top()[1]
|
||||
popn(1)
|
||||
st.append(("sym", f"{base}.{key}"))
|
||||
elif n == "MAKE_REG_PROP_REF":
|
||||
key = top()[1]
|
||||
popn(1)
|
||||
k, v = st.pop() if st else ("sym", "?")
|
||||
st.append(("sym", f"{regs.get(v, f'r{v}')}.{key}"))
|
||||
elif n == "GET_GLOBAL_PROP":
|
||||
st[-1] = ("sym", f"c.{top()[1]}")
|
||||
elif n in ("GET_PROP", "GET_ELEM"):
|
||||
pass
|
||||
elif n in ("ADD", "SUB", "MUL", "DIV", "MOD", "BIT_AND", "BIT_OR", "BIT_XOR",
|
||||
"SHL", "SHR", "SHR_U", "EQ", "EQ_STRICT", "GREATER"):
|
||||
if len(st) >= 2:
|
||||
b = top()[1]
|
||||
popn(1)
|
||||
a = top()[1]
|
||||
popn(1)
|
||||
st.append(("sym", f"({a} {n} {b})"))
|
||||
elif n in ("NEW_FUNC", "NEW_METHOD"):
|
||||
args = popn(int(ops[0]) if ops else 0)
|
||||
fn = st.pop()[1] if st else "?"
|
||||
st.append(("sym", f"new {fn}({','.join(x[1] for x in args)})"))
|
||||
elif n == "CALL_FUNC":
|
||||
args = popn(int(ops[0]) if ops else 0)
|
||||
fn = st.pop()[1] if st else "?"
|
||||
st.append(("sym", f"{fn}({','.join(x[1] for x in args)})"))
|
||||
elif n == "CALL_METHOD":
|
||||
args = popn(int(ops[0]) if ops else 0)
|
||||
tgt = st.pop() if st else ("sym", "?")
|
||||
st.append(("sym", f"{tgt[1]}({','.join(x[1] for x in args)})"))
|
||||
elif n == "STORE_REG":
|
||||
v = st.pop()[1] if st else "?"
|
||||
k, ref = st.pop() if st else ("sym", "?")
|
||||
if k == "reg":
|
||||
regs[ref] = v
|
||||
out.append((f"r{ref}", v))
|
||||
elif n == "DUP":
|
||||
if st:
|
||||
st.append(st[-1])
|
||||
elif n == "REVERSE_TOP2":
|
||||
if len(st) >= 2:
|
||||
st[-1], st[-2] = st[-2], st[-1]
|
||||
elif n in ("POP", "SET_PROP", "ALLOC_REG", "SET_STACK_LEN", "REPLACE_TOP_IMM"):
|
||||
if n in ("POP", "SET_PROP") and st:
|
||||
st.pop()
|
||||
if len(out) >= limit:
|
||||
break
|
||||
return out
|
||||
|
||||
def find_strings(self, pattern: str) -> List[Tuple[int, str, int]]:
|
||||
"""返回 (pc, 串, 所属子程序入口)。"""
|
||||
rx = re.compile(pattern, re.I)
|
||||
|
||||
Reference in New Issue
Block a user