In the course of my recent work, I needed to analyze the TLS handshake between an iOS device and a web server. I found success using three tools in concert:
Since iOS 5, devices have included a Remote Virtual Interface (RVI) facility. An RVI allows us to forward packets from a connected iOS device to a virtual network interface on your Mac.
With an RVI set up on your Mac, it’s trivial to capture the device’s packets to disk using the Unix workhorse
tcpdump is great, but its output is not human readable. To filter and analyze the raw packet data, we’ll be using Wireshark.
As I walk you through getting started with this setup, I’ll be assuming that you:
- Are comfortable using a Mac and the command line
- Have Xcode installed
- Have an iOS device and USB cable handy
- Have downloaded and installed Wireshark
Ready? Let’s go!
Set up a Remote Virtual Interface
The first step is to connect your iOS device to your Mac, and note its device identifier (UDID). Start Xcode and open the Devices window from the Window menu. Your device’s UDID is listed as the identifier.
Next, let’s take a quick look at what network interfaces are currently available on your Mac.
$ ifconfig -l
lo0 gif0 stf0 en0 en1 en2 bridge0 p2p0 awdl0 utun0
Great! To create the RVI, use the
rvictl command and pass the UDID of your connected iOS device. On Catalina, you can find it in
$ rvictl -s dd55c5b6XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXStarting device dd55c5b6XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX [SUCCEEDED] with interface rvi0
If you run
ifconfig again, you should now see an RVI interface listed.
$ ifconfig -l
lo0 gif0 stf0 en0 en1 en2 bridge0 p2p0 awdl0 utun0 rvi0
Capture some packets
We can now run
tcpdump to capture packets from the RVI and write them out to a local file.
$ sudo tcpdump -i rvi0 -w trace.pcap
You’ll be prompted for your account password because of the
sudo, but after that, you should be capturing!
tcpdump: WARNING: rvi0: That device doesn't support promiscuous mode
(BIOCPROMISC: Operation not supported on socket)
tcpdump: listening on rvi0, link-type PKTAP (Apple DLT_PKTAP), capture size 262144 bytes
Now’s the time to use your application and trigger the network traffic you’d like to capture. When you’re done, press Ctrl+C to terminate
^C3369 packets captured
3369 packets received by filter
0 packets dropped by kernel
tcpdump stopped, you can tear down the RVI with another call to
$ rvictl -x dd55c5b6XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXStopping device dd55c5b6XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX [SUCCEEDED]
Review the captured packets
Start Wireshark and open the trace.pcap file that
tcpdump generated. If you see an error warning you about the capture file having been cut short in the middle of a packet…
…don’t sweat it! It shouldn’t affect the operation of Wireshark. You should now be able to see the list of packets that were sent and received by your device during the capture.
Wireshark is an incredibly powerful tool, and there’s a ton of great information out there about how to use it well. That said, here are a few quick tips that helped me find the information I needed.
Wireshark understands the structure of many network protocols, and provides a display filter language to help you find exactly the right packets.
By entering ssl.handshake, we can narrow the list to just the packets that were part of a TLS handshake.
So now we’ve filtered the packets down to just TLS handshake packets, but it’s likely that there are many instances of this kind of exchange in the capture. It’d be great if we could visualize where one handshake stops and the next one begins.
Wireshark calls complete network exchanges “conversations”, and allows you to change the background color for all rows in a particular conversation. To do this, right click on a packet and select Colorize Conversation > TCP > Color 1 from the menu.
Poof! The bounds of that handshake conversation become clear as day.
Follow a stream
Another way to approach filtering is to ask Wireshark to follow the stream. Right click on a packet and select Follow > SSL Stream from the menu.
The result displays just the packets in the first TCP stream we recorded. You’ll notice that this clears the ssl.handshake filter we applied earlier, and substitutes another filter expression, tcp.stream eq 0.
No problem! We can combine the expressions to get exactly the packets we want. Change the filter expression to be tcp.stream eq 0 and ssl.handshake, and we’re down to just seven packets.
Examine packet contents
All that’s left is to look through the data. The bottom part of the window displays the structure and contents of the packet selected in the list above, and expanding the tree structure reveals the fields and data contained therein.
If you’re like me, and you want to nerd out on the raw data, you can expose it by dragging up another hidden split view from the bottom of the window.
Wireshark will helpfully highlight the relevant bytes in the bottom display as you select data fields in the tree view above.
So there you have it. With this workflow you can combine
tcpdump, and Wireshark to easily capture and review detailed information about an iOS device’s network traffic. The next time you need to understand what your iOS device is seeing over the network, and Charles doesn’t quite have enough teeth, I hope you won’t be afraid to go deep.