iOS Packet Capture

Technical Q&A QA1176 explains the steps needed to capture network packets from iOS devices. I created this little python wrapper of the rvictl tool so I don't have to bother with device UDIDs.

#!/usr/bin/env python

# http://thrysoee.dk/iospcap/rvictl.py
# https://developer.apple.com/library/Mac/qa/qa1176/_index.html

import subprocess
import plistlib
import sys

def get_plist():
    cmd = ["system_profiler", "SPUSBDataType", "-xml", "-detailLevel", "mini"]
    plist_str = subprocess.check_output(cmd)
    plist = plistlib.readPlistFromString(plist_str)
    return plist

def get_udid_list(verbose=True):
    udid_list = []

    def append_udid_list(item):
        if item['_name'] in ('iPad', 'iPhone'):
            name = item['_name']
            udid = item['serial_num']
            if verbose:
                print "Name=%s, UDID=%s" % (name, udid)
            udid_list.append(udid)
            return True
        return False

    for a1 in get_plist():
        for a2 in a1['_items']:
            if '_items' in a2:
                for a3 in a2['_items']:
                    if not append_udid_list(a3):
                        if '_items' in a3:
                            for a4 in a3['_items']:
                                append_udid_list(a4)
    return udid_list

def rvictl(action, udid_list=[]):
    cmd = ["rvictl", action]
    cmd.extend(udid_list)
    subprocess.call(cmd)

def start(udid_list):
    rvictl("-s", udid_list)

def stop(udid_list):
    rvictl("-x", udid_list)

def list():
    rvictl("-l")

usage=False
if len(sys.argv) > 1:
    udid_list = get_udid_list()

    if sys.argv[1] == 'start':
        start(udid_list)

    elif sys.argv[1] == 'stop':
        stop(udid_list)

    elif sys.argv[1] == 'list':
        list()
    else:
        usage=True
else:
    usage=True

if usage:
    print "usage: sys.argv[0] <start | stop | list>"

Download: rvictl.py

This is how it works. First connect the iOS devices to the Mac via USB. Then set up the remote virtual network interfaces, RVI, for all the connected devices by running:

$ ./rvictl.py start
Name=iPad, UDID=bsfie7fmaejpng7l8z7a21feflejlfl1shkb04fb
Name=iPhone, UDID=akfh577m84mdkpd0vjvei1oz7snehycwfh328xe3

Starting device akfh577m84mdkpd0vjvei1oz7snehycwfh328xe3 [SUCCEEDED] with interface rvi0


Starting device bsfie7fmaejpng7l8z7a21feflejlfl1shkb04fb [SUCCEEDED] with interface rvi1

This shows that the iPhone is connected to the rvi0 interface, and we can now capture packets from it with Wireshark, or tcpdump:

sudo tcpdump -i rvi0 -n -s0 -w iphone.pcapng

After the packet capturing session is over, stop the RVIs by running:

$ ./rvictl.py stop
Name=iPad, UDID=bsfie7fmaejpng7l8z7a21feflejlfl1shkb04fb
Name=iPhone, UDID=akfh577m84mdkpd0vjvei1oz7snehycwfh328xe3

Stopping device akfh577m84mdkpd0vjvei1oz7snehycwfh328xe3 [SUCCEEDED]


Stopping device bsfie7fmaejpng7l8z7a21feflejlfl1shkb04fb [SUCCEEDED]