点击关注下方公众号,架构师全套资料 都在这里

0、2T架构师学习资料干货分享

上一篇:一个悄然成为世界最流行的操作系统

大家好,我是互联网架构师。

来源:juejin.im/post/6890344584078721031

背景

之前扒过飞书的源码,从代码设计架构层面里里外外学习一把,飞书还是挺 “大方” 的,源码在客户端和网页端都一览无余,不过好像新版本已经看不到了。相关的文章由于在内网技术论坛发过了不便于再发出来(泄露内部资料会被查水表的),因此这次周末抽时间换一个鸟窝来掏一掏。

一不小心发现迅雷的客户端竟然也是基于 Electron 开发的,那代码就好扒拉了。(先吐槽一下这新版本的某 lei 为什么要抄钉钉的界面,这些年某 lei 都不知道自己要干什么了,每个版本都招人嫌)。

拆解篇

1、一点背景知识说明

基于前端技术栈 Electron 构建的桌面应用,本质上都是加载本地前端资源文件,而这些文件通常是用 asar 格式(类似 windows iso 镜像)的方式进行打包,然后运行时再通过挂在到内存实现前端资源文件 js/css/html/img 等文件的读取。

这么说 asar 想办法挂载就可以随意阅读源码了吗?不是的。同时 asar 会提供一套通过加密方式防止任意解压,飞书就是这么做的,直接通过 asar extract 的方式无法解包出来。但是由于 node 端和 rust 构建的二进制文件如果打包到 asar 会导致无法链接到这些二进制文件,因此需要从 asar 中独立出来,因而导致有部分 js 文件仍然裸露在外面。不过即便没有任何 js 是暴露的仍然是有办法爆破的。

啊,跑偏了,先不谈飞书,今天的主菜是迅雷。

那迅雷的前端资源文件是怎么管理的呢?

是在下想多了,不好意思,迅雷梅川酷子,都摊着在那呢,根本没用 asar 打包 / 加密。

2、开撬

既然 js 都暴露了,也没什么好绕的,直接植入代码吧。我们都知道 Electron 是有 render 进程和 Node 进程的,接下来这一步需要猜猜看哪个文件是负责 render 主进程的?

好吧不用猜,名字都非常人类可读,就 main-renderer(主窗口渲染进程)。打开找到 html 文件(js 也可以)插入如下这串。

双击启动,调试窗口出来了,可以大致看到整体页面结构了。

然后看了一下,迅雷的悬浮小圆圈和主窗口,分别用一个 BrowserWindow 来实现。另外搜索公众号互联网架构师后台回复“2T”,获取一份惊喜礼包。有趣的是那个小圆圈窗口其实并不小,鼠标悬停出来的那个浮窗也是它的一部分,为了让小圆圈在屏幕的任何位置都可以看到悬浮窗,所以整个小圆圈的 BrowserWindow 是大约 4 倍的悬浮窗口大小。

独立窗口的检视界面 - 窗口实际是 4 倍 浮窗大小,灰色部分全都是这个 “小” 浮窗所使用的 BrowserWindow 区域。

3、一点防御措施

从代码来看,nodejs 进程只有一个文件 main.js ,是 webpack 的构建产物,看源码这里的 BrowserWindow 的 webPreference 参数是把 devTools 禁用掉的,导致直接在命令行里敲 openDevTools 是不能检视任意窗口的。

当然了,这里即便是混淆过了也没关系,毕竟还是明文,把 1 改成 0,把它打开就好(双叹号 /true/1 啥都行,开心就好)。不过由于迅雷的窗口实在是太多了,下载弹窗是独立窗口,选择文件夹是独立窗口,各种广告窗口也是,需要改的配置点很多,这里就不列了,总共有 10 个窗口,这个配置点按需打开(批量替换也行,谨慎操作就行)。

进程结构

呃…… 然后要干啥…… 好像也没什么好看的了,代码是混淆过的,也没有 map 文件。而且前端部分的代码也没什么技术含量可以说的,哪个 web 页面都那样。那看看进程分工吧。

1、进程树

在进程树里可以看出来,几乎全部的进程都是 Thunder.exe,可见 Thunder.exe 作为进程派发入口(类似 server 的网关,而并不直接是业务本身),用户启动的时候传参是 --StartType:DesktopIcon,随后它唤起了两组进程,一组是 Electron main 进程,main 进程唤起相关的 renderer;然后是下载的 SDK 服务 DownlaodSDKServer。

那么迅雷的进程关系差不多是清楚了:多个 Electron 窗口,对应一个 DownloadSDK。

2、通信方式

那么 Electron 的进程(甭管 main-process 还是 renderer-process,统称 electron 进程) 和 DownloadSDK 是如何通信的呢?

进程间通信一般都是依靠 ipc 管道的形式来实现。不过迅雷似乎没按套路来,它的 DownloadSDK 是控制台程序,意味着很有可能是通过 stdio 的方式来进行交互的(后续证明不是)。

通过观察进程打开的句柄,看到很诡异的一个现象:DownloadSDK 并没有打开任何 ipc 管道,反倒是前端进程打开了一个。

3、前端的 ipc

而 Electron 打开的这个 handler 进程名称,查了一下,竟然全是 Electron 进程使用的,而且是所有进程。

那么不妨做出一个大胆的推测:前端多窗口之间是靠自建的 ipc 通道实现的,而 ipc 是 1 server 对 N client 的方式,那么 server 很有可能就是在主窗口上的,也就是前文看到那个及其明显的 main-renderer 进程,通过控制台查看,确实如此,nodejs 的 net 方式创建了一个 server,并且将一个叫做 __xdasIPCServerInstance 的对象暴露在全局环境供前端 js 调用,也即 jsapi。

而小窗口并不存在上述 server 实例,而相对应的有一个 client 实例。

4、和 DownloadSDK 的通讯方式

这样看起来就很奇葩了,前端进程之间是通过自建的 ipc 管道通信的,但是并没有跟 DownloadSDK 有任何通信管道,难道它俩是心有灵犀无言自通?啊这…… 程序员是唯物主义的!

那怎么查它到底是怎么跟前端进程交互的呢?既然前端暴露了 server sdk instance,那意味着 DownLoadSDK 肯定是以一种 proxy 的方式暴露在这上面作为 jsapi 的。另外搜索公众号互联网架构师后台回复“2T”,获取一份惊喜礼包。可以拿【创建一个下载任务 api】来顺藤摸瓜。看了主窗口的 server instance 一下果然有这个方法:createTask ,应该就是前端用于创建下载任务用的 api。

chrome 浏览器里查代码不方便,转战 vscode 看源码,搜索 createTask 这个函数的声明位置,看到这一段(篇幅控制,此处删减了部分代码)。

createTask(e, t)
{return n(this, void 0, void 0, function* () {
.....          }
switch (e) {case h.DownloadKernel.TaskType.P2sp:
...case h.DownloadKernel.TaskType.Bt:
...case h.DownloadKernel.TaskType.Emule:
...case h.DownloadKernel.TaskType.Group:
...case h.DownloadKernel.TaskType.Magnet:
...default:              i = !1;}return(
... _.fireTaskEvent(h.DownloadKernel.TaskEventType.TaskCreated, [          );        });      }

没跑了,证实了我前面的猜想,这个 __xdasIPCServerInstance 就是 download sdk 封装到前端的 proxy。

继续查,这个 fireTaskEvent 是怎么处理的,阅读代码过程繁琐按下不提,就看这两段代码 (有删减整理)。

// 片段一
(e.getDownloadSdkVersion = function () {
let e = a.join(__rootDir, "../bin/SDK/DownloadSDKServer.exe");
return v.getFileVersion(e);
}),
// 片段二
y = l.default(o.join(__rootDir, "../bin/ThunderHelper.node"));
let F = "/ssdkver " + u.DownloadKernelManager.getDownloadSdkVersion();
B.push(F)
y.shellExecute(0, "open", o, B, H, "SW_SHOW");

很显然,DownloadSDK 是通过一个 ThunderHelper.node 的 nodejs addon 模块来启动、通信的。

我们知道,nodejs 可以通过 ffi 等方式实现内存共享,以达到两个进程不需要通过 pipe/sock 等管道就达到通信的目的。而通过工具观察 Thunder.exe 的唤起关系、句柄关系,两者的关系就更加一目了然了:ELectron 前端进程加载 DownloadSDK 进程,并且通过 \Sessions\5\BaseNamedObjects\xx@22123720|SendShareMemory 这种内存通道来实现的通信,句柄一一对应上了。

总结

扒拉了半天,扒完了有点空虚是怎么回事?

  • 迅雷的代码架构关系是轻 node 而重前端,把所有的 node 加载、进程管理、多窗口通信都放在前端进程的主窗口进程里。关于这个做法,我尊重而不认同。前端进程不应该做太重的底层交互,尤其是 js 这种单线程语言,天然的就运行效率低,而且主窗口使用这么频繁就不怕卡住吗

  • Electron 天然就有 ipc 通信能力,完全可以在 node 端做一个消息网关,达成每个窗口通信的能力,完全不需要自建一个 ipc server-client 体系。可能这也是一开始就把大量工作放在前端 (主窗口) 了导致后期的程序设计受限。说不定是个历史包袱

  • 用一个 node addon 的方式来跟 DownloadSDK 来通信,这点是可以点个赞的,虽然是业界标准(飞书是通过 rust,基本原理类似),但是我目前所负责的业务并没有做到这样,所以在惭愧的同时也给它点个赞

  • 迅雷使用的 Electron 版本是 9.2.1,vscode 也是这个版本,好神奇!非常好奇为何业界都用这个版本,事实上 electron 9.x 最新版本已经更新到 9.3.3 了(2020 年 10 月 28 日)这个 9.2.1 有什么魔力让业界都用它吗

  • 这里说明一下,Electron 从 6.0 开始就不支持 windows7 (非 sp1) 及以下的版本了。

  • 我在 win7 系统上用迅雷安装器安装迅雷最新版本,发现 electron 用的是 1.8.6 版本

  • Electron 的主入口是处理过了的,通过 Thunder.exe 程序干了很多除了启动前端以外的事情,这个定制还是挺棒的,因为这样就可以把各种进程模块管理起来,不会出现多个独立进程。就我所看到的不少 Electron 应用其实都没有定制过。

以上是纯粹技术挖掘,没有破坏到迅雷的核心机密,仅做学习交流使用哈~

卧槽!迅雷的代码竟然被扒了精光!相关推荐

  1. 卧槽,迅雷的代码结构被扒了精光

    背景 之前扒过飞书的源码,从代码设计架构层面里里外外学习一把,飞书还是挺 "大方" 的,源码在客户端和网页端都一览无余,不过好像新版本已经看不到了.相关的文章由于在内网技术论坛发过 ...

  2. 卧槽!迅雷的代码结构竟然被扒了精光!

    背景 之前扒过飞书的源码,从代码设计架构层面里里外外学习一把,飞书还是挺 "大方" 的,源码在客户端和网页端都一览无余,不过好像新版本已经看不到了.相关的文章由于在内网技术论坛发过 ...

  3. 我擦!迅雷的代码结构竟然被扒了精光~

    作者:jiawen 链接:juejin.im/post/6890344584078721031 # 背景 之前扒过飞书的源码,从代码设计架构层面里里外外学习一把,飞书还是挺"大方" ...

  4. YYDS!迅雷的代码结构,竟然被大佬“扒了精光”!

    点击上方"Java精选",选择"设为星标" 别问别人为什么,多问自己凭什么! 下方有惊喜留言必回,有问必答! 每天 08:15 更新文章,每天进步一点点... ...

  5. 大神尝试扒迅雷的代码,竟然被扒了个精光!

    欢迎大家关注 来源:juejin.im/post/6890344584078721031 文末送书5本 背景 之前扒过飞书的源码,从代码设计架构层面里里外外学习一把,飞书还是挺 "大方&qu ...

  6. 《看聊天记录都学不会C语言?太菜了吧》(14)这么神奇?我写了20行代码竟然一行就可以搞定?

    好消息2020年4月13日晚7.30我在CSDN开播,等你来聊天 预约连接:https://live.csdn.net/room/A757291228/MJWK0Gem 本系列文章将会以通俗易懂的对话 ...

  7. 卧槽!红警代码竟然开源了!!!

    公众号关注 "GitHubDaily" 设为 "星标",每天带你逛 GitHub! 大家好,我是小 G. 如果你这两天一直有关注 GitHub Trending ...

  8. 卧槽,SQL注入竟然把我们的系统搞挂了!

    前言 先来给大家看个漫画,原创是xkcd,我把它翻译成了中文. 如果你能在看漫画的时候能会心一笑,基本上就理解什么是SQL注入了,这里再啰嗦两句, 来个更简单的例子, 例如这个SQL做了字符串拼接: ...

  9. 卧槽,小小的单例模式竟然有这么多种写法?

    来源:juejin.cn/post/6903802160665460743 单例模式无论是在现实世界中还是在程序员的代码世界里都是十分常见的,同时也是面试中比较常见的热身问题,不仅仅是因为单例模式在业 ...

  10. 卧槽!VSCode 上竟然也能画流程图了???

    大家好,我是厂长. 作为一款开源的主流代码编辑器,VSCode 在发布之后一直受到不少开发者的喜爱. 因此,有很多水友也经常调侃道,这世界上就不存在 VSCode 做不到的事. 看到大家对 VSCod ...

最新文章

  1. Android入门教程 (二) 第一个App HelloWorld
  2. 【DIY】DIYarduino温湿度计视频图文教程
  3. 不得不看的17 个linux实用技巧
  4. 字符串面试题(一)— 字符串逆序
  5. html中字段是日期控件,jQuery日历插件datepicker用法详解
  6. MySQL创建字段+数据处理函数+汇总数据(聚集函数)+分组数据
  7. php培训js重要么,php何以被戏称为“世界上最好的语言”?请看它与js的对比!...
  8. 如何合理命名你的代码
  9. IT报表开发者必看:别加班了,真正解放双手的低代码开发神器来了
  10. 超柔磨绒印花空调被(200*230cm) -凡客诚品工商银行团购专区- VANCL凡客诚品
  11. 声源测向: TDOA-GCC-PATH方法
  12. 低代码大势所趋,RDP报表3.0应运而生
  13. 15款Chrome浏览器插件让设计师告别拖延症
  14. VMware Workstation 三种网络连接配置详解
  15. 连接redis集群报错: no reachable node in cluster
  16. Coursera | Andrew Ng (01-week-3-3.8)—激活函数的导数
  17. 【经验】通过JVM调优,让凯哥个人博客响应速度提升了不少
  18. 【漏洞复现】CVE-2020-0796永恒之黑漏洞复现
  19. 银行业大数据分析:如何用大数据技术优化ATM运营
  20. 30分钟学会XAML

热门文章

  1. 如何用Camtasia进行内容补充?
  2. 图片处理和验证码识别
  3. C语言实现的Python扩展模块
  4. python 传参open
  5. js高级编号笔记[新]-事件
  6. RH4 构建ORACLE ASM
  7. git 操作 中文文件名的时候,显示乱码 ,解决方法
  8. 苹果Mac照片编辑插件套件:Nik Collection
  9. 如何在“家庭”应用中创建场景?
  10. 如何通过Multitouch为Mac电脑添加更多手势控制?