Electron 包检查
Electron app.asar 只读检查
结论先行
app.asar 不是压缩包,是 16 字节头 + JSON 目录 + 原始文件拼接。读文件只需要算对 数据区起点 + offset,不需要任何依赖。
检查阶段不要写回——写回要重算每个文件的 integrity,属于另一个技能(见 dsh-desktop-wallpaper)。
asar 结构(实测)
[0..3] UInt32LE = 4 常量
[4..7] UInt32LE = jsonLen + 8 header pickle 总长
[8..11] UInt32LE = jsonLen + 4
[12..15] UInt32LE = jsonLen JSON 字节长度
[16..] JSON header(4 字节对齐,尾部空格填充)
[16+headerLen ..] 数据区;JSON 里每个文件的 offset 相对数据区起点
步骤
1. 列目录 + 找目标文件
```powershell
$p='C:\Program Files\sanshan-kaiwu\resources\app.asar'
# 用 Node 一次读头、打印文件树
node -e "const fs=require('fs');const fd=fs.openSync(process.argv[1],'r');const b=Buffer.alloc(16);fs.readSync(fd,b,0,16,0);const n=b.readUInt32LE(12);const h=Buffer.alloc(n);fs.readSync(fd,h,0,n,16);console.log(JSON.stringify(JSON.parse(h.toString('utf8')),null,2).slice(0,4000))" "$p"
```
检查点:能看到 files.src.main.js 之类的节点,带 size / offset / integrity。
2. 提取文件(只读,输出到临时目录)
```powershell
node -e "
const fs=require('fs');
const [asar,entry,out]=process.argv.slice(1);
const fd=fs.openSync(asar,'r');
const b=Buffer.alloc(16); fs.readSync(fd,b,0,16,0);
const n=b.readUInt32LE(12); const h=Buffer.alloc(n); fs.readSync(fd,h,0,n,16);
let node=JSON.parse(h.toString('utf8'));
for (const k of entry.split('/')) node=node.files[k];
const buf=Buffer.alloc(node.size);
fs.readSync(fd,buf,0,node.size,16+n+Number(node.offset));
fs.writeFileSync(out,buf);
console.log('extracted',out,node.size,'bytes');
" "$p" "src/main.js" "$env:TEMP\main.js"
```
3. 读 main.js 里的三个关键点
- runtimeRoot():打包后指向 resources/runtime,开发时指向工程 runtime/。
- 启动子进程的 spawn(...):常见形式是 node.exe <dsh>/lib/bin.js web --no-open --port 0,并注入 DSH_HOME。
- 解析就绪日志的正则:形如 dsh web: (http://127\.0\.0\.1:\d+/\S+),从 stdout 抓端口和 token。
4. 确认当前跑的是哪一份(日志 / 进程 / 端口三角定位)
```powershell
Get-Content "$env:APPDATA\三山开物\logs\dsh.log" -Tail 20
Get-Process | Where-Object { $_.ProcessName -match '三山|electron' } | Select-Object Id,ProcessName,StartTime
netstat -ano | Select-String ':55162'
```
日志里 starting dsh: <路径> 决定"用的是哪份运行时",ready: http://127.0.0.1:<port> 决定"当前窗口连的是哪个端口"。
坑
- PowerShell 读中文 JSON 会乱码:
Get-Content -Raw | ConvertFrom-Json在控制台代码页下可能把 UTF-8 当 ANSI,报Invalid object passed in。用 Node 的fs.readFileSync(path,'utf8')读。 Get-CimInstance Win32_Process可能被拒绝访问:用Get-Process+ 日志即可,不必强求命令行。- Electron 的主进程与渲染进程同名:
三山开物会出现 2 个进程,别以为多开;用启动日志的 PID/端口对齐。 - asar 里没有
.bin之类的真实快捷方式:别在 asar 里找node_modules/.bin,运行时是整目录带出来的。
验收清单
- [ ] 能列出 asar 内文件树,
main.js大小与头里一致 - [ ] 提取出的
main.js能搜到runtimeRoot/spawn/ 就绪正则 - [ ] 日志里的
starting dsh路径与预期安装目录一致 - [ ] 端口与当前窗口 URL 一致
- [ ] 全程没有写入 asar
别做的事
- 别用
Expand-Archive或通用解压工具处理 asar(不是 zip)。 - 别在检查阶段改文件(改了必须重算 integrity,否则整包失效)。
- 别把提取出来的代码当成源码去改——真源码在工程目录里,改工程再打包。