From 4b68c81262b4a18354d7b6309d5a78350cf834f8 Mon Sep 17 00:00:00 2001 From: eyjhb Date: Mon, 29 Jan 2024 11:07:03 +0100 Subject: [PATCH] Now uses time.After, and both lazy and mac have a timeout now --- ios/pcap/ipfinder.go | 143 ++++++++++++++++++++----------------------- main.go | 25 +++++--- 2 files changed, 80 insertions(+), 88 deletions(-) diff --git a/ios/pcap/ipfinder.go b/ios/pcap/ipfinder.go index 1766277d..645d26fa 100644 --- a/ios/pcap/ipfinder.go +++ b/ios/pcap/ipfinder.go @@ -2,6 +2,7 @@ package pcap import ( "fmt" + "io" "time" "github.com/danielpaulus/go-ios/ios" @@ -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) @@ -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, "" @@ -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 { diff --git a/main.go b/main.go index 88f71366..15a8ac67 100644 --- a/main.go +++ b/main.go @@ -95,7 +95,7 @@ Usage: ios httpproxy remove [options] ios pair [--p12file=] [--password=] [options] ios ps [--apps] [options] - ios ip [options] [--lazy] [--duration ] + ios ip [options] [--lazy] [--timeout=] ios forward [options] ios dproxy [--binary] ios readpair [options] @@ -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 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=] 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] 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. @@ -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