Capturing Network Traffic (pcap)¶
pymobiledevice3 pcap records every packet an iPhone or iPad sends and receives, on every
interface (Wi-Fi, cellular, VPN, loopback), and labels each packet with the process that sent or
received it. It needs no jailbreak, no Developer Mode, no Xcode and no Mac, and runs the same on
macOS, Linux and Windows. It uses the device's built-in pcapd service, the same one behind Apple's
macOS-only rvictl.
$ pymobiledevice3 pcap --process com.apple.WebKit -c 3
2026-11-05 09:31:54.113152: Process com.apple.WebKit (730), Interface: en0 (ethernetCsmacd), Family: AF_INET
00000000: 02 00 00 00 00 01 02 00 00 00 00 02 08 00 45 00 ..............E.
00000010: 00 40 00 00 40 00 40 06 49 D3 C0 A8 32 D5 8C 52 .@..@.@.I...2..R
00000020: 71 15 D8 AE 01 BB BF 0B 07 75 00 00 00 00 B0 C2 q........u......
00000030: FF FF 06 5E 00 00 02 04 05 B4 01 03 03 06 01 01 ...^............
00000040: 08 0A 4D 27 50 E7 00 00 00 00 04 02 00 00 ..M'P.........
2026-11-05 09:31:54.256712: Process com.apple.WebKit (730), Interface: en0 (ethernetCsmacd), Family: AF_INET
00000000: 02 00 00 00 00 01 02 00 00 00 00 02 08 00 45 00 ..............E.
00000010: 00 3C 00 00 40 00 25 06 64 D7 8C 52 71 15 C0 A8 .<..@.%.d..Rq...
00000020: 32 D5 01 BB D8 AE E3 FB 09 20 BF 0B 07 76 A0 52 2........ ...v.R
00000030: FF FF 70 C4 00 00 02 04 05 9C 04 02 08 0A 23 92 ..p...........#.
00000040: 96 73 4D 27 50 E7 01 03 03 0A .sM'P.....
2026-11-05 09:31:54.257935: Process com.apple.WebKit (730), Interface: en0 (ethernetCsmacd), Family: AF_INET
00000000: 02 00 00 00 00 01 02 00 00 00 00 02 08 00 45 00 ..............E.
00000010: 00 34 00 00 40 00 40 06 49 DF C0 A8 32 D5 8C 52 .4..@.@.I...2..R
00000020: 71 15 D8 AE 01 BB BF 0B 07 76 E3 FB 09 21 80 10 q........v...!..
00000030: 08 16 97 15 00 00 01 01 08 0A 4D 27 51 77 23 92 ..........M'Qw#.
00000040: 96 73 .sQuick start¶
Connect the device over USB, unlock it and trust the computer, then:
Use the device, press Ctrl+C to stop, and open capture.pcapng in
Wireshark. While capturing, each packet is also printed to the terminal
(a one-line summary plus a hex dump); without --out, that terminal output is all you get.
The file is always pcapng
The output is written in the pcapng format whatever extension you give it, because pcapng is what
carries the per-packet process labels. Name it .pcapng so every tool recognizes it.
Filtering while you capture¶
| Option | Keeps only packets... | Example |
|---|---|---|
--process NAME_OR_PID |
owned by this process (exact name, or PID) | --process MobileSafari |
-i, --interface NAME |
on this interface (en0), or on all units of one (en) |
-i en0 |
-c, --count N |
...and stops after N of them | -c 200 |
The options combine:
# The first 200 Wi-Fi packets of Safari
pymobiledevice3 pcap --process MobileSafari -i en0 -c 200 --out safari.pcapng
- Process names are the executable names the kernel reports (as
pymobiledevice3 processes psshows them), not the app's display name, and are truncated to 16 characters. Matching is exact and case-sensitive. A PID only matches while that process runs, so the name is usually the better choice. - Interfaces you will typically see:
en0(Wi-Fi),pdp_ip0and friends (cellular),utun0... (VPNs and system tunnels),lo0(loopback). Capture without-ifirst to see which ones carry traffic on your device.
Which process sent this packet?¶
Every packet in the file carries a comment like:
ProcName/PIDis the process that owns the socket.EProcName/EPIDis the effective process: the one the traffic is really for. iOS often lets a system daemon do networking on an app's behalf (for examplensurlsessiondfor background transfers). ThenProcNameis the daemon andEProcNameis the app. For ordinary traffic both name the same process.
--process matches ProcName only. To find everything an app caused, including work delegated to
daemons, capture without --process and filter on EProcName afterwards.
In Wireshark¶
Wireshark shows the comment under Packet comments in the packet details. Right-click it and choose Apply as Column to see the process next to every packet. Display filters:
frame.comment contains "EProcName: MyApp,"
frame.comment matches "^PID: [0-9]+, ProcName: MobileSafari,"
The first keeps everything done for MyApp; the second keeps only packets whose socket
MobileSafari itself owns. The trailing comma stops MyApp from also matching MyAppExtension.
With tshark¶
tshark (Wireshark's command-line tool) can answer the common questions directly:
# Packets per effective process, busiest first
tshark -r capture.pcapng -T fields -e frame.comment \
| sed -E 's/.*EProcName: ([^,]*),.*/\1/' | sort | uniq -c | sort -rn
# Which hosts did each process connect to over TLS (from the SNI)
tshark -r capture.pcapng -Y tls.handshake.extensions_server_name \
-T fields -e frame.comment -e tls.handshake.extensions_server_name
# DNS lookups and who asked for them
tshark -r capture.pcapng -Y "dns.flags.response == 0" -T fields -e frame.comment -e dns.qry.name
Live capture in Wireshark¶
On macOS and Linux, point Wireshark at a named pipe and let pymobiledevice3 write into it:
mkfifo /tmp/ios.pcapng
wireshark -k -i /tmp/ios.pcapng &
pymobiledevice3 pcap --out /tmp/ios.pcapng > /dev/null
Packets appear in Wireshark as they arrive (> /dev/null silences the terminal copy). On macOS,
wireshark is /Applications/Wireshark.app/Contents/MacOS/Wireshark unless you installed the
command-line wrapper.
What a capture shows¶
- Encrypted traffic (HTTPS, QUIC, most app traffic) shows its endpoints, sizes, timing and the TLS server name, not its contents. DNS queries are usually readable.
- Packets from interfaces without an Ethernet header (cellular, VPN, loopback) get a placeholder
Ethernet header with the address
be:ef:be:ef:be:efso every pcap tool can open the file. That address is not real hardware. - The capture includes everything on the device, possibly personal data. Treat capture files as sensitive.
From Python¶
import asyncio
from pymobiledevice3.lockdown import create_using_usbmux
from pymobiledevice3.services.pcapd import PcapdService
async def main():
async with await create_using_usbmux() as lockdown, PcapdService(lockdown) as pcap:
async for packet in pcap.watch(packets_count=20):
print(packet.comm, packet.ecomm, packet.interface_name, len(packet.data))
# Or write a pcapng file, with the same filters as the CLI
with open("capture.pcapng", "wb") as out:
await pcap.write_to_pcap(out, pcap.watch(packets_count=100, process="MobileSafari"))
asyncio.run(main())
See the PcapdService reference for every field of a captured packet.