Skip to content

Commit aab43a8

Browse files
committed
adds --lazy option to ip, as well as timeout for default IP
The lazy option will find the IP based on how many packets it sees with X address. The IP with the most packets to/from should most likely be the address of the device itself, unless it only contacts a single other host.
1 parent e4f3850 commit aab43a8

File tree

2 files changed

+153
-55
lines changed

2 files changed

+153
-55
lines changed

ios/pcap/ipfinder.go

Lines changed: 131 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package pcap
22

33
import (
4+
"fmt"
5+
"io"
6+
"time"
7+
48
"github.com/danielpaulus/go-ios/ios"
59
"github.com/google/gopacket"
610
"github.com/google/gopacket/layers"
@@ -13,81 +17,158 @@ type NetworkInfo struct {
1317
IPv6 string
1418
}
1519

16-
func (n NetworkInfo) complete() bool {
17-
return n.IPv6 != "" && n.Mac != "" && n.IPv4 != ""
20+
type PacketInfo struct {
21+
Mac string
22+
IPv4 string
23+
IPv6 string
1824
}
1925

20-
// FindIp reads pcap packets until one is found that matches the given MAC
26+
func (n PacketInfo) complete() bool {
27+
return (n.IPv6 != "" && n.Mac != "") || (n.IPv4 != "" && n.Mac != "")
28+
}
29+
30+
// FindIPByMac reads pcap packets until one is found that matches the given MAC
2131
// and contains an IP address. This won't work if the iOS device "automatic Wifi address" privacy
2232
// feature is enabled. The MAC needs to be static.
23-
func FindIp(device ios.DeviceEntry) (NetworkInfo, error) {
33+
func FindIPByMac(device ios.DeviceEntry, capture_timeout time.Duration) (NetworkInfo, error) {
2434
mac, err := ios.GetWifiMac(device)
2535
if err != nil {
26-
return NetworkInfo{}, err
36+
return NetworkInfo{}, fmt.Errorf("FindIPMyMac: unable to get WiFi MAC Address: %w", err)
2737
}
28-
return findIp(device, mac)
29-
}
3038

31-
func findIp(device ios.DeviceEntry, mac string) (NetworkInfo, error) {
32-
intf, err := ios.ConnectToService(device, "com.apple.pcapd")
39+
log.Infof("FindIPByMac: connected to pcapd. timeout %v", capture_timeout)
40+
41+
pcapService, err := ios.ConnectToService(device, "com.apple.pcapd")
3342
if err != nil {
34-
return NetworkInfo{}, err
43+
return NetworkInfo{}, fmt.Errorf("FindIPByMac: failed connecting to com.apple.pcapd with err: %w", err)
3544
}
36-
plistCodec := ios.NewPlistCodec()
37-
info := NetworkInfo{}
38-
info.Mac = mac
45+
46+
endSignal := time.After(capture_timeout)
47+
48+
L:
3949
for {
40-
b, err := plistCodec.Decode(intf.Reader())
41-
if err != nil {
42-
return NetworkInfo{}, err
43-
}
44-
decodedBytes, err := fromBytes(b)
45-
if err != nil {
46-
return NetworkInfo{}, err
47-
}
48-
_, packet, err := getPacket(decodedBytes)
49-
if err != nil {
50-
return NetworkInfo{}, err
50+
select {
51+
case <-endSignal:
52+
break L
53+
default:
54+
packet, err := readPacket(pcapService.Reader())
55+
if err != nil {
56+
return NetworkInfo{}, fmt.Errorf("FindIPByMac: error reading pcap packet: %w", err)
57+
}
58+
59+
if packet.Mac == mac {
60+
return NetworkInfo{Mac: packet.Mac, IPv4: packet.IPv4, IPv6: packet.IPv6}, nil
61+
}
5162
}
52-
if len(packet) > 0 {
53-
err := findIP(packet, &info)
63+
}
64+
65+
return NetworkInfo{}, fmt.Errorf("failed to get any IP matching the MAC: %s", mac)
66+
}
67+
68+
// FindIPByLazy reads pcap packets for a specified duration, whereafter it will
69+
// try to find the IP address that occurs the most times, and assume that is the IP of the device.
70+
// This is of course based on, that the device contacts mulitple IPs, and that there is some traffic.
71+
// If the device only contains a single IP, then it would be 50/50 which IP will be returned.
72+
// This is best effort! It's important to generate some traffic, when this function runs to get better results.
73+
func FindIPByLazy(device ios.DeviceEntry, capture_duration time.Duration) (NetworkInfo, error) {
74+
pcapService, err := ios.ConnectToService(device, "com.apple.pcapd")
75+
if err != nil {
76+
return NetworkInfo{}, fmt.Errorf("FindIPByLazy: failed connecting to com.apple.pcapd with err: %w", err)
77+
}
78+
79+
log.Infof("FindIPByLazy: connected to pcapd. waiting %v", capture_duration)
80+
81+
ipv6Hits := make(map[string]int)
82+
ipv4Hits := make(map[string]int)
83+
endSignal := time.After(capture_duration)
84+
85+
L:
86+
for {
87+
select {
88+
case <-endSignal:
89+
break L
90+
default:
91+
packet, err := readPacket(pcapService.Reader())
5492
if err != nil {
55-
return NetworkInfo{}, err
93+
return NetworkInfo{}, fmt.Errorf("FindIPByLazy: error reading pcap packet: %w", err)
5694
}
57-
if info.complete() {
58-
return info, nil
95+
if packet.IPv4 != "" {
96+
ipv4Hits[packet.IPv4] += 1
5997
}
98+
if packet.IPv6 != "" {
99+
ipv6Hits[packet.IPv6] += 1
100+
}
101+
60102
}
61103
}
104+
105+
highestIPv4Hits, highestIPv4Addr := 0, ""
106+
for ipv4, hits := range ipv4Hits {
107+
if hits > highestIPv4Hits {
108+
highestIPv4Hits = hits
109+
highestIPv4Addr = ipv4
110+
}
111+
}
112+
113+
highestIPv6Hits, highestIPv6Addr := 0, ""
114+
for ipv6, hits := range ipv6Hits {
115+
if hits > highestIPv6Hits {
116+
highestIPv6Hits = hits
117+
highestIPv6Addr = ipv6
118+
}
119+
}
120+
121+
return NetworkInfo{IPv4: highestIPv4Addr, IPv6: highestIPv6Addr}, nil
62122
}
63123

64-
func findIP(p []byte, info *NetworkInfo) error {
124+
var plistCodec = ios.NewPlistCodec()
125+
126+
func readPacket(r io.Reader) (PacketInfo, error) {
127+
b, err := plistCodec.Decode(r)
128+
if err != nil {
129+
return PacketInfo{}, fmt.Errorf("readPacket: failed decoding plistCodec err: %w", err)
130+
}
131+
decodedBytes, err := fromBytes(b)
132+
if err != nil {
133+
return PacketInfo{}, fmt.Errorf("readPacket: failed decoding fromBytes err: %w", err)
134+
}
135+
_, packet, err := getPacket(decodedBytes)
136+
if err != nil {
137+
return PacketInfo{}, fmt.Errorf("readPacket: failed getPacket err: %w", err)
138+
}
139+
if len(packet) > 0 {
140+
pInfo := parsePacket(packet)
141+
if pInfo.complete() {
142+
return pInfo, nil
143+
}
144+
}
145+
return PacketInfo{}, nil
146+
}
147+
148+
func parsePacket(p []byte) PacketInfo {
65149
packet := gopacket.NewPacket(p, layers.LayerTypeEthernet, gopacket.Default)
150+
res := PacketInfo{}
151+
66152
// Get the TCP layer from this packet
67153
if tcpLayer := packet.Layer(layers.LayerTypeEthernet); tcpLayer != nil {
68154
tcp, _ := tcpLayer.(*layers.Ethernet)
69-
if tcp.SrcMAC.String() == info.Mac {
70-
if log.IsLevelEnabled(log.DebugLevel) {
71-
log.Debugf("found packet for %s", info.Mac)
72-
for _, layer := range packet.Layers() {
73-
log.Debugf("layer:%s", layer.LayerType().String())
74-
}
75-
}
76-
if ipv4Layer := packet.Layer(layers.LayerTypeIPv4); ipv4Layer != nil {
77-
ipv4, ok := ipv4Layer.(*layers.IPv4)
78-
if ok {
79-
info.IPv4 = ipv4.SrcIP.String()
80-
log.Debugf("ip4 found:%s", info.IPv4)
81-
}
155+
res.Mac = tcp.SrcMAC.String()
156+
157+
if ipv4Layer := packet.Layer(layers.LayerTypeIPv4); ipv4Layer != nil {
158+
ipv4, ok := ipv4Layer.(*layers.IPv4)
159+
if ok {
160+
res.IPv4 = ipv4.SrcIP.String()
161+
log.Debugf("ip4 found:%s", res.IPv4)
82162
}
83-
if ipv6Layer := packet.Layer(layers.LayerTypeIPv6); ipv6Layer != nil {
84-
ipv6, ok := ipv6Layer.(*layers.IPv6)
85-
if ok {
86-
info.IPv6 = ipv6.SrcIP.String()
87-
log.Debugf("ip6 found:%s", info.IPv6)
88-
}
163+
}
164+
if ipv6Layer := packet.Layer(layers.LayerTypeIPv6); ipv6Layer != nil {
165+
ipv6, ok := ipv6Layer.(*layers.IPv6)
166+
if ok {
167+
res.IPv6 = ipv6.SrcIP.String()
168+
log.Debugf("ip6 found:%s", res.IPv6)
89169
}
90170
}
91171
}
92-
return nil
172+
173+
return res
93174
}

main.go

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ Usage:
9595
ios httpproxy remove [options]
9696
ios pair [--p12file=<orgid>] [--password=<p12password>] [options]
9797
ios ps [--apps] [options]
98-
ios ip [options]
98+
ios ip [options] [--lazy] [--timeout=<sec>]
9999
ios forward [options] <hostPort> <targetPort>
100100
ios dproxy [--binary]
101101
ios readpair [options]
@@ -184,11 +184,13 @@ The commands work as following:
184184
> Use --nojson for a human-readable listing including BundleID when available. (not included with JSON output)
185185
> --apps limits output to processes flagged by iOS as "isApplication". This greatly-filtered list
186186
> should at least include user-installed software. Additional packages will also be displayed depending on the version of iOS.
187-
ios ip [options] Uses the live pcap iOS packet capture to wait until it finds one that contains the IP address of the device.
187+
ios ip [options] [--lazy] [--timeout=<sec>] Uses the live pcap iOS packet capture to wait until it finds one that contains the IP address of the device.
188188
> It relies on the MAC address of the WiFi adapter to know which is the right IP.
189-
> You have to disable the "automatic wifi address"-privacy feature of the device for this to work.
189+
> You have to disable the "automatic wifi address"-privacy feature of the device for this to work (if not possible, look at lazy option).
190190
> If you wanna speed it up, open apple maps or similar to force network traffic.
191-
> f.ex. "ios launch com.apple.Maps"
191+
> f.ex. "ios launch com.apple.Maps".
192+
> If using lazy, it will listen for a predefined time, and will return the IP with the most requests, which does not require turning off randomized MAC.
193+
> It is a good idea to launch e.g. apple maps before starting lazy IP finding, as it creates a lot of unique traffic.
192194
ios forward [options] <hostPort> <targetPort> Similar to iproxy, forward a TCP connection to the device.
193195
ios dproxy [--binary] Starts the reverse engineering proxy server.
194196
> It dumps every communication in plain text so it can be implemented easily.
@@ -378,7 +380,22 @@ The commands work as following:
378380

379381
b, _ = arguments.Bool("ip")
380382
if b {
381-
ip, err := pcap.FindIp(device)
383+
var ip pcap.NetworkInfo
384+
var err error
385+
386+
// determine timeout for commands
387+
timeout, _ := arguments.Int("--timeout")
388+
if timeout == 0 {
389+
timeout = 10
390+
}
391+
392+
lazy, _ := arguments.Bool("--lazy")
393+
if !lazy {
394+
ip, err = pcap.FindIPByMac(device, time.Second*time.Duration(timeout))
395+
} else {
396+
ip, err = pcap.FindIPByLazy(device, time.Second*time.Duration(timeout))
397+
}
398+
382399
exitIfError("failed", err)
383400
println(convertToJSONString(ip))
384401
return

0 commit comments

Comments
 (0)