检测函数重定义需检查 tostring() 是否含 [native code],原型篡改则依赖可信快照比对或 object.freeze 锁定;高安全场景应结合原始引用比对与金丝雀属性验证。

JavaScript 中函数重定义和原型篡改是常见但高风险的操作,既可能用于合法的增强逻辑(如日志拦截、性能监控),也可能被用于反调试、混淆或恶意劫持。检测它们的关键不在于“是否发生过”,而在于“当前状态是否可信”——即能否确认函数仍是原生、原型仍保持初始结构。
检测函数是否被重定义(Monkey Patching)
最常用且轻量的方法是检查 toString() 输出是否含 [native code]:
- 原生函数(如
window.fetch、Array.prototype.push)调用toString()通常返回类似"function fetch() { [native code] }"的字符串; - 一旦被重定义(哪怕只是包装调用),其
toString()就会输出实际函数体,不再包含[native code]; - 可封装为工具函数:
function isNative(fn) { return typeof fn === 'function' && /\[native code\]/.test(fn.toString()); }。
⚠️ 注意:该方法可被绕过(例如重写函数自身的 toString 方法,或在伪造函数体中硬编码 [native code] 字符串)。若需更高安全性,应结合原始引用比对——在页面最早期缓存关键函数(如 const originalFetch = window.fetch),后续用 === 严格比较是否仍为同一引用。
检测原型是否被篡改
无法回溯“何时被改”,但能识别“当前是否偏离预期”。核心思路是建立并维护一个可信快照:
- 在第三方脚本加载前(如
<script></script>标签最顶部),用Object.getOwnPropertyNames(Object.prototype)等获取原始属性列表,并保存为只读副本; - 运行时对比当前原型属性与快照差异,新增或删除的属性即为篡改迹象;
- 更主动的方式是使用
Object.freeze(Object.prototype)或Object.seal(Array.prototype)锁定关键原型——一旦尝试添加/删除属性会静默失败(严格模式下抛错)。
补充技巧:可设置“金丝雀属性”(如 Object.prototype.__sanity_check = Symbol()),定期验证它是否存在且未被覆盖,作为快速健康检查点。
防御性实践建议
单纯检测不如提前设防。推荐组合策略:
- 禁止扩展原生原型:通过 ESLint 规则
no-extend-native在开发阶段拦截; - 冻结关键对象:在应用初始化时调用
Object.freeze锁定Object.prototype、Array.prototype等; - 敏感函数优先用原始引用:避免反复访问
window.fetch,改为闭包内缓存const _fetch = window.fetch; - 对自定义类使用 Proxy 监控原型访问:如需动态响应,可在构造函数中为原型代理读写操作,记录异常变更。
函数重定义与原型篡改本身不是问题,失控才是风险来源。把检测嵌入启动流程,把防护落在执行前,才能真正守住 JS 运行时的信任边界。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











