Skip to content

Commit

Permalink
feat: capture pcap file with PID/ProcName/bundleID
Browse files Browse the repository at this point in the history
  • Loading branch information
debugtalk committed Dec 21, 2022
1 parent 5e2ab36 commit 6d70a0e
Show file tree
Hide file tree
Showing 12 changed files with 243 additions and 38 deletions.
4 changes: 2 additions & 2 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# Release History

## v4.3.1 (2022-12-16)
## v4.3.1 (2022-12-22)

**go version**

- feat: add option WithScreenShot
- feat: run xctest before start ios automation
- feat: run step with specified loop times
- feat: add options for FindTexts
- feat: capture pcap file for iOS, including CLI `hrp ios pcap` and option `uixt.WithIOSPcapOn(true)`
- feat: capture pcap file for iOS, including CLI `hrp ios pcap` and option `uixt.WithIOSPcapOptions(...)`
- feat: add performance monitor for iOS, including CLI `hrp ios perf` and options `uixt.WithIOSPerfOptions(...)`
- refactor: move all UI APIs to uixt pkg
- docs: add examples for UI APIs
Expand Down
7 changes: 6 additions & 1 deletion examples/worldcup/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,12 @@ func TestIOSDouyinWorldCupLive(t *testing.T) {
uixt.WithIOSPerfNetwork(true),
// uixt.WithIOSPerfBundleID("com.ss.iphone.ugc.Aweme"),
),
uixt.WithIOSPcapOn(true),
uixt.WithIOSPcapOptions(
// uixt.WithIOSPcapAll(true),
// uixt.WithIOSPcapPID(1234),
// uixt.WithIOSPcapProcName("Awe"),
uixt.WithIOSPcapBundleID("com.ss.iphone.ugc.Aweme"),
),
),
TestSteps: []hrp.IStep{
hrp.NewStep("启动抖音").
Expand Down
24 changes: 23 additions & 1 deletion hrp/cmd/ios/pcap.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,23 @@ var pcapCmd = &cobra.Command{
Use: "pcap",
Short: "capture ios network packets",
RunE: func(cmd *cobra.Command, args []string) error {
pcapOptions := []uixt.IOSPcapOption{}
if pid > 0 {
pcapOptions = append(pcapOptions, uixt.WithIOSPcapPID(pid))
}
if procName != "" {
pcapOptions = append(pcapOptions, uixt.WithIOSPcapProcName(procName))
}
if bundleID != "" {
pcapOptions = append(pcapOptions, uixt.WithIOSPcapBundleID(bundleID))
}
if len(pcapOptions) == 0 {
pcapOptions = append(pcapOptions, uixt.WithIOSPcapAll(true))
}

device, err := uixt.NewIOSDevice(
uixt.WithUDID(udid),
uixt.WithIOSPcapOptions(pcapOptions...),
)
if err != nil {
log.Fatal().Err(err).Msg("failed to init ios device")
Expand Down Expand Up @@ -50,10 +65,17 @@ var pcapCmd = &cobra.Command{
},
}

var timeDuration int
var (
timeDuration int
pid int
procName string
)

func init() {
pcapCmd.Flags().StringVarP(&udid, "udid", "u", "", "specify device by udid")
pcapCmd.Flags().IntVarP(&pid, "pid", "p", 0, "specify process ID")
pcapCmd.Flags().StringVarP(&procName, "procName", "n", "", "specify process name")
pcapCmd.Flags().StringVarP(&bundleID, "bundleID", "b", "", "specify bundle ID")
pcapCmd.Flags().IntVarP(&timeDuration, "duration", "t", 10, "specify time duraion in seconds")
iosRootCmd.AddCommand(pcapCmd)
}
31 changes: 29 additions & 2 deletions hrp/pkg/gidevice/device.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"strings"
"time"

"github.com/rs/zerolog/log"
uuid "github.com/satori/go.uuid"
"howett.net/plist"

Expand Down Expand Up @@ -549,12 +550,38 @@ func (d *device) GetInterfaceOrientation() (orientation libimobiledevice.Orienta
return
}

func (d *device) PcapStart() (lines <-chan []byte, err error) {
func (d *device) PcapStart(opts ...PcapOption) (lines <-chan []byte, err error) {
pcapOptions := &PcapOptions{}
for _, fn := range opts {
fn(pcapOptions)
}
log.Info().Interface("options", pcapOptions).Msg("pcap start")

// wait until get pid for bundle id
if pcapOptions.BundleID != "" {
instruments, err := d.newInstrumentsService()
if err != nil {
fmt.Printf("get pid by bundle id failed: %v\n", err)
os.Exit(1)
}

for {
pid, err := instruments.getPidByBundleID(pcapOptions.BundleID)
if err != nil {
time.Sleep(1 * time.Second)
continue
}
pcapOptions.Pid = pid
break
}
}

if d.pcapd == nil {
if _, err = d.lockdownService(); err != nil {
return nil, err
}
if d.pcapd, err = d.lockdown.PcapdService(); err != nil {
if d.pcapd, err = d.lockdown.PcapdService(
pcapOptions.Pid, pcapOptions.ProcName); err != nil {
return nil, err
}
}
Expand Down
2 changes: 1 addition & 1 deletion hrp/pkg/gidevice/idevice.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ type Device interface {
Syslog() (lines <-chan string, err error)
SyslogStop()

PcapStart() (packet <-chan []byte, err error)
PcapStart(opts ...PcapOption) (packet <-chan []byte, err error)
PcapStop()

Reboot() error
Expand Down
4 changes: 2 additions & 2 deletions hrp/pkg/gidevice/lockdown.go
Original file line number Diff line number Diff line change
Expand Up @@ -449,12 +449,12 @@ func (c *lockdown) SyslogRelayService() (syslogRelay SyslogRelay, err error) {
return
}

func (c *lockdown) PcapdService() (pcapd Pcapd, err error) {
func (c *lockdown) PcapdService(targetPID int, targetProcName string) (pcapd Pcapd, err error) {
var innerConn InnerConn
if innerConn, err = c._startService(libimobiledevice.PcapdServiceName, nil); err != nil {
return nil, err
}
pcapdClient := libimobiledevice.NewPcapdClient(innerConn)
pcapdClient := libimobiledevice.NewPcapdClient(innerConn, targetPID, targetProcName)
return newPcapdClient(pcapdClient), nil
}

Expand Down
37 changes: 37 additions & 0 deletions hrp/pkg/gidevice/pcapd.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,39 @@ import (
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
)

type PcapOptions struct {
All bool // capture all packets
Pid int // capture packets from specific PID
ProcName string // capture packets from specific process name
BundleID string // convert to PID first, then capture packets from the PID
}

type PcapOption func(*PcapOptions)

func WithPcapAll(all bool) PcapOption {
return func(opt *PcapOptions) {
opt.All = all
}
}

func WithPcapProcName(procName string) PcapOption {
return func(opt *PcapOptions) {
opt.ProcName = procName
}
}

func WithPcapPID(pid int) PcapOption {
return func(opt *PcapOptions) {
opt.Pid = pid
}
}

func WithPcapBundleID(bundleID string) PcapOption {
return func(opt *PcapOptions) {
opt.BundleID = bundleID
}
}

type pcapdClient struct {
stop chan struct{}
c *libimobiledevice.PcapdClient
Expand Down Expand Up @@ -38,6 +71,10 @@ func (c *pcapdClient) Packet() <-chan []byte {
close(packetCh)
return
}
if raw == nil {
// filtered packet
continue
}
res, err := c.c.CreatePacket(raw)
if err != nil {
log.Println("failed to create packet")
Expand Down
66 changes: 66 additions & 0 deletions hrp/pkg/gidevice/pcapd_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//go:build localtest

package gidevice

import (
"fmt"
"testing"
"time"
)

func TestPcapWithPID(t *testing.T) {
setupLockdownSrv(t)

data, err := dev.PcapStart(WithPcapPID(1234))
if err != nil {
t.Fatal(err)
}
timer := time.NewTimer(time.Duration(time.Second * 10))
for {
select {
case <-timer.C:
dev.PcapStop()
return
case d := <-data:
fmt.Println(string(d))
}
}
}

func TestPcapWithProcName(t *testing.T) {
setupLockdownSrv(t)

data, err := dev.PcapStart(WithPcapProcName("Awe"))
if err != nil {
t.Fatal(err)
}
timer := time.NewTimer(time.Duration(time.Second * 10))
for {
select {
case <-timer.C:
dev.PcapStop()
return
case d := <-data:
fmt.Println(string(d))
}
}
}

func TestPcapWithBundleID(t *testing.T) {
setupLockdownSrv(t)

data, err := dev.PcapStart(WithPcapBundleID("com.ss.iphone.ugc.Aweme"))
if err != nil {
t.Fatal(err)
}
timer := time.NewTimer(time.Duration(time.Second * 10))
for {
select {
case <-timer.C:
dev.PcapStop()
return
case d := <-data:
fmt.Println(string(d))
}
}
}
19 changes: 0 additions & 19 deletions hrp/pkg/gidevice/perfd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,22 +159,3 @@ func TestPerfAll(t *testing.T) {
}
}
}

func TestPcap(t *testing.T) {
setupLockdownSrv(t)

data, err := dev.PcapStart()
if err != nil {
t.Fatal(err)
}
timer := time.NewTimer(time.Duration(time.Second * 10))
for {
select {
case <-timer.C:
dev.PcapStop()
return
case d := <-data:
fmt.Println(string(d))
}
}
}
38 changes: 36 additions & 2 deletions hrp/pkg/gidevice/pkg/libimobiledevice/pcapd.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,33 @@ import (
"bytes"
"encoding/binary"
"fmt"
"io"
"io/ioutil"
"strings"
"time"

"github.com/lunixbochs/struc"
)

const PcapdServiceName = "com.apple.pcapd"

func NewPcapdClient(innerConn InnerConn) *PcapdClient {
func filterPacket(pid int, procName string) func(*IOSPacketHeader) bool {
return func(iph *IOSPacketHeader) bool {
if pid > 0 {
return iph.Pid == int32(pid) ||
iph.Pid2 == int32(pid)
}
if procName != "" {
return strings.HasPrefix(iph.ProcName, procName) ||
strings.HasPrefix(iph.ProcName2, procName)
}
return true
}
}

func NewPcapdClient(innerConn InnerConn, targetPID int, targetProcName string) *PcapdClient {
return &PcapdClient{
filter: filterPacket(targetPID, targetProcName),
client: newServicePacketClient(innerConn),
}
}
Expand Down Expand Up @@ -48,6 +65,11 @@ func (c *PcapdClient) ReceivePacket() (respPkt Packet, err error) {
return
}

const (
PacketHeaderSize = uint32(95)
)

// ref: https://github.com/danielpaulus/go-ios/blob/fc943b9d236571f9775f5c593e3d49bb5bd67afd/ios/pcap/pcap.go#L27
type IOSPacketHeader struct {
HdrSize uint32 `struc:"uint32,big"`
Version uint8 `struc:"uint8,big"`
Expand Down Expand Up @@ -78,12 +100,24 @@ func (c *PcapdClient) GetPacket(buf []byte) ([]byte, error) {
}
}

// support ios 15 beta4
if iph.HdrSize > PacketHeaderSize {
buf := make([]byte, iph.HdrSize-PacketHeaderSize)
_, err := io.ReadFull(preader, buf)
if err != nil {
return []byte{}, err
}
}

packet, err := ioutil.ReadAll(preader)
if err != nil {
return packet, err
}
if iph.FramePreLength == 0 {
ext := []byte{0xbe, 0xfe, 0xbe, 0xfe, 0xbe, 0xfe, 0xbe, 0xfe, 0xbe, 0xfe, 0xbe, 0xfe, 0x08, 0x00}
ext := []byte{
0xbe, 0xfe, 0xbe, 0xfe, 0xbe, 0xfe, 0xbe, 0xfe,
0xbe, 0xfe, 0xbe, 0xfe, 0x08, 0x00,
}
return append(ext, packet...), nil
}
return packet, nil
Expand Down

0 comments on commit 6d70a0e

Please sign in to comment.