mirror of
https://github.com/httprunner/httprunner.git
synced 2026-06-12 11:19:39 +08:00
refactor: move gidevice to hrp pkg
This commit is contained in:
53
hrp/pkg/gidevice/pkg/ipa/ipa.go
Normal file
53
hrp/pkg/gidevice/pkg/ipa/ipa.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package ipa
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"fmt"
|
||||
"io"
|
||||
"path"
|
||||
|
||||
"howett.net/plist"
|
||||
)
|
||||
|
||||
func Info(ipaPath string) (info map[string]interface{}, err error) {
|
||||
var reader *zip.ReadCloser
|
||||
if reader, err = zip.OpenReader(ipaPath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
err = reader.Close()
|
||||
}()
|
||||
|
||||
for _, file := range reader.File {
|
||||
matched, _err := path.Match("Payload/*.app/Info.plist", file.Name)
|
||||
if _err != nil {
|
||||
err = _err
|
||||
continue
|
||||
}
|
||||
if !matched {
|
||||
continue
|
||||
}
|
||||
|
||||
var rd io.ReadCloser
|
||||
if rd, _err = file.Open(); _err != nil {
|
||||
return nil, _err
|
||||
}
|
||||
data, _err := io.ReadAll(rd)
|
||||
if _err != nil {
|
||||
return nil, _err
|
||||
}
|
||||
|
||||
info = make(map[string]interface{})
|
||||
_, _err = plist.Unmarshal(data, &info)
|
||||
if _err != nil {
|
||||
return nil, _err
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil && len(info) == 0 {
|
||||
return nil, fmt.Errorf("find Info.plist: %w", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
21
hrp/pkg/gidevice/pkg/ipa/ipa_test.go
Normal file
21
hrp/pkg/gidevice/pkg/ipa/ipa_test.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package ipa
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestInfo(t *testing.T) {
|
||||
name := "/Users/hero/Documents/Workspace/GitHub/taobao-iphone-device/tests/testdata/WebDriverAgentRunner.ipa"
|
||||
name = "/private/tmp/derivedDataPath/Build/Products/Release-iphoneos/WebDriverAgentRunner-Runner.ipa"
|
||||
|
||||
info, err := Info(name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for k, v := range info {
|
||||
t.Logf("%-50s\t%v", k, v)
|
||||
}
|
||||
|
||||
t.Log(info["CFBundleIdentifier"])
|
||||
}
|
||||
105
hrp/pkg/gidevice/pkg/libimobiledevice/afc.go
Normal file
105
hrp/pkg/gidevice/pkg/libimobiledevice/afc.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package libimobiledevice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const AfcServiceName = "com.apple.afc"
|
||||
|
||||
func NewAfcClient(innerConn InnerConn) *AfcClient {
|
||||
return &AfcClient{
|
||||
innerConn: innerConn,
|
||||
}
|
||||
}
|
||||
|
||||
type AfcClient struct {
|
||||
innerConn InnerConn
|
||||
packetNum uint64
|
||||
}
|
||||
|
||||
func (c *AfcClient) newPacket(operation uint64, data, payload []byte) Packet {
|
||||
c.packetNum++
|
||||
pkt := &afcPacket{
|
||||
operation: operation,
|
||||
packetNum: c.packetNum,
|
||||
entireLen: 40,
|
||||
thisLen: 40,
|
||||
}
|
||||
if data != nil {
|
||||
n := uint64(len(data))
|
||||
pkt.entireLen += n
|
||||
pkt.thisLen += n
|
||||
}
|
||||
if payload != nil {
|
||||
pkt.entireLen += uint64(len(payload))
|
||||
}
|
||||
return pkt
|
||||
}
|
||||
|
||||
func (c *AfcClient) Send(operation uint64, data, payload []byte) (err error) {
|
||||
pkt := c.newPacket(operation, data, payload)
|
||||
var raw []byte
|
||||
if raw, err = pkt.Pack(); err != nil {
|
||||
return fmt.Errorf("send packet (afc): %w", err)
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
buf.Write(raw)
|
||||
if data != nil {
|
||||
debugLog(fmt.Sprintf("--> %s ...afc data...\n", pkt))
|
||||
buf.Write(data)
|
||||
} else {
|
||||
debugLog(fmt.Sprintf("--> %s\n", pkt))
|
||||
}
|
||||
|
||||
if err = c.innerConn.Write(buf.Bytes()); err != nil {
|
||||
return fmt.Errorf("send packet (afc): %w", err)
|
||||
}
|
||||
|
||||
if payload != nil {
|
||||
if err = c.innerConn.Write(payload); err != nil {
|
||||
return fmt.Errorf("send packet (afc): %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *AfcClient) Receive() (respMsg *AfcMessage, err error) {
|
||||
var bufHeader []byte
|
||||
if bufHeader, err = c.innerConn.Read(40); err != nil {
|
||||
return nil, fmt.Errorf("receive packet (afc): %w", err)
|
||||
}
|
||||
buffer := new(bytes.Buffer)
|
||||
buffer.Write(bufHeader)
|
||||
var respPkt *afcPacket
|
||||
if respPkt, err = new(afcPacket).unpack(buffer); err != nil {
|
||||
return nil, fmt.Errorf("receive packet (afc): %w", err)
|
||||
}
|
||||
|
||||
respMsg = new(AfcMessage)
|
||||
respMsg.Operation = respPkt.operation
|
||||
|
||||
buffer.Reset()
|
||||
if respPkt.entireLen > 40 {
|
||||
length := int(respPkt.entireLen - 40)
|
||||
var bufDataAndPayload []byte
|
||||
if bufDataAndPayload, err = c.innerConn.Read(length); err != nil {
|
||||
return nil, fmt.Errorf("receive packet (afc): %w", err)
|
||||
}
|
||||
buffer.Write(bufDataAndPayload)
|
||||
}
|
||||
|
||||
bufData := make([]byte, respPkt.thisLen-40)
|
||||
if _, err = buffer.Read(bufData); err != nil {
|
||||
return nil, fmt.Errorf("receive packet (afc buffer): %w", err)
|
||||
}
|
||||
respMsg.Data = bufData
|
||||
respMsg.Payload = buffer.Bytes()
|
||||
|
||||
debugLog(fmt.Sprintf("<-- %s\n%s\n%s", respPkt, hex.Dump(respMsg.Data), hex.Dump(respMsg.Payload)))
|
||||
|
||||
return
|
||||
}
|
||||
187
hrp/pkg/gidevice/pkg/libimobiledevice/afcmessage.go
Normal file
187
hrp/pkg/gidevice/pkg/libimobiledevice/afcmessage.go
Normal file
@@ -0,0 +1,187 @@
|
||||
package libimobiledevice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
)
|
||||
|
||||
type AfcMessage struct {
|
||||
Operation uint64
|
||||
Data []byte
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
func (m *AfcMessage) Map() map[string]string {
|
||||
ret := make(map[string]string)
|
||||
ss := m.Strings()
|
||||
if ss != nil {
|
||||
for i := 0; i < len(ss); i += 2 {
|
||||
ret[ss[i]] = ss[i+1]
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (m *AfcMessage) Strings() []string {
|
||||
if m.Operation == AfcOperationData {
|
||||
bs := bytes.Split(m.Payload, []byte{0})
|
||||
ss := make([]string, len(bs)-1)
|
||||
for i := 0; i < len(ss); i++ {
|
||||
ss[i] = string(bs[i])
|
||||
}
|
||||
return ss
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *AfcMessage) Uint64() uint64 {
|
||||
return binary.LittleEndian.Uint64(m.Data)
|
||||
}
|
||||
|
||||
func (m *AfcMessage) Err() error {
|
||||
if m.Operation == AfcOperationStatus {
|
||||
status := m.Uint64()
|
||||
if status != AfcErrSuccess {
|
||||
return toError(status)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func toError(status uint64) error {
|
||||
switch status {
|
||||
case AfcErrUnknownError:
|
||||
return errors.New("UnknownError")
|
||||
case AfcErrOperationHeaderInvalid:
|
||||
return errors.New("OperationHeaderInvalid")
|
||||
case AfcErrNoResources:
|
||||
return errors.New("NoResources")
|
||||
case AfcErrReadError:
|
||||
return errors.New("ReadError")
|
||||
case AfcErrWriteError:
|
||||
return errors.New("WriteError")
|
||||
case AfcErrUnknownPacketType:
|
||||
return errors.New("UnknownPacketType")
|
||||
case AfcErrInvalidArgument:
|
||||
return errors.New("InvalidArgument")
|
||||
case AfcErrObjectNotFound:
|
||||
return errors.New("ObjectNotFound")
|
||||
case AfcErrObjectIsDir:
|
||||
return errors.New("ObjectIsDir")
|
||||
case AfcErrPermDenied:
|
||||
return errors.New("PermDenied")
|
||||
case AfcErrServiceNotConnected:
|
||||
return errors.New("ServiceNotConnected")
|
||||
case AfcErrOperationTimeout:
|
||||
return errors.New("OperationTimeout")
|
||||
case AfcErrTooMuchData:
|
||||
return errors.New("TooMuchData")
|
||||
case AfcErrEndOfData:
|
||||
return errors.New("EndOfData")
|
||||
case AfcErrOperationNotSupported:
|
||||
return errors.New("OperationNotSupported")
|
||||
case AfcErrObjectExists:
|
||||
return errors.New("ObjectExists")
|
||||
case AfcErrObjectBusy:
|
||||
return errors.New("ObjectBusy")
|
||||
case AfcErrNoSpaceLeft:
|
||||
return errors.New("NoSpaceLeft")
|
||||
case AfcErrOperationWouldBlock:
|
||||
return errors.New("OperationWouldBlock")
|
||||
case AfcErrIoError:
|
||||
return errors.New("IoError")
|
||||
case AfcErrOperationInterrupted:
|
||||
return errors.New("OperationInterrupted")
|
||||
case AfcErrOperationInProgress:
|
||||
return errors.New("OperationInProgress")
|
||||
case AfcErrInternalError:
|
||||
return errors.New("InternalError")
|
||||
case AfcErrMuxError:
|
||||
return errors.New("MuxError")
|
||||
case AfcErrNoMemory:
|
||||
return errors.New("NoMemory")
|
||||
case AfcErrNotEnoughData:
|
||||
return errors.New("NotEnoughData")
|
||||
case AfcErrDirNotEmpty:
|
||||
return errors.New("DirNotEmpty")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
AfcOperationInvalid = 0x00000000 /* Invalid */
|
||||
AfcOperationStatus = 0x00000001 /* Status */
|
||||
AfcOperationData = 0x00000002 /* Data */
|
||||
AfcOperationReadDir = 0x00000003 /* ReadDir */
|
||||
AfcOperationReadFile = 0x00000004 /* ReadFile */
|
||||
AfcOperationWriteFile = 0x00000005 /* WriteFile */
|
||||
AfcOperationWritePart = 0x00000006 /* WritePart */
|
||||
AfcOperationTruncateFile = 0x00000007 /* TruncateFile */
|
||||
AfcOperationRemovePath = 0x00000008 /* RemovePath */
|
||||
AfcOperationMakeDir = 0x00000009 /* MakeDir */
|
||||
AfcOperationGetFileInfo = 0x0000000A /* GetFileInfo */
|
||||
AfcOperationGetDeviceInfo = 0x0000000B /* GetDeviceInfo */
|
||||
AfcOperationWriteFileAtomic = 0x0000000C /* WriteFileAtomic (tmp file+rename) */
|
||||
AfcOperationFileOpen = 0x0000000D /* FileRefOpen */
|
||||
AfcOperationFileOpenResult = 0x0000000E /* FileRefOpenResult */
|
||||
AfcOperationFileRead = 0x0000000F /* FileRefRead */
|
||||
AfcOperationFileWrite = 0x00000010 /* FileRefWrite */
|
||||
AfcOperationFileSeek = 0x00000011 /* FileRefSeek */
|
||||
AfcOperationFileTell = 0x00000012 /* FileRefTell */
|
||||
AfcOperationFileTellResult = 0x00000013 /* FileRefTellResult */
|
||||
AfcOperationFileClose = 0x00000014 /* FileRefClose */
|
||||
AfcOperationFileSetSize = 0x00000015 /* FileRefSetFileSize (ftruncate) */
|
||||
AfcOperationGetConnectionInfo = 0x00000016 /* GetConnectionInfo */
|
||||
AfcOperationSetConnectionOptions = 0x00000017 /* SetConnectionOptions */
|
||||
AfcOperationRenamePath = 0x00000018 /* RenamePath */
|
||||
AfcOperationSetFSBlockSize = 0x00000019 /* SetFSBlockSize (0x800000) */
|
||||
AfcOperationSetSocketBlockSize = 0x0000001A /* SetSocketBlockSize (0x800000) */
|
||||
AfcOperationFileRefLock = 0x0000001B /* FileRefLock */
|
||||
AfcOperationMakeLink = 0x0000001C /* MakeLink */
|
||||
AfcOperationGetFileHash = 0x0000001D /* GetFileHash */
|
||||
AfcOperationSetFileModTime = 0x0000001E /* SetModTime */
|
||||
AfcOperationGetFileHashRange = 0x0000001F /* GetFileHashWithRange */
|
||||
/* iOS 6+ */
|
||||
AfcOperationFileSetImmutableHint = 0x00000020 /* FileRefSetImmutableHint */
|
||||
AfcOperationGetSizeOfPathContents = 0x00000021 /* GetSizeOfPathContents */
|
||||
AfcOperationRemovePathAndContents = 0x00000022 /* RemovePathAndContents */
|
||||
AfcOperationDirectoryEnumeratorRefOpen = 0x00000023 /* DirectoryEnumeratorRefOpen */
|
||||
AfcOperationDirectoryEnumeratorRefOpenResult = 0x00000024 /* DirectoryEnumeratorRefOpenResult */
|
||||
AfcOperationDirectoryEnumeratorRefRead = 0x00000025 /* DirectoryEnumeratorRefRead */
|
||||
AfcOperationDirectoryEnumeratorRefClose = 0x00000026 /* DirectoryEnumeratorRefClose */
|
||||
/* iOS 7+ */
|
||||
AfcOperationFileRefReadWithOffset = 0x00000027 /* FileRefReadWithOffset */
|
||||
AfcOperationFileRefWriteWithOffset = 0x00000028 /* FileRefWriteWithOffset */
|
||||
)
|
||||
|
||||
const (
|
||||
AfcErrSuccess = 0
|
||||
AfcErrUnknownError = 1
|
||||
AfcErrOperationHeaderInvalid = 2
|
||||
AfcErrNoResources = 3
|
||||
AfcErrReadError = 4
|
||||
AfcErrWriteError = 5
|
||||
AfcErrUnknownPacketType = 6
|
||||
AfcErrInvalidArgument = 7
|
||||
AfcErrObjectNotFound = 8
|
||||
AfcErrObjectIsDir = 9
|
||||
AfcErrPermDenied = 10
|
||||
AfcErrServiceNotConnected = 11
|
||||
AfcErrOperationTimeout = 12
|
||||
AfcErrTooMuchData = 13
|
||||
AfcErrEndOfData = 14
|
||||
AfcErrOperationNotSupported = 15
|
||||
AfcErrObjectExists = 16
|
||||
AfcErrObjectBusy = 17
|
||||
AfcErrNoSpaceLeft = 18
|
||||
AfcErrOperationWouldBlock = 19
|
||||
AfcErrIoError = 20
|
||||
AfcErrOperationInterrupted = 21
|
||||
AfcErrOperationInProgress = 22
|
||||
AfcErrInternalError = 23
|
||||
AfcErrMuxError = 30
|
||||
AfcErrNoMemory = 31
|
||||
AfcErrNotEnoughData = 32
|
||||
AfcErrDirNotEmpty = 33
|
||||
)
|
||||
138
hrp/pkg/gidevice/pkg/libimobiledevice/auxbuffer.go
Normal file
138
hrp/pkg/gidevice/pkg/libimobiledevice/auxbuffer.go
Normal file
@@ -0,0 +1,138 @@
|
||||
package libimobiledevice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/nskeyedarchiver"
|
||||
)
|
||||
|
||||
type AuxBuffer struct {
|
||||
buf *bytes.Buffer
|
||||
}
|
||||
|
||||
func NewAuxBuffer() *AuxBuffer {
|
||||
return &AuxBuffer{
|
||||
buf: new(bytes.Buffer),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *AuxBuffer) AppendObject(obj interface{}) error {
|
||||
marshal, err := nskeyedarchiver.Marshal(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.AppendUInt32(10)
|
||||
m.AppendUInt32(2)
|
||||
m.AppendUInt32(uint32(len(marshal)))
|
||||
m.buf.Write(marshal)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *AuxBuffer) AppendInt64(v int64) {
|
||||
m.AppendUInt32(10)
|
||||
m.AppendUInt32(4)
|
||||
m.AppendUInt64(uint64(v))
|
||||
}
|
||||
|
||||
func (m *AuxBuffer) AppendInt32(v int32) {
|
||||
m.AppendUInt32(10)
|
||||
m.AppendUInt32(3)
|
||||
m.AppendUInt32(uint32(v))
|
||||
}
|
||||
|
||||
func (m *AuxBuffer) AppendUInt32(v uint32) {
|
||||
_ = binary.Write(m.buf, binary.LittleEndian, v)
|
||||
}
|
||||
|
||||
func (m *AuxBuffer) AppendUInt64(v uint64) {
|
||||
_ = binary.Write(m.buf, binary.LittleEndian, v)
|
||||
}
|
||||
|
||||
func (m *AuxBuffer) AppendBytes(b []byte) {
|
||||
m.buf.Write(b)
|
||||
}
|
||||
|
||||
func (m *AuxBuffer) Len() int {
|
||||
return m.buf.Len()
|
||||
}
|
||||
|
||||
func (m *AuxBuffer) Bytes() []byte {
|
||||
dup := m.buf.Bytes()
|
||||
b := make([]byte, 16)
|
||||
binary.LittleEndian.PutUint64(b, 0x01f0)
|
||||
binary.LittleEndian.PutUint64(b[8:], uint64(m.Len()))
|
||||
return append(b, dup...)
|
||||
}
|
||||
|
||||
func UnmarshalAuxBuffer(b []byte) ([]interface{}, error) {
|
||||
reader := bytes.NewReader(b)
|
||||
var magic, pkgLen uint64
|
||||
if err := binary.Read(reader, binary.LittleEndian, &magic); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := binary.Read(reader, binary.LittleEndian, &pkgLen); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// if magic != 0x1df0 {
|
||||
// TODO magic
|
||||
// return nil, errors.New("magic not equal 0x1df0")
|
||||
// }
|
||||
|
||||
if pkgLen > uint64(len(b)-16) {
|
||||
return nil, errors.New("package length not enough")
|
||||
}
|
||||
|
||||
var ret []interface{}
|
||||
|
||||
for reader.Len() > 0 {
|
||||
var flag, typ uint32
|
||||
if err := binary.Read(reader, binary.LittleEndian, &flag); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := binary.Read(reader, binary.LittleEndian, &typ); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch typ {
|
||||
case 2:
|
||||
var l uint32
|
||||
if err := binary.Read(reader, binary.LittleEndian, &l); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
plistBuf := make([]byte, l)
|
||||
if _, err := reader.Read(plistBuf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
archiver := NewNSKeyedArchiver()
|
||||
d, err := archiver.Unmarshal(plistBuf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret = append(ret, d)
|
||||
case 3, 5:
|
||||
var i int32
|
||||
if err := binary.Read(reader, binary.LittleEndian, &i); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret = append(ret, i)
|
||||
case 4, 6:
|
||||
var i int64
|
||||
if err := binary.Read(reader, binary.LittleEndian, &i); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret = append(ret, i)
|
||||
case 10:
|
||||
// TODO Dictionary key
|
||||
// fmt.Println("Dictionary key!")
|
||||
continue
|
||||
default:
|
||||
// fmt.Printf("unknown type %d\n", typ)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
405
hrp/pkg/gidevice/pkg/libimobiledevice/client_dtxmessage.go
Normal file
405
hrp/pkg/gidevice/pkg/libimobiledevice/client_dtxmessage.go
Normal file
@@ -0,0 +1,405 @@
|
||||
package libimobiledevice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/nskeyedarchiver"
|
||||
)
|
||||
|
||||
const (
|
||||
_unregistered = "_Golang-iDevice_Unregistered"
|
||||
_over = "_Golang-iDevice_Over"
|
||||
)
|
||||
|
||||
func newDtxMessageClient(innerConn InnerConn) *dtxMessageClient {
|
||||
c := &dtxMessageClient{
|
||||
innerConn: innerConn,
|
||||
msgID: 0,
|
||||
publishedChannels: make(map[string]int32),
|
||||
openedChannels: make(map[string]uint32),
|
||||
toReply: make(chan *dtxMessageHeaderPacket),
|
||||
|
||||
mu: sync.Mutex{},
|
||||
resultMap: make(map[interface{}]*DTXMessageResult),
|
||||
|
||||
callbackMap: make(map[string]func(m DTXMessageResult)),
|
||||
}
|
||||
c.RegisterCallback(_unregistered, func(m DTXMessageResult) {})
|
||||
c.RegisterCallback(_over, func(m DTXMessageResult) {})
|
||||
c.ctx, c.cancelFunc = context.WithCancel(context.Background())
|
||||
c.startReceive()
|
||||
c.startWaitingForReply()
|
||||
return c
|
||||
}
|
||||
|
||||
type dtxMessageClient struct {
|
||||
innerConn InnerConn
|
||||
msgID uint32
|
||||
|
||||
publishedChannels map[string]int32
|
||||
openedChannels map[string]uint32
|
||||
|
||||
toReply chan *dtxMessageHeaderPacket
|
||||
|
||||
mu sync.Mutex
|
||||
resultMap map[interface{}]*DTXMessageResult
|
||||
|
||||
callbackMap map[string]func(m DTXMessageResult)
|
||||
|
||||
ctx context.Context
|
||||
cancelFunc context.CancelFunc
|
||||
}
|
||||
|
||||
func (c *dtxMessageClient) SendDTXMessage(selector string, aux []byte, channelCode uint32, expectsReply bool) (msgID uint32, err error) {
|
||||
payload := new(dtxMessagePayloadPacket)
|
||||
header := &dtxMessageHeaderPacket{
|
||||
ExpectsReply: 1,
|
||||
}
|
||||
|
||||
flag := 0x1000
|
||||
if !expectsReply {
|
||||
flag = 0
|
||||
header.ExpectsReply = 0
|
||||
}
|
||||
|
||||
var sel []byte
|
||||
if sel, err = nskeyedarchiver.Marshal(selector); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if aux == nil {
|
||||
aux = make([]byte, 0)
|
||||
}
|
||||
|
||||
payload.Flags = uint32(0x2 | flag)
|
||||
payload.AuxiliaryLength = uint32(len(aux))
|
||||
payload.TotalLength = uint64(len(aux)) + uint64(len(sel))
|
||||
|
||||
header.Magic = 0x1F3D5B79
|
||||
header.CB = uint32(unsafe.Sizeof(*header))
|
||||
header.FragmentId = 0
|
||||
header.FragmentCount = 1
|
||||
header.Length = uint32(unsafe.Sizeof(*payload)) + uint32(payload.TotalLength)
|
||||
c.msgID++
|
||||
header.Identifier = c.msgID
|
||||
header.ConversationIndex = 0
|
||||
header.ChannelCode = channelCode
|
||||
|
||||
msgPkt := new(dtxMessagePacket)
|
||||
msgPkt.Header = header
|
||||
msgPkt.Payload = payload
|
||||
msgPkt.Aux = aux
|
||||
msgPkt.Sel = sel
|
||||
|
||||
raw, err := msgPkt.Pack()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
debugLog(fmt.Sprintf("--> %s\n", msgPkt))
|
||||
msgID = header.Identifier
|
||||
err = c.innerConn.Write(raw)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *dtxMessageClient) ReceiveDTXMessage() (result *DTXMessageResult, err error) {
|
||||
bufPayload := new(bytes.Buffer)
|
||||
|
||||
var header *dtxMessageHeaderPacket = nil
|
||||
var needToReply *dtxMessageHeaderPacket = nil
|
||||
|
||||
for {
|
||||
header = new(dtxMessageHeaderPacket)
|
||||
|
||||
lenHeader := int(unsafe.Sizeof(*header))
|
||||
var bufHeader []byte
|
||||
if bufHeader, err = c.innerConn.Read(lenHeader); err != nil {
|
||||
return nil, fmt.Errorf("receive: length of DTXMessageHeader: %w", err)
|
||||
}
|
||||
|
||||
if header, err = header.unpack(bytes.NewBuffer(bufHeader)); err != nil {
|
||||
return nil, fmt.Errorf("receive: DTXMessageHeader unpack: %w", err)
|
||||
}
|
||||
|
||||
if header.ExpectsReply == 1 {
|
||||
needToReply = header
|
||||
}
|
||||
|
||||
if header.Magic != 0x1F3D5B79 {
|
||||
return nil, fmt.Errorf("receive: bad magic %x", header.Magic)
|
||||
}
|
||||
|
||||
if header.ConversationIndex == 1 {
|
||||
if header.Identifier != c.msgID {
|
||||
return nil, fmt.Errorf("receive: except identifier %d new identifier %d", c.msgID, header.Identifier)
|
||||
}
|
||||
} else if header.ConversationIndex == 0 {
|
||||
if header.Identifier > c.msgID {
|
||||
c.msgID = header.Identifier
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("receive: invalid conversationIndex %d", header.ConversationIndex)
|
||||
}
|
||||
|
||||
if header.FragmentId == 0 && header.FragmentCount > 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
var data []byte
|
||||
if data, err = c.innerConn.Read(int(header.Length)); err != nil {
|
||||
return nil, fmt.Errorf("receive: length of DTXMessageHeader: %w", err)
|
||||
}
|
||||
bufPayload.Write(data)
|
||||
|
||||
if header.FragmentId == header.FragmentCount-1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
rawPayload := bufPayload.Bytes()
|
||||
payload := new(dtxMessagePayloadPacket)
|
||||
if payload, err = payload.unpack(bufPayload); err != nil {
|
||||
return nil, fmt.Errorf("receive: unpack DTXMessagePayload: %w", err)
|
||||
}
|
||||
|
||||
compress := (payload.Flags & 0xff000) >> 12
|
||||
if compress != 0 {
|
||||
return nil, fmt.Errorf("receive: message is compressed type %d", compress)
|
||||
}
|
||||
|
||||
payloadSize := uint32(unsafe.Sizeof(*payload))
|
||||
objOffset := uint64(payloadSize + payload.AuxiliaryLength)
|
||||
|
||||
var aux, obj []byte
|
||||
|
||||
// see https://github.com/electricbubble/gidevice/issues/28
|
||||
if r, l := payloadSize+payload.AuxiliaryLength, len(rawPayload); int(r) <= l {
|
||||
aux = rawPayload[payloadSize:r]
|
||||
} else {
|
||||
debugLog(fmt.Sprintf("<-- DTXMessage %s\n%s\n"+
|
||||
"[aux] bounds out of range [:%d] with capacity %d",
|
||||
header.String(), payload.String(),
|
||||
r, l,
|
||||
))
|
||||
}
|
||||
if r, l := objOffset+(payload.TotalLength-uint64(payload.AuxiliaryLength)), len(rawPayload); int(r) <= l {
|
||||
obj = rawPayload[objOffset:r]
|
||||
} else {
|
||||
debugLog(fmt.Sprintf("<-- DTXMessage %s\n%s\n"+
|
||||
"[obj] bounds out of range [:%d] with capacity %d",
|
||||
header.String(), payload.String(),
|
||||
r, l,
|
||||
))
|
||||
}
|
||||
|
||||
debugLog(fmt.Sprintf(
|
||||
"<-- DTXMessage %s\n%s\n"+
|
||||
"%s\n%s\n",
|
||||
header.String(), payload.String(),
|
||||
hex.Dump(aux), hex.Dump(obj),
|
||||
))
|
||||
|
||||
result = new(DTXMessageResult)
|
||||
|
||||
if len(aux) > 0 {
|
||||
if aux, err := UnmarshalAuxBuffer(aux); err != nil {
|
||||
return nil, fmt.Errorf("receive: unpack AUX: %w", err)
|
||||
} else {
|
||||
result.Aux = aux
|
||||
}
|
||||
}
|
||||
|
||||
if len(obj) > 0 {
|
||||
if obj, err := NewNSKeyedArchiver().Unmarshal(obj); err != nil {
|
||||
return nil, fmt.Errorf("receive: unpack NSKeyedArchiver: %w", err)
|
||||
} else {
|
||||
result.Obj = obj
|
||||
}
|
||||
}
|
||||
|
||||
sObj, ok := result.Obj.(string)
|
||||
if fn, do := c.callbackMap[sObj]; do {
|
||||
fn(*result)
|
||||
} else {
|
||||
c.callbackMap[_unregistered](*result)
|
||||
}
|
||||
|
||||
if needToReply != nil {
|
||||
go func() { c.toReply <- needToReply }()
|
||||
} else {
|
||||
var sk interface{} = header.Identifier
|
||||
|
||||
if ok && sObj == "_notifyOfPublishedCapabilities:" {
|
||||
sk = "_notifyOfPublishedCapabilities:"
|
||||
}
|
||||
c.mu.Lock()
|
||||
c.resultMap[sk] = result
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *dtxMessageClient) Connection() (publishedChannels map[string]int32, err error) {
|
||||
args := NewAuxBuffer()
|
||||
if err = args.AppendObject(map[string]interface{}{
|
||||
"com.apple.private.DTXBlockCompression": uint64(2),
|
||||
"com.apple.private.DTXConnection": uint64(1),
|
||||
}); err != nil {
|
||||
return nil, fmt.Errorf("connection DTXMessage: %w", err)
|
||||
}
|
||||
|
||||
selector := "_notifyOfPublishedCapabilities:"
|
||||
if _, err = c.SendDTXMessage(selector, args.Bytes(), 0, false); err != nil {
|
||||
return nil, fmt.Errorf("connection send: %w", err)
|
||||
}
|
||||
|
||||
var result *DTXMessageResult
|
||||
if result, err = c.GetResult(selector); err != nil {
|
||||
return nil, fmt.Errorf("connection receive: %w", err)
|
||||
}
|
||||
|
||||
if result.Obj.(string) != "_notifyOfPublishedCapabilities:" {
|
||||
return nil, fmt.Errorf("connection: response mismatch: %s", result.Obj)
|
||||
}
|
||||
|
||||
aux := result.Aux[0].(map[string]interface{})
|
||||
for k, v := range aux {
|
||||
c.publishedChannels[k] = int32(v.(uint64))
|
||||
}
|
||||
|
||||
return c.publishedChannels, nil
|
||||
}
|
||||
|
||||
func (c *dtxMessageClient) MakeChannel(channel string) (id uint32, err error) {
|
||||
var ok bool
|
||||
if id, ok = c.openedChannels[channel]; ok {
|
||||
return id, nil
|
||||
}
|
||||
|
||||
id = uint32(len(c.openedChannels) + 1)
|
||||
args := NewAuxBuffer()
|
||||
args.AppendInt32(int32(id))
|
||||
if err = args.AppendObject(channel); err != nil {
|
||||
return 0, fmt.Errorf("make channel DTXMessage: %w", err)
|
||||
}
|
||||
|
||||
selector := "_requestChannelWithCode:identifier:"
|
||||
|
||||
var msgID uint32
|
||||
if msgID, err = c.SendDTXMessage(selector, args.Bytes(), 0, true); err != nil {
|
||||
return 0, fmt.Errorf("make channel send: %w", err)
|
||||
}
|
||||
|
||||
if _, err = c.GetResult(msgID); err != nil {
|
||||
return 0, fmt.Errorf("make channel receive: %w", err)
|
||||
}
|
||||
|
||||
c.openedChannels[channel] = id
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *dtxMessageClient) RegisterCallback(obj string, cb func(m DTXMessageResult)) {
|
||||
c.callbackMap[obj] = cb
|
||||
}
|
||||
|
||||
func (c *dtxMessageClient) GetResult(key interface{}) (*DTXMessageResult, error) {
|
||||
startTime := time.Now()
|
||||
for {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
c.mu.Lock()
|
||||
if v, ok := c.resultMap[key]; ok {
|
||||
delete(c.resultMap, key)
|
||||
c.mu.Unlock()
|
||||
return v, nil
|
||||
} else {
|
||||
c.mu.Unlock()
|
||||
}
|
||||
if elapsed := time.Since(startTime); elapsed > 30*time.Second {
|
||||
return nil, fmt.Errorf("dtx: get result: timeout after %v", elapsed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *dtxMessageClient) Close() {
|
||||
c.cancelFunc()
|
||||
c.innerConn.Close()
|
||||
}
|
||||
|
||||
func (c *dtxMessageClient) startReceive() {
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-c.ctx.Done():
|
||||
return
|
||||
default:
|
||||
if _, err := c.ReceiveDTXMessage(); err != nil {
|
||||
debugLog(fmt.Sprintf("dtx: receive: %s", err))
|
||||
if strings.Contains(err.Error(), io.EOF.Error()) {
|
||||
c.cancelFunc()
|
||||
c.callbackMap[_over](DTXMessageResult{})
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (c *dtxMessageClient) startWaitingForReply() {
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-c.ctx.Done():
|
||||
return
|
||||
case reqHeader := <-c.toReply:
|
||||
replyPayload := new(dtxMessagePayloadPacket)
|
||||
replyPayload.Flags = 0
|
||||
replyPayload.AuxiliaryLength = 0
|
||||
replyPayload.TotalLength = 0
|
||||
|
||||
replyHeader := new(dtxMessageHeaderPacket)
|
||||
replyHeader.Magic = 0x1F3D5B79
|
||||
replyHeader.CB = uint32(unsafe.Sizeof(*replyHeader))
|
||||
replyHeader.FragmentId = 0
|
||||
replyHeader.FragmentCount = 1
|
||||
replyHeader.Length = uint32(unsafe.Sizeof(*replyPayload)) + uint32(replyPayload.TotalLength)
|
||||
replyHeader.Identifier = reqHeader.Identifier
|
||||
replyHeader.ConversationIndex = reqHeader.ConversationIndex + 1
|
||||
replyHeader.ChannelCode = reqHeader.ChannelCode
|
||||
replyHeader.ExpectsReply = 0
|
||||
|
||||
replyPkt := new(dtxMessagePacket)
|
||||
replyPkt.Header = replyHeader
|
||||
replyPkt.Payload = replyPayload
|
||||
replyPkt.Aux = nil
|
||||
replyPkt.Sel = nil
|
||||
|
||||
raw, err := replyPkt.Pack()
|
||||
if err != nil {
|
||||
debugLog(fmt.Sprintf("pack: reply DTXMessage: %s", err))
|
||||
continue
|
||||
}
|
||||
|
||||
if err = c.innerConn.Write(raw); err != nil {
|
||||
debugLog(fmt.Sprintf("send: reply DTXMessage: %s", err))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
type DTXMessageResult struct {
|
||||
Obj interface{}
|
||||
Aux []interface{}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package libimobiledevice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"howett.net/plist"
|
||||
)
|
||||
|
||||
func newServicePacketClient(innerConn InnerConn) *servicePacketClient {
|
||||
return &servicePacketClient{
|
||||
innerConn: innerConn,
|
||||
}
|
||||
}
|
||||
|
||||
type servicePacketClient struct {
|
||||
innerConn InnerConn
|
||||
}
|
||||
|
||||
func (c *servicePacketClient) NewXmlPacket(req interface{}) (Packet, error) {
|
||||
return c.newPacket(req, plist.XMLFormat)
|
||||
}
|
||||
|
||||
func (c *servicePacketClient) NewBinaryPacket(req interface{}) (Packet, error) {
|
||||
return c.newPacket(req, plist.BinaryFormat)
|
||||
}
|
||||
|
||||
func (c *servicePacketClient) newPacket(req interface{}, format int) (Packet, error) {
|
||||
pkt := new(servicePacket)
|
||||
if buf, err := plist.Marshal(req, format); err != nil {
|
||||
return nil, fmt.Errorf("plist packet marshal: %w", err)
|
||||
} else {
|
||||
pkt.body = buf
|
||||
}
|
||||
pkt.length = uint32(len(pkt.body))
|
||||
return pkt, nil
|
||||
}
|
||||
|
||||
func (c *servicePacketClient) SendPacket(pkt Packet) (err error) {
|
||||
var raw []byte
|
||||
if raw, err = pkt.Pack(); err != nil {
|
||||
return fmt.Errorf("send packet: %w", err)
|
||||
}
|
||||
debugLog(fmt.Sprintf("--> %s\n", pkt))
|
||||
return c.innerConn.Write(raw)
|
||||
}
|
||||
|
||||
func (c *servicePacketClient) ReceivePacket() (respPkt Packet, err error) {
|
||||
var bufLen []byte
|
||||
if bufLen, err = c.innerConn.Read(4); err != nil {
|
||||
return nil, fmt.Errorf("receive packet: %w", err)
|
||||
}
|
||||
lenPkg := binary.BigEndian.Uint32(bufLen)
|
||||
|
||||
buffer := bytes.NewBuffer([]byte{})
|
||||
buffer.Write(bufLen)
|
||||
|
||||
var buf []byte
|
||||
if buf, err = c.innerConn.Read(int(lenPkg)); err != nil {
|
||||
return nil, fmt.Errorf("receive packet: %w", err)
|
||||
}
|
||||
buffer.Write(buf)
|
||||
|
||||
if respPkt, err = new(servicePacket).Unpack(buffer); err != nil {
|
||||
return nil, fmt.Errorf("receive packet: %w", err)
|
||||
}
|
||||
|
||||
debugLog(fmt.Sprintf("<-- %s\n", respPkt))
|
||||
|
||||
var reply LockdownBasicResponse
|
||||
if err = respPkt.Unmarshal(&reply); err != nil {
|
||||
return nil, fmt.Errorf("receive packet: %w", err)
|
||||
}
|
||||
|
||||
if reply.Error != "" {
|
||||
return nil, fmt.Errorf("receive packet: %s", reply.Error)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
20
hrp/pkg/gidevice/pkg/libimobiledevice/crashreportmover.go
Normal file
20
hrp/pkg/gidevice/pkg/libimobiledevice/crashreportmover.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package libimobiledevice
|
||||
|
||||
const (
|
||||
CrashReportMoverServiceName = "com.apple.crashreportmover"
|
||||
CrashReportCopyMobileServiceName = "com.apple.crashreportcopymobile"
|
||||
)
|
||||
|
||||
func NewCrashReportMoverClient(innerConn InnerConn) *CrashReportMoverClient {
|
||||
return &CrashReportMoverClient{
|
||||
newServicePacketClient(innerConn),
|
||||
}
|
||||
}
|
||||
|
||||
type CrashReportMoverClient struct {
|
||||
client *servicePacketClient
|
||||
}
|
||||
|
||||
func (c *CrashReportMoverClient) InnerConn() InnerConn {
|
||||
return c.client.innerConn
|
||||
}
|
||||
39
hrp/pkg/gidevice/pkg/libimobiledevice/diagnosticsrelay.go
Normal file
39
hrp/pkg/gidevice/pkg/libimobiledevice/diagnosticsrelay.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package libimobiledevice
|
||||
|
||||
const (
|
||||
DiagnosticsRelayServiceName = "com.apple.mobile.diagnostics_relay"
|
||||
)
|
||||
|
||||
type DiagnosticsRelayBasicRequest struct {
|
||||
Request string `plist:"Request"`
|
||||
Label string `plist:"Label"`
|
||||
}
|
||||
|
||||
func NewDiagnosticsRelayClient(innerConn InnerConn) *DiagnosticsRelayClient {
|
||||
return &DiagnosticsRelayClient{
|
||||
newServicePacketClient(innerConn),
|
||||
}
|
||||
}
|
||||
|
||||
type DiagnosticsRelayClient struct {
|
||||
client *servicePacketClient
|
||||
}
|
||||
|
||||
func (c *DiagnosticsRelayClient) InnerConn() InnerConn {
|
||||
return c.client.innerConn
|
||||
}
|
||||
|
||||
func (c *DiagnosticsRelayClient) NewBasicRequest(relayType string) *DiagnosticsRelayBasicRequest {
|
||||
return &DiagnosticsRelayBasicRequest{
|
||||
Request: relayType,
|
||||
Label: BundleID,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *DiagnosticsRelayClient) NewXmlPacket(req interface{}) (Packet, error) {
|
||||
return c.client.NewXmlPacket(req)
|
||||
}
|
||||
|
||||
func (c *DiagnosticsRelayClient) SendPacket(pkt Packet) (err error) {
|
||||
return c.client.SendPacket(pkt)
|
||||
}
|
||||
56
hrp/pkg/gidevice/pkg/libimobiledevice/housearrest.go
Normal file
56
hrp/pkg/gidevice/pkg/libimobiledevice/housearrest.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package libimobiledevice
|
||||
|
||||
const HouseArrestServiceName = "com.apple.mobile.house_arrest"
|
||||
|
||||
const (
|
||||
CommandTypeVendDocuments CommandType = "VendDocuments"
|
||||
CommandTypeVendContainer CommandType = "VendContainer"
|
||||
)
|
||||
|
||||
func NewHouseArrestClient(innerConn InnerConn) *HouseArrestClient {
|
||||
return &HouseArrestClient{
|
||||
newServicePacketClient(innerConn),
|
||||
}
|
||||
}
|
||||
|
||||
type HouseArrestClient struct {
|
||||
client *servicePacketClient
|
||||
}
|
||||
|
||||
func (c *HouseArrestClient) NewBasicRequest(cmdType CommandType, bundleID string) *HouseArrestBasicRequest {
|
||||
return &HouseArrestBasicRequest{
|
||||
Command: cmdType,
|
||||
Identifier: bundleID,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *HouseArrestClient) NewDocumentsRequest(bundleID string) *HouseArrestBasicRequest {
|
||||
return c.NewBasicRequest(CommandTypeVendDocuments, bundleID)
|
||||
}
|
||||
|
||||
func (c *HouseArrestClient) NewContainerRequest(bundleID string) *HouseArrestBasicRequest {
|
||||
return c.NewBasicRequest(CommandTypeVendContainer, bundleID)
|
||||
}
|
||||
|
||||
func (c *HouseArrestClient) NewXmlPacket(req interface{}) (Packet, error) {
|
||||
return c.client.NewXmlPacket(req)
|
||||
}
|
||||
|
||||
func (c *HouseArrestClient) SendPacket(pkt Packet) (err error) {
|
||||
return c.client.SendPacket(pkt)
|
||||
}
|
||||
|
||||
func (c *HouseArrestClient) ReceivePacket() (respPkt Packet, err error) {
|
||||
return c.client.ReceivePacket()
|
||||
}
|
||||
|
||||
func (c *HouseArrestClient) InnerConn() InnerConn {
|
||||
return c.client.innerConn
|
||||
}
|
||||
|
||||
type (
|
||||
HouseArrestBasicRequest struct {
|
||||
Command CommandType `plist:"Command"`
|
||||
Identifier string `plist:"Identifier"`
|
||||
}
|
||||
)
|
||||
107
hrp/pkg/gidevice/pkg/libimobiledevice/imagemounter.go
Normal file
107
hrp/pkg/gidevice/pkg/libimobiledevice/imagemounter.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package libimobiledevice
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const ImageMounterServiceName = "com.apple.mobile.mobile_image_mounter"
|
||||
|
||||
var ErrDeviceLocked = errors.New("device locked")
|
||||
|
||||
type CommandType string
|
||||
|
||||
const (
|
||||
CommandTypeLookupImage CommandType = "LookupImage"
|
||||
CommandTypeReceiveBytes CommandType = "ReceiveBytes"
|
||||
CommandTypeMountImage CommandType = "MountImage"
|
||||
)
|
||||
|
||||
func NewImageMounterClient(innerConn InnerConn) *ImageMounterClient {
|
||||
return &ImageMounterClient{
|
||||
client: newServicePacketClient(innerConn),
|
||||
}
|
||||
}
|
||||
|
||||
type ImageMounterClient struct {
|
||||
client *servicePacketClient
|
||||
}
|
||||
|
||||
func (c *ImageMounterClient) NewBasicRequest(cmdType CommandType, imgType string) *ImageMounterBasicRequest {
|
||||
return &ImageMounterBasicRequest{
|
||||
Command: cmdType,
|
||||
ImageType: imgType,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ImageMounterClient) NewReceiveBytesRequest(imgType string, imgSize uint32, imgSignature []byte) *ImageMounterReceiveBytesRequest {
|
||||
return &ImageMounterReceiveBytesRequest{
|
||||
ImageMounterBasicRequest: *c.NewBasicRequest(CommandTypeReceiveBytes, imgType),
|
||||
ImageSize: imgSize,
|
||||
ImageSignature: imgSignature,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ImageMounterClient) NewMountImageRequest(imgType, imgPath string, imgSignature []byte) *ImageMounterMountImageRequest {
|
||||
return &ImageMounterMountImageRequest{
|
||||
ImageMounterBasicRequest: *c.NewBasicRequest(CommandTypeMountImage, imgType),
|
||||
ImagePath: imgPath,
|
||||
ImageSignature: imgSignature,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ImageMounterClient) NewXmlPacket(req interface{}) (Packet, error) {
|
||||
return c.client.NewXmlPacket(req)
|
||||
}
|
||||
|
||||
func (c *ImageMounterClient) SendPacket(pkt Packet) (err error) {
|
||||
return c.client.SendPacket(pkt)
|
||||
}
|
||||
|
||||
func (c *ImageMounterClient) ReceivePacket() (respPkt Packet, err error) {
|
||||
respPkt, err = c.client.ReceivePacket()
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), io.EOF.Error()) {
|
||||
return nil, ErrDeviceLocked
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *ImageMounterClient) SendDmg(data []byte) (err error) {
|
||||
debugLog(fmt.Sprintf("--> ...DmgData...\n"))
|
||||
return c.client.innerConn.Write(data)
|
||||
}
|
||||
|
||||
type (
|
||||
ImageMounterBasicRequest struct {
|
||||
Command CommandType `plist:"Command"`
|
||||
ImageType string `plist:"ImageType"`
|
||||
}
|
||||
|
||||
ImageMounterReceiveBytesRequest struct {
|
||||
ImageMounterBasicRequest
|
||||
ImageSignature []byte `plist:"ImageSignature"`
|
||||
ImageSize uint32 `plist:"ImageSize"`
|
||||
}
|
||||
|
||||
ImageMounterMountImageRequest struct {
|
||||
ImageMounterBasicRequest
|
||||
ImagePath string `plist:"ImagePath"`
|
||||
ImageSignature []byte `plist:"ImageSignature"`
|
||||
}
|
||||
)
|
||||
|
||||
type (
|
||||
ImageMounterBasicResponse struct {
|
||||
LockdownBasicResponse
|
||||
Status string `plist:"Status"`
|
||||
}
|
||||
|
||||
ImageMounterLookupImageResponse struct {
|
||||
ImageMounterBasicResponse
|
||||
ImageSignature [][]byte `plist:"ImageSignature"`
|
||||
}
|
||||
)
|
||||
119
hrp/pkg/gidevice/pkg/libimobiledevice/installationproxy.go
Normal file
119
hrp/pkg/gidevice/pkg/libimobiledevice/installationproxy.go
Normal file
@@ -0,0 +1,119 @@
|
||||
package libimobiledevice
|
||||
|
||||
const InstallationProxyServiceName = "com.apple.mobile.installation_proxy"
|
||||
|
||||
const (
|
||||
CommandTypeBrowse CommandType = "Browse"
|
||||
CommandTypeLookup CommandType = "Lookup"
|
||||
CommandTypeInstall CommandType = "Install"
|
||||
CommandTypeUninstall CommandType = "Uninstall"
|
||||
)
|
||||
|
||||
type ApplicationType string
|
||||
|
||||
const (
|
||||
ApplicationTypeSystem ApplicationType = "System"
|
||||
ApplicationTypeUser ApplicationType = "User"
|
||||
ApplicationTypeInternal ApplicationType = "internal"
|
||||
ApplicationTypeAny ApplicationType = "Any"
|
||||
)
|
||||
|
||||
func NewInstallationProxyClient(innerConn InnerConn) *InstallationProxyClient {
|
||||
return &InstallationProxyClient{
|
||||
client: newServicePacketClient(innerConn),
|
||||
}
|
||||
}
|
||||
|
||||
type InstallationProxyClient struct {
|
||||
client *servicePacketClient
|
||||
}
|
||||
|
||||
func (c *InstallationProxyClient) NewBasicRequest(cmdType CommandType, opt *InstallationProxyOption) *InstallationProxyBasicRequest {
|
||||
req := &InstallationProxyBasicRequest{Command: cmdType}
|
||||
if opt != nil {
|
||||
req.ClientOptions = opt
|
||||
}
|
||||
return req
|
||||
}
|
||||
|
||||
func (c *InstallationProxyClient) NewInstallRequest(bundleID, packagePath string) *InstallationProxyInstallRequest {
|
||||
opt := &InstallationProxyOption{
|
||||
BundleID: bundleID,
|
||||
}
|
||||
req := &InstallationProxyInstallRequest{
|
||||
Command: CommandTypeInstall,
|
||||
ClientOptions: opt,
|
||||
PackagePath: packagePath,
|
||||
}
|
||||
return req
|
||||
}
|
||||
|
||||
func (c *InstallationProxyClient) NewUninstallRequest(bundleID string) *InstallationProxyUninstallRequest {
|
||||
req := &InstallationProxyUninstallRequest{
|
||||
Command: CommandTypeUninstall,
|
||||
BundleID: bundleID,
|
||||
}
|
||||
return req
|
||||
}
|
||||
|
||||
func (c *InstallationProxyClient) NewXmlPacket(req interface{}) (Packet, error) {
|
||||
return c.client.NewXmlPacket(req)
|
||||
}
|
||||
|
||||
func (c *InstallationProxyClient) SendPacket(pkt Packet) (err error) {
|
||||
return c.client.SendPacket(pkt)
|
||||
}
|
||||
|
||||
func (c *InstallationProxyClient) ReceivePacket() (respPkt Packet, err error) {
|
||||
return c.client.ReceivePacket()
|
||||
}
|
||||
|
||||
type InstallationProxyOption struct {
|
||||
ApplicationType ApplicationType `plist:"ApplicationType,omitempty"`
|
||||
ReturnAttributes []string `plist:"ReturnAttributes,omitempty"`
|
||||
MetaData bool `plist:"com.apple.mobile_installation.metadata,omitempty"`
|
||||
BundleIDs []string `plist:"BundleIDs,omitempty"` // for Lookup
|
||||
BundleID string `plist:"CFBundleIdentifier,omitempty"` // for Install
|
||||
}
|
||||
|
||||
type (
|
||||
InstallationProxyBasicRequest struct {
|
||||
Command CommandType `plist:"Command"`
|
||||
ClientOptions *InstallationProxyOption `plist:"ClientOptions,omitempty"`
|
||||
}
|
||||
|
||||
InstallationProxyInstallRequest struct {
|
||||
Command CommandType `plist:"Command"`
|
||||
ClientOptions *InstallationProxyOption `plist:"ClientOptions"`
|
||||
PackagePath string `plist:"PackagePath"`
|
||||
}
|
||||
|
||||
InstallationProxyUninstallRequest struct {
|
||||
Command CommandType `plist:"Command"`
|
||||
BundleID string `plist:"ApplicationIdentifier"`
|
||||
}
|
||||
)
|
||||
|
||||
type (
|
||||
InstallationProxyBasicResponse struct {
|
||||
Status string `plist:"Status"`
|
||||
}
|
||||
|
||||
InstallationProxyLookupResponse struct {
|
||||
InstallationProxyBasicResponse
|
||||
LookupResult interface{} `plist:"LookupResult"`
|
||||
}
|
||||
|
||||
InstallationProxyBrowseResponse struct {
|
||||
InstallationProxyBasicResponse
|
||||
CurrentAmount int `plist:"CurrentAmount"`
|
||||
CurrentIndex int `plist:"CurrentIndex"`
|
||||
CurrentList []interface{} `plist:"CurrentList"`
|
||||
}
|
||||
|
||||
InstallationProxyInstallResponse struct {
|
||||
InstallationProxyBasicResponse
|
||||
Error string `plist:"Error"`
|
||||
ErrorDescription string `plist:"ErrorDescription"`
|
||||
}
|
||||
)
|
||||
41
hrp/pkg/gidevice/pkg/libimobiledevice/instruments.go
Normal file
41
hrp/pkg/gidevice/pkg/libimobiledevice/instruments.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package libimobiledevice
|
||||
|
||||
const (
|
||||
InstrumentsServiceName = "com.apple.instruments.remoteserver"
|
||||
InstrumentsSecureProxyServiceName = "com.apple.instruments.remoteserver.DVTSecureSocketProxy"
|
||||
)
|
||||
|
||||
func NewInstrumentsClient(innerConn InnerConn) *InstrumentsClient {
|
||||
return &InstrumentsClient{
|
||||
client: newDtxMessageClient(innerConn),
|
||||
}
|
||||
}
|
||||
|
||||
type InstrumentsClient struct {
|
||||
client *dtxMessageClient
|
||||
}
|
||||
|
||||
func (c *InstrumentsClient) NotifyOfPublishedCapabilities() (publishedChannels map[string]int32, err error) {
|
||||
return c.client.Connection()
|
||||
}
|
||||
|
||||
func (c *InstrumentsClient) RequestChannel(channel string) (id uint32, err error) {
|
||||
return c.client.MakeChannel(channel)
|
||||
}
|
||||
|
||||
func (c *InstrumentsClient) Invoke(selector string, args *AuxBuffer, channelCode uint32, expectsReply bool) (result *DTXMessageResult, err error) {
|
||||
var msgID uint32
|
||||
if msgID, err = c.client.SendDTXMessage(selector, args.Bytes(), channelCode, expectsReply); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if expectsReply {
|
||||
if result, err = c.client.GetResult(msgID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *InstrumentsClient) RegisterCallback(obj string, cb func(m DTXMessageResult)) {
|
||||
c.client.RegisterCallback(obj, cb)
|
||||
}
|
||||
260
hrp/pkg/gidevice/pkg/libimobiledevice/keyedarchiver.go
Normal file
260
hrp/pkg/gidevice/pkg/libimobiledevice/keyedarchiver.go
Normal file
@@ -0,0 +1,260 @@
|
||||
package libimobiledevice
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"howett.net/plist"
|
||||
)
|
||||
|
||||
const nsNull = "$null"
|
||||
|
||||
func newKeyedArchiver() *KeyedArchiver {
|
||||
return &KeyedArchiver{
|
||||
Archiver: "NSKeyedArchiver",
|
||||
Version: 100000,
|
||||
}
|
||||
}
|
||||
|
||||
type KeyedArchiver struct {
|
||||
Archiver string `plist:"$archiver"`
|
||||
Objects []interface{} `plist:"$objects"`
|
||||
Top ArchiverRoot `plist:"$top"`
|
||||
Version int `plist:"$version"`
|
||||
}
|
||||
|
||||
func (ka *KeyedArchiver) UID() plist.UID {
|
||||
return plist.UID(len(ka.Objects))
|
||||
}
|
||||
|
||||
type ArchiverRoot struct {
|
||||
Root plist.UID `plist:"root"`
|
||||
}
|
||||
|
||||
type ArchiverClasses struct {
|
||||
Classes []string `plist:"$classes"`
|
||||
ClassName string `plist:"$classname"`
|
||||
}
|
||||
|
||||
var (
|
||||
NSMutableDictionaryClass = &ArchiverClasses{
|
||||
Classes: []string{"NSMutableDictionary", "NSDictionary", "NSObject"},
|
||||
ClassName: "NSMutableDictionary",
|
||||
}
|
||||
NSDictionaryClass = &ArchiverClasses{
|
||||
Classes: []string{"NSDictionary", "NSObject"},
|
||||
ClassName: "NSDictionary",
|
||||
}
|
||||
NSMutableArrayClass = &ArchiverClasses{
|
||||
Classes: []string{"NSMutableArray", "NSArray", "NSObject"},
|
||||
ClassName: "NSMutableArray",
|
||||
}
|
||||
NSArrayClass = &ArchiverClasses{
|
||||
Classes: []string{"NSArray", "NSObject"},
|
||||
ClassName: "NSArray",
|
||||
}
|
||||
NSMutableDataClass = &ArchiverClasses{
|
||||
Classes: []string{"NSMutableArray", "NSArray", "NSObject"},
|
||||
ClassName: "NSMutableArray",
|
||||
}
|
||||
NSDataClass = &ArchiverClasses{
|
||||
Classes: []string{"NSData", "NSObject"},
|
||||
ClassName: "NSData",
|
||||
}
|
||||
NSDateClass = &ArchiverClasses{
|
||||
Classes: []string{"NSDate", "NSObject"},
|
||||
ClassName: "NSDate",
|
||||
}
|
||||
NSErrorClass = &ArchiverClasses{
|
||||
Classes: []string{"NSError", "NSObject"},
|
||||
ClassName: "NSError",
|
||||
}
|
||||
)
|
||||
|
||||
type NSObject struct {
|
||||
Class plist.UID `plist:"$class"`
|
||||
}
|
||||
|
||||
type NSArray struct {
|
||||
NSObject
|
||||
Values []plist.UID `plist:"NS.objects"`
|
||||
}
|
||||
|
||||
type NSDictionary struct {
|
||||
NSArray
|
||||
Keys []plist.UID `plist:"NS.keys"`
|
||||
}
|
||||
|
||||
type NSData struct {
|
||||
NSObject
|
||||
Data []byte `plist:"NS.data"`
|
||||
}
|
||||
|
||||
type NSError struct {
|
||||
NSCode int
|
||||
NSDomain string
|
||||
NSUserInfo interface{}
|
||||
}
|
||||
|
||||
type NSKeyedArchiver struct {
|
||||
objRefVal []interface{}
|
||||
objRef map[interface{}]plist.UID
|
||||
}
|
||||
|
||||
func NewNSKeyedArchiver() *NSKeyedArchiver {
|
||||
return &NSKeyedArchiver{
|
||||
objRef: make(map[interface{}]plist.UID),
|
||||
}
|
||||
}
|
||||
|
||||
func (ka *NSKeyedArchiver) id(v interface{}) plist.UID {
|
||||
var ref plist.UID
|
||||
if id, ok := ka.objRef[v]; !ok {
|
||||
ref = plist.UID(len(ka.objRef))
|
||||
ka.objRefVal = append(ka.objRefVal, v)
|
||||
ka.objRef[v] = ref
|
||||
} else {
|
||||
ref = id
|
||||
}
|
||||
return ref
|
||||
}
|
||||
|
||||
func (ka *NSKeyedArchiver) flushToStruct(root *KeyedArchiver) {
|
||||
for i := 0; i < len(ka.objRefVal); i++ {
|
||||
val := ka.objRefVal[i]
|
||||
vt := reflect.ValueOf(val)
|
||||
if vt.Kind() == reflect.Ptr {
|
||||
val = vt.Elem().Interface()
|
||||
}
|
||||
root.Objects = append(root.Objects, val)
|
||||
}
|
||||
}
|
||||
|
||||
func (ka *NSKeyedArchiver) clear() {
|
||||
ka.objRef = make(map[interface{}]plist.UID)
|
||||
ka.objRefVal = []interface{}{}
|
||||
}
|
||||
|
||||
type XCTestConfiguration struct {
|
||||
Contents map[string]interface{}
|
||||
}
|
||||
|
||||
func (ka *NSKeyedArchiver) Marshal(obj interface{}) ([]byte, error) {
|
||||
val := reflect.ValueOf(obj)
|
||||
typ := val.Type()
|
||||
|
||||
root := newKeyedArchiver()
|
||||
|
||||
var tmpTop plist.UID
|
||||
|
||||
ka.id(nsNull)
|
||||
|
||||
switch typ.Kind() {
|
||||
case reflect.Map:
|
||||
m := &NSDictionary{}
|
||||
m.Class = ka.id(NSDictionaryClass)
|
||||
keys := val.MapKeys()
|
||||
for _, v := range keys {
|
||||
m.Keys = append(m.Keys, ka.id(v.Interface()))
|
||||
m.Values = append(m.Values, ka.id(val.MapIndex(v).Interface()))
|
||||
}
|
||||
tmpTop = ka.id(m)
|
||||
case reflect.Slice, reflect.Array:
|
||||
if typ.Elem().Kind() == reflect.Uint8 {
|
||||
d := &NSData{}
|
||||
d.Class = ka.id(NSDataClass)
|
||||
var w []byte
|
||||
for i := 0; i < val.Len(); i++ {
|
||||
w = append(w, uint8(val.Index(i).Uint()))
|
||||
}
|
||||
d.Data = w
|
||||
}
|
||||
a := &NSArray{}
|
||||
a.Class = ka.id(NSArrayClass)
|
||||
for i := 0; i < val.Len(); i++ {
|
||||
a.Values = append(a.Values, ka.id(val.Index(i).Interface()))
|
||||
}
|
||||
tmpTop = ka.id(a)
|
||||
case reflect.String:
|
||||
tmpTop = ka.id(obj)
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
tmpTop = ka.id(obj)
|
||||
}
|
||||
|
||||
root.Top.Root = tmpTop
|
||||
|
||||
ka.flushToStruct(root)
|
||||
|
||||
ka.clear()
|
||||
|
||||
return plist.Marshal(root, plist.BinaryFormat)
|
||||
}
|
||||
|
||||
func (ka *NSKeyedArchiver) convertValue(v interface{}) interface{} {
|
||||
if m, ok := v.(map[string]interface{}); ok {
|
||||
className := ka.objRefVal[m["$class"].(plist.UID)].(map[string]interface{})["$classname"]
|
||||
|
||||
switch className {
|
||||
case NSMutableDictionaryClass.Classes[0], NSDictionaryClass.Classes[0]:
|
||||
ret := make(map[string]interface{})
|
||||
keys := m["NS.keys"].([]interface{})
|
||||
values := m["NS.objects"].([]interface{})
|
||||
|
||||
for i := 0; i < len(keys); i++ {
|
||||
var keyValue string
|
||||
key := ka.objRefVal[keys[i].(plist.UID)]
|
||||
switch key.(type) {
|
||||
case uint64:
|
||||
keyValue = strconv.Itoa(int(key.(uint64)))
|
||||
break
|
||||
default:
|
||||
keyValue = key.(string)
|
||||
}
|
||||
val := ka.convertValue(ka.objRefVal[values[i].(plist.UID)])
|
||||
ret[keyValue] = val
|
||||
}
|
||||
return ret
|
||||
case NSMutableArrayClass.Classes[0], NSArrayClass.Classes[0]:
|
||||
ret := make([]interface{}, 0)
|
||||
values := m["NS.objects"].([]interface{})
|
||||
for i := 0; i < len(values); i++ {
|
||||
ret = append(ret, ka.convertValue(values[i]))
|
||||
}
|
||||
return ret
|
||||
case NSMutableDataClass.Classes[0], NSDataClass.Classes[0]:
|
||||
return m["NS.data"].([]byte)
|
||||
case NSDateClass.Classes[0]:
|
||||
return time.Date(2001, 1, 1, 0, 0, 0, 0, time.UTC).
|
||||
Add(time.Duration(m["NS.time"].(float64)) * time.Second)
|
||||
case NSErrorClass.Classes[0]:
|
||||
err := &NSError{}
|
||||
err.NSCode = int(m["NSCode"].(uint64))
|
||||
err.NSDomain = ka.objRefVal[m["NSDomain"].(plist.UID)].(string)
|
||||
err.NSUserInfo = ka.convertValue(ka.objRefVal[m["NSUserInfo"].(plist.UID)])
|
||||
return *err
|
||||
}
|
||||
} else if uid, ok := v.(plist.UID); ok {
|
||||
return ka.convertValue(ka.objRefVal[uid])
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (ka *NSKeyedArchiver) Unmarshal(b []byte) (interface{}, error) {
|
||||
archiver := new(KeyedArchiver)
|
||||
|
||||
if _, err := plist.Unmarshal(b, archiver); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, v := range archiver.Objects {
|
||||
ka.objRefVal = append(ka.objRefVal, v)
|
||||
}
|
||||
|
||||
ret := ka.convertValue(ka.objRefVal[archiver.Top.Root])
|
||||
|
||||
ka.clear()
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
28
hrp/pkg/gidevice/pkg/libimobiledevice/lib.go
Normal file
28
hrp/pkg/gidevice/pkg/libimobiledevice/lib.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package libimobiledevice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
)
|
||||
|
||||
type Packet interface {
|
||||
Pack() ([]byte, error)
|
||||
Unpack(buffer *bytes.Buffer) (Packet, error)
|
||||
Unmarshal(v interface{}) error
|
||||
|
||||
String() string
|
||||
}
|
||||
|
||||
var debugFlag = false
|
||||
|
||||
// SetDebug sets debug mode
|
||||
func SetDebug(debug bool) {
|
||||
debugFlag = debug
|
||||
}
|
||||
|
||||
func debugLog(msg string) {
|
||||
if !debugFlag {
|
||||
return
|
||||
}
|
||||
log.Printf("[%s-debug] %s\n", ProgramName, msg)
|
||||
}
|
||||
183
hrp/pkg/gidevice/pkg/libimobiledevice/lockdown.go
Normal file
183
hrp/pkg/gidevice/pkg/libimobiledevice/lockdown.go
Normal file
@@ -0,0 +1,183 @@
|
||||
package libimobiledevice
|
||||
|
||||
const ProtocolVersion = "2"
|
||||
|
||||
const LockdownPort = 62078
|
||||
|
||||
type RequestType string
|
||||
|
||||
const (
|
||||
RequestTypeQueryType RequestType = "QueryType"
|
||||
RequestTypeSetValue RequestType = "SetValue"
|
||||
RequestTypeGetValue RequestType = "GetValue"
|
||||
RequestTypePair RequestType = "Pair"
|
||||
RequestTypeEnterRecovery RequestType = "EnterRecovery"
|
||||
RequestTypeStartSession RequestType = "StartSession"
|
||||
RequestTypeStopSession RequestType = "StopSession"
|
||||
RequestTypeStartService RequestType = "StartService"
|
||||
)
|
||||
|
||||
type LockdownType struct {
|
||||
Type string `plist:"Type"`
|
||||
}
|
||||
|
||||
func NewLockdownClient(innerConn InnerConn) *LockdownClient {
|
||||
return &LockdownClient{
|
||||
client: newServicePacketClient(innerConn),
|
||||
}
|
||||
}
|
||||
|
||||
type LockdownClient struct {
|
||||
client *servicePacketClient
|
||||
}
|
||||
|
||||
func (c *LockdownClient) NewBasicRequest(reqType RequestType) *LockdownBasicRequest {
|
||||
return &LockdownBasicRequest{
|
||||
Label: BundleID,
|
||||
ProtocolVersion: ProtocolVersion,
|
||||
Request: reqType,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *LockdownClient) NewGetValueRequest(domain, key string) *LockdownValueRequest {
|
||||
return &LockdownValueRequest{
|
||||
LockdownBasicRequest: *c.NewBasicRequest(RequestTypeGetValue),
|
||||
Domain: domain,
|
||||
Key: key,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *LockdownClient) NewSetValueRequest(domain, key string, value interface{}) *LockdownValueRequest {
|
||||
return &LockdownValueRequest{
|
||||
LockdownBasicRequest: *c.NewBasicRequest(RequestTypeSetValue),
|
||||
Domain: domain,
|
||||
Key: key,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *LockdownClient) NewEnterRecoveryRequest() *LockdownBasicRequest {
|
||||
return c.NewBasicRequest(RequestTypeEnterRecovery)
|
||||
}
|
||||
|
||||
func (c *LockdownClient) NewPairRequest(pairRecord *PairRecord) *LockdownPairRequest {
|
||||
return &LockdownPairRequest{
|
||||
LockdownBasicRequest: *c.NewBasicRequest(RequestTypePair),
|
||||
PairRecord: pairRecord,
|
||||
PairingOptions: map[string]interface{}{
|
||||
"ExtendedPairingErrors": true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *LockdownClient) NewStartSessionRequest(buid, hostID string) *LockdownStartSessionRequest {
|
||||
return &LockdownStartSessionRequest{
|
||||
LockdownBasicRequest: *c.NewBasicRequest(RequestTypeStartSession),
|
||||
SystemBUID: buid,
|
||||
HostID: hostID,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *LockdownClient) NewStopSessionRequest(sessionID string) *LockdownStopSessionRequest {
|
||||
return &LockdownStopSessionRequest{
|
||||
LockdownBasicRequest: *c.NewBasicRequest(RequestTypeStopSession),
|
||||
SessionID: sessionID,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *LockdownClient) NewStartServiceRequest(service string) *LockdownStartServiceRequest {
|
||||
return &LockdownStartServiceRequest{
|
||||
LockdownBasicRequest: *c.NewBasicRequest(RequestTypeStartService),
|
||||
Service: service,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *LockdownClient) NewXmlPacket(req interface{}) (Packet, error) {
|
||||
return c.client.NewXmlPacket(req)
|
||||
}
|
||||
|
||||
func (c *LockdownClient) SendPacket(pkt Packet) (err error) {
|
||||
return c.client.SendPacket(pkt)
|
||||
}
|
||||
|
||||
func (c *LockdownClient) ReceivePacket() (respPkt Packet, err error) {
|
||||
return c.client.ReceivePacket()
|
||||
}
|
||||
|
||||
func (c *LockdownClient) EnableSSL(version []int, pairRecord *PairRecord) (err error) {
|
||||
return c.client.innerConn.Handshake(version, pairRecord)
|
||||
}
|
||||
|
||||
type (
|
||||
LockdownBasicRequest struct {
|
||||
Label string `plist:"Label"`
|
||||
ProtocolVersion string `plist:"ProtocolVersion"`
|
||||
Request RequestType `plist:"Request"`
|
||||
}
|
||||
|
||||
LockdownValueRequest struct {
|
||||
LockdownBasicRequest
|
||||
Domain string `plist:"Domain,omitempty"`
|
||||
Key string `plist:"Key,omitempty"`
|
||||
Value interface{} `plist:"Value,omitempty"`
|
||||
}
|
||||
|
||||
LockdownPairRequest struct {
|
||||
LockdownBasicRequest
|
||||
PairRecord *PairRecord `plist:"PairRecord"`
|
||||
PairingOptions map[string]interface{} `plist:"PairingOptions"`
|
||||
}
|
||||
|
||||
LockdownStartSessionRequest struct {
|
||||
LockdownBasicRequest
|
||||
SystemBUID string `plist:"SystemBUID"`
|
||||
HostID string `plist:"HostID"`
|
||||
}
|
||||
|
||||
LockdownStopSessionRequest struct {
|
||||
LockdownBasicRequest
|
||||
SessionID string `plist:"SessionID"`
|
||||
}
|
||||
|
||||
LockdownStartServiceRequest struct {
|
||||
LockdownBasicRequest
|
||||
Service string `plist:"Service"`
|
||||
EscrowBag []byte `plist:"EscrowBag,omitempty"`
|
||||
}
|
||||
)
|
||||
|
||||
type (
|
||||
LockdownBasicResponse struct {
|
||||
Request string `plist:"Request"`
|
||||
Error string `plist:"Error"`
|
||||
}
|
||||
|
||||
LockdownTypeResponse struct {
|
||||
LockdownBasicResponse
|
||||
Type string `plist:"Type"`
|
||||
}
|
||||
|
||||
LockdownValueResponse struct {
|
||||
LockdownBasicResponse
|
||||
Key string `plist:"Key"`
|
||||
Value interface{} `plist:"Value"`
|
||||
}
|
||||
|
||||
LockdownPairResponse struct {
|
||||
LockdownBasicResponse
|
||||
EscrowBag []byte `plist:"EscrowBag"`
|
||||
}
|
||||
|
||||
LockdownStartSessionResponse struct {
|
||||
LockdownBasicResponse
|
||||
EnableSessionSSL bool `plist:"EnableSessionSSL"`
|
||||
SessionID string `plist:"SessionID"`
|
||||
}
|
||||
|
||||
LockdownStartServiceResponse struct {
|
||||
LockdownBasicResponse
|
||||
EnableServiceSSL bool `plist:"EnableServiceSSL"`
|
||||
Port int `plist:"Port"`
|
||||
Service string `plist:"Service"`
|
||||
}
|
||||
)
|
||||
86
hrp/pkg/gidevice/pkg/libimobiledevice/packet_afc.go
Normal file
86
hrp/pkg/gidevice/pkg/libimobiledevice/packet_afc.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package libimobiledevice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var afcHeader = []byte{0x43, 0x46, 0x41, 0x36, 0x4C, 0x50, 0x41, 0x41}
|
||||
|
||||
var _ Packet = (*afcPacket)(nil)
|
||||
|
||||
type afcPacket struct {
|
||||
entireLen uint64
|
||||
thisLen uint64
|
||||
packetNum uint64
|
||||
operation uint64
|
||||
}
|
||||
|
||||
func (p *afcPacket) Pack() ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
buf.Write(afcHeader)
|
||||
|
||||
b := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(b, p.entireLen)
|
||||
buf.Write(b)
|
||||
binary.LittleEndian.PutUint64(b, p.thisLen)
|
||||
buf.Write(b)
|
||||
binary.LittleEndian.PutUint64(b, p.packetNum)
|
||||
buf.Write(b)
|
||||
binary.LittleEndian.PutUint64(b, p.operation)
|
||||
buf.Write(b)
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (p *afcPacket) Unpack(buffer *bytes.Buffer) (Packet, error) {
|
||||
return p.unpack(buffer)
|
||||
}
|
||||
|
||||
func (p *afcPacket) unpack(buffer *bytes.Buffer) (*afcPacket, error) {
|
||||
magic := make([]byte, 8)
|
||||
if _, err := buffer.Read(magic); err != nil {
|
||||
return nil, fmt.Errorf("afc packet unpack: %w", err)
|
||||
}
|
||||
if bytes.Compare(magic, afcHeader) != 0 {
|
||||
return nil, errors.New("afc packet unpack: header not match")
|
||||
}
|
||||
|
||||
respPkt := new(afcPacket)
|
||||
if err := binary.Read(buffer, binary.LittleEndian, &respPkt.entireLen); err != nil {
|
||||
return nil, fmt.Errorf("afc packet unpack: %w", err)
|
||||
}
|
||||
if err := binary.Read(buffer, binary.LittleEndian, &respPkt.thisLen); err != nil {
|
||||
return nil, fmt.Errorf("afc packet unpack: %w", err)
|
||||
}
|
||||
if err := binary.Read(buffer, binary.LittleEndian, &respPkt.packetNum); err != nil {
|
||||
return nil, fmt.Errorf("afc packet unpack: %w", err)
|
||||
}
|
||||
if err := binary.Read(buffer, binary.LittleEndian, &respPkt.operation); err != nil {
|
||||
return nil, fmt.Errorf("afc packet unpack: %w", err)
|
||||
}
|
||||
return respPkt, nil
|
||||
}
|
||||
|
||||
func (p *afcPacket) Unmarshal(v interface{}) error {
|
||||
// switch msg := v.(type) {
|
||||
// case *AfcMessage:
|
||||
// // msg.EntireLen = p.entireLen
|
||||
// // msg.ThisLen = p.thisLen
|
||||
// // msg.PacketNum = p.packetNum
|
||||
// msg.Operation = p.operation
|
||||
// default:
|
||||
// return errors.New("the type of the method parameter must be '*AfcMessage'")
|
||||
// }
|
||||
// return nil
|
||||
panic("never use (afcPacket)")
|
||||
}
|
||||
|
||||
func (p *afcPacket) String() string {
|
||||
return fmt.Sprintf(
|
||||
"EntireLen: %d, ThisLen: %d, PacketNum: %d, Operation: %X\n",
|
||||
p.entireLen, p.thisLen, p.packetNum, p.operation,
|
||||
)
|
||||
}
|
||||
203
hrp/pkg/gidevice/pkg/libimobiledevice/packet_dtxmessage.go
Normal file
203
hrp/pkg/gidevice/pkg/libimobiledevice/packet_dtxmessage.go
Normal file
@@ -0,0 +1,203 @@
|
||||
package libimobiledevice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var (
|
||||
_ Packet = (*dtxMessagePayloadPacket)(nil)
|
||||
_ Packet = (*dtxMessageHeaderPacket)(nil)
|
||||
_ Packet = (*dtxMessagePacket)(nil)
|
||||
)
|
||||
|
||||
type dtxMessagePayloadPacket struct {
|
||||
Flags uint32
|
||||
AuxiliaryLength uint32
|
||||
TotalLength uint64
|
||||
}
|
||||
|
||||
func (p *dtxMessagePayloadPacket) Pack() ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
b := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(b, p.Flags)
|
||||
buf.Write(b)
|
||||
binary.LittleEndian.PutUint32(b, p.AuxiliaryLength)
|
||||
buf.Write(b)
|
||||
|
||||
b = make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(b, p.TotalLength)
|
||||
buf.Write(b)
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (p *dtxMessagePayloadPacket) Unpack(buffer *bytes.Buffer) (pkt Packet, err error) {
|
||||
return p.unpack(buffer)
|
||||
}
|
||||
|
||||
func (p *dtxMessagePayloadPacket) unpack(buffer *bytes.Buffer) (pkt *dtxMessagePayloadPacket, err error) {
|
||||
respPkt := new(dtxMessagePayloadPacket)
|
||||
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.Flags); err != nil {
|
||||
return nil, fmt.Errorf("packet (DTXMessagePayloadHeader) unpack: %w", err)
|
||||
}
|
||||
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.AuxiliaryLength); err != nil {
|
||||
return nil, fmt.Errorf("packet (DTXMessagePayloadHeader) unpack: %w", err)
|
||||
}
|
||||
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.TotalLength); err != nil {
|
||||
return nil, fmt.Errorf("packet (DTXMessagePayloadHeader) unpack: %w", err)
|
||||
}
|
||||
return respPkt, nil
|
||||
}
|
||||
|
||||
func (p *dtxMessagePayloadPacket) Unmarshal(v interface{}) error {
|
||||
panic("never use (dtxMessagePayloadHeader)")
|
||||
}
|
||||
|
||||
func (p *dtxMessagePayloadPacket) String() string {
|
||||
return fmt.Sprintf("DTXMessagePayloadHeader Flags: %d, AuxiliaryLength: %d, TotalLength: %d\n",
|
||||
p.Flags, p.AuxiliaryLength, p.TotalLength,
|
||||
)
|
||||
}
|
||||
|
||||
type dtxMessageHeaderPacket struct {
|
||||
Magic uint32
|
||||
CB uint32
|
||||
FragmentId uint16
|
||||
FragmentCount uint16
|
||||
Length uint32
|
||||
Identifier uint32
|
||||
ConversationIndex uint32
|
||||
ChannelCode uint32
|
||||
ExpectsReply uint32
|
||||
}
|
||||
|
||||
func (p *dtxMessageHeaderPacket) Pack() ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
b := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(b, p.Magic)
|
||||
buf.Write(b)
|
||||
binary.LittleEndian.PutUint32(b, p.CB)
|
||||
buf.Write(b)
|
||||
|
||||
b = make([]byte, 2)
|
||||
binary.LittleEndian.PutUint16(b, p.FragmentId)
|
||||
buf.Write(b)
|
||||
binary.LittleEndian.PutUint16(b, p.FragmentCount)
|
||||
buf.Write(b)
|
||||
|
||||
b = make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(b, p.Length)
|
||||
buf.Write(b)
|
||||
binary.LittleEndian.PutUint32(b, p.Identifier)
|
||||
buf.Write(b)
|
||||
binary.LittleEndian.PutUint32(b, p.ConversationIndex)
|
||||
buf.Write(b)
|
||||
binary.LittleEndian.PutUint32(b, p.ChannelCode)
|
||||
buf.Write(b)
|
||||
binary.LittleEndian.PutUint32(b, p.ExpectsReply)
|
||||
buf.Write(b)
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (p *dtxMessageHeaderPacket) Unpack(buffer *bytes.Buffer) (pkt Packet, err error) {
|
||||
return p.unpack(buffer)
|
||||
}
|
||||
|
||||
func (p *dtxMessageHeaderPacket) unpack(buffer *bytes.Buffer) (pkt *dtxMessageHeaderPacket, err error) {
|
||||
respPkt := new(dtxMessageHeaderPacket)
|
||||
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.Magic); err != nil {
|
||||
return nil, fmt.Errorf("packet (DTXMessageHeader) unpack: %w", err)
|
||||
}
|
||||
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.CB); err != nil {
|
||||
return nil, fmt.Errorf("packet (DTXMessageHeader) unpack: %w", err)
|
||||
}
|
||||
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.FragmentId); err != nil {
|
||||
return nil, fmt.Errorf("packet (DTXMessageHeader) unpack: %w", err)
|
||||
}
|
||||
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.FragmentCount); err != nil {
|
||||
return nil, fmt.Errorf("packet (DTXMessageHeader) unpack: %w", err)
|
||||
}
|
||||
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.Length); err != nil {
|
||||
return nil, fmt.Errorf("packet (DTXMessageHeader) unpack: %w", err)
|
||||
}
|
||||
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.Identifier); err != nil {
|
||||
return nil, fmt.Errorf("packet (DTXMessageHeader) unpack: %w", err)
|
||||
}
|
||||
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.ConversationIndex); err != nil {
|
||||
return nil, fmt.Errorf("packet (DTXMessageHeader) unpack: %w", err)
|
||||
}
|
||||
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.ChannelCode); err != nil {
|
||||
return nil, fmt.Errorf("packet (DTXMessageHeader) unpack: %w", err)
|
||||
}
|
||||
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.ExpectsReply); err != nil {
|
||||
return nil, fmt.Errorf("packet (DTXMessageHeader) unpack: %w", err)
|
||||
}
|
||||
return respPkt, nil
|
||||
}
|
||||
|
||||
func (p *dtxMessageHeaderPacket) Unmarshal(v interface{}) error {
|
||||
panic("never use (DTXMessageHeader)")
|
||||
}
|
||||
|
||||
func (p *dtxMessageHeaderPacket) String() string {
|
||||
return fmt.Sprintf("DTXMessageHeader Magic: %d, CB: %d, FragmentId: %d, FragmentCount: %d\n"+
|
||||
"Length: %d, Identifier: %d, ConversationIndex: %d, ChannelCode: %d, ExpectsReply: %d\n",
|
||||
p.Magic, p.CB, p.FragmentId, p.FragmentCount,
|
||||
p.Length, p.Identifier, p.ConversationIndex, p.ChannelCode, p.ExpectsReply,
|
||||
)
|
||||
}
|
||||
|
||||
type dtxMessagePacket struct {
|
||||
Header *dtxMessageHeaderPacket
|
||||
Payload *dtxMessagePayloadPacket
|
||||
Aux []byte
|
||||
Sel []byte
|
||||
}
|
||||
|
||||
func (p *dtxMessagePacket) Pack() ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
raw, err := p.Header.Pack()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("packet (DTXMessagePacket) pack: %w", err)
|
||||
}
|
||||
buf.Write(raw)
|
||||
|
||||
if raw, err = p.Payload.Pack(); err != nil {
|
||||
return nil, fmt.Errorf("packet (DTXMessagePacket) pack: %w", err)
|
||||
}
|
||||
buf.Write(raw)
|
||||
|
||||
if p.Aux != nil || len(p.Aux) != 0 {
|
||||
buf.Write(p.Aux)
|
||||
}
|
||||
if p.Sel != nil || len(p.Sel) != 0 {
|
||||
buf.Write(p.Sel)
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (p *dtxMessagePacket) Unpack(buffer *bytes.Buffer) (Packet, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (p *dtxMessagePacket) Unmarshal(v interface{}) error {
|
||||
panic("never use (DTXMessagePacket)")
|
||||
}
|
||||
|
||||
func (p *dtxMessagePacket) String() string {
|
||||
return fmt.Sprintf(
|
||||
"DTXMessagePacket %s\n%s\n"+
|
||||
"%s\n%s\n",
|
||||
p.Header.String(), p.Payload.String(),
|
||||
// p.Aux, p.Sel,
|
||||
hex.Dump(p.Aux), hex.Dump(p.Sel),
|
||||
)
|
||||
}
|
||||
53
hrp/pkg/gidevice/pkg/libimobiledevice/packet_location.go
Normal file
53
hrp/pkg/gidevice/pkg/libimobiledevice/packet_location.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package libimobiledevice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var _ Packet = (*locationPacket)(nil)
|
||||
|
||||
type locationPacket struct {
|
||||
lon float64
|
||||
lat float64
|
||||
}
|
||||
|
||||
func (l *locationPacket) Pack() ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
if err := binary.Write(buf, binary.BigEndian, uint32(0)); err != nil {
|
||||
return nil, fmt.Errorf("packet (location) pack: %w", err)
|
||||
}
|
||||
|
||||
latS := []byte(strconv.FormatFloat(l.lat, 'E', -1, 64))
|
||||
if err := binary.Write(buf, binary.BigEndian, uint32(len(latS))); err != nil {
|
||||
return nil, fmt.Errorf("packet (location) pack: %w", err)
|
||||
}
|
||||
if err := binary.Write(buf, binary.BigEndian, latS); err != nil {
|
||||
return nil, fmt.Errorf("packet (location) pack: %w", err)
|
||||
}
|
||||
|
||||
lonS := []byte(strconv.FormatFloat(l.lon, 'E', -1, 64))
|
||||
if err := binary.Write(buf, binary.BigEndian, uint32(len(lonS))); err != nil {
|
||||
return nil, fmt.Errorf("packet (location) pack: %w", err)
|
||||
}
|
||||
if err := binary.Write(buf, binary.BigEndian, lonS); err != nil {
|
||||
return nil, fmt.Errorf("packet (location) pack: %w", err)
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (l *locationPacket) Unpack(buffer *bytes.Buffer) (Packet, error) {
|
||||
panic("never use (location)")
|
||||
}
|
||||
|
||||
func (l *locationPacket) Unmarshal(v interface{}) error {
|
||||
panic("never use (location)")
|
||||
}
|
||||
|
||||
func (l *locationPacket) String() string {
|
||||
return fmt.Sprintf("lon: %v, lat: %v\n", l.lon, l.lat)
|
||||
}
|
||||
47
hrp/pkg/gidevice/pkg/libimobiledevice/packet_service.go
Normal file
47
hrp/pkg/gidevice/pkg/libimobiledevice/packet_service.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package libimobiledevice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"howett.net/plist"
|
||||
)
|
||||
|
||||
var _ Packet = (*servicePacket)(nil)
|
||||
|
||||
type servicePacket struct {
|
||||
length uint32
|
||||
body []byte
|
||||
}
|
||||
|
||||
func (p *servicePacket) Pack() ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
b := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(b, p.length)
|
||||
buf.Write(b)
|
||||
|
||||
buf.Write(p.body)
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (p *servicePacket) Unpack(buffer *bytes.Buffer) (pkt Packet, err error) {
|
||||
respPkt := new(servicePacket)
|
||||
if err = binary.Read(buffer, binary.BigEndian, &respPkt.length); err != nil {
|
||||
return nil, fmt.Errorf("packet (service) unpack: %w", err)
|
||||
}
|
||||
respPkt.body = buffer.Bytes()
|
||||
|
||||
return respPkt, nil
|
||||
}
|
||||
|
||||
func (p *servicePacket) Unmarshal(v interface{}) (err error) {
|
||||
_, err = plist.Unmarshal(p.body, v)
|
||||
return
|
||||
}
|
||||
|
||||
func (p *servicePacket) String() string {
|
||||
return fmt.Sprintf("Length: %d\n%s", p.length, p.body)
|
||||
}
|
||||
112
hrp/pkg/gidevice/pkg/libimobiledevice/packet_usbmux.go
Normal file
112
hrp/pkg/gidevice/pkg/libimobiledevice/packet_usbmux.go
Normal file
@@ -0,0 +1,112 @@
|
||||
package libimobiledevice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"howett.net/plist"
|
||||
)
|
||||
|
||||
var _ Packet = (*packet)(nil)
|
||||
|
||||
type packet struct {
|
||||
length uint32
|
||||
version ProtoVersion
|
||||
msgType ProtoMessageType
|
||||
tag uint32
|
||||
body []byte
|
||||
}
|
||||
|
||||
func (p *packet) Pack() ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
b := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(b, p.length)
|
||||
buf.Write(b)
|
||||
binary.LittleEndian.PutUint32(b, uint32(p.version))
|
||||
buf.Write(b)
|
||||
binary.LittleEndian.PutUint32(b, uint32(p.msgType))
|
||||
buf.Write(b)
|
||||
binary.LittleEndian.PutUint32(b, p.tag)
|
||||
buf.Write(b)
|
||||
|
||||
buf.Write(p.body)
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (p *packet) Unpack(buffer *bytes.Buffer) (pkt Packet, err error) {
|
||||
respPkt := new(packet)
|
||||
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.length); err != nil {
|
||||
return nil, fmt.Errorf("packet unpack: %w", err)
|
||||
}
|
||||
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.version); err != nil {
|
||||
return nil, fmt.Errorf("packet unpack: %w", err)
|
||||
}
|
||||
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.msgType); err != nil {
|
||||
return nil, fmt.Errorf("packet unpack: %w", err)
|
||||
}
|
||||
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.tag); err != nil {
|
||||
return nil, fmt.Errorf("packet unpack: %w", err)
|
||||
}
|
||||
respPkt.body = buffer.Bytes()
|
||||
return respPkt, nil
|
||||
}
|
||||
|
||||
func (p *packet) Unmarshal(v interface{}) (err error) {
|
||||
_, err = plist.Unmarshal(p.body, v)
|
||||
return
|
||||
}
|
||||
|
||||
func (p *packet) String() string {
|
||||
return fmt.Sprintf(
|
||||
"Length: %d, Version: %d, Type: %d, Tag: %d\n%s",
|
||||
p.length, p.version, p.msgType, p.tag, p.body,
|
||||
)
|
||||
}
|
||||
|
||||
type (
|
||||
BasicRequest struct {
|
||||
MessageType MessageType `plist:"MessageType"`
|
||||
BundleID string `plist:"BundleID,omitempty"`
|
||||
ProgramName string `plist:"ProgName,omitempty"`
|
||||
ClientVersionString string `plist:"ClientVersionString"`
|
||||
LibUSBMuxVersion uint `plist:"kLibUSBMuxVersion"`
|
||||
}
|
||||
|
||||
ConnectRequest struct {
|
||||
BasicRequest
|
||||
DeviceID int `plist:"DeviceID"`
|
||||
PortNumber int `plist:"PortNumber"`
|
||||
}
|
||||
|
||||
ReadPairRecordRequest struct {
|
||||
BasicRequest
|
||||
PairRecordID string `plist:"PairRecordID"`
|
||||
}
|
||||
|
||||
SavePairRecordRequest struct {
|
||||
BasicRequest
|
||||
PairRecordID string `plist:"PairRecordID"`
|
||||
PairRecordData []byte `plist:"PairRecordData"`
|
||||
DeviceID int `plist:"DeviceID"`
|
||||
}
|
||||
|
||||
DeletePairRecordRequest struct {
|
||||
BasicRequest
|
||||
PairRecordID string `plist:"PairRecordID"`
|
||||
}
|
||||
)
|
||||
|
||||
type PairRecord struct {
|
||||
DeviceCertificate []byte `plist:"DeviceCertificate"`
|
||||
EscrowBag []byte `plist:"EscrowBag,omitempty"`
|
||||
HostCertificate []byte `plist:"HostCertificate"`
|
||||
HostPrivateKey []byte `plist:"HostPrivateKey,omitempty"`
|
||||
HostID string `plist:"HostID"`
|
||||
RootCertificate []byte `plist:"RootCertificate"`
|
||||
RootPrivateKey []byte `plist:"RootPrivateKey,omitempty"`
|
||||
SystemBUID string `plist:"SystemBUID"`
|
||||
WiFiMACAddress string `plist:"WiFiMACAddress,omitempty"`
|
||||
}
|
||||
119
hrp/pkg/gidevice/pkg/libimobiledevice/pcapd.go
Normal file
119
hrp/pkg/gidevice/pkg/libimobiledevice/pcapd.go
Normal file
@@ -0,0 +1,119 @@
|
||||
package libimobiledevice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"time"
|
||||
|
||||
"github.com/lunixbochs/struc"
|
||||
)
|
||||
|
||||
const PcapdServiceName = "com.apple.pcapd"
|
||||
|
||||
func NewPcapdClient(innerConn InnerConn) *PcapdClient {
|
||||
return &PcapdClient{
|
||||
client: newServicePacketClient(innerConn),
|
||||
}
|
||||
}
|
||||
|
||||
type PcapdClient struct {
|
||||
filter func(*IOSPacketHeader) bool
|
||||
client *servicePacketClient
|
||||
}
|
||||
|
||||
func (c *PcapdClient) ReceivePacket() (respPkt Packet, err error) {
|
||||
var bufLen []byte
|
||||
if bufLen, err = c.client.innerConn.Read(4); err != nil {
|
||||
return nil, fmt.Errorf("lockdown(Pcapd) receive: %w", err)
|
||||
}
|
||||
lenPkg := binary.BigEndian.Uint32(bufLen)
|
||||
|
||||
buffer := bytes.NewBuffer([]byte{})
|
||||
buffer.Write(bufLen)
|
||||
|
||||
var buf []byte
|
||||
if buf, err = c.client.innerConn.Read(int(lenPkg)); err != nil {
|
||||
return nil, fmt.Errorf("lockdown(Pcapd) receive: %w", err)
|
||||
}
|
||||
buffer.Write(buf)
|
||||
|
||||
if respPkt, err = new(servicePacket).Unpack(buffer); err != nil {
|
||||
return nil, fmt.Errorf("lockdown(Pcapd) receive: %w", err)
|
||||
}
|
||||
|
||||
debugLog(fmt.Sprintf("<-- %s\n", respPkt))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type IOSPacketHeader struct {
|
||||
HdrSize uint32 `struc:"uint32,big"`
|
||||
Version uint8 `struc:"uint8,big"`
|
||||
PacketSize uint32 `struc:"uint32,big"`
|
||||
Type uint8 `struc:"uint8,big"`
|
||||
Unit uint16 `struc:"uint16,big"`
|
||||
IO uint8 `struc:"uint8,big"`
|
||||
ProtocolFamily uint32 `struc:"uint32,big"`
|
||||
FramePreLength uint32 `struc:"uint32,big"`
|
||||
FramePstLength uint32 `struc:"uint32,big"`
|
||||
IFName string `struc:"[16]byte"`
|
||||
Pid int32 `struc:"int32,little"`
|
||||
ProcName string `struc:"[17]byte"`
|
||||
Unknown uint32 `struc:"uint32,little"`
|
||||
Pid2 int32 `struc:"int32,little"`
|
||||
ProcName2 string `struc:"[17]byte"`
|
||||
Unknown2 [8]byte `struc:"[8]byte"`
|
||||
}
|
||||
|
||||
func (c *PcapdClient) GetPacket(buf []byte) ([]byte, error) {
|
||||
iph := IOSPacketHeader{}
|
||||
preader := bytes.NewReader(buf)
|
||||
_ = struc.Unpack(preader, &iph)
|
||||
|
||||
if c.filter != nil {
|
||||
if !c.filter(&iph) {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
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}
|
||||
return append(ext, packet...), nil
|
||||
}
|
||||
return packet, nil
|
||||
}
|
||||
|
||||
type PcaprecHdrS struct {
|
||||
TsSec int `struc:"uint32,little"` /* timestamp seconds */
|
||||
TsUsec int `struc:"uint32,little"` /* timestamp microseconds */
|
||||
InclLen int `struc:"uint32,little"` /* number of octets of packet saved in file */
|
||||
OrigLen int `struc:"uint32,little"` /* actual length of packet */
|
||||
}
|
||||
|
||||
func (c *PcapdClient) CreatePacket(packet []byte) ([]byte, error) {
|
||||
now := time.Now()
|
||||
phs := &PcaprecHdrS{
|
||||
int(now.Unix()),
|
||||
int(now.UnixNano()/1e3 - now.Unix()*1e6),
|
||||
len(packet),
|
||||
len(packet),
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
err := struc.Pack(&buf, phs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buf.Write(packet)
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (c *PcapdClient) Close() {
|
||||
c.client.innerConn.Close()
|
||||
}
|
||||
52
hrp/pkg/gidevice/pkg/libimobiledevice/screenshot.go
Normal file
52
hrp/pkg/gidevice/pkg/libimobiledevice/screenshot.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package libimobiledevice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const ScreenshotServiceName = "com.apple.mobile.screenshotr"
|
||||
|
||||
func NewScreenshotClient(innerConn InnerConn) *ScreenshotClient {
|
||||
return &ScreenshotClient{
|
||||
client: newServicePacketClient(innerConn),
|
||||
}
|
||||
}
|
||||
|
||||
type ScreenshotClient struct {
|
||||
client *servicePacketClient
|
||||
}
|
||||
|
||||
func (c *ScreenshotClient) NewBinaryPacket(req interface{}) (Packet, error) {
|
||||
return c.client.NewBinaryPacket(req)
|
||||
}
|
||||
|
||||
func (c *ScreenshotClient) SendPacket(pkt Packet) (err error) {
|
||||
return c.client.SendPacket(pkt)
|
||||
}
|
||||
|
||||
func (c *ScreenshotClient) ReceivePacket() (respPkt Packet, err error) {
|
||||
var bufLen []byte
|
||||
if bufLen, err = c.client.innerConn.Read(4); err != nil {
|
||||
return nil, fmt.Errorf("lockdown(Screenshot) receive: %w", err)
|
||||
}
|
||||
lenPkg := binary.BigEndian.Uint32(bufLen)
|
||||
|
||||
buffer := bytes.NewBuffer([]byte{})
|
||||
buffer.Write(bufLen)
|
||||
|
||||
var buf []byte
|
||||
if buf, err = c.client.innerConn.Read(int(lenPkg)); err != nil {
|
||||
return nil, fmt.Errorf("lockdown(Screenshot) receive: %w", err)
|
||||
}
|
||||
buffer.Write(buf)
|
||||
|
||||
if respPkt, err = new(servicePacket).Unpack(buffer); err != nil {
|
||||
return nil, fmt.Errorf("lockdown(Screenshot) receive: %w", err)
|
||||
}
|
||||
|
||||
debugLog(fmt.Sprintf("<-- %s\n", respPkt))
|
||||
|
||||
return
|
||||
}
|
||||
128
hrp/pkg/gidevice/pkg/libimobiledevice/simulatelocation.go
Normal file
128
hrp/pkg/gidevice/pkg/libimobiledevice/simulatelocation.go
Normal file
@@ -0,0 +1,128 @@
|
||||
package libimobiledevice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const SimulateLocationServiceName = "com.apple.dt.simulatelocation"
|
||||
|
||||
type CoordinateSystem string
|
||||
|
||||
const (
|
||||
CoordinateSystemWGS84 CoordinateSystem = "WGS84"
|
||||
CoordinateSystemBD09 CoordinateSystem = "BD09"
|
||||
CoordinateSystemGCJ02 CoordinateSystem = "GCJ02"
|
||||
)
|
||||
|
||||
func NewSimulateLocationClient(innerConn InnerConn) *SimulateLocationClient {
|
||||
return &SimulateLocationClient{
|
||||
client: newServicePacketClient(innerConn),
|
||||
}
|
||||
}
|
||||
|
||||
type SimulateLocationClient struct {
|
||||
client *servicePacketClient
|
||||
}
|
||||
|
||||
func (c *SimulateLocationClient) NewLocationPacket(lon, lat float64, coordinateSystem CoordinateSystem) Packet {
|
||||
switch CoordinateSystem(strings.ToUpper(string(coordinateSystem))) {
|
||||
case CoordinateSystemGCJ02:
|
||||
lon, lat = gcj02ToWGS84(lon, lat)
|
||||
case CoordinateSystemBD09:
|
||||
lon, lat = bd09ToWGS84(lon, lat)
|
||||
case CoordinateSystemWGS84:
|
||||
_, _ = lon, lat
|
||||
default:
|
||||
_, _ = lon, lat
|
||||
}
|
||||
|
||||
pkt := new(locationPacket)
|
||||
pkt.lon = lon
|
||||
pkt.lat = lat
|
||||
return pkt
|
||||
}
|
||||
|
||||
func (c *SimulateLocationClient) SendPacket(pkt Packet) (err error) {
|
||||
return c.client.SendPacket(pkt)
|
||||
}
|
||||
|
||||
// Recover try to revert back
|
||||
func (c *SimulateLocationClient) Recover() error {
|
||||
data := []byte{0x00, 0x00, 0x00, 0x01}
|
||||
debugLog(fmt.Sprintf("--> %+v\n", data))
|
||||
return c.client.innerConn.Write(data)
|
||||
}
|
||||
|
||||
const (
|
||||
xPi = math.Pi * 3000.0 / 180.0
|
||||
offset = 0.00669342162296594323
|
||||
axis = 6378245.0
|
||||
)
|
||||
|
||||
func isOutOfChina(lon, lat float64) bool {
|
||||
return !(lon > 73.66 && lon < 135.05 && lat > 3.86 && lat < 53.55)
|
||||
}
|
||||
|
||||
func delta(lon, lat float64) (float64, float64) {
|
||||
dLat := transformLat(lon-105.0, lat-35.0)
|
||||
dLon := transformLng(lon-105.0, lat-35.0)
|
||||
|
||||
radLat := lat / 180.0 * math.Pi
|
||||
magic := math.Sin(radLat)
|
||||
magic = 1 - offset*magic*magic
|
||||
sqrtMagic := math.Sqrt(magic)
|
||||
|
||||
dLat = (dLat * 180.0) / ((axis * (1 - offset)) / (magic * sqrtMagic) * math.Pi)
|
||||
dLon = (dLon * 180.0) / (axis / sqrtMagic * math.Cos(radLat) * math.Pi)
|
||||
|
||||
mgLat := lat + dLat
|
||||
mgLon := lon + dLon
|
||||
|
||||
return mgLon, mgLat
|
||||
}
|
||||
|
||||
func transformLat(lon, lat float64) float64 {
|
||||
ret := -100.0 + 2.0*lon + 3.0*lat + 0.2*lat*lat + 0.1*lon*lat + 0.2*math.Sqrt(math.Abs(lon))
|
||||
ret += (20.0*math.Sin(6.0*lon*math.Pi) + 20.0*math.Sin(2.0*lon*math.Pi)) * 2.0 / 3.0
|
||||
ret += (20.0*math.Sin(lat*math.Pi) + 40.0*math.Sin(lat/3.0*math.Pi)) * 2.0 / 3.0
|
||||
ret += (160.0*math.Sin(lat/12.0*math.Pi) + 320*math.Sin(lat*math.Pi/30.0)) * 2.0 / 3.0
|
||||
return ret
|
||||
}
|
||||
|
||||
func transformLng(lon, lat float64) float64 {
|
||||
ret := 300.0 + lon + 2.0*lat + 0.1*lon*lon + 0.1*lon*lat + 0.1*math.Sqrt(math.Abs(lon))
|
||||
ret += (20.0*math.Sin(6.0*lon*math.Pi) + 20.0*math.Sin(2.0*lon*math.Pi)) * 2.0 / 3.0
|
||||
ret += (20.0*math.Sin(lon*math.Pi) + 40.0*math.Sin(lon/3.0*math.Pi)) * 2.0 / 3.0
|
||||
ret += (150.0*math.Sin(lon/12.0*math.Pi) + 300.0*math.Sin(lon/30.0*math.Pi)) * 2.0 / 3.0
|
||||
return ret
|
||||
}
|
||||
|
||||
func gcj02ToWGS84(lon, lat float64) (float64, float64) {
|
||||
if isOutOfChina(lon, lat) {
|
||||
return lon, lat
|
||||
}
|
||||
|
||||
mgLon, mgLat := delta(lon, lat)
|
||||
|
||||
return lon*2 - mgLon, lat*2 - mgLat
|
||||
}
|
||||
|
||||
func bd09ToGCJ02(lon, lat float64) (float64, float64) {
|
||||
x := lon - 0.0065
|
||||
y := lat - 0.006
|
||||
|
||||
z := math.Sqrt(x*x+y*y) - 0.00002*math.Sin(y*xPi)
|
||||
theta := math.Atan2(y, x) - 0.000003*math.Cos(x*xPi)
|
||||
|
||||
gLon := z * math.Cos(theta)
|
||||
gLat := z * math.Sin(theta)
|
||||
|
||||
return gLon, gLat
|
||||
}
|
||||
|
||||
func bd09ToWGS84(lon, lat float64) (float64, float64) {
|
||||
lon, lat = bd09ToGCJ02(lon, lat)
|
||||
return gcj02ToWGS84(lon, lat)
|
||||
}
|
||||
53
hrp/pkg/gidevice/pkg/libimobiledevice/springboard.go
Normal file
53
hrp/pkg/gidevice/pkg/libimobiledevice/springboard.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package libimobiledevice
|
||||
|
||||
type IconPNGDataResponse struct {
|
||||
PNGData []byte `plist:"pngData"`
|
||||
}
|
||||
|
||||
type InterfaceOrientationResponse struct {
|
||||
Orientation OrientationState `plist:"interfaceOrientation"`
|
||||
}
|
||||
|
||||
type OrientationState int64
|
||||
|
||||
const (
|
||||
Unknown OrientationState = iota
|
||||
Portrait
|
||||
PortraitUpsideDown
|
||||
LandscapeRight
|
||||
LandscapeLeft
|
||||
)
|
||||
|
||||
const (
|
||||
SpringBoardServiceName = "com.apple.springboardservices"
|
||||
)
|
||||
|
||||
func NewSpringBoardClient(innerConn InnerConn) *SpringBoardClient {
|
||||
return &SpringBoardClient{
|
||||
newServicePacketClient(innerConn),
|
||||
}
|
||||
}
|
||||
|
||||
type SpringBoardClient struct {
|
||||
client *servicePacketClient
|
||||
}
|
||||
|
||||
func (c *SpringBoardClient) InnerConn() InnerConn {
|
||||
return c.client.innerConn
|
||||
}
|
||||
|
||||
func (c *SpringBoardClient) NewXmlPacket(req interface{}) (Packet, error) {
|
||||
return c.client.NewXmlPacket(req)
|
||||
}
|
||||
|
||||
func (c *SpringBoardClient) SendPacket(pkt Packet) (err error) {
|
||||
return c.client.SendPacket(pkt)
|
||||
}
|
||||
|
||||
func (c *SpringBoardClient) ReceivePacket() (respPkt Packet, err error) {
|
||||
return c.client.ReceivePacket()
|
||||
}
|
||||
|
||||
func (c *SpringBoardClient) NewBinaryPacket(req interface{}) (Packet, error) {
|
||||
return c.client.NewBinaryPacket(req)
|
||||
}
|
||||
21
hrp/pkg/gidevice/pkg/libimobiledevice/syslogrelay.go
Normal file
21
hrp/pkg/gidevice/pkg/libimobiledevice/syslogrelay.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package libimobiledevice
|
||||
|
||||
const SyslogRelayServiceName = "com.apple.syslog_relay"
|
||||
|
||||
func NewSyslogRelayClient(innerConn InnerConn) *SyslogRelayClient {
|
||||
return &SyslogRelayClient{
|
||||
newServicePacketClient(innerConn),
|
||||
}
|
||||
}
|
||||
|
||||
type SyslogRelayClient struct {
|
||||
client *servicePacketClient
|
||||
}
|
||||
|
||||
func (c *SyslogRelayClient) InnerConn() InnerConn {
|
||||
return c.client.innerConn
|
||||
}
|
||||
|
||||
func (c *SyslogRelayClient) Close() {
|
||||
c.client.innerConn.Close()
|
||||
}
|
||||
45
hrp/pkg/gidevice/pkg/libimobiledevice/testmanagerd.go
Normal file
45
hrp/pkg/gidevice/pkg/libimobiledevice/testmanagerd.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package libimobiledevice
|
||||
|
||||
const (
|
||||
TestmanagerdSecureServiceName = "com.apple.testmanagerd.lockdown.secure"
|
||||
TestmanagerdServiceName = "com.apple.testmanagerd.lockdown"
|
||||
)
|
||||
|
||||
func NewTestmanagerdClient(innerConn InnerConn) *TestmanagerdClient {
|
||||
return &TestmanagerdClient{
|
||||
client: newDtxMessageClient(innerConn),
|
||||
}
|
||||
}
|
||||
|
||||
type TestmanagerdClient struct {
|
||||
client *dtxMessageClient
|
||||
}
|
||||
|
||||
func (t *TestmanagerdClient) Connection() (publishedChannels map[string]int32, err error) {
|
||||
return t.client.Connection()
|
||||
}
|
||||
|
||||
func (t *TestmanagerdClient) MakeChannel(channel string) (id uint32, err error) {
|
||||
return t.client.MakeChannel(channel)
|
||||
}
|
||||
|
||||
func (t *TestmanagerdClient) Invoke(selector string, args *AuxBuffer, channelCode uint32, expectsReply bool) (result *DTXMessageResult, err error) {
|
||||
var msgID uint32
|
||||
if msgID, err = t.client.SendDTXMessage(selector, args.Bytes(), channelCode, expectsReply); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if expectsReply {
|
||||
if result, err = t.client.GetResult(msgID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (t *TestmanagerdClient) RegisterCallback(obj string, cb func(m DTXMessageResult)) {
|
||||
t.client.RegisterCallback(obj, cb)
|
||||
}
|
||||
|
||||
func (t *TestmanagerdClient) Close() {
|
||||
t.client.Close()
|
||||
}
|
||||
414
hrp/pkg/gidevice/pkg/libimobiledevice/usbmux.go
Normal file
414
hrp/pkg/gidevice/pkg/libimobiledevice/usbmux.go
Normal file
@@ -0,0 +1,414 @@
|
||||
package libimobiledevice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"howett.net/plist"
|
||||
)
|
||||
|
||||
var DefaultDeadlineTimeout = 30 * time.Second
|
||||
|
||||
const (
|
||||
BundleID = "electricbubble.libimobiledevice"
|
||||
ProgramName = "libimobiledevice"
|
||||
ClientVersion = "libimobiledevice-beta"
|
||||
LibUSBMuxVersion = 3
|
||||
)
|
||||
|
||||
type ReplyCode uint64
|
||||
|
||||
const (
|
||||
ReplyCodeOK ReplyCode = iota
|
||||
ReplyCodeBadCommand
|
||||
ReplyCodeBadDevice
|
||||
ReplyCodeConnectionRefused
|
||||
_ // ignore `4`
|
||||
_ // ignore `5`
|
||||
ReplyCodeBadVersion
|
||||
)
|
||||
|
||||
func (rc ReplyCode) String() string {
|
||||
switch rc {
|
||||
case ReplyCodeOK:
|
||||
return "ok"
|
||||
case ReplyCodeBadCommand:
|
||||
return "bad command"
|
||||
case ReplyCodeBadDevice:
|
||||
return "bad device"
|
||||
case ReplyCodeConnectionRefused:
|
||||
return "connection refused"
|
||||
case ReplyCodeBadVersion:
|
||||
return "bad version"
|
||||
default:
|
||||
return "unknown reply code: " + strconv.Itoa(int(rc))
|
||||
}
|
||||
}
|
||||
|
||||
type ProtoVersion uint32
|
||||
|
||||
// proto_version == 1
|
||||
// construct message plist
|
||||
// else `0`? res == `RESULT_BADVERSION`
|
||||
// binary packet
|
||||
|
||||
const (
|
||||
ProtoVersionBinary ProtoVersion = iota
|
||||
ProtoVersionPlist
|
||||
)
|
||||
|
||||
type ProtoMessageType uint32
|
||||
|
||||
const (
|
||||
_ ProtoMessageType = iota
|
||||
ProtoMessageTypeResult
|
||||
ProtoMessageTypeConnect
|
||||
ProtoMessageTypeListen
|
||||
ProtoMessageTypeDeviceAdd
|
||||
ProtoMessageTypeDeviceRemove
|
||||
ProtoMessageTypeDevicePaired
|
||||
_ // `7`
|
||||
ProtoMessageTypePlist
|
||||
)
|
||||
|
||||
type MessageType string
|
||||
|
||||
const (
|
||||
MessageTypeResult MessageType = "Result"
|
||||
MessageTypeConnect MessageType = "Connect"
|
||||
MessageTypeListen MessageType = "Listen"
|
||||
MessageTypeDeviceAdd MessageType = "Attached"
|
||||
MessageTypeDeviceRemove MessageType = "Detached"
|
||||
MessageTypeReadBUID MessageType = "ReadBUID"
|
||||
MessageTypeReadPairRecord MessageType = "ReadPairRecord"
|
||||
MessageTypeSavePairRecord MessageType = "SavePairRecord"
|
||||
MessageTypeDeletePairRecord MessageType = "DeletePairRecord"
|
||||
MessageTypeDeviceList MessageType = "ListDevices"
|
||||
)
|
||||
|
||||
type BaseDevice struct {
|
||||
MessageType MessageType `plist:"MessageType"`
|
||||
DeviceID int `plist:"DeviceID"`
|
||||
Properties DeviceProperties `plist:"Properties"`
|
||||
}
|
||||
|
||||
type DeviceProperties struct {
|
||||
DeviceID int `plist:"DeviceID"`
|
||||
ConnectionType string `plist:"ConnectionType"`
|
||||
ConnectionSpeed int `plist:"ConnectionSpeed"`
|
||||
ProductID int `plist:"ProductID"`
|
||||
LocationID int `plist:"LocationID"`
|
||||
SerialNumber string `plist:"SerialNumber"`
|
||||
UDID string `plist:"UDID"`
|
||||
USBSerialNumber string `plist:"USBSerialNumber"`
|
||||
|
||||
EscapedFullServiceName string `plist:"EscapedFullServiceName"`
|
||||
InterfaceIndex int `plist:"InterfaceIndex"`
|
||||
NetworkAddress []byte `plist:"NetworkAddress"`
|
||||
}
|
||||
|
||||
func NewUsbmuxClient(timeout ...time.Duration) (c *UsbmuxClient, err error) {
|
||||
if len(timeout) == 0 {
|
||||
timeout = []time.Duration{DefaultDeadlineTimeout}
|
||||
}
|
||||
c = &UsbmuxClient{version: ProtoVersionPlist}
|
||||
var conn net.Conn
|
||||
if conn, err = rawDial(timeout[0]); err != nil {
|
||||
return nil, fmt.Errorf("usbmux connect: %w", err)
|
||||
}
|
||||
|
||||
c.innerConn = newInnerConn(conn, timeout[0])
|
||||
return
|
||||
}
|
||||
|
||||
type UsbmuxClient struct {
|
||||
innerConn InnerConn
|
||||
version ProtoVersion
|
||||
tag uint32
|
||||
}
|
||||
|
||||
func (c *UsbmuxClient) NewBasicRequest(msgType MessageType) *BasicRequest {
|
||||
return &BasicRequest{
|
||||
MessageType: msgType,
|
||||
BundleID: BundleID,
|
||||
ProgramName: ProgramName,
|
||||
ClientVersionString: ClientVersion,
|
||||
LibUSBMuxVersion: LibUSBMuxVersion,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *UsbmuxClient) NewConnectRequest(deviceID, port int) *ConnectRequest {
|
||||
return &ConnectRequest{
|
||||
BasicRequest: *c.NewBasicRequest(MessageTypeConnect),
|
||||
DeviceID: deviceID,
|
||||
PortNumber: ((port << 8) & 0xFF00) | (port >> 8),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *UsbmuxClient) NewReadPairRecordRequest(udid string) *ReadPairRecordRequest {
|
||||
return &ReadPairRecordRequest{
|
||||
BasicRequest: *c.NewBasicRequest(MessageTypeReadPairRecord),
|
||||
PairRecordID: udid,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *UsbmuxClient) NewSavePairRecordRequest(udid string, deviceID int, data []byte) *SavePairRecordRequest {
|
||||
return &SavePairRecordRequest{
|
||||
BasicRequest: *c.NewBasicRequest(MessageTypeSavePairRecord),
|
||||
PairRecordID: udid,
|
||||
PairRecordData: data,
|
||||
DeviceID: deviceID,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *UsbmuxClient) NewDeletePairRecordRequest(udid string) *DeletePairRecordRequest {
|
||||
return &DeletePairRecordRequest{
|
||||
BasicRequest: *c.NewBasicRequest(MessageTypeDeletePairRecord),
|
||||
PairRecordID: udid,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *UsbmuxClient) NewPacket(protoMsgType ProtoMessageType) Packet {
|
||||
return c.newPacket(protoMsgType)
|
||||
}
|
||||
|
||||
func (c *UsbmuxClient) newPacket(protoMsgType ProtoMessageType) *packet {
|
||||
c.tag++
|
||||
pkt := &packet{
|
||||
version: c.version,
|
||||
msgType: protoMsgType,
|
||||
tag: c.tag,
|
||||
}
|
||||
return pkt
|
||||
}
|
||||
|
||||
func (c *UsbmuxClient) NewPlistPacket(req interface{}) (Packet, error) {
|
||||
pkt := c.newPacket(ProtoMessageTypePlist)
|
||||
if buf, err := plist.Marshal(req, plist.XMLFormat); err != nil {
|
||||
return nil, fmt.Errorf("plist packet marshal: %w", err)
|
||||
} else {
|
||||
pkt.body = buf
|
||||
}
|
||||
pkt.length = uint32(len(pkt.body) + 4*4)
|
||||
return pkt, nil
|
||||
}
|
||||
|
||||
func (c *UsbmuxClient) SendPacket(pkt Packet) (err error) {
|
||||
var raw []byte
|
||||
if raw, err = pkt.Pack(); err != nil {
|
||||
return fmt.Errorf("usbmux send: %w", err)
|
||||
}
|
||||
// debugLog(fmt.Sprintf("--> Length: %d, Version: %d, Type: %d, Tag: %d\n%s\n", pkt.Length(), pkt.Version(), pkt.Type(), pkt.Tag(), pkt.Body()))
|
||||
debugLog(fmt.Sprintf("--> %s\n", pkt))
|
||||
return c.innerConn.Write(raw)
|
||||
}
|
||||
|
||||
func (c *UsbmuxClient) ReceivePacket() (respPkt Packet, err error) {
|
||||
var bufLen []byte
|
||||
if bufLen, err = c.innerConn.Read(4); err != nil {
|
||||
return nil, fmt.Errorf("usbmux receive: %w", err)
|
||||
}
|
||||
lenPkg := binary.LittleEndian.Uint32(bufLen)
|
||||
|
||||
buffer := bytes.NewBuffer([]byte{})
|
||||
buffer.Write(bufLen)
|
||||
|
||||
var buf []byte
|
||||
if buf, err = c.innerConn.Read(int(lenPkg - 4)); err != nil {
|
||||
return nil, fmt.Errorf("usbmux receive: %w", err)
|
||||
}
|
||||
buffer.Write(buf)
|
||||
|
||||
if respPkt, err = new(packet).Unpack(buffer); err != nil {
|
||||
return nil, fmt.Errorf("usbmux receive: %w", err)
|
||||
}
|
||||
|
||||
// debugLog(fmt.Sprintf("<-- Length: %d, Version: %d, Type: %d, Tag: %d\n%s\n", respPkt.Length(), respPkt.Version(), respPkt.Type(), respPkt.Tag(), respPkt.Body()))
|
||||
debugLog(fmt.Sprintf("<-- %s\n", respPkt))
|
||||
|
||||
reply := struct {
|
||||
MessageType string `plist:"MessageType"`
|
||||
Number ReplyCode `plist:"Number"`
|
||||
}{}
|
||||
if err = respPkt.Unmarshal(&reply); err != nil {
|
||||
return nil, fmt.Errorf("usbmux receive: %w", err)
|
||||
}
|
||||
|
||||
if reply.Number != ReplyCodeOK {
|
||||
return nil, fmt.Errorf("usbmux receive: %s", reply.Number.String())
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *UsbmuxClient) Close() {
|
||||
c.innerConn.Close()
|
||||
}
|
||||
|
||||
func (c *UsbmuxClient) RawConn() net.Conn {
|
||||
return c.innerConn.RawConn()
|
||||
}
|
||||
|
||||
func (c *UsbmuxClient) InnerConn() InnerConn {
|
||||
return c.innerConn
|
||||
}
|
||||
|
||||
func rawDial(timeout time.Duration) (net.Conn, error) {
|
||||
dialer := net.Dialer{
|
||||
Timeout: timeout,
|
||||
}
|
||||
|
||||
var network, address string
|
||||
switch runtime.GOOS {
|
||||
case "darwin", "android", "linux":
|
||||
network, address = "unix", "/var/run/usbmuxd"
|
||||
case "windows":
|
||||
network, address = "tcp", "127.0.0.1:27015"
|
||||
default:
|
||||
return nil, fmt.Errorf("raw dial: unsupported system: %s", runtime.GOOS)
|
||||
}
|
||||
|
||||
return dialer.Dial(network, address)
|
||||
}
|
||||
|
||||
type InnerConn interface {
|
||||
Write(data []byte) (err error)
|
||||
Read(length int) (data []byte, err error)
|
||||
Handshake(version []int, pairRecord *PairRecord) (err error)
|
||||
DismissSSL() (err error)
|
||||
Close()
|
||||
RawConn() net.Conn
|
||||
Timeout(time.Duration)
|
||||
}
|
||||
|
||||
func newInnerConn(conn net.Conn, timeout time.Duration) InnerConn {
|
||||
return &safeConn{
|
||||
conn: conn,
|
||||
timeout: timeout,
|
||||
}
|
||||
}
|
||||
|
||||
type safeConn struct {
|
||||
conn net.Conn
|
||||
sslConn *tls.Conn
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
func (c *safeConn) Write(data []byte) (err error) {
|
||||
conn := c.RawConn()
|
||||
if c.timeout <= 0 {
|
||||
err = conn.SetWriteDeadline(time.Time{})
|
||||
} else {
|
||||
err = conn.SetWriteDeadline(time.Now().Add(c.timeout))
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for totalSent := 0; totalSent < len(data); {
|
||||
var sent int
|
||||
if sent, err = conn.Write(data[totalSent:]); err != nil {
|
||||
return err
|
||||
}
|
||||
if sent == 0 {
|
||||
return err
|
||||
}
|
||||
totalSent += sent
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *safeConn) Read(length int) (data []byte, err error) {
|
||||
conn := c.RawConn()
|
||||
if c.timeout <= 0 {
|
||||
err = conn.SetReadDeadline(time.Time{})
|
||||
} else {
|
||||
err = conn.SetReadDeadline(time.Now().Add(c.timeout))
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data = make([]byte, 0, length)
|
||||
for len(data) < length {
|
||||
buf := make([]byte, length-len(data))
|
||||
_n, _err := 0, error(nil)
|
||||
if _n, _err = conn.Read(buf); _err != nil && _n == 0 {
|
||||
return nil, _err
|
||||
}
|
||||
data = append(data, buf[:_n]...)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *safeConn) Handshake(version []int, pairRecord *PairRecord) (err error) {
|
||||
minVersion := uint16(tls.VersionTLS11)
|
||||
maxVersion := uint16(tls.VersionTLS11)
|
||||
|
||||
if version[0] > 10 {
|
||||
minVersion = tls.VersionTLS11
|
||||
maxVersion = tls.VersionTLS13
|
||||
}
|
||||
|
||||
cert, err := tls.X509KeyPair(pairRecord.RootCertificate, pairRecord.RootPrivateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config := &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
InsecureSkipVerify: true,
|
||||
MinVersion: minVersion,
|
||||
MaxVersion: maxVersion,
|
||||
}
|
||||
|
||||
c.sslConn = tls.Client(c.conn, config)
|
||||
|
||||
if err = c.sslConn.Handshake(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *safeConn) DismissSSL() (err error) {
|
||||
if c.sslConn != nil {
|
||||
// err = c.sslConn.CloseWrite()
|
||||
// if err = c.sslConn.CloseWrite(); err != nil {
|
||||
// return err
|
||||
// }
|
||||
c.sslConn = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *safeConn) Close() {
|
||||
if c.sslConn != nil {
|
||||
if err := c.sslConn.Close(); err != nil {
|
||||
debugLog(fmt.Sprintf("close: %s", err))
|
||||
}
|
||||
}
|
||||
if c.conn != nil {
|
||||
if err := c.conn.Close(); err != nil {
|
||||
debugLog(fmt.Sprintf("close: %s", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RawConn `sslConn` first
|
||||
func (c *safeConn) RawConn() net.Conn {
|
||||
if c.sslConn != nil {
|
||||
return c.sslConn
|
||||
}
|
||||
return c.conn
|
||||
}
|
||||
|
||||
func (c *safeConn) Timeout(duration time.Duration) {
|
||||
c.timeout = duration
|
||||
}
|
||||
37
hrp/pkg/gidevice/pkg/nskeyedarchiver/nsarray.go
Normal file
37
hrp/pkg/gidevice/pkg/nskeyedarchiver/nsarray.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package nskeyedarchiver
|
||||
|
||||
import "howett.net/plist"
|
||||
|
||||
type NSArray struct {
|
||||
internal []interface{}
|
||||
}
|
||||
|
||||
func NewNSArray(value []interface{}) *NSArray {
|
||||
return &NSArray{
|
||||
internal: value,
|
||||
}
|
||||
}
|
||||
|
||||
func (ns *NSArray) archive(objects []interface{}) []interface{} {
|
||||
objs := make([]interface{}, 0, len(ns.internal))
|
||||
|
||||
info := map[string]interface{}{}
|
||||
objects = append(objects, info)
|
||||
|
||||
for _, v := range ns.internal {
|
||||
var uid plist.UID
|
||||
objects, uid = archive(objects, v)
|
||||
objs = append(objs, uid)
|
||||
}
|
||||
|
||||
info["NS.objects"] = objs
|
||||
info["$class"] = plist.UID(len(objects))
|
||||
|
||||
cls := map[string]interface{}{
|
||||
"$classname": "NSArray",
|
||||
"$classes": []interface{}{"NSArray", "NSObject"},
|
||||
}
|
||||
objects = append(objects, cls)
|
||||
|
||||
return objects
|
||||
}
|
||||
18
hrp/pkg/gidevice/pkg/nskeyedarchiver/nsarray_test.go
Normal file
18
hrp/pkg/gidevice/pkg/nskeyedarchiver/nsarray_test.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package nskeyedarchiver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNSArray_archive(t *testing.T) {
|
||||
objs := make([]interface{}, 0, 1)
|
||||
value := []interface{}{
|
||||
"a", 1,
|
||||
"b", "2",
|
||||
"c", false,
|
||||
}
|
||||
array := NewNSArray(value)
|
||||
objects := array.archive(objs)
|
||||
fmt.Println(objects)
|
||||
}
|
||||
44
hrp/pkg/gidevice/pkg/nskeyedarchiver/nsdictionary.go
Normal file
44
hrp/pkg/gidevice/pkg/nskeyedarchiver/nsdictionary.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package nskeyedarchiver
|
||||
|
||||
import (
|
||||
"howett.net/plist"
|
||||
)
|
||||
|
||||
type NSDictionary struct {
|
||||
internal map[string]interface{}
|
||||
}
|
||||
|
||||
func NewNSDictionary(value map[string]interface{}) *NSDictionary {
|
||||
return &NSDictionary{
|
||||
internal: value,
|
||||
}
|
||||
}
|
||||
|
||||
func (ns *NSDictionary) archive(objects []interface{}) []interface{} {
|
||||
keys := make([]interface{}, 0, len(ns.internal))
|
||||
objs := make([]interface{}, 0, len(ns.internal))
|
||||
|
||||
info := map[string]interface{}{}
|
||||
objects = append(objects, info)
|
||||
|
||||
for k, v := range ns.internal {
|
||||
uid := plist.UID(len(objects))
|
||||
keys = append(keys, uid)
|
||||
objects = append(objects, k)
|
||||
|
||||
objects, uid = archive(objects, v)
|
||||
objs = append(objs, uid)
|
||||
}
|
||||
|
||||
info["NS.keys"] = keys
|
||||
info["NS.objects"] = objs
|
||||
info["$class"] = plist.UID(len(objects))
|
||||
|
||||
cls := map[string]interface{}{
|
||||
"$classname": "NSDictionary",
|
||||
"$classes": []interface{}{"NSDictionary", "NSObject"},
|
||||
}
|
||||
objects = append(objects, cls)
|
||||
|
||||
return objects
|
||||
}
|
||||
18
hrp/pkg/gidevice/pkg/nskeyedarchiver/nsdictionary_test.go
Normal file
18
hrp/pkg/gidevice/pkg/nskeyedarchiver/nsdictionary_test.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package nskeyedarchiver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNSDictionary_archive(t *testing.T) {
|
||||
objs := make([]interface{}, 0, 1)
|
||||
value := map[string]interface{}{
|
||||
"a": 1,
|
||||
"b": "2",
|
||||
"c": true,
|
||||
}
|
||||
dict := NewNSDictionary(value)
|
||||
objects := dict.archive(objs)
|
||||
fmt.Println(objects)
|
||||
}
|
||||
80
hrp/pkg/gidevice/pkg/nskeyedarchiver/nskeyedarchiver.go
Normal file
80
hrp/pkg/gidevice/pkg/nskeyedarchiver/nskeyedarchiver.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package nskeyedarchiver
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"howett.net/plist"
|
||||
)
|
||||
|
||||
func Marshal(obj interface{}) (raw []byte, err error) {
|
||||
objects := []interface{}{"$null"}
|
||||
objects, _ = archive(objects, obj)
|
||||
archiver := map[string]interface{}{
|
||||
"$version": 100000,
|
||||
"$archiver": "NSKeyedArchiver",
|
||||
"$top": map[string]interface{}{"root": plist.UID(1)},
|
||||
"$objects": objects,
|
||||
}
|
||||
// if len(format) == 0 {
|
||||
// format = []int{plist.BinaryFormat}
|
||||
// }
|
||||
// return plist.Marshal(archiver, format[0])
|
||||
return plist.Marshal(archiver, plist.BinaryFormat)
|
||||
}
|
||||
|
||||
func archive(_objects []interface{}, _value interface{}) (objects []interface{}, uid plist.UID) {
|
||||
val := reflect.ValueOf(_value)
|
||||
typ := val.Type()
|
||||
|
||||
switch typ.Kind() {
|
||||
case reflect.String,
|
||||
reflect.Bool,
|
||||
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
||||
reflect.Uintptr:
|
||||
uid = plist.UID(len(_objects))
|
||||
objects = append(_objects, _value)
|
||||
return
|
||||
case reflect.Map:
|
||||
uid = plist.UID(len(_objects))
|
||||
vv := make(map[string]interface{})
|
||||
keys := val.MapKeys()
|
||||
for _, k := range keys {
|
||||
vv[k.String()] = val.MapIndex(k).Interface()
|
||||
}
|
||||
objects = NewNSDictionary(vv).archive(_objects)
|
||||
return
|
||||
case reflect.Slice, reflect.Array:
|
||||
uid = plist.UID(len(_objects))
|
||||
vv := make([]interface{}, val.Len())
|
||||
for i := 0; i < val.Len(); i++ {
|
||||
vv[i] = val.Index(i).Interface()
|
||||
}
|
||||
objects = NewNSArray(vv).archive(_objects)
|
||||
return
|
||||
case reflect.Struct, reflect.Ptr:
|
||||
if typ.Kind() == reflect.Ptr {
|
||||
typ = typ.Elem()
|
||||
val = val.Elem()
|
||||
}
|
||||
|
||||
switch typ.Name() {
|
||||
case "NSUUID":
|
||||
uid = plist.UID(len(_objects))
|
||||
objects = NewNSUUID(val.Field(0).Bytes()).archive(_objects)
|
||||
return
|
||||
case "NSURL":
|
||||
uid = plist.UID(len(_objects))
|
||||
objects = NewNSURL(val.Field(0).String()).archive(_objects)
|
||||
return
|
||||
case "XCTestConfiguration":
|
||||
uid = plist.UID(len(_objects))
|
||||
objects = newXCTestConfiguration(_value).archive(_objects)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// TODO unarchive
|
||||
39
hrp/pkg/gidevice/pkg/nskeyedarchiver/nskeyedarchiver_test.go
Normal file
39
hrp/pkg/gidevice/pkg/nskeyedarchiver/nskeyedarchiver_test.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package nskeyedarchiver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
uuid "github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
func TestMarshal(t *testing.T) {
|
||||
// value := map[string]interface{}{
|
||||
// "a": 1,
|
||||
// "b": "2",
|
||||
// "c": true,
|
||||
// }
|
||||
|
||||
// value := []interface{}{
|
||||
// "a", 1,
|
||||
// "b", "2",
|
||||
// "c", false,
|
||||
// }
|
||||
|
||||
// value := NewNSUUID(uuid.NewV4().Bytes())
|
||||
|
||||
// value := NewNSURL("/tmp")
|
||||
|
||||
value := NewXCTestConfiguration(NewNSUUID(uuid.NewV4().Bytes()), NewNSURL("/tmp"), "", "")
|
||||
|
||||
raw, err := Marshal(value)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, v := range raw {
|
||||
fmt.Printf("%x", v)
|
||||
}
|
||||
fmt.Println()
|
||||
// fmt.Println(raw)
|
||||
}
|
||||
27
hrp/pkg/gidevice/pkg/nskeyedarchiver/nsnull.go
Normal file
27
hrp/pkg/gidevice/pkg/nskeyedarchiver/nsnull.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package nskeyedarchiver
|
||||
|
||||
import (
|
||||
"howett.net/plist"
|
||||
)
|
||||
|
||||
type NSNull struct{}
|
||||
|
||||
func NewNSNull() *NSNull {
|
||||
return &NSNull{}
|
||||
}
|
||||
|
||||
func (ns *NSNull) archive(objects []interface{}) []interface{} {
|
||||
info := map[string]interface{}{}
|
||||
|
||||
objects = append(objects, info)
|
||||
|
||||
info["$class"] = plist.UID(len(objects))
|
||||
|
||||
cls := map[string]interface{}{
|
||||
"$classname": "NSNull",
|
||||
"$classes": []interface{}{"NSNull", "NSObject"},
|
||||
}
|
||||
objects = append(objects, cls)
|
||||
|
||||
return objects
|
||||
}
|
||||
38
hrp/pkg/gidevice/pkg/nskeyedarchiver/nsurl.go
Normal file
38
hrp/pkg/gidevice/pkg/nskeyedarchiver/nsurl.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package nskeyedarchiver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"howett.net/plist"
|
||||
)
|
||||
|
||||
type NSURL struct {
|
||||
internal string
|
||||
}
|
||||
|
||||
func NewNSURL(path string) *NSURL {
|
||||
return &NSURL{
|
||||
internal: path,
|
||||
}
|
||||
}
|
||||
|
||||
func (ns *NSURL) archive(objects []interface{}) []interface{} {
|
||||
info := map[string]interface{}{}
|
||||
|
||||
objects = append(objects, info)
|
||||
|
||||
uid := plist.UID(0)
|
||||
info["NS.base"] = uid
|
||||
objects, uid = archive(objects, fmt.Sprintf("file://%s", ns.internal))
|
||||
info["NS.relative"] = uid
|
||||
|
||||
info["$class"] = plist.UID(len(objects))
|
||||
|
||||
cls := map[string]interface{}{
|
||||
"$classname": "NSURL",
|
||||
"$classes": []interface{}{"NSURL", "NSObject"},
|
||||
}
|
||||
objects = append(objects, cls)
|
||||
|
||||
return objects
|
||||
}
|
||||
13
hrp/pkg/gidevice/pkg/nskeyedarchiver/nsurl_test.go
Normal file
13
hrp/pkg/gidevice/pkg/nskeyedarchiver/nsurl_test.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package nskeyedarchiver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNSURL_archive(t *testing.T) {
|
||||
objs := make([]interface{}, 0, 1)
|
||||
nsurl := NewNSURL("/tmp")
|
||||
objects := nsurl.archive(objs)
|
||||
fmt.Println(objects)
|
||||
}
|
||||
51
hrp/pkg/gidevice/pkg/nskeyedarchiver/nsuuid.go
Normal file
51
hrp/pkg/gidevice/pkg/nskeyedarchiver/nsuuid.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package nskeyedarchiver
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
|
||||
"howett.net/plist"
|
||||
)
|
||||
|
||||
type NSUUID struct {
|
||||
internal []byte
|
||||
}
|
||||
|
||||
func NewNSUUID(uuid []byte) *NSUUID {
|
||||
return &NSUUID{
|
||||
internal: uuid,
|
||||
}
|
||||
}
|
||||
|
||||
func (ns *NSUUID) archive(objects []interface{}) []interface{} {
|
||||
info := map[string]interface{}{
|
||||
"NS.uuidbytes": ns.internal,
|
||||
}
|
||||
|
||||
objects = append(objects, info)
|
||||
|
||||
info["$class"] = plist.UID(len(objects))
|
||||
|
||||
cls := map[string]interface{}{
|
||||
"$classname": "NSUUID",
|
||||
"$classes": []interface{}{"NSUUID", "NSObject"},
|
||||
}
|
||||
objects = append(objects, cls)
|
||||
|
||||
return objects
|
||||
}
|
||||
|
||||
func (ns *NSUUID) String() string {
|
||||
buf := make([]byte, 36)
|
||||
|
||||
hex.Encode(buf[0:8], ns.internal[0:4])
|
||||
buf[8] = '-'
|
||||
hex.Encode(buf[9:13], ns.internal[4:6])
|
||||
buf[13] = '-'
|
||||
hex.Encode(buf[14:18], ns.internal[6:8])
|
||||
buf[18] = '-'
|
||||
hex.Encode(buf[19:23], ns.internal[8:10])
|
||||
buf[23] = '-'
|
||||
hex.Encode(buf[24:], ns.internal[10:])
|
||||
|
||||
return string(buf)
|
||||
}
|
||||
15
hrp/pkg/gidevice/pkg/nskeyedarchiver/nsuuid_test.go
Normal file
15
hrp/pkg/gidevice/pkg/nskeyedarchiver/nsuuid_test.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package nskeyedarchiver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
uuid "github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
func TestNSUUID_archive(t *testing.T) {
|
||||
objs := make([]interface{}, 0, 1)
|
||||
nsuuid := NewNSUUID(uuid.NewV4().Bytes())
|
||||
objects := nsuuid.archive(objs)
|
||||
fmt.Println(objects)
|
||||
}
|
||||
10
hrp/pkg/gidevice/pkg/nskeyedarchiver/xctcapabilities.go
Normal file
10
hrp/pkg/gidevice/pkg/nskeyedarchiver/xctcapabilities.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package nskeyedarchiver
|
||||
|
||||
type XCTCapabilities struct {
|
||||
internal map[string]interface{}
|
||||
}
|
||||
|
||||
func (caps *XCTCapabilities) archive(objects []interface{}) []interface{} {
|
||||
// TODO caps
|
||||
return nil
|
||||
}
|
||||
89
hrp/pkg/gidevice/pkg/nskeyedarchiver/xctestconfiguration.go
Normal file
89
hrp/pkg/gidevice/pkg/nskeyedarchiver/xctestconfiguration.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package nskeyedarchiver
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"howett.net/plist"
|
||||
)
|
||||
|
||||
type XCTestConfiguration struct {
|
||||
internal map[string]interface{}
|
||||
}
|
||||
|
||||
func newXCTestConfiguration(cfg interface{}) *XCTestConfiguration {
|
||||
return cfg.(*XCTestConfiguration)
|
||||
}
|
||||
|
||||
func NewXCTestConfiguration(nsuuid *NSUUID, nsurl *NSURL, targetBundleID, targetAppPath string) *XCTestConfiguration {
|
||||
contents := map[string]interface{}{
|
||||
"aggregateStatisticsBeforeCrash": map[string]interface{}{
|
||||
"XCSuiteRecordsKey": map[string]interface{}{},
|
||||
},
|
||||
"automationFrameworkPath": "/Developer/Library/PrivateFrameworks/XCTAutomationSupport.framework",
|
||||
"baselineFileRelativePath": nil,
|
||||
"baselineFileURL": nil,
|
||||
"defaultTestExecutionTimeAllowance": nil,
|
||||
"disablePerformanceMetrics": false,
|
||||
"emitOSLogs": false,
|
||||
"formatVersion": 2,
|
||||
"gatherLocalizableStringsData": false,
|
||||
"initializeForUITesting": true,
|
||||
"maximumTestExecutionTimeAllowance": nil,
|
||||
"productModuleName": "WebDriverAgentRunner", // set to other value is also OK
|
||||
"randomExecutionOrderingSeed": nil,
|
||||
"reportActivities": true,
|
||||
"reportResultsToIDE": true,
|
||||
"systemAttachmentLifetime": 2,
|
||||
"targetApplicationArguments": []interface{}{}, // maybe useless
|
||||
"targetApplicationEnvironment": nil,
|
||||
"targetApplicationPath": targetAppPath,
|
||||
"testApplicationDependencies": map[string]interface{}{},
|
||||
"testApplicationUserOverrides": nil,
|
||||
"testBundleRelativePath": nil,
|
||||
"testExecutionOrdering": 0,
|
||||
"testTimeoutsEnabled": false,
|
||||
"testsDrivenByIDE": false,
|
||||
"testsMustRunOnMainThread": true,
|
||||
"testsToRun": nil,
|
||||
"testsToSkip": nil,
|
||||
"treatMissingBaselinesAsFailures": false,
|
||||
"userAttachmentLifetime": 1,
|
||||
"testBundleURL": nsurl,
|
||||
"sessionIdentifier": nsuuid,
|
||||
"targetApplicationBundleID": targetBundleID,
|
||||
// "targetApplicationBundleID": "",
|
||||
}
|
||||
return &XCTestConfiguration{internal: contents}
|
||||
}
|
||||
|
||||
func (cfg *XCTestConfiguration) archive(objects []interface{}) []interface{} {
|
||||
info := map[string]interface{}{}
|
||||
objects = append(objects, info)
|
||||
|
||||
info["$class"] = plist.UID(len(objects))
|
||||
|
||||
cls := map[string]interface{}{
|
||||
"$classname": "XCTestConfiguration",
|
||||
"$classes": []interface{}{"XCTestConfiguration", "NSObject"},
|
||||
}
|
||||
objects = append(objects, cls)
|
||||
|
||||
for k, v := range cfg.internal {
|
||||
val := reflect.ValueOf(v)
|
||||
if !val.IsValid() {
|
||||
info[k] = plist.UID(0)
|
||||
continue
|
||||
}
|
||||
|
||||
typ := val.Type()
|
||||
|
||||
if k != "formatVersion" && (typ.Kind() == reflect.Bool || typ.Kind() == reflect.Uintptr || typ.Kind() == reflect.Int) {
|
||||
info[k] = v
|
||||
} else {
|
||||
var uid plist.UID
|
||||
objects, uid = archive(objects, v)
|
||||
info[k] = uid
|
||||
}
|
||||
}
|
||||
return objects
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package nskeyedarchiver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
uuid "github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
func TestXCTestConfiguration_archive(t *testing.T) {
|
||||
objs := make([]interface{}, 0, 1)
|
||||
xcTestConfiguration := NewXCTestConfiguration(NewNSUUID(uuid.NewV4().Bytes()), NewNSURL("/tmp"), "", "")
|
||||
objects := xcTestConfiguration.archive(objs)
|
||||
fmt.Println(objects)
|
||||
}
|
||||
Reference in New Issue
Block a user