From 6efc49e5dc31714220006621a37518c40438d79e Mon Sep 17 00:00:00 2001 From: David Missmann Date: Tue, 20 Feb 2024 14:01:18 +0100 Subject: [PATCH 1/5] debug proxy for tunnel interfaces --- ios/debugproxy/{ => usbmuxd}/binforward.go | 2 +- ios/debugproxy/{ => usbmuxd}/debugproxy.go | 7 +- ios/debugproxy/{ => usbmuxd}/decoders.go | 2 +- ios/debugproxy/{ => usbmuxd}/dumpingconn.go | 2 +- .../{ => usbmuxd}/lockdownhandler.go | 2 +- ios/debugproxy/{ => usbmuxd}/muxhandler.go | 2 +- ios/debugproxy/{ => usbmuxd}/socket_mover.go | 2 +- ios/debugproxy/utun/capture.go | 96 +++++++++++ ios/debugproxy/utun/capture_noop.go | 14 ++ ios/debugproxy/utun/connection.go | 157 ++++++++++++++++++ ios/debugproxy/utun/decoding.go | 138 +++++++++++++++ ios/debugproxy/utun/session.go | 101 +++++++++++ main.go | 63 ++++++- 13 files changed, 572 insertions(+), 16 deletions(-) rename ios/debugproxy/{ => usbmuxd}/binforward.go (99%) rename ios/debugproxy/{ => usbmuxd}/debugproxy.go (98%) rename ios/debugproxy/{ => usbmuxd}/decoders.go (99%) rename ios/debugproxy/{ => usbmuxd}/dumpingconn.go (98%) rename ios/debugproxy/{ => usbmuxd}/lockdownhandler.go (99%) rename ios/debugproxy/{ => usbmuxd}/muxhandler.go (99%) rename ios/debugproxy/{ => usbmuxd}/socket_mover.go (98%) create mode 100644 ios/debugproxy/utun/capture.go create mode 100644 ios/debugproxy/utun/capture_noop.go create mode 100644 ios/debugproxy/utun/connection.go create mode 100644 ios/debugproxy/utun/decoding.go create mode 100644 ios/debugproxy/utun/session.go diff --git a/ios/debugproxy/binforward.go b/ios/debugproxy/usbmuxd/binforward.go similarity index 99% rename from ios/debugproxy/binforward.go rename to ios/debugproxy/usbmuxd/binforward.go index cb986252..51b547a0 100644 --- a/ios/debugproxy/binforward.go +++ b/ios/debugproxy/usbmuxd/binforward.go @@ -1,4 +1,4 @@ -package debugproxy +package usbmuxd import ( "encoding/hex" diff --git a/ios/debugproxy/debugproxy.go b/ios/debugproxy/usbmuxd/debugproxy.go similarity index 98% rename from ios/debugproxy/debugproxy.go rename to ios/debugproxy/usbmuxd/debugproxy.go index 1aeb91c9..8ca5fb9f 100644 --- a/ios/debugproxy/debugproxy.go +++ b/ios/debugproxy/usbmuxd/debugproxy.go @@ -1,4 +1,4 @@ -package debugproxy +package usbmuxd import ( "encoding/json" @@ -77,8 +77,8 @@ func (d *DebugProxy) retrieveServiceInfoByPort(port uint16) (PhoneServiceInforma } // NewDebugProxy creates a new Default proxy -func NewDebugProxy() *DebugProxy { - return &DebugProxy{mux: sync.Mutex{}, serviceList: []PhoneServiceInformation{}} +func NewDebugProxy(workdir string) *DebugProxy { + return &DebugProxy{mux: sync.Mutex{}, serviceList: []PhoneServiceInformation{}, WorkingDir: workdir} } // Launch moves the original /var/run/usbmuxd to /var/run/usbmuxd.real and starts the server at /var/run/usbmuxd @@ -104,7 +104,6 @@ func (d *DebugProxy) Launch(device ios.DeviceEntry, binaryMode bool) error { log.WithFields(log.Fields{"error": err, "socket": ios.GetUsbmuxdSocket()}).Error("Unable to move, lacking permissions?") return err } - d.setupDirectory() listener, err := net.Listen("unix", ios.ToUnixSocketPath(ios.GetUsbmuxdSocket())) if err != nil { log.Error("Could not listen on usbmuxd socket, do I have access permissions?", err) diff --git a/ios/debugproxy/decoders.go b/ios/debugproxy/usbmuxd/decoders.go similarity index 99% rename from ios/debugproxy/decoders.go rename to ios/debugproxy/usbmuxd/decoders.go index a387feb0..d28695d2 100644 --- a/ios/debugproxy/decoders.go +++ b/ios/debugproxy/usbmuxd/decoders.go @@ -1,4 +1,4 @@ -package debugproxy +package usbmuxd import ( "bytes" diff --git a/ios/debugproxy/dumpingconn.go b/ios/debugproxy/usbmuxd/dumpingconn.go similarity index 98% rename from ios/debugproxy/dumpingconn.go rename to ios/debugproxy/usbmuxd/dumpingconn.go index 9494c98b..a8553837 100644 --- a/ios/debugproxy/dumpingconn.go +++ b/ios/debugproxy/usbmuxd/dumpingconn.go @@ -1,4 +1,4 @@ -package debugproxy +package usbmuxd import ( "encoding/hex" diff --git a/ios/debugproxy/lockdownhandler.go b/ios/debugproxy/usbmuxd/lockdownhandler.go similarity index 99% rename from ios/debugproxy/lockdownhandler.go rename to ios/debugproxy/usbmuxd/lockdownhandler.go index 4b22a23f..01189d53 100644 --- a/ios/debugproxy/lockdownhandler.go +++ b/ios/debugproxy/usbmuxd/lockdownhandler.go @@ -1,4 +1,4 @@ -package debugproxy +package usbmuxd import ( "bytes" diff --git a/ios/debugproxy/muxhandler.go b/ios/debugproxy/usbmuxd/muxhandler.go similarity index 99% rename from ios/debugproxy/muxhandler.go rename to ios/debugproxy/usbmuxd/muxhandler.go index 70913f06..daf834d5 100644 --- a/ios/debugproxy/muxhandler.go +++ b/ios/debugproxy/usbmuxd/muxhandler.go @@ -1,4 +1,4 @@ -package debugproxy +package usbmuxd import ( "bytes" diff --git a/ios/debugproxy/socket_mover.go b/ios/debugproxy/usbmuxd/socket_mover.go similarity index 98% rename from ios/debugproxy/socket_mover.go rename to ios/debugproxy/usbmuxd/socket_mover.go index cff2a0f1..07b15158 100644 --- a/ios/debugproxy/socket_mover.go +++ b/ios/debugproxy/usbmuxd/socket_mover.go @@ -1,4 +1,4 @@ -package debugproxy +package usbmuxd import ( "fmt" diff --git a/ios/debugproxy/utun/capture.go b/ios/debugproxy/utun/capture.go new file mode 100644 index 00000000..64db23cd --- /dev/null +++ b/ios/debugproxy/utun/capture.go @@ -0,0 +1,96 @@ +//go:build darwin + +package utun + +import ( + "context" + "errors" + "fmt" + "io" + "net" + + "github.com/danielpaulus/go-ios/ios" + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + "github.com/google/gopacket/pcap" + log "github.com/sirupsen/logrus" +) + +type direction uint8 + +const ( + outgoing = iota + incoming +) + +type connections map[connectionId]*connection + +type connectionId struct { + localPort layers.TCPPort + remotePort layers.TCPPort +} + +func Live(ctx context.Context, iface string, provider ios.RsdPortProvider, dumpDir string) error { + addr, err := ifaceAddr(iface) + if err != nil { + return err + } + log.Infof("Capture traffice for iface %s with address %s", iface, addr) + if handle, err := pcap.OpenLive(iface, 64*1024, true, pcap.BlockForever); err != nil { + return fmt.Errorf("Live: failed to connect to iface %s. %w", iface, err) + } else { + packetSource := gopacket.NewPacketSource(handle, handle.LinkType()) + s := newSession(packetSource.Packets(), addr, provider, dumpDir) + s.readPackets(ctx) + } + return nil +} + +func ifaceAddr(name string) (net.IP, error) { + ifaces, err := pcap.FindAllDevs() + if err != nil { + return nil, err + } + for _, iface := range ifaces { + if iface.Name == name { + return iface.Addresses[1].IP, nil + } + } + return nil, fmt.Errorf("ifaceAddr: could not find iface with name %s", name) +} + +func (s *session) connectionIdentifier(ip *layers.IPv6, tcp *layers.TCP) connectionId { + if ip.SrcIP.String() == s.localAddr.String() { + return connectionId{ + localPort: tcp.SrcPort, + remotePort: tcp.DstPort, + } + } else { + return connectionId{ + localPort: tcp.DstPort, + remotePort: tcp.SrcPort, + } + } +} + +type payloadWriter struct { + incoming io.WriteCloser + outgoing io.WriteCloser +} + +func (p payloadWriter) Close() error { + inErr := p.incoming.Close() + outErr := p.outgoing.Close() + return errors.Join(inErr, outErr) +} + +func (p payloadWriter) Write(d direction, b []byte) (int, error) { + switch d { + case outgoing: + return p.outgoing.Write(b) + case incoming: + return p.incoming.Write(b) + default: + return 0, fmt.Errorf("Write: unknown direction %d", d) + } +} diff --git a/ios/debugproxy/utun/capture_noop.go b/ios/debugproxy/utun/capture_noop.go new file mode 100644 index 00000000..c0c94068 --- /dev/null +++ b/ios/debugproxy/utun/capture_noop.go @@ -0,0 +1,14 @@ +//go:build !darwin + +package utun + +import ( + "context" + "errors" + + "github.com/danielpaulus/go-ios/ios" +) + +func Live(ctx context.Context, iface string, provider ios.RsdPortProvider, dumpDir string) error { + return errors.New("capturing traffic on the utun interface is only supported on MacOS") +} diff --git a/ios/debugproxy/utun/connection.go b/ios/debugproxy/utun/connection.go new file mode 100644 index 00000000..3111f941 --- /dev/null +++ b/ios/debugproxy/utun/connection.go @@ -0,0 +1,157 @@ +//go:build darwin + +package utun + +import ( + "errors" + "fmt" + "io" + "os" + "path" + + "github.com/google/gopacket/layers" + "github.com/sirupsen/logrus" +) + +type connection struct { + id connectionId + w payloadWriter + outPath string + inPath string + service string +} + +func newConnection(id connectionId, p string, service string) (*connection, error) { + inPath := path.Join(p, "incoming") + incoming, err := os.OpenFile(inPath, os.O_CREATE|os.O_WRONLY, os.ModePerm) + if err != nil { + return nil, fmt.Errorf("newConnection: could not open file for incoming connection dump: %w", err) + } + outPath := path.Join(p, "outgoing") + outgoing, err := os.OpenFile(outPath, os.O_CREATE|os.O_WRONLY, os.ModePerm) + if err != nil { + return nil, fmt.Errorf("newConnection: could not open file for outgoing connection dump: %w", err) + } + pw := payloadWriter{ + incoming: incoming, + outgoing: outgoing, + } + return &connection{ + id: id, + w: pw, + outPath: outPath, + inPath: inPath, + service: service, + }, nil +} + +func (c connection) handlePacket(tcp *layers.TCP) { + if tcp.SYN && tcp.SrcPort == c.id.localPort { + logrus.Infof("new connection %s", c.id) + } + if len(tcp.Payload) > 0 { + c.w.Write(c.direction(tcp), tcp.Payload) + } +} + +func (c connection) direction(tcp *layers.TCP) direction { + if c.id.localPort == tcp.SrcPort { + return outgoing + } else { + return incoming + } +} + +func (c connection) Close() error { + _ = c.w.Close() + logrus.WithField("connection", c.id.String()).WithField("service", c.service).Info("closing connection") + err := parseConnectionData(c.outPath, c.inPath) + if err != nil { + logrus.WithField("connection", c.id.String()). + WithField("service", c.service). + WithError(err). + Warn("failed parsing data") + } + return nil +} + +func (c connectionId) String() string { + return fmt.Sprintf("%d-%d", c.localPort, c.remotePort) +} + +func parseConnectionData(outgoing string, incoming string) error { + dir := path.Dir(outgoing) + + outFile, err := os.OpenFile(outgoing, os.O_RDONLY, os.ModePerm) + if err != nil { + return err + } + defer outFile.Close() + inFile, err := os.OpenFile(incoming, os.O_RDONLY, os.ModePerm) + if err != nil { + return err + } + defer inFile.Close() + + t := detectType(outFile) + + switch t { + case http2: + _ = createDecodingFiles(dir, "http.frames", func(outgoing, incoming pair) error { + outErr := decodeHttp2FrameHeaders(outgoing.w, outFile, true) + inErr := decodeHttp2FrameHeaders(incoming.w, inFile, false) + return errors.Join(outErr, inErr) + }) + _, _ = outFile.Seek(0, io.SeekStart) + _, _ = inFile.Seek(0, io.SeekStart) + return createDecodingFiles(dir, "http.bin", func(outgoing, incoming pair) error { + outErr := decodeHttp2(outgoing.w, outFile, true) + inErr := decodeHttp2(incoming.w, inFile, false) + if err := errors.Join(outErr, inErr); err != nil { + //return err + } + return parseConnectionData(outgoing.p, incoming.p) + }) + case remoteXpc: + return createDecodingFiles(dir, "xpc.jsonl", func(outgoing, incoming pair) error { + outErr := decodeRemoteXpc(outgoing.w, outFile) + inErr := decodeRemoteXpc(incoming.w, inFile) + return errors.Join(outErr, inErr) + }) + case remoteDtx: + return createDecodingFiles(dir, "dtx", func(outgoing, incoming pair) error { + outErr := decodeRemoteDtx(outgoing.w, outFile) + inErr := decodeRemoteDtx(incoming.w, inFile) + return errors.Join(outErr, inErr) + }) + default: + stat, _ := os.Stat(outgoing) + if stat.Size() == 0 { + return nil + } + return fmt.Errorf("unknown content type: %s/%s", outgoing, incoming) + } +} + +func createDecodingFiles(dir, suffix string, consumer func(outgoing, incoming pair) error) error { + outPath := path.Join(dir, fmt.Sprintf("outgoing.%s", suffix)) + inPath := path.Join(dir, fmt.Sprintf("incoming.%s", suffix)) + + outFile, err := os.OpenFile(outPath, os.O_CREATE|os.O_WRONLY, os.ModePerm) + if err != nil { + return err + } + defer outFile.Close() + inFile, err := os.OpenFile(inPath, os.O_CREATE|os.O_WRONLY, os.ModePerm) + if err != nil { + return err + } + defer inFile.Close() + + return consumer(pair{outPath, outFile}, pair{inPath, inFile}) +} + +type pair struct { + p string + w io.Writer +} diff --git a/ios/debugproxy/utun/decoding.go b/ios/debugproxy/utun/decoding.go new file mode 100644 index 00000000..6357a34d --- /dev/null +++ b/ios/debugproxy/utun/decoding.go @@ -0,0 +1,138 @@ +//go:build darwin + +package utun + +import ( + "bytes" + "encoding/binary" + "encoding/json" + "errors" + "fmt" + "io" + + dtx "github.com/danielpaulus/go-ios/ios/dtx_codec" + "github.com/danielpaulus/go-ios/ios/xpc" + log "github.com/sirupsen/logrus" + http22 "golang.org/x/net/http2" +) + +type contentType int + +const ( + http2 = contentType(iota) + remoteXpc + remoteDtx + unknown +) + +func detectType(r io.ReadSeeker) contentType { + offset, err := r.Seek(0, io.SeekCurrent) + if err != nil { + return unknown + } + defer func() { + r.Seek(offset, io.SeekStart) + }() + b := make([]byte, 4) + _, err = r.Read(b) + if err != nil { + return unknown + } + if string(b) == "PRI " { + return http2 + } + i := binary.LittleEndian.Uint32(b) + if i == 0x29b00b92 { + return remoteXpc + } + if string(b[:3]) == "y[=" { + return remoteDtx + } + + return unknown +} + +func decodeHttp2(w io.Writer, r io.Reader, needSkip bool) error { + if needSkip { + _, err := io.CopyN(io.Discard, r, 24) + if err != nil { + return err + } + } + framer := http22.NewFramer(io.Discard, r) + for { + f, err := framer.ReadFrame() + if err != nil { + return err + } + if f.Header().Type == http22.FrameData { + dataFrame := f.(*http22.DataFrame) + if _, err := w.Write(dataFrame.Data()); err != nil { + return err + } + } + } +} + +func decodeHttp2FrameHeaders(w io.Writer, r io.Reader, needSkip bool) error { + if needSkip { + _, err := io.CopyN(io.Discard, r, 24) + if err != nil { + return err + } + } + framer := http22.NewFramer(io.Discard, r) + for { + f, err := framer.ReadFrame() + if err != nil { + return err + } + _, err = w.Write(append([]byte(f.Header().String()), '\n')) + if err != nil { + return err + } + } +} + +func decodeRemoteXpc(w io.Writer, r io.Reader) error { + for { + m, err := xpc.DecodeMessage(r) + if err != nil { + if errors.Is(err, io.EOF) { + return nil + } + return fmt.Errorf("decodeRemoteXpc: failed to decode message: %w", err) + } + + b, err := json.Marshal(m) + if err != nil { + return fmt.Errorf("decodeRemoteXpc: failed to marshal data: %w", err) + } + buf := bytes.NewBuffer(nil) + _ = json.Compact(buf, b) + if _, err := io.Copy(w, buf); err != nil { + return fmt.Errorf("decodeRemoteXpc: failed to write json output: %w", err) + } + if m.IsFileOpen() { + log.Info("file transfer started, skipping remaining data ") + return nil + } + } +} + +func decodeRemoteDtx(w io.Writer, r io.Reader) error { + for { + m, err := dtx.ReadMessage(r) + if err != nil { + if errors.Is(err, io.EOF) { + return nil + } + return fmt.Errorf("decodeRemoteDtx: failed to read dtx message: %w", err) + } + + buf := bytes.NewBufferString(m.StringDebug() + "\n") + if _, err := io.Copy(w, buf); err != nil { + return fmt.Errorf("decodeRemoteDtx: failed to ") + } + } +} diff --git a/ios/debugproxy/utun/session.go b/ios/debugproxy/utun/session.go new file mode 100644 index 00000000..21ba6279 --- /dev/null +++ b/ios/debugproxy/utun/session.go @@ -0,0 +1,101 @@ +//go:build darwin + +package utun + +import ( + "context" + "fmt" + "net" + "os" + "path" + "sync/atomic" + + "github.com/danielpaulus/go-ios/ios" + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + log "github.com/sirupsen/logrus" +) + +type session struct { + localAddr net.IP + packetSrc chan gopacket.Packet + activeConnections connections + dumpDir string + connectionNum atomic.Uint32 + rsdProvider ios.RsdPortProvider +} + +func newSession(packets chan gopacket.Packet, addr net.IP, provider ios.RsdPortProvider, dumpDir string) session { + return session{ + localAddr: addr, + packetSrc: packets, + activeConnections: connections{}, + dumpDir: dumpDir, + rsdProvider: provider, + } +} + +func (s *session) readPackets(ctx context.Context) { + for { + select { + case <-ctx.Done(): + log.Infof("context cancelled. closing all connections") + for _, c := range s.activeConnections { + c.Close() + } + return + case packet := <-s.packetSrc: + err := s.handlePacket(packet) + if err != nil { + log.Warnf("failed to handle packet: %s", packet.Dump()) + } + } + } +} + +func (s *session) handlePacket(p gopacket.Packet) error { + ip, ok := p.NetworkLayer().(*layers.IPv6) + if !ok { + return fmt.Errorf("handlePacket: can not handle packet. only IPv6 is supported") + } + tcp, ok := p.TransportLayer().(*layers.TCP) + if !ok { + return fmt.Errorf("handlePacket: can not handle packet. only TCP is supported") + } + id := s.connectionIdentifier(ip, tcp) + conn, err := s.getOrCreateConnection(id) + if err != nil { + return fmt.Errorf("handlePacket: failed to get connection: %w", err) + } + conn.handlePacket(tcp) + if tcp.RST || tcp.FIN { + _ = conn.Close() + delete(s.activeConnections, id) + } + return nil +} + +func (s *session) getOrCreateConnection(id connectionId) (*connection, error) { + c, ok := s.activeConnections[id] + if ok { + return c, nil + } else { + service := s.rsdProvider.GetService(int(id.remotePort)) + if service == "" { + service = "unknown" + } + log.Infof("connection to service %s (%d)", service, id.remotePort) + p := path.Join(s.dumpDir, fmt.Sprintf("%04d-%s", s.connectionNum.Add(1), service)) + err := os.MkdirAll(p, os.ModePerm) + if err != nil { + return nil, fmt.Errorf("getOrCreateConnection: failed to create directory for connection dump: %w", err) + } + + conn, err := newConnection(id, p, service) + if err != nil { + return nil, fmt.Errorf("getOrCreateConnection: failed to create new connection: %w", err) + } + s.activeConnections[id] = conn + return conn, nil + } +} diff --git a/main.go b/main.go index 92ff15e6..f692b5e9 100644 --- a/main.go +++ b/main.go @@ -18,7 +18,8 @@ import ( "syscall" "time" - "github.com/danielpaulus/go-ios/ios/debugproxy" + "github.com/danielpaulus/go-ios/ios/debugproxy/usbmuxd" + "github.com/danielpaulus/go-ios/ios/debugproxy/utun" "github.com/danielpaulus/go-ios/ios/tunnel" "github.com/danielpaulus/go-ios/ios/amfi" @@ -101,7 +102,7 @@ Usage: ios ps [--apps] [options] ios ip [options] ios forward [options] - ios dproxy [--binary] + ios dproxy [options] [--binary] [--mode=] [--iface= --address= --rsd-port=] ios readpair [options] ios pcap [options] [--pid=] [--process=] ios install --path= [options] @@ -204,11 +205,12 @@ The commands work as following: > If you wanna speed it up, open apple maps or similar to force network traffic. > f.ex. "ios launch com.apple.Maps" ios forward [options] Similar to iproxy, forward a TCP connection to the device. - ios dproxy [--binary] Starts the reverse engineering proxy server. + ios dproxy [options] [--binary] [--mode=] [--iface= --address= --rsd-port=] Starts the reverse engineering proxy server. > It dumps every communication in plain text so it can be implemented easily. > Use "sudo launchctl unload -w /Library/Apple/System/Library/LaunchDaemons/com.apple.usbmuxd.plist" > to stop usbmuxd and load to start it again should the proxy mess up things. > The --binary flag will dump everything in raw binary without any decoding. + > Address and rsd port is mandatory to sniff tunnel traffic with utun mode. ios readpair Dump detailed information about the pairrecord for a device. ios install --path= [options] Specify a .app folder or an installable ipa file that will be installed. ios pcap [options] [--pid=] [--process=] Starts a pcap dump of network traffic, use --pid or --process to filter specific processes. @@ -546,10 +548,59 @@ The commands work as following: b, _ = arguments.Bool("dproxy") if b { + ctx := context.Background() + + // trap Ctrl+C and call cancel on the context + ctx, cancel := context.WithCancel(ctx) + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt) + defer func() { + signal.Stop(c) + cancel() + }() + go func() { + select { + case <-c: + cancel() + case <-ctx.Done(): + } + }() + dumpDir := filepath.Join(".", "dump-"+time.Now().UTC().Format("2006.01.02-15.04.05.000")) + os.MkdirAll(dumpDir, os.ModePerm) + usbmuxDir := filepath.Join(dumpDir, "usbmuxd") + os.MkdirAll(usbmuxDir, os.ModePerm) + tunDir := filepath.Join(dumpDir, "utun") + os.MkdirAll(tunDir, os.ModePerm) log.SetFormatter(&log.TextFormatter{}) // log.SetLevel(log.DebugLevel) binaryMode, _ := arguments.Bool("--binary") - startDebugProxy(device, binaryMode) + mode, _ := arguments.String("--mode") + iface, _ := arguments.String("--iface") + switch mode { + case "": + fallthrough + case "all": + fallthrough + case "utun": + if iface == "" { + log.Fatal("the '--iface' argument is required") + } + } + switch mode { + case "": + fallthrough + case "all": + go startDebugProxy(device, binaryMode, usbmuxDir) + go utun.Live(ctx, iface, device.Rsd, tunDir) + select {} + case "usbmuxd": + startDebugProxy(device, binaryMode, usbmuxDir) + case "utun": + utun.Live(ctx, iface, device.Rsd, tunDir) + default: + log.Fatalf("Uknown mode '%s'", mode) + + } return } @@ -1504,8 +1555,8 @@ func printVersion() { } } -func startDebugProxy(device ios.DeviceEntry, binaryMode bool) { - proxy := debugproxy.NewDebugProxy() +func startDebugProxy(device ios.DeviceEntry, binaryMode bool, dumpDir string) { + proxy := usbmuxd.NewDebugProxy(dumpDir) go func() { defer func() { From 14feac74c558c3e6a4dc50b3ed2272c089a24ffa Mon Sep 17 00:00:00 2001 From: David Missmann Date: Tue, 19 Nov 2024 10:31:14 +0100 Subject: [PATCH 2/5] use the exported magic for detecting DTX --- ios/debugproxy/utun/connection.go | 2 +- ios/debugproxy/utun/decoding.go | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ios/debugproxy/utun/connection.go b/ios/debugproxy/utun/connection.go index 3111f941..ec86154f 100644 --- a/ios/debugproxy/utun/connection.go +++ b/ios/debugproxy/utun/connection.go @@ -108,7 +108,7 @@ func parseConnectionData(outgoing string, incoming string) error { outErr := decodeHttp2(outgoing.w, outFile, true) inErr := decodeHttp2(incoming.w, inFile, false) if err := errors.Join(outErr, inErr); err != nil { - //return err + return err } return parseConnectionData(outgoing.p, incoming.p) }) diff --git a/ios/debugproxy/utun/decoding.go b/ios/debugproxy/utun/decoding.go index 6357a34d..6ca6acd7 100644 --- a/ios/debugproxy/utun/decoding.go +++ b/ios/debugproxy/utun/decoding.go @@ -41,11 +41,12 @@ func detectType(r io.ReadSeeker) contentType { if string(b) == "PRI " { return http2 } - i := binary.LittleEndian.Uint32(b) - if i == 0x29b00b92 { + le := binary.LittleEndian.Uint32(b) + be := binary.BigEndian.Uint32(b) + if le == 0x29b00b92 { return remoteXpc } - if string(b[:3]) == "y[=" { + if be == dtx.DtxMessageMagic { return remoteDtx } From 75917bd50dc5d657d71e3a05c30870d3b287afce Mon Sep 17 00:00:00 2001 From: David Missmann Date: Tue, 19 Nov 2024 10:56:52 +0100 Subject: [PATCH 3/5] make the utun dproxy available everywhere pcap is not exclusive to MacOS --- ios/debugproxy/utun/capture.go | 2 -- ios/debugproxy/utun/capture_noop.go | 14 -------------- ios/debugproxy/utun/connection.go | 2 -- ios/debugproxy/utun/decoding.go | 2 -- ios/debugproxy/utun/session.go | 2 -- 5 files changed, 22 deletions(-) delete mode 100644 ios/debugproxy/utun/capture_noop.go diff --git a/ios/debugproxy/utun/capture.go b/ios/debugproxy/utun/capture.go index 64db23cd..62953070 100644 --- a/ios/debugproxy/utun/capture.go +++ b/ios/debugproxy/utun/capture.go @@ -1,5 +1,3 @@ -//go:build darwin - package utun import ( diff --git a/ios/debugproxy/utun/capture_noop.go b/ios/debugproxy/utun/capture_noop.go deleted file mode 100644 index c0c94068..00000000 --- a/ios/debugproxy/utun/capture_noop.go +++ /dev/null @@ -1,14 +0,0 @@ -//go:build !darwin - -package utun - -import ( - "context" - "errors" - - "github.com/danielpaulus/go-ios/ios" -) - -func Live(ctx context.Context, iface string, provider ios.RsdPortProvider, dumpDir string) error { - return errors.New("capturing traffic on the utun interface is only supported on MacOS") -} diff --git a/ios/debugproxy/utun/connection.go b/ios/debugproxy/utun/connection.go index ec86154f..6cda4392 100644 --- a/ios/debugproxy/utun/connection.go +++ b/ios/debugproxy/utun/connection.go @@ -1,5 +1,3 @@ -//go:build darwin - package utun import ( diff --git a/ios/debugproxy/utun/decoding.go b/ios/debugproxy/utun/decoding.go index 6ca6acd7..b7bd2ae4 100644 --- a/ios/debugproxy/utun/decoding.go +++ b/ios/debugproxy/utun/decoding.go @@ -1,5 +1,3 @@ -//go:build darwin - package utun import ( diff --git a/ios/debugproxy/utun/session.go b/ios/debugproxy/utun/session.go index 21ba6279..2f349937 100644 --- a/ios/debugproxy/utun/session.go +++ b/ios/debugproxy/utun/session.go @@ -1,5 +1,3 @@ -//go:build darwin - package utun import ( From 15e8d84f4be2168fdc1f19a6aab30300998cd3e1 Mon Sep 17 00:00:00 2001 From: David Missmann Date: Tue, 19 Nov 2024 11:03:08 +0100 Subject: [PATCH 4/5] Revert "make the utun dproxy available everywhere" This reverts commit 75917bd50dc5d657d71e3a05c30870d3b287afce. --- ios/debugproxy/utun/capture.go | 2 ++ ios/debugproxy/utun/capture_noop.go | 14 ++++++++++++++ ios/debugproxy/utun/connection.go | 2 ++ ios/debugproxy/utun/decoding.go | 2 ++ ios/debugproxy/utun/session.go | 2 ++ 5 files changed, 22 insertions(+) create mode 100644 ios/debugproxy/utun/capture_noop.go diff --git a/ios/debugproxy/utun/capture.go b/ios/debugproxy/utun/capture.go index 62953070..64db23cd 100644 --- a/ios/debugproxy/utun/capture.go +++ b/ios/debugproxy/utun/capture.go @@ -1,3 +1,5 @@ +//go:build darwin + package utun import ( diff --git a/ios/debugproxy/utun/capture_noop.go b/ios/debugproxy/utun/capture_noop.go new file mode 100644 index 00000000..c0c94068 --- /dev/null +++ b/ios/debugproxy/utun/capture_noop.go @@ -0,0 +1,14 @@ +//go:build !darwin + +package utun + +import ( + "context" + "errors" + + "github.com/danielpaulus/go-ios/ios" +) + +func Live(ctx context.Context, iface string, provider ios.RsdPortProvider, dumpDir string) error { + return errors.New("capturing traffic on the utun interface is only supported on MacOS") +} diff --git a/ios/debugproxy/utun/connection.go b/ios/debugproxy/utun/connection.go index 6cda4392..ec86154f 100644 --- a/ios/debugproxy/utun/connection.go +++ b/ios/debugproxy/utun/connection.go @@ -1,3 +1,5 @@ +//go:build darwin + package utun import ( diff --git a/ios/debugproxy/utun/decoding.go b/ios/debugproxy/utun/decoding.go index b7bd2ae4..6ca6acd7 100644 --- a/ios/debugproxy/utun/decoding.go +++ b/ios/debugproxy/utun/decoding.go @@ -1,3 +1,5 @@ +//go:build darwin + package utun import ( diff --git a/ios/debugproxy/utun/session.go b/ios/debugproxy/utun/session.go index 2f349937..21ba6279 100644 --- a/ios/debugproxy/utun/session.go +++ b/ios/debugproxy/utun/session.go @@ -1,3 +1,5 @@ +//go:build darwin + package utun import ( From 118a41ab56d8b5600119050e6a6e3446985cd912 Mon Sep 17 00:00:00 2001 From: David Missmann Date: Tue, 19 Nov 2024 11:16:32 +0100 Subject: [PATCH 5/5] remove duplicate args --- main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.go b/main.go index e4eefb65..564fe6f3 100644 --- a/main.go +++ b/main.go @@ -104,7 +104,7 @@ Usage: ios ps [--apps] [options] ios ip [options] ios forward [options] - ios dproxy [options] [--binary] [--mode=] [--iface= --address= --rsd-port=] + ios dproxy [options] [--binary] [--mode=] [--iface=] ios readpair [options] ios sysmontap [options] ios pcap [options] [--pid=] [--process=] @@ -216,7 +216,7 @@ The commands work as following: > If you wanna speed it up, open apple maps or similar to force network traffic. > f.ex. "ios launch com.apple.Maps" ios forward [options] Similar to iproxy, forward a TCP connection to the device. - ios dproxy [options] [--binary] [--mode=] [--iface= --address= --rsd-port=] Starts the reverse engineering proxy server. + ios dproxy [options] [--binary] [--mode=] [--iface=] Starts the reverse engineering proxy server. > It dumps every communication in plain text so it can be implemented easily. > Use "sudo launchctl unload -w /Library/Apple/System/Library/LaunchDaemons/com.apple.usbmuxd.plist" > to stop usbmuxd and load to start it again should the proxy mess up things.