如何使用CharlesProxy检查iOS模拟器/设备的WebSocket流量

42

我想检查通过Web Socket传输的网络流量,但我无法控制网络代码,因为这是一个二进制库,我没有源代码,因此我不能在代码的网络部分做任何日志/断点。

我尝试使用最新版本的Charles Proxy,它声称可以嗅探WebSocket,但当我尝试使用WebSocket的URL和API时,在我的iPhone上调用的端点列表中甚至没有提到。

Version 3.11 release notes

我已经验证了CharlesProxy的配置是正确的,因为我可以检查甚至在SSL下的非WebSocket流量。

所以我的问题是:有人找到使用CharlesProxy检查通过WebSocket传输的流量的解决方案吗?

注意:我在使用iOS9时已禁用ATS

谢谢!


4
你解决问题了吗?我装的是Charles 3.11.2,但还是看不到WebSocket流量。 - klimat
2
我无法在Charles中看到来自iOS的Websockets,但是当它们从JavaScript发起时,我可以看到它们。 - Pauls
你有没有找到这个问题的答案? - Andrew Paul Simmons
2
@AndrewPaulSimmons 是的,它可以与设备和模拟器很好地配合使用。在Charles中转到代理设置>启用Socks代理。同时启用HTTP代理可能也会有所帮助。 - matanwrites
1
我们正在使用Starscream,但是WS数据没有被捕获。 - daihovey
我使用了@matanwrites推荐的方法,并且在最新版本的Charles、Apollo iOS SDK订阅中表现出色,这是基于Startscream的。但是,在SSL设置中处理证书和其他繁琐的事情确实很麻烦。 - Kristaps Grinbergs
5个回答

27

我终于找到了答案。

Charles 3.11.2完美支持WebSocket。

我使用socketIO,在协商阶段已经看到了发送的http请求,但是我错过了websocket的流量

一开始,socketIO尝试使用polling,然后切换到使用websockets

当您访问状态为"Sending request body"的请求时,就可以看到websocket流量,它实际上是wss://请求。

甚至有一个专门用来显示这种流量的选项卡。其余消息将在此处显示。

输入图像描述

PS1.确保连接到socket后再在Charles中查看。
PS2.建议使用socketIO,它对于全双工流量(如websocket)是一个重大改进。


太好了!我差点就要设置Wireshark作为替代方案了。 - JCarlosR
3
问题是关于在iOS模拟器中使用Web Sockets。当通过JS发送时,使用Charles可以看到Web Sockets没有问题。 - Andrew Paul Simmons

12

2022年6月更新:尽管socket.io-client-swift具有启用SOCKS代理的API,但似乎Starscream v4已经删除了内置的SOCKS代理支持,因此此选项实际上没有任何作用!

因此,看起来我们又回到了手动修补Starscream以启用SOCKS代理的地步。

简而言之,我们已经从Starscream支持SOCKS代理但是socket.io-client-swift没有API来启用它,变成了现在socket.io-client-swift具有启用SOCKS代理的API,但Starscream不再支持该功能。


2019年6月更新:据说socket.io-client-swift v15.1.0现在可以正确支持SOCKS代理。我还没有尝试过,但这意味着不再需要对Starscream进行手动编辑。


已接受的答案似乎无法在iOS设备上使用Socket.IO。

最新版本的Socket.IO-Client-Swift(撰写本文时为15.0.0)在iOS/OS X上使用Starscream进行WebSockets。

好消息是,Starscream支持SOCKS代理,但是:

  1. Socket.IO不公开Starscream websocket,也没有提供任何API来启用SOCKS代理行为。

  2. 内置在Starscream中的SOCKS代理使用操作系统的SOCKS代理设置,这些设置很难设置(至少对于iOS而言)。

如果我有时间,我可能会建议一个PR来更彻底地解决这个问题,但考虑到它需要对Starscream和Socket.IO-Client-Swift进行工作,这并不完全简单。

临时解决此问题的最简单方法(这是Charles的使用情况!)是编辑WebSocket.swift文件,并替换此代码:

if enableSOCKSProxy {
    let proxyDict = CFNetworkCopySystemProxySettings()
    let socksConfig = CFDictionaryCreateMutableCopy(nil, 0, proxyDict!.takeRetainedValue())
    let propertyKey = CFStreamPropertyKey(rawValue: kCFStreamPropertySOCKSProxy)
    CFWriteStreamSetProperty(outputStream, propertyKey, socksConfig)
    CFReadStreamSetProperty(inputStream, propertyKey, socksConfig)
}

使用这段代码:

let socksConfig = CFDictionaryCreateMutableCopy(nil, 0, CFNetworkCopySystemProxySettings()!.takeRetainedValue()) as! [String: Any]
let propertyKey = CFStreamPropertyKey(rawValue: kCFStreamPropertySOCKSProxy)
let ip = socksConfig["HTTPSProxy"]
let proxySocksConfig = ["SOCKSProxy": ip, "SOCKSPort": 8889, "SOCKSEnable": true] as CFDictionary // Where 8889 is the SOCKS proxy port in Charles
CFWriteStreamSetProperty(outputStream, propertyKey, proxySocksConfig)
CFReadStreamSetProperty(inputStream, propertyKey, proxySocksConfig)

这将确保默认启用SOCKS代理,并通过Charles路由所有的Websockets流量。

然后,您需要确保在iOS中配置HTTP代理设置(因为相同IP将被用于HTTP和SOCKS),在Charles中启用SOCKS代理,并且端口与上述代码中的端口匹配(默认为8889)。


在更新了WebSocket.swift之后,它仍然对我不起作用。您能否详细说明最后一部分?我已经在Charles中启用了SOCKS代理,并且端口匹配,但我想知道是否有遗漏的地方。 - Robert Gummesson
没事了,我搞定了。我忘了enableSOCKSProxy必须是true。 - Robert Gummesson
如果您替换整个部分(包括if enableSOCKSProxy { ... }),那么您就不需要设置enableSOCKSProxy = true,它将默认为所有连接启用。 - Jonathan Ellis

3

感谢您非常有帮助的回答Jonathan Ellis!我正在使用Pusher,这个方法很有效!

但是,我发现socksConfig并不总是包含有效数据,当我从那里提取IP时,它未能正常工作,或者会导致应用程序崩溃。由于我们从那里只获取本地主机IP,因此我只需在WebSocket.swift中替换以下内容。

if enableSOCKSProxy {
    let proxyDict = CFNetworkCopySystemProxySettings()
    let socksConfig = CFDictionaryCreateMutableCopy(nil, 0, proxyDict!.takeRetainedValue())
    let propertyKey = CFStreamPropertyKey(rawValue: kCFStreamPropertySOCKSProxy)
    CFWriteStreamSetProperty(outputStream, propertyKey, socksConfig)
    CFReadStreamSetProperty(inputStream, propertyKey, socksConfig)
}

使用以下内容:

let propertyKey = CFStreamPropertyKey(rawValue: kCFStreamPropertySOCKSProxy)
let proxySocksConfig = ["SOCKSProxy": "127.0.0.1", "SOCKSPort": 8889, "SOCKSEnable": true] as CFDictionary // Where 8889 is the SOCKS proxy port in Charles
CFWriteStreamSetProperty(outputStream, propertyKey, proxySocksConfig)
CFReadStreamSetProperty(inputStream, propertyKey, proxySocksConfig)

按照您的描述,我已经在Charles中启用了socks代理。

再次感谢!


1
发现了一种解决方法: 经过尝试,我得出结论,虽然iOS模拟器遵循系统代理设置来处理HTTP,但WebSocket则不会。Socks5代理设置也不会生效。 然而,我们可以在Charles中使用反向代理强制模拟器使用它。 在Charles中,点击代理 -> 反向代理,设置一个本地地址作为您的WebSocket服务器的透明代理,然后在您的new WebSocket(<address>)中使用该地址(在React Native的情况下),然后您就可以在Charles中看到您的WebSocket连接了。

你能分享一个包含所有字段的例子吗?本地地址字段默认是禁用的。 - Stanislav Mayorov

0

使用CharlesProxy连接iOS WebSocket

var rd: InputStream
var wr: OutputStream

let dict: NSDictionary = [
    StreamSOCKSProxyConfiguration.hostKey.rawValue : <ip>,
    StreamSOCKSProxyConfiguration.portKey.rawValue : <port>
]
rd.setProperty(dict, forKey: Stream.PropertyKey.socksProxyConfigurationKey)
wr.setProperty(dict, forKey: Stream.PropertyKey.socksProxyConfigurationKey)

或者

let proxyDict = CFNetworkCopySystemProxySettings()
let prop = CFDictionaryCreateMutableCopy(nil, 0, proxyDict!.takeRetainedValue())
rd.setProperty(prop, forKey: Stream.PropertyKey(rawValue: kCFStreamPropertySOCKSProxy as String as String))
wr.setProperty(prop, forKey: Stream.PropertyKey(rawValue: kCFStreamPropertySOCKSProxy as String as String))

CharlesProxy设置

Proxy -> Proxy Settings... -> Proxies -> Enable SOCKS Proxy

同时,您可以使用websocket-tester检查/审查套接字连接。

enter image description here

请注意,出于安全原因,它应仅在调试模式下使用,并且可能会有其他逻辑(例如检查签名...)来防止拦截流量。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接