Skip to content

Commit

Permalink
Now uses time.After, and both lazy and mac have a timeout now
Browse files Browse the repository at this point in the history
  • Loading branch information
eyJhb committed Jan 29, 2024
1 parent cef9074 commit 4b68c81
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 88 deletions.
143 changes: 65 additions & 78 deletions ios/pcap/ipfinder.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package pcap

import (
"fmt"
"io"
"time"

"github.com/danielpaulus/go-ios/ios"
Expand Down Expand Up @@ -29,33 +30,36 @@ func (n PacketInfo) complete() bool {
// FindIPByMac reads pcap packets until one is found that matches the given MAC
// and contains an IP address. This won't work if the iOS device "automatic Wifi address" privacy
// feature is enabled. The MAC needs to be static.
func FindIPByMac(device ios.DeviceEntry) (NetworkInfo, error) {
func FindIPByMac(device ios.DeviceEntry, capture_timeout time.Duration) (NetworkInfo, error) {
mac, err := ios.GetWifiMac(device)
if err != nil {
return NetworkInfo{}, fmt.Errorf("FindIPMyMac: unable to get WiFi MAC Address: %w", err)
}

// channel to receive PacketInfo
c := make(chan PacketInfo)
done := make(chan bool)
captureError := make(chan error)

go func() {
captureError <- startCapture(device, c, done)
}()

// captureDuration := time.Duration(10 * time.Second)
// startTime := time.Now()
for packet := range c {
if packet.Mac == mac {
done <- true
return NetworkInfo{Mac: packet.Mac, IPv4: packet.IPv4, IPv6: packet.IPv6}, nil
}
log.Infof("FindIPByMac: connected to pcapd. timeout %v", capture_timeout)

pcapService, err := ios.ConnectToService(device, "com.apple.pcapd")
if err != nil {
return NetworkInfo{}, fmt.Errorf("FindIPByMac: failed connecting to com.apple.pcapd with err: %w", err)
}

captureErrorResult := <-captureError
if captureErrorResult != nil {
return NetworkInfo{}, fmt.Errorf("FindIPByMac: failed to startCapture got err: %w", captureErrorResult)
endSignal := time.After(capture_timeout)

L:
for {
select {
case <-endSignal:
break L
default:
packet, err := readPacket(pcapService.Reader())
if err != nil {
return NetworkInfo{}, fmt.Errorf("FindIPByMac: error reading pcap packet: %w", err)
}

if packet.Mac == mac {
return NetworkInfo{Mac: packet.Mac, IPv4: packet.IPv4, IPv6: packet.IPv6}, nil
}
}
}

return NetworkInfo{}, fmt.Errorf("failed to get any IP matching the MAC: %s", mac)
Expand All @@ -67,36 +71,35 @@ func FindIPByMac(device ios.DeviceEntry) (NetworkInfo, error) {
// If the device only contains a single IP, then it would be 50/50 which IP will be returned.
// This is best effort! It's important to generate some traffic, when this function runs to get better results.
func FindIPByLazy(device ios.DeviceEntry, capture_duration time.Duration) (NetworkInfo, error) {
// channel to receive PacketInfo
c := make(chan PacketInfo)
done := make(chan bool)
captureError := make(chan error)
pcapService, err := ios.ConnectToService(device, "com.apple.pcapd")
if err != nil {
return NetworkInfo{}, fmt.Errorf("FindIPByLazy: failed connecting to com.apple.pcapd with err: %w", err)
}

go func() {
captureError <- startCapture(device, c, done)
}()
log.Infof("FindIPByLazy: connected to pcapd. waiting %v", capture_duration)

// captureDuration := time.Duration(10 * time.Second)
startTime := time.Now()
ipv6Hits := make(map[string]int)
ipv4Hits := make(map[string]int)
for packet := range c {
if time.Now().Sub(startTime) > capture_duration {
done <- true
break
}
endSignal := time.After(capture_duration)

if packet.IPv4 != "" {
ipv4Hits[packet.IPv4] += 1
}
if packet.IPv6 != "" {
ipv6Hits[packet.IPv6] += 1
}
}
L:
for {
select {
case <-endSignal:
break L
default:
packet, err := readPacket(pcapService.Reader())
if err != nil {
return NetworkInfo{}, fmt.Errorf("FindIPByLazy: error reading pcap packet: %w", err)
}
if packet.IPv4 != "" {
ipv4Hits[packet.IPv4] += 1
}
if packet.IPv6 != "" {
ipv6Hits[packet.IPv6] += 1
}

captureErrorResult := <-captureError
if captureErrorResult != nil {
return NetworkInfo{}, fmt.Errorf("FindIPByLazy: failed to startCapture got err: %w", captureErrorResult)
}
}

highestIPv4Hits, highestIPv4Addr := 0, ""
Expand All @@ -118,44 +121,28 @@ func FindIPByLazy(device ios.DeviceEntry, capture_duration time.Duration) (Netwo
return NetworkInfo{IPv4: highestIPv4Addr, IPv6: highestIPv6Addr}, nil
}

func startCapture(device ios.DeviceEntry, c chan PacketInfo, done chan bool) error {
intf, err := ios.ConnectToService(device, "com.apple.pcapd")
var plistCodec = ios.NewPlistCodec()

func readPacket(r io.Reader) (PacketInfo, error) {
b, err := plistCodec.Decode(r)
if err != nil {
return fmt.Errorf("startCapture: failed connecting to com.apple.pcapd with err: %w", err)
return PacketInfo{}, fmt.Errorf("readPacket: failed decoding plistCodec err: %w", err)
}

plistCodec := ios.NewPlistCodec()
for {
// if the channel is signalling to stop
select {
case <-done:
return nil
default:
}

b, err := plistCodec.Decode(intf.Reader())
if err != nil {
return fmt.Errorf("startCapture: failed decoding plistCodec err: %w", err)
}
decodedBytes, err := fromBytes(b)
if err != nil {
return fmt.Errorf("startCapture: failed decoding fromBytes err: %w", err)
}
_, packet, err := getPacket(decodedBytes)
if err != nil {
return fmt.Errorf("startCapture: failed getPacket err: %w", err)
}
if len(packet) > 0 {
pInfo := parsePacket(packet)
if !pInfo.complete() {
continue
}

c <- pInfo
decodedBytes, err := fromBytes(b)
if err != nil {
return PacketInfo{}, fmt.Errorf("readPacket: failed decoding fromBytes err: %w", err)
}
_, packet, err := getPacket(decodedBytes)
if err != nil {
return PacketInfo{}, fmt.Errorf("readPacket: failed getPacket err: %w", err)
}
if len(packet) > 0 {
pInfo := parsePacket(packet)
if pInfo.complete() {
return pInfo, nil
}
}

return nil
return PacketInfo{}, nil
}

func parsePacket(p []byte) PacketInfo {
Expand Down
25 changes: 15 additions & 10 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ Usage:
ios httpproxy remove [options]
ios pair [--p12file=<orgid>] [--password=<p12password>] [options]
ios ps [--apps] [options]
ios ip [options] [--lazy] [--duration <sec>]
ios ip [options] [--lazy] [--timeout=<sec>]
ios forward [options] <hostPort> <targetPort>
ios dproxy [--binary]
ios readpair [options]
Expand Down Expand Up @@ -184,12 +184,13 @@ The commands work as following:
> Use --nojson for a human-readable listing including BundleID when available. (not included with JSON output)
> --apps limits output to processes flagged by iOS as "isApplication". This greatly-filtered list
> should at least include user-installed software. Additional packages will also be displayed depending on the version of iOS.
ios ip [options] [--lazy] [--duration <sec> Uses the live pcap iOS packet capture to wait until it finds one that contains the IP address of the device.
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.
> It relies on the MAC address of the WiFi adapter to know which is the right IP.
> You have to disable the "automatic wifi address"-privacy feature of the device for this to work.
> You have to disable the "automatic wifi address"-privacy feature of the device for this to work (if not possible, look at lazy option).
> If you wanna speed it up, open apple maps or similar to force network traffic.
> f.ex. "ios launch com.apple.Maps"
> if using lazy, it will listen for a predefined time, and will return the IP with the most requests
> f.ex. "ios launch com.apple.Maps".
> 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.
> It is a good idea to launch e.g. apple maps before starting lazy IP finding, as it creates a lot of unique traffic.
ios forward [options] <hostPort> <targetPort> Similar to iproxy, forward a TCP connection to the device.
ios dproxy [--binary] Starts the reverse engineering proxy server.
> It dumps every communication in plain text so it can be implemented easily.
Expand Down Expand Up @@ -382,15 +383,19 @@ The commands work as following:
var ip pcap.NetworkInfo
var err error

// determine timeout for commands
timeout, _ := arguments.Int("--timeout")
if timeout == 0 {
timeout = 10
}

lazy, _ := arguments.Bool("--lazy")
if !lazy {
ip, err = pcap.FindIPByMac(device)
ip, err = pcap.FindIPByMac(device, time.Second*time.Duration(timeout))
} else {
// duration, err := arguments.Int("--duration")
duration := 10
ip, err = pcap.FindIPByLazy(device, time.Duration(time.Duration(duration)*time.Second))

ip, err = pcap.FindIPByLazy(device, time.Second*time.Duration(timeout))
}

exitIfError("failed", err)
println(convertToJSONString(ip))
return
Expand Down

0 comments on commit 4b68c81

Please sign in to comment.