首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
逆向工程
发新帖
0
1
[原创]Tauri 2 / WebView2 桌面应用运行时分析:从 COM 回调到 ICoreWebView2 捕获
发表于: 2026-8-29 21:49
283
[原创]Tauri 2 / WebView2 桌面应用运行时分析:从 COM 回调到 ICoreWebView2 捕获
mb_uwvcnlbx
2026-8-29 21:49
283
最近研究了一下 iShellPro。 这是一个基于 **Tauri 2 + WebView2** 的 Windows 程序,最后实现了免登录、纯离线解锁全部本地功能。 一开始按传统思路去逆 Rust Native 层,但很快发现一个问题: > 对于 Tauri 这类 Hybrid Desktop Application,大量真正的业务逻辑其实运行在 WebView 的 JavaScript 环境中。 与其在几十 MB 的 Rust Release 二进制里追业务函数,不如换个目标: > **直接拿到宿主创建的 `ICoreWebView2`。** 最终实现的核心链路如下: ```text Proxy DLL ↓ 进入目标进程 ↓ Hook WebView2 Environment 创建 ↓ ICoreWebView2Environment ↓ Hook Controller 创建 ↓ ICoreWebView2Controller ↓ get_CoreWebView2() ↓ ICoreWebView2 ↓ 切回 WebView 所在线程 ↓ AddScriptToExecuteOnDocumentCreated ↓ JavaScript Runtime ``` 本文主要记录实现过程中几个比较关键的点。 --- ## 0x01 为什么直接盯 WebView2 Tauri 2 在 Windows 上的结构大致可以理解成: ```text Rust / Tauri ↓ WRY ↓ WebView2 ↓ HTML / JavaScript ``` 如果目标逻辑最终由前端消费,那么持续追踪 Rust Native 层并不一定是效率最高的路线。 WebView2 本身有一套比较清晰的生命周期: ```text Create Environment ↓ ICoreWebView2Environment ↓ Create Controller ↓ ICoreWebView2Controller ↓ get_CoreWebView2() ↓ ICoreWebView2 ``` 所以思路也很直接: **不去关心 Tauri 内部究竟怎么保存 WebView,而是在 WebView2 生命周期中把对象截出来。** --- ## 0x02 捕获 ICoreWebView2Environment 第一步 Hook: ```cpp CreateCoreWebView2EnvironmentWithOptions ``` WebView2 的 Environment 创建是异步的,最终结果通过 COM Completion Handler 返回: ```cpp handler->Invoke( HRESULT, ICoreWebView2Environment* ); ``` 这里没有直接修改宿主原本的 Handler,而是在外面套一层 Proxy: ```cpp HRESULT EnvironmentCompletedProxy::Invoke( HRESULT error, ICoreWebView2Environment* environment) { if (SUCCEEDED(error) && environment) InstallEnvironmentHooks(environment); return original_->Invoke( error, environment ); } ``` 这样做有两个目的: 1. 捕获 `ICoreWebView2Environment` 2. 不破坏宿主原来的初始化流程 COM Proxy 中的: ```text QueryInterface AddRef Release ``` 也必须正确实现。 否则即使 Hook 本身没有问题,也很容易因为引用计数或对象生命周期出现随机崩溃。 --- ## 0x03 从 Environment 截获 Controller 创建 拿到: ```cpp ICoreWebView2Environment* ``` 之后,就可以继续从 COM vtable 中截获 Controller 创建函数。 项目里处理了几条常见路径: ```text CreateCoreWebView2Controller CreateCoreWebView2CompositionController CreateCoreWebView2ControllerWithOptions CreateCoreWebView2CompositionControllerWithOptions ``` 这里不能只盯着最基础的 `CreateCoreWebView2Controller`。 不同的 WebView2 Runtime、框架版本以及初始化方式,可能会走不同的 Environment Interface。 所以我的处理方式是: > 先通过 `QueryInterface` 探测高版本 Environment Interface,再对实际存在的创建入口分别安装 Hook。 这样比单押某一个接口稳定得多。 --- ## 0x04 捕获 ICoreWebView2 Controller 的创建同样通过 Completion Handler 完成。 因此继续使用 Proxy: ```cpp HRESULT ControllerCompletedProxy::Invoke( HRESULT error, ICoreWebView2Controller* controller) { ICoreWebView2* webview = nullptr; if (SUCCEEDED(error) && controller) controller->get_CoreWebView2(&webview); auto hr = original_->Invoke( error, controller ); if (webview) { SaveWebView(webview); webview->Release(); } return hr; } ``` 到这里,真正需要的对象就拿到了: ```cpp ICoreWebView2* ``` 理论上接下来已经可以通过 WebView2 API 与页面交互。 但这里有一个比 Hook 本身更容易踩的坑。 --- ## 0x05 最大的坑:COM STA 线程 一开始拿到 `ICoreWebView2*` 后,我直接尝试: ```cpp webview->ExecuteScript(...) ``` 然后很快遇到了: ```text RPC_E_WRONG_THREAD ``` 原因是 WebView2 基于 COM STA。 简单理解: ```text WebView 在线程 A 创建 ↓ 相关 WebView2 API 通常也必须在线程 A 调用 ``` 所以不能在一个工作线程里保存 `ICoreWebView2*`,然后想什么时候调就什么时候调。 最后采用的方案是: > **在 WebView 所在线程创建一个 Message-Only Window,把它作为 Dispatcher。** ```cpp CreateWindowExW( 0, className, L"", 0, 0, 0, 0, 0, HWND_MESSAGE, nullptr, module, nullptr ); ``` 其他线程只负责: ```cpp PostMessageW(...) ``` 真正涉及 WebView2 的操作统一回到窗口所属线程: ```text Worker Thread ↓ PostMessage ↓ WebView UI Thread ↓ ICoreWebView2 API ``` 包括部分 COM 对象的 `Release()`,也最好回到原线程完成。 这个问题在单窗口、短时间测试时可能并不明显,但到了: ```text 多窗口 Reload 窗口关闭 WebView 重建 ``` 之后,对稳定性的影响会非常明显。 整个项目里,我认为这一点比单纯“拿到 `ICoreWebView2`”更值得注意。 --- ## 0x06 补齐创建入口,以及为什么必须足够早 仅仅 Hook: ```text WebView2Loader.dll ``` 并不一定能覆盖所有情况。 实际运行中,框架还可能动态解析内部入口: ```text CreateWebViewEnvironmentWithOptionsInternal ``` 因此项目同时 Hook 了: ```cpp GetProcAddress ``` 当目标程序解析该内部函数时,再补装对应 Hook。 也就是说,Environment 的捕获实际上覆盖了两类入口: ```text Public WebView2Loader │ ├──────────────┐ ↓ ↓ Public Hook Internal Runtime ↓ GetProcAddress ↓ Internal Hook └───────┬──────┘ ↓ Environment Proxy ↓ Controller Hook ↓ ICoreWebView2 ``` 但这里还有一个前提: > **这些 Hook 必须在 WebView2 Environment 创建之前安装完成。** 所以 Proxy DLL 真正重要的并不是“代理哪个 DLL”,而是: > **哪个可利用 DLL 能在目标进程启动阶段足够早地被加载。** 实际观察模块加载顺序后,我排除了几类不适合作为入口的 DLL: ```text KnownDLLs COM 方式加载的 DLL 由其他模块间接加载的 DLL ``` 最后选择了剩余候选中加载时机最早的 **dwmapi.dll**。 原因很简单。 如果发生: ```text WebView2 Environment 已创建 ↓ Proxy DLL 才进入进程 ``` 那么即使后面的 Hook 实现完全正确,也已经错过最关键的生命周期节点。 代理 DLL 本身,只需要保证原有导出正常转发,不影响宿主启动即可。 真正的核心仍然是后面的 WebView2 Capture。 --- ## 0x07 为什么用 AddScriptToExecuteOnDocumentCreated 拿到 `ICoreWebView2` 后,可以直接: ```cpp ExecuteScript(...) ``` 但 `ExecuteScript` 更适合页面已经运行之后临时执行 JavaScript。 如果目标是观察页面初始化阶段,比如: ```text fetch localStorage 全局对象 框架初始化 ``` 更合适的是: ```cpp AddScriptToExecuteOnDocumentCreated ``` 它的执行时机更早,大致可以理解成: ```text Global Object Created ↓ Injected Script ↓ HTML Parse ↓ Page JavaScript ``` --- ## 0x08 从 Target Patch 到 Framework Instrumentation 如果只是针对单一版本做修改,传统思路通常是: ```text 找到业务判断 ↓ 修改分支 ↓ 结束 ``` 问题是这种方式往往高度依赖: ```text 版本 Offset 具体编译结果 ``` 而这次做的事情不太一样。 Hook 的目标主要是: ```text WebView2 生命周期 COM Interface Framework Runtime ``` 关注点从: ```text Target Specific Patch ``` 变成了: ```text Framework Level Instrumentation ``` 因此 WebView2 Capture 这一层并不完全依赖 iShellPro 本身。 对于其他使用 WebView2 的程序,例如: ```text Tauri WPF WebView2 WinForms WebView2 WinUI WebView2 ``` 同样具有一定复用价值。 这也是这次分析里我觉得最有意思的地方。 --- ## 0x09 总结 整个项目真正想解决的问题其实只有一个: > **如何稳定地拿到宿主程序创建的 `ICoreWebView2`,并在正确的 STA 线程上,在页面脚本之前进入 JavaScript Runtime。** 围绕这个目标,最终串起来的知识点包括: ```text DLL Proxy / API Forwarding Inline Hook COM / vtable Reference Counting WebView2 Lifecycle STA Windows Message Pump JavaScript Runtime ``` 对于 Tauri / WebView2 这类 Hybrid Desktop Application,我觉得比较值得记住的并不是某个具体业务字段,而是一个分析思路: > **不一定非要从业务代码本身下手,也可以从承载业务代码的 Framework Runtime 和生命周期下手。** 目前代码中,我认为最值得单独抽象出来的也是 WebView2 Capture 这一层。 以后遇到类似目标,基本可以沿着: ```text Environment ↓ Controller ↓ ICoreWebView2 ``` 继续往下分析。 仓库: ```text https://github.com/lwtw123456/iShellPro-Hack ``` 如有错误,欢迎各位师傅指正。
传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!!
收藏
・
0
点赞
・
1
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
新气象
非常支持你的观点!
2026-8-30 12:09
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
1
)
mb_uwvcnlbx
雪 币:
0
能力值:
( LV1,RANK:0 )
在线值:
发帖
2
回帖
2
粉丝
0
关注
私信
mb_uwvcnlbx
2
楼
Tauri 2看来也是太冷门了。。。通用的crack方法都没人感兴趣
2026-8-31 14:59
0
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
mb_uwvcnlbx
2
发帖
2
回帖
0
RANK
关注
私信
他的文章
[原创]Tauri 2 / WebView2 桌面应用运行时分析:从 COM 回调到 ICoreWebView2 捕获
283
[原创]Flutter Windows x64 AOT 逆向研究:基于 R15/PP 与 Object Pool 重建 Dart 对象引用
528
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部