速来报名!AICon北京站鸿蒙专场~ 了解详情
写点什么

有赞移动助手 App 本地抓包方案

  • 2019-07-21
  • 本文字数:4608 字

    阅读完需:约 15 分钟

有赞移动助手App本地抓包方案

一、概述

有赞移动助手(下面简称助手 App)网关切换功能(有赞移动 App 一键切换网关实践),在我们有赞移动整个开发,测试回归,产品回归验收等扮演着重要的角色,近期我们在 App 集成了本地抓包的功能,更是如虎添翼。在测试抓包,线上问题排查等场景发挥着重要的作用。

二、背景

在目前的抓包的场景中,大部分通过手机连接 PC,进行 IP 代理,用三方抓包工具(Charles,Fiddler,Wireshark 等)进行抓包,具体有如下痛点:


  • 抓包需要将移动设备的 Wi-Fi 设置中将代理手动设置成 PC 的 IP,和对应的端口号,过程比较繁琐

  • 移动设备需要安装 Charles 提供的 Root 证书

  • 不能随时随地的用移动设备抓包,必须强依赖于 PC 端


因此我们需要在 App 增加本地抓包的功能, 通过一种技术手段可以实时监听到通过移动助手网关功能连接到 ZanProxy 服务器的所有网络请求,经过调研,实现这个功能应该具备以下几个条件:


  • 利用 Socket 协议来实现消息推送经过助手 App 网关功能的网络请求到客户端

  • 当我们切换到其他 APP 进行网络请求时,要让助手 App 能在后台保活,甚至是常驻在后台,来达到对网络监听的目的

  • 需要在有赞助手有对应的页面去展示监听到的网络请求,header,response,request 等数据

三、有赞移动助手

我们助手 App 网关功能的原理是 Android 提供的 VpnService ,iOS 的 NetworkExtension 将 TCP 连接的 IP 数据包通过 tun2socks 转化成 socks5 代理,将数据转发到 ZanProxy 服务器中,实现整个网关功能。

3.1 tun2socks

tun2socks 实现一种机制,它可以让你无需改动任何应用程序而完全同名地机那个数据用 socks 协议封装,转发给一个 socks 代理,然后由代理程序负责于正式服务器之间转发应用数据。使用代理有两种方式,一种是显示配置代理,数据离开你的主机时它的目标地址就是代理服务器。另一种是做透明代理,即在中途把原始数据重定向到一个应用程序,由该代理程序代理转发。tun2socks 在第二种的基础上,完成了 socks 协议的封装,并且实现该机制时使用了强大的 tun 虚拟网卡而不必再去配置复杂的 iptables 规则,如下图所示


3.2 socks 代理

socks 运作原理,就是在 TCP 数据外包一层 socks 协议头,到达 socks 代理服务器后,脱去 socks 头,然后通过 socks 服务器与真实服务器之间建立的连接将 TCP 数据传给真实服务器,socks 代理并不理解任何应用层协议,它只是负责转发应用层数据而已,这一点使 socks 成为了一个通用的代理协议,这一点和 HTTP 代理服务器是完全不同。

四、技术方案

了解了整个流程以后我们回到最初的痛点,需要利用 Socket 协议来让服务器和 App 建立长链接,实时监听通过助手 App 网关的所有 http/https 请求

4.1 Websocket

WebSocket 是 HTML5 新增的一种通信协议。WebSocket 协议是一种持久化的双向通信协议,它建立在 TCP 之上,同 HTTP 一样通过 TCP 来传输数据,但是它和 HTTP 最大的不同有两点:


  • WebSocket 是一种双向通信协议,在建立连接后,WebSocket 服务器和 Browser/UA 都能主动的向对方发送或接收数据,就像 Socket 一样,不同的是 WebSocket 是一种建立在 Web 基础上的一种简单模拟 Socket 的协议。

  • WebSocket 需要通过握手连接,类似于 TCP,它也需要客户端和服务器端进行握手连接,连接成功后才能相互通信。


下面是一个简单的建立握手的时序图:


4.2 Socket.IO

socket.io 是支持浏览器和服务器之间实时、双向基于事件通信的库,它包括 Node.js 服务端 API 和浏览器的 JavaScript 端的 API,是一个完全由 JavaScript 实现、基于 Node.js、支持 WebSocket 协议的用于实时通信、跨平台的开源框架。底层是基于 engine.io,在此基础上增加了 Namespace、room、自动重连等特性。


socket.io 设计的目标是支持任何的浏览器,任何 Mobile 设备。支持主流的 PC 浏览器 (IE,Safari,Chrome,Firefox,Opera 等),Mobile 浏览器(iphone Safari/ipad Safari/Android WebKit/WebOS WebKit 等)。socket.io 旨在使实时应用在每个浏览器和移动设备上成为可能,模糊不同的传输机制之间的差异。


但是,WebSocket 协议是 HTML5 新推出的协议,浏览器对它的支持并不完善,由此可以看出,socket.io 不可能仅仅是对 WebSocket 的实现,它还支持其他的通信方式,如上面介绍过的 AJAX 轮询和 Long Polling。根据浏览器的支持程度,自主选择使用哪种方式进行通讯。

4.2.1 Socket.io 支持的通信方式:

  • WebSocket

  • Adobe Flash Socket

  • AJAX long-polling

  • AJAX multipart streaming

  • Forever IFrame

  • JSONP polling

4.2.2 可靠性

socket.io 即使在下列情况下也能建立联系:


  • 代理和负载平衡器

  • 个人防火墙和杀毒软件

4.2.3 自动重连机制

除非有特殊指示,否则断开连接的客户端将尝试重新连接,直到服务器再次可用为止

4.2.4 断开检测

engine.io 级别实现心跳极值,允许服务器和客户端都知道对方何时不再响应。通过在服务器和客户端设置计时器,在握手连接期间共享超时值(pinginterval 和 pingTimeout 参数),可以实现该功能。

4.2.5 多路复用

为了在应用程序中创建关注点分离(例如每个模块,或者基于权限),socket.io 允许您创建多个名称空间,这些名称空间将作为单独的通信通道,但将共享相同的底层连接。


其他更多的包括,二进制的支持、房间支持等特性,由于想到以后的可扩展性以及客户端的支持程度,以及因此我们选择 socket.io 作为我们消息推送的技术方案。

4.2.6 源码分析

在建立连接后,每个客户端会被自动加入到一个默认的命名空间。在每个命名空间中,socket 会被默认加入两个名为 None 和 sid 的房间。None 的房间用于广播,而 sid 是当前客户端的 session id,用于单播。除默认的房间外,我们可以根据需要将对应 socket 加入自定义房间,roomid 唯一即可。socket.io 基于engine.io,支持 websocket 和 long polling。如果是 long polling,会定时发送 GET, POST 请求,当没有数据时,GET 请求在拉取队列消息时会 hang 住(超时时间为 pingTimeout ),如果 hang 住期间服务器一直没有数据产生,则需要等到客户端发送下一个 POST 请求时,此时服务器会往队列中存储 POST 请求中的消息,这样上一个 GET 请求才会返回。如果 upgrade 到了 Websocket 连接,则探测成功之后会定期 ping/pong 来保活连接。流程如下图所示:


4.3 Socket.IO 应用

在本次需求中移动端 iOS 用的开源库 socket.io-client-swift 是 swift 语言编写的。Andriod 端用的开源库 socket.io-android-chat 。下面以 iOS 为例看下如何使用:


 // MARK: Initializers
/// Type safe way to create a new SocketIOClient. `opts` can be omitted. /// /// - parameter socketURL: The url of the socket.io server. /// - parameter config: The config for this socket. public init(socketURL: URL, config: SocketIOClientConfiguration = []) { self._config = config self.socketURL = socketURL
super.init()
setConfigs(_config) }
复制代码


创建初始化 SocketManager 实例,指定 socketUrl ,指定对应的 nameSpace(名称空间)来保证单独的通信通道,将 UUID(设备唯一 id)当作 cookie 添加到 header 中,保证能够识别是哪台设备和服务端建立的 socket 通信,调用 connect 方法进行 socket 连接。


socket.on(clientEvent: .connect) { data, ack in            print("connect success")            YZVPNSettingsNetwork.bindDevice(deviceId: device ?? "")                .subscribe (                    onNext: {  response in                        print("websocket, 绑定成功 ")                },                    onError: { e in                        print("websocket, 绑定成功 ")                }            )        }
复制代码


在连接成功后将设备信息发到我们服务器进行设备的绑定。


socket.on("rows") { (data, ack) in            guard let cur = data[0] as? String else { return }            print(cur)        }        socket.on(clientEvent: .disconnect) {data, ack in            print("socket disconnect")        }        socket.on(clientEvent: .error) {data, ack in            print("socket error")        }        socket.on(clientEvent: .pong) {data, ack in            print("socket pong" + data.description)        }
复制代码


接下来就是各种事件的监听,进行相应的处理。我们这边和服务端约定好定义了名叫 rows 的事件,那么在该事件中就可以收到 http/https 请求的所有信息 originRequest、requestData、response 信息,那么如何将每个请求的信息对应起来呢?我们这里封装了一个名叫 HttpSession 的类如下:


class  HttpSession : HandyJSON {
var originRequest : String? var requestData : String? var response : String? var id : String? required init() {
}}
复制代码


返回的数据里面有 id 来作为每个 http/https 请求的唯一标示,当接收到数据时,用 id 进行本地映射,将数据塞入对应的 HttpSession 里面,当每个 HttpSession 对象的所有数据都被拿到时,我们再将该条请求信息展示出来。如下图所示:



以上整个本地抓包功能都已经完结,但是还有一个问题,我们去抓包肯定是打开别的 APP 将助手 App 退出到后台甚至是程序挂起状态,那么如何保证在助手 App 退出到后台模式后还能收到服务端推来的 socket 消息呢?那就需要让程序在后台长时间运行,iOS 有以下几种方法:


  • VOIP

  • Background Audio 后台播放音乐

  • Location Services 定位服务

  • Newsstand downloads 后台下载

  • Remote notifications 静默推送

  • 注册一个后台任务


一般而言,音乐应用在后台是避免 kill 的,如果在后台应用可用时间即将为 0 时,播放一段音乐,就会使应用变为假前端状态。可以尝试的解决方案如下:应用申请到后台执行任务后,使用 NSTimer 开启一个定时任务,主要负责监控应用剩余的后台可执行时间,当可用的时间少于一个值时,播放一段默声音乐,然后调用 UIApplication 对象的 beginBackgroundTaskWithExpirationHandler 方法将之前申请的后台执行任务结束掉,最后再重新申请一个后台执行任务,这样就可以实现后台不限时执行任务了


因此我们这边采用后台音乐和 beginBackgroundTaskWithExpirationHandler 方法保证 APP 能在后台不被 kill 。

五、总结

以上就是整个本地抓包涉及到的所有技术点,总结一下 App 如何完成本地抓包的流程:


  1. 当 App 通过 VpnService/NetworkExtension 配置 VPN 和定制、扩展核心网络功能

  2. 通过 tun2Socks,将请求 TCP 数据外包一层 socks 协议头,到达 socks 代理服务器后,脱去 socks 头,然后通过 socks 服务器与真实服务器之间建立的连接将 TCP 数据传给真实服务器,然后进行 ZanProxy 代理访问,完成一次 http/https 的完整请求

  3. 在整个请求过程中,我们用 Socket.IO 将服务端与 助手 App 建立长连接,将访问的 http/https 数据实时推送给 App 端

  4. 助手 App 监听长连接事件,拿到数据进行处理,展示出来


由于篇幅有限,期间还涉及到一些其他的技术点,有兴趣的同学可以去查阅对应的文献资料,也欢迎大家积极参沟通,写出这篇文章,是抛砖引玉,也是为了其他感兴趣的同学提供一些思路。


参考文献


https://socket.io/docs/logging-and-debugging/


https://socket.io/


本文转载自公众号有赞 coder(ID:youzan_coder)


原文链接


https://mp.weixin.qq.com/s/DY6c2_IBtG6XjKHZiNemdQ


2019-07-21 08:0016030

评论 1 条评论

发布
用户头像
类似的应用app store是有的,贵公司的app开源或者上架了吗
2019-07-22 11:01
回复
没有更多了
发现更多内容

赫拉利其人其书之我见(2)

石君

28天写作 简史 科技简史

音频特征提取方法和工具汇总

行者AI

音视频

DAPP智能合约交易系统开发、DAPP系统开发的详细解释

W13902449729

DAPP智能合约交易系统开发 DAPP系统开发

SpringCloud 从入门到精通 05--- 订单模块

Felix

助力金三银四跳槽季,《Java面试突击版》第四版强势来袭

Java架构之路

Java 程序员 架构 面试 编程语言

学习,不是一件一蹴而就的事情

Sandy

侵犯商业秘密罪律师提醒区块链技术与商业秘密的安全保管

CECBC

时间戳

波场链DAPP软件APP开发|波场链DAPP系统开发

系统开发

霸榜各个网站的阿里独有的高并发高并发手册:Netty、Redis、Zookeeper,看完惊呆了!

996小迁

redis zookeeper 架构 面试 Netty

数智化浪潮之中,传统企业如何抓住转型机遇?

京东科技开发者

DevOps

产品风控:短信验证码的风控策略

香芋味的猫丶

短信防刷 产品安全 短信验证码 短信防轰炸 短信防火墙

案例研究之聊聊 QLExpress 源码 (三)

小诚信驿站

刘晓成 小诚信驿站 28天写作 QLExpress源码 聊聊源码

2020年度编程语言排行榜 C语言称霸,Java遭遇滑铁卢?

架构精进之路

编程语言 28天写作

凭借这份Java超硬核面试 “备战” 手册!我刚面试完字节跳动、阿里、华为、小米等后端岗位

Java架构之路

Java 程序员 架构 面试 编程语言

理财之我见

三石

理财 28天写作

不交“人脉”交朋友:新荣耀的底气与新机

脑极体

分布式批量任务调度、自动化运维管理监控平台Taskctl

敏捷调度TASKCTL

kettle 分布式系统 海豚调度 自动化部署 ETL

区块链技术应用新阶段有五大趋势

CECBC

比特币 区块链 数字货币

ArgoCD + KubeVela:以开发者为中心的 GitOps

阿里巴巴云原生

阿里云 开源 容器 云原生 k8s

第1周架构方法总结

Richard

UML 需求分析 概要设计 软件架构设计 详细设计

全面开创城市数字经济新时代

CECBC

数字经济

腾讯十年,总结出这份Java架构师知识路线,保你稳拿40k+

Java架构追梦

Java 面试 架构师成长笔记 金三银四 全栈知识点

分布式全链路灰度发布的探索与实践

阿里巴巴云原生

阿里云 微服务 运维 云原生 中间件

助力ARM生态 —Dragonwell新增aarch64支持

阿里云基础软件团队

LeetCode题解:236. 二叉树的最近公共祖先,递归,JavaScript,详细注释

Lee Chen

算法 大前端 LeetCode

跪了!Alibaba内部出品贼火的Java面试手册,全面对标蚂蚁金服、头条、拼多多等

Java架构之路

Java 程序员 架构 面试 编程语言

花火交易所系统开发、雷达模式系统搭建开发

W13902449729

花火交易所系统开发 雷达模式系统搭建开发

没想到,学习带给我最宝贵的东西是底气

Sandy

小马哥刷LeetCode 1480. 一维数组的动态和

小马哥

Java 面试 数据结构与算法 28天写作

什么是区块链挖矿?区块链怎么挖矿?

v16629866266

博弈论 - 海盗分金

石云升

博弈论 28天写作 海盗分金

有赞移动助手App本地抓包方案_移动_杨彬_InfoQ精选文章