首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
逆向工程
发新帖
0
0
[原创]某代理程序的逆向分析
发表于: 1小时前
60
[原创]某代理程序的逆向分析
zhugeshi
1小时前
60
# 前言 起因是我原来用了一款代理软件,本来可以导入配置到 clash 中还算比较方便。结果因为一些总所周知的原因,他们选择了使用了不知道什么协议,并放弃了 clash 导入配置的选项,只能使用他们提供的第三方桌面程序启动。但是他们写的第三方桌面程序只能说是灾难(Electron),不仅启动慢、控件响应问题也很严重,对窗口的支持也不好。终于我忍不了了,选择分析一下他们的流程。 # 需要了解的一些框架知识 ## 大致的架构 electron 是前后端分离的一个程序,由 Chromium 和 Node.js 组成,采用多进程的方式运行。大致的结构如下:  主进程大致可以看作后端,渲染大致可以看作后端。两者独立,通过 ipc 进行通信。 ## Elecltron IPC 通信机制 这里的核心是通过 ipcMain 和 ipcRender 按照 channel 收发。 # 分析流程 想要上手分析程序,首先大概得先知道它的框架是什么吧,于是打开 die 看看是什么情况。可以看出来很明显是 electron 程序。  可是对于分析 electron 程序我并没有什么经验,于是借助 ai 和浏览器大致了解一下 electron 打包后的结构。 ```txt <AppName>/ <AppName>.exe ### 主程序 *.dll *.pak *.bin ### 运行时依赖库 locales/ swiftshader/ resources/ app.asar ### js 代码打包 app.asar.unpacked/ ### 不能进 asar 的原生文件(.node/.exe/.dll) app-update.yml ### electron-updater 的更新源 ``` 根据这里的信息,我们可以知道最重要的似乎就是 resoureces 中的这个 app.asar 了,根据我搜索到的信息,electron 本质上是将 js 代码打包在我的环境中用 node 启动 webview 界面运行。 那么接下来应该做什么呢?根据我以往分析程序的经验,想要分析控件最好是附加调试一个进程,通过动态的方式定位逻辑,于是我打开了 x64dbg 尝试分析。  不过马上遇到了一个棘手的问题,有很多同名的进程,我应该选择附加哪一个呢?虽然大致根据窗口名称能判断出是最后一个,但是抱着问问看的想法询问了一下 ai。  通过无 --type 参数来看确定是最后一个进程无疑了。于是我选择附加进程调试,但是马上遇到了第二个问题:以往我们定位逻辑都是通过 windows api 按钮控件定位代码,那 electron 这种 webview 程序应该如何定位。事情到这里似乎有点卡住了,想要分析功能,我们就必须找到控件对应的代码,这个和安卓逆向的思路有点类似。 那有什么方法能获取控件对应的逻辑呢?我们想到了这个前端是否能够开启 devtools 帮助我们分析,于是在尝试 F12 和 Ctrl+Shift+I 没用后,搜索到了似乎可以通过附加参数 --remote-debugging-port=<端口号> 启动的方式启动 Devtools。 electron 默认启动调试后会通过这几个地址显示信息: ``` /json 或 /json/list ### 列出可调试 target /json/version ### 浏览器信息 + 浏览器级 ws /json/new?url ### 新建标签 /json/activate|close/<id> ```  给了我们 devtoolsFrontendUrl 的地址,根据这个地址能在浏览器中打开 devtools 界面。到了这里,我们终于能有方便的工具来帮助我们分析了。 接下来就需要找到按钮对应的控件函数了,在侧边的 event listener breakpoint 中找到 Mouse -> click 打下断点。  在打下断点后,点击我们要分析的程序按钮,追踪它的流程。  询问 ai 得知这里是 vue 包装的一层 handler 函数,所以我们单步跟踪进入。  在这里找到了 invoker,查看 invoker.fns 就能找到我们想要的东西了。点击跟到对应的代码部分。  ```js startProxy() { let self = this const storage = window.localStorage; self.animateCSS('.Ellipse0', 'scaleAnimate') self.animateCSS('.Ellipse1', 'scaleAnimate1') self.animateCSS('.Ellipse2', 'scaleAnimate2') self.animateCSS('.Ellipse3', 'scaleAnimate3') if (self.nodes.length < 1) { self.Toast(self.$t('lang.No_line_available'), "warn") return } if (self.isAccountExhausted) { self.isShowAccountTrafficExhausted = true if (self.isStart) { self.sendMess("stopProxy", { "name": "stopProxy" }) self.isStart = false self.Reset() } return } if (self.accountExpire != "") { if (self.isExpired(self.accountExpire)) { self.isShowAccountExp = true self.isAccountExp = true if (self.isStart) { self.sendMess("stopProxy", { "name": "stopProxy" }) self.isStart = false self.Reset() } return; } else { self.isShowAccountExp = false self.isAccountExp = false } } if (self.isStarting) { return } if (self.isStart) { self.statusText = "disconnecting" onClickControl('Stop', self.getTunState()) } else { self.statusText = "connecting" onClickControl('Connect', [self.getTunState(), self.isStart]) } self.statusText = "connecting" self.isStarting = true }, ``` ```js if (self.isStart) { self.statusText = "disconnecting" onClickControl('Stop', self.getTunState()) } else { self.statusText = "connecting" onClickControl('Connect', [self.getTunState(), self.isStart]) } ``` 关键看这个地方,程序检测状态,然后根据状态使用 onClickControl 来与主进程通信。那么意味着我们接下来就要去在主进程对应的代码中搜寻对应的代码。也就是我们需要解包分析 app.asar 里的代码。 使用 `npx @electron/asar extract app.asar out` 解包得到 out 目录 ```shell . ├── main.node ├── package.json └── ???? src └── main ├── decrypt-main.js ├── embedded-require-patch.js └── main.js ``` 很遗憾,main.js 遭到了很严重的混淆。但是,我们转念一想这种小规模的开发者并不可能有什么比较强的反逆向技巧,所以我们尝试寻找有没有现成的工具能帮助我们解混淆。  在询问 ai 得知这个混淆主要是平坦化加虚拟机混淆,借助 ai 找到一个叫 webcrack 的项目。经过一顿操作后,发现解混淆出来的代码基本可读的。  根据原来我们 onClickControl 函数搜索到这个内核启动代码。 ```js (async () => { try { const R = await checkCoreRunning(); if (!R) { logger.info(F.rUHBI); await F.XkbYP(startCoreProcess); } else { logger.info(F.CpYQX); } if (q === 0) { logger.info(F.jeAIH); F.oVrEa(setSystemProxy, true); webContentsSend("statusJS", F.dHfXt); } else { setSystemProxy(false); logger.info(F.rwrat); try { await setTun(true); await waitMs(1000); const o = await F.ZRZcE(checkTunAdapter); if (o) { logger.info("[Connect] ✓ TUN 网卡已就绪"); } else { logger.warn(F.JKpnu); } F.AfFDr(webContentsSend, "statusJS", "true"); } catch (B) { logger.error("[Connect] 启用 TUN 失败: " + B.message); F.znBXt(webContentsSend, "statusJS", "false"); sendConnectError(F.KkeVk); } } } catch (i) { logger.error("[Connect] 处理失败: " + (i.message || i)); webContentsSend("statusJS", F.dAgle); sendConnectError(classifyConnectError(i.message, "core_error")); } finally { isConnecting = false; } })(); ``` 重点关注下面这段内容,看到比较重要的内核怎么启动的部分。 ```js const R = await checkCoreRunning(); if (!R) { logger.info(F.rUHBI); await F.XkbYP(startCoreProcess); } else { logger.info(F.CpYQX); } ``` 追踪找到这个内核启动命令。解析后可以得到大概就是 `"...\extra\mihomo-windows-386.exe" -d "...\resources\extra"` 这条命令。 ```js coreServer = spawn(k, ["-d", BIN_PATH], { cwd: BIN_PATH, stdio: [c.rzIVR, "pipe", c.SoyHt], windowsHide: true }); ``` 我们还能找到配置文件对应的代码 `const CONFIG_PATH = path.join(BIN_PATH, "config.yaml");` 但是这个配置文件依旧是加密的。但是可以找到这个保存这个配置文件的函数。  还原里面的逻辑大概是这样的流程,同时我们还能在里面找到硬编码的 aes 密钥和 iv。 ```txt 2083:X = Buffer.from(fileData,'utf8').toString('base64') 2089:U = AESencrypt(X, KEY, IV) const CLASH_CONFIG_KEY = "4422a60e08c97f30"; // 16B → AES-128 const CLASH_CONFIG_IV = "8c97f304422a60e0"; // 16B ``` 逆向操作就能得到复原出来的 config.yaml。里面有一些节点信息和个人凭证,这个东西别人拿到就随便刷你账号流量(可能也没什么用 大雾) 哦还可以修改端口了,原来版本甚至不支持修改端口。就先分析到这里吧,这个配置文件是服务器下发的,暂时想不到还有什么用处。 知道了启动流程让 ai 写了个启动 cli,避免了他原来 electron 的神秘启动流程速度。中间还遇到了死锁问题,这个以后再说吧。 
冰与火的战歌:Windows内核攻防实战高级班!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。
最后于
1小时前 被zhugeshi编辑 ,原因: 修改图片
#VM保护
#问题讨论
#加密算法
#调试逆向
收藏
・
0
点赞
・
0
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
0
)
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
zhugeshi
1
发帖
1
回帖
0
RANK
关注
私信
他的文章
[原创]某代理程序的逆向分析
27
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部