返回列表 发新帖

【技术分享】纯 Node.js 手撸二进制:注入 CChaha 异常处理

[复制链接]

9

主题

284

回帖

972

积分

高中生

Rank: 4

金币
878
好评
1
信誉
100
发表于 2026-7-27 02:12:00 | 显示全部楼层 | 阅读模式  来自 河南
CChaha(Claude Code Haha,[NanmiCoder/cc-haha](https://github.com/NanmiCoder/cc-haha) 的 Electron 桌面端)在代理(v2rayN / Clash)一断的时候,主进程抛 ECONNRESET,Electron 默认弹阻塞式红叉错误框,关一个冒一个、UI 冻死。我写了个零依赖 Node 小工具,直接改 app.asar 的二进制,在主进程入口 electron-dist/main.cjs 顶端注入一个全局异常处理器,只静默吞网络类错误,其它错误照常抛。双击 apply.cmd 即可,自动备份、可还原、幂等。

下面讲怎么做的:

根因

代理断 → 主进程网络请求 read ECONNRESET → 主进程没有 uncaughtException 兜底 → Electron 默认行为 = 弹阻塞错误对话框,失败一次弹一次。所以解法不是修网络(也修不了),是给主进程加一个兜底 handler

注入的代码(约 600 字节)
  1. process.on("uncaughtException", function (err) {
  2.   if (err && ["ECONNRESET","ECONNREFUSED","ENOTFOUND","ETIMEDOUT","EPIPE"].includes(err.code)) {
  3.     try { console.warn("[cchaha-patch] suppressed:", err.code, err.message); } catch(_){}
  4.     return; // 吞掉,不弹框
  5.   }
  6.   throw err; // 真正的 bug 照常抛,不被掩盖
  7. });
  8. process.on("unhandledRejection", function (reason) {
  9.   if (reason && typeof reason === "object" && ["ECONNRESET","ECONNREFUSED","ENOTFOUND","ETIMEDOUT","EPIPE"].includes(reason.code)) {
  10.     try { console.warn("[cchaha-patch] suppressed rejection:", reason.code, reason.message); } catch(_){}
  11.     return;
  12.   }
  13. });
复制代码

关键点:白名单只吞网络错误码,其它一律 throw——不能让补丁把真 bug 也盖住,那是给自己挖坑。

硬核部分:不用 @electron/asar,手撸 asar 二进制

这块是重点。asar 是 Electron 的归档格式,布局是:
  1. [ 16 字节 preamble ][ JSON header(4 字节对齐填充)][ 文件数据区 ]
复制代码
- preamble 的偏移 12 处是 `headerStringSize`(uint32 LE);
- 填充后的头大小 = `(headerStringSize + 3) & ~3`;
- 数据区起始 = `16 + 填充后头大小`。

我没引官方 @electron/asar(不想 npm install、要保持零依赖、纯内置模块),直接 Buffer 操作:

1. readPreamble 读 16 字节,算出 header 与 data 的边界;
2. 递归走 header 的 files 树,按路径 electron-dist/main.cjs 定位到那条 entry 的 offset / size
3. 把注入代码拼到 main.cjs 原文最前面
4. 递归重算 header 树里所有 offset:凡 offset ≥ 被改文件的原 offset 的,全部 (因为数据区整体后移了);
5. 用 4MB 分块的流式 IO 重建 187MB 的 asar(不一次性读进内存);
6. 头对齐 (n+3)&~3
7. 稳定点迭代:注入后 header 的 JSON 变长 → 填充后头大小可能变 → data 起始变 → 又影响 header 里写的 offset……所以迭代重算,直到"填充后头大小"两轮相等(收敛)才落盘;
8. 写一个 PATCH_MARKER 字符串做幂等:再跑一次先扫 marker,已注入就跳过,不会重复插。

到此, 搞定 !

使用
  1. git clone https://github.com/donggua12339/cchaha-econnreset-patch.git
  2. cd cchaha-econnreset-patch
  3. apply.cmd
复制代码
致谢 & 开源

上游桌面端 [NanmiCoder/cc-haha](https://github.com/NanmiCoder/cc-haha),感谢作者。

欢迎各位大佬指正 asar 改写这块的实现,有更优雅的收敛 / 对齐写法欢迎 PR。
回复

使用道具 举报

51

主题

2972

回帖

8674

积分

硕士生

看看

Rank: 6Rank: 6

金币
132
好评
8
信誉
140

考神MT论坛帅哥MT论坛最佳新人MT论坛新人MT论坛活跃会员

发表于 2026-7-27 02:13:58 来自手机  | 显示全部楼层  来自 河南
沙发
回复

使用道具 举报

0

主题

2972

回帖

6911

积分

硕士生

Rank: 6Rank: 6

金币
3330
好评
0
信誉
100

MT论坛新人考神MT论坛帅哥

发表于 2026-7-27 02:27:55 来自手机  | 显示全部楼层  来自 浙江
感谢分享
回复

使用道具 举报

20

主题

402

回帖

900

积分

高中生

Rank: 4

金币
585
好评
3
信誉
52
发表于 2026-7-27 11:38:35 来自手机  | 显示全部楼层  来自 江西
厉害了
回复

使用道具 举报

发表回复

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表