拆解一款免Root共享VPN神器:双App架构加Binder通信,让热点设备也能走VPN隧道

前言:手机开热点后VPN失效?本文逆向拆解一款免Root共享VPN应用,揭秘其双App架构与Binder跨进程通信设计:主App监听HTTP、SOCKS5、Shadowsocks端口接客,插件App借UID策略路由走VPN跑腿,不动内核一行代码,把安卓手机变成全屋设备的代理服务器,彻底解决热点设备无法走VPN的死锁难题。

我拆了一个免Root共享VPN的App,发现了一个特别骚的双App架构

先想象一个画面。

你手机上开着VPN,一切正常。然后你打开热点,让笔记本连上,满心期待地打开Telegram,转圈,转圈,还是转圈。你查了一下IP,显示的是你运营商的地址,VPN仿佛从来没存在过。

你大概率遇到过这种事,或者至少听人抱怨过。这不是手机的bug,也不是VPN软件坏了,你去任何技术论坛搜一下,这个问题被问了几百遍。

为啥会这样呢。

Android的VPN,说穿了就是在系统里插了一块虚拟网卡,通常叫tun0。系统把手机上各个App发起的流量,按UID一个一个塞进这块虚拟网卡,先在VPN里绕一圈再出去。但热点是另一套逻辑,连上热点的设备的流量,在内核层直接从热点网卡转发到流量出口,走的是一条完全不同的路,压根不路过tun0。

一句话,VPN只管手机自己的App,热点是后娘养的。

想改这条转发路径,得动内核的iptables规则,那需要root。所以很长一段时间里,把VPN共享给热点设备这件事,在Android上就是个无解的题。

有root的同学有开源项目VPNHotspot,直接改路由和防火墙,一劳永逸。没root的呢,有个很有名的NetShare,思路是不用系统自带热点,自己用Wi-Fi Direct起一个网络,然后让连接的设备手动配一个代理,把流量送回主手机。能用,但配置繁琐,兼容性还看脸。

你可能会说,我为什么要折腾这个,电脑直接装个VPN客户端不就行了。我非常理解这种想法,但现实是,你家里可能有电视盒子,有Chromecast,有游戏机,有公司发的没有管理员权限的笔记本,这些设备有的压根装不了VPN,有的不让你装。这时候手机就是唯一的出口。

然后,鬼使神差的,我在APKPure上翻到了另一个App。

手机开热点,全屋设备连上来
手机开热点,全屋设备连上来

名字起得特别直白,就叫VPN+Proxy+Server Tethering NoRoot,1.9.5版本。商店页面上写着,它能在手机上跑一个代理服务器,支持HTTP、SOCKS5、Shadowsocks,不用root,就能把手机上的VPN通过热点或者局域网分享给其他设备,给你的Telegram和其他软件提供代理服务。

更让我在意的是这个开发者写文案的语气。如果此应用不符合您的期望,请原谅开发者,给您带来麻烦,敬请原谅。广告不会影响产品使用,是为了开发者有更多的资源来维护和更新产品,请不要介意。

一个独立开发者,卑微得让人有点心酸。

我当时就好奇了,不动内核,不动系统,一个普通App,凭什么能做到root才能做到的事。

于是我把它拆了。

反编译,看Manifest,看Dex,顺着核心包一层一层往下翻。翻完之后我盯着屏幕看了半天,这个App的实现,比我想象的干净太多了。

先说第一个让我意外的点,它没有任何so库。整个App里唯一的native库就是个官方的DataStore计数器,跟核心功能一点关系没有。所有网络逻辑全是Java和Kotlin写的,网络引擎用的是Netty 4,纯NIO。一个前台服务常驻后台,拿着WifiLock防止Wi-Fi休眠,就这么简单粗暴。

第二个发现更有意思,它一口气起了三种代理服务端。HTTP和HTTPS代理绑在8088端口,支持CONNECT隧道,还能加Basic Auth账密认证。SOCKS5绑在1098端口,握手认证的完整流程都实现了。最骚的是Shadowsocks,绑在1090端口,它自带一套纯Java实现的SS加密套件,rc4-md5、aes-gcm、chacha20都有,也就是说你这台手机不光能当代理,还能直接当一台Shadowsocks服务器用。

一个手机,瞬间变成一台代理服务器。它甚至还支持深色模式,一个干脏活的服务端App,居然在意你的眼睛。

这还只是热身。

真正的核心逻辑是这样的。热点设备连上手机后,在Wi-Fi设置里把代理填成手机IP的8088端口。之后设备发的每个请求,都会先到这个App手里,App收到请求,自己发起一个Socket去连真正的目标服务器。

关键就在这一步。

Android的策略路由是按UID算的,本机App进程发起的外出连接,会被系统自动塞进当前活动的VPN接口。也就是说,这个App替热点设备跑腿去连目标服务器的时候,它的流量天然就走VPN。

它没有改任何系统规则,没有hook任何东西。它只是站在了系统规则顺手的地方,借了个道。

我看到这里的时候觉得,行,思路清楚了,事情到这就讲完了。

结果没有。往下翻代码,翻到了更好玩的东西。

因为直连模式有个绕不过去的死锁。你手机开着Clash或者v2rayNG这种全局VPN的时候,VPN的虚拟网卡经常顺手把局域网进来的连接也拦了,热点设备根本连不上8088端口。那你在VPN客户端里把这个App设成绕过VPN呢,热点设备是能连上了,但这个App自己出去的流量也绕过了VPN,等于白忙一场,共享的还是裸网。

进也不行,退也不行。入站和出站,被拴在了同一个App的同一个UID上,怎么调都是按下葫芦浮起瓢。

坦率的讲,我拆到这里的时候,真心觉得这题无解了。结果这个开发者给出的答案,给我一下子整不会了。

他把一个App,拆成了两个。

一个接客,一个跑腿
一个接客,一个跑腿

主App负责接客,负责监听端口,在VPN的分应用代理里被设置成不走VPN。插件App负责出门,是个没有界面的后台服务,被设置成走VPN。两个App,两个UID,一个站在门外迎宾,一个站在门里跑腿。

那两个App之间怎么传数据呢。

主App通过bindService绑定插件的服务,定义了IRegister、IPublisher、IHandler几个跨进程接口,然后自己设计了一套二进制帧协议。四个字节的连接ID,一个字节的命令,3是建立连接,0是转发数据,2是关闭,再跟四个字节的载荷长度,后面就是目标地址或者原始数据。

热点设备发来一个请求,主App把它打包成一帧一帧的二进制,通过Android原生的Binder IPC递给插件App。插件App用自己的身份,也就是走VPN的那个身份,向目标服务器发起TCP连接。数据回来了,再一帧一帧递回主App,写回给热点设备。

像两个特工隔着墙对暗号,一个负责收信,一个负责送信,墙就是进程的边界,暗号就是那套二进制帧。

系统不是按UID分流吗,行,那我就造两个UID出来。

说实话,我看到代码里连接处理器从直连模式自动切换到桥接模式的那一刻,愣了半天。一个独立开发者,用两个App加一套私有协议,绕开了Android在系统层面都没给你留门的问题。这已经不是写代码了,这是在跟操作系统下棋。

顺着这个思路再聊聊,其实这种「不跟系统对抗,用系统自己的规则」的玩法,在Android的地下世界里一直有传统。比如很多VPN应用为了绕开某些国产ROM对默认路由的兼容性问题,会用0.0.0.0/1和128.0.0.0/1两条路由把整个IP地址空间拼出来,前缀更长优先级更高,所有ROM都认。你看,跟这个双App架构是同一个哲学,规则不让你走的路,你就在规则里面找到一条它不得不让你走的路。

我顺着这个App的功能列表,想象了一下它都是给谁用的。可能是那种出差住酒店的人,酒店的Wi-Fi要房间号登录,设备一多就烦,手机开着VPN开个热点,电脑平板全走加密隧道。也可能是家里有个压根装不了VPN的电视盒子,手机成了全屋的出口。再或者,就是想帮朋友的女朋友的手机也挂上代理,又不好意思让人家装一堆软件的人。

它甚至还能跟网络抓包App配合,把其他设备的网络请求拦截下来看,做开发调试的同学应该懂这个有多好用。

当然,这玩意儿也有门槛。官方自己都说这是一个专业的软件,用户需要了解网络和代理的知识。热点设备得手动配代理,而且从逆向看到的实现来看,它主要处理TCP流量,游戏加速这种UDP重度的场景,就别指望了。工具是好工具,但它伺候的是懂一点网络的人,这一点它没有装糊涂。

回到开头的那个画面。

笔记本连上热点,代理填上手机IP的8088,Telegram的转圈停了,消息一条一条跳出来。查一下IP,已经变成了VPN出口的地址。

手机还是那台手机,系统还是那个系统,一行内核代码都没动。变的只是有人在规则的缝隙里,给你搭了一座桥。

这类工具的存在,让我一直觉得技术社区最动人的地方就在这。官方不解决的问题,总有人在业余时间,用一种近乎偏执的巧劲把它解决掉,然后卑微地在商店页面上写,如果您对它感到满意,请给予好评。

磨平一些信息差,有时候靠的不是多么高深的技术,是有人肯把一个死锁拆开,看看里面到底卡住了什么。


/ 注意,本项目仅在 SyncMein 的500人小群分享。
/ 谢谢你看完了我的文章,我们下次再见吧。
/ 作者:明察
/ 投稿或爆料,请联系邮箱:mingcha@gqmg.com

© 版权声明
THE END
喜欢就支持一下吧
点赞13 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容