Files
httprunner/hrp/pkg/gidevice/lockdown.go
2022-12-22 00:37:02 +08:00

685 lines
17 KiB
Go

package gidevice
import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"math/big"
"strconv"
"strings"
"time"
uuid "github.com/satori/go.uuid"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
)
var _ Lockdown = (*lockdown)(nil)
func newLockdown(dev *device) *lockdown {
return &lockdown{
umClient: dev.umClient,
client: dev.lockdownClient,
dev: dev,
}
}
type lockdown struct {
umClient *libimobiledevice.UsbmuxClient
client *libimobiledevice.LockdownClient
sessionID string
dev *device
iOSVersion []int
pairRecord *PairRecord
}
func (c *lockdown) QueryType() (LockdownType, error) {
pkt, err := c.client.NewXmlPacket(
c.client.NewBasicRequest(libimobiledevice.RequestTypeQueryType),
)
if err != nil {
return LockdownType{}, err
}
if err = c.client.SendPacket(pkt); err != nil {
return LockdownType{}, err
}
var respPkt libimobiledevice.Packet
if respPkt, err = c.client.ReceivePacket(); err != nil {
return LockdownType{}, err
}
var reply libimobiledevice.LockdownTypeResponse
if err = respPkt.Unmarshal(&reply); err != nil {
return LockdownType{}, err
}
return LockdownType{Type: reply.Type}, nil
}
func (c *lockdown) GetValue(domain, key string) (v interface{}, err error) {
pkt, err := c.client.NewXmlPacket(
c.client.NewGetValueRequest(domain, key),
)
if err != nil {
return nil, err
}
if err = c.client.SendPacket(pkt); err != nil {
return nil, err
}
var respPkt libimobiledevice.Packet
if respPkt, err = c.client.ReceivePacket(); err != nil {
return nil, err
}
var reply libimobiledevice.LockdownValueResponse
if err = respPkt.Unmarshal(&reply); err != nil {
return nil, err
}
v = reply.Value
return
}
func (c *lockdown) SetValue(domain, key string, value interface{}) (err error) {
var pkt libimobiledevice.Packet
if pkt, err = c.client.NewXmlPacket(
c.client.NewSetValueRequest(domain, key, value),
); err != nil {
return err
}
if err = c.client.SendPacket(pkt); err != nil {
return err
}
var respPkt libimobiledevice.Packet
if respPkt, err = c.client.ReceivePacket(); err != nil {
return err
}
var reply libimobiledevice.LockdownValueResponse
if err = respPkt.Unmarshal(&reply); err != nil {
return err
}
if !reply.Value.(bool) {
return errors.New("lockdown SetValue: Failed")
}
return
}
func (c *lockdown) EnterRecovery() (err error) {
var pkt libimobiledevice.Packet
if pkt, err = c.client.NewXmlPacket(
c.client.NewEnterRecoveryRequest(),
); err != nil {
return err
}
if err = c.client.SendPacket(pkt); err != nil {
return err
}
if _, err = c.client.ReceivePacket(); err != nil {
return err
}
return
}
func (c *lockdown) handshake() (err error) {
var lockdownType LockdownType
if lockdownType, err = c.QueryType(); err != nil {
return err
}
if lockdownType.Type != "com.apple.mobile.lockdown" {
return fmt.Errorf("lockdown handshake 'QueryType': %s", lockdownType.Type)
}
// if (device->version < DEVICE_VERSION(7,0,0))
// for older devices, we need to validate pairing to receive trusted host status
if c.pairRecord, err = c.dev.ReadPairRecord(); err == nil {
return nil
}
if !strings.Contains(err.Error(), libimobiledevice.ReplyCodeBadDevice.String()) {
return err
}
if c.pairRecord, err = c.Pair(); err != nil {
return err
}
err = c.dev.SavePairRecord(c.pairRecord)
return
}
func (c *lockdown) Pair() (pairRecord *PairRecord, err error) {
var buid string
if buid, err = newUsbmux(c.umClient).ReadBUID(); err != nil {
return nil, err
}
var devPublicKeyPem []byte
var devWiFiAddr string
if lockdownValue, err := c.GetValue("", "DevicePublicKey"); err != nil {
return nil, err
} else {
devPublicKeyPem = lockdownValue.([]byte)
}
if lockdownValue, err := c.GetValue("", "WiFiAddress"); err != nil {
return nil, err
} else {
devWiFiAddr = lockdownValue.(string)
}
if pairRecord, err = generatePairRecord(devPublicKeyPem); err != nil {
return nil, err
}
pairRecord.SystemBUID = buid
pairRecord.HostID = strings.ToUpper(uuid.NewV4().String())
hostPrivateKey := pairRecord.HostPrivateKey
pairRecord.HostPrivateKey = nil
rootPrivateKey := pairRecord.RootPrivateKey
pairRecord.RootPrivateKey = nil
var pkt libimobiledevice.Packet
if pkt, err = c.client.NewXmlPacket(
c.client.NewPairRequest(pairRecord),
); err != nil {
return nil, err
}
if err = c.client.SendPacket(pkt); err != nil {
return nil, err
}
var respPkt libimobiledevice.Packet
if respPkt, err = c.client.ReceivePacket(); err != nil {
return nil, err
}
var reply libimobiledevice.LockdownPairResponse
if err = respPkt.Unmarshal(&reply); err != nil {
return nil, err
}
pairRecord.EscrowBag = reply.EscrowBag
pairRecord.WiFiMACAddress = devWiFiAddr
pairRecord.HostPrivateKey = hostPrivateKey
pairRecord.RootPrivateKey = rootPrivateKey
return
}
func (c *lockdown) startSession(pairRecord *PairRecord) (err error) {
// if we have a running session, stop current one first
if c.sessionID != "" {
if err = c.stopSession(); err != nil {
return err
}
}
var pkt libimobiledevice.Packet
if pkt, err = c.client.NewXmlPacket(
c.client.NewStartSessionRequest(pairRecord.SystemBUID, pairRecord.HostID),
); err != nil {
return err
}
if err = c.client.SendPacket(pkt); err != nil {
return err
}
var respPkt libimobiledevice.Packet
if respPkt, err = c.client.ReceivePacket(); err != nil {
return fmt.Errorf("lockdown start session: %w", err)
}
var reply libimobiledevice.LockdownStartSessionResponse
if err = respPkt.Unmarshal(&reply); err != nil {
return err
}
if reply.EnableSessionSSL {
if err = c.client.EnableSSL(c.iOSVersion, pairRecord); err != nil {
return err
}
}
c.sessionID = reply.SessionID
return
}
func (c *lockdown) stopSession() (err error) {
if c.sessionID == "" {
return nil
}
var pkt libimobiledevice.Packet
if pkt, err = c.client.NewXmlPacket(
c.client.NewStopSessionRequest(c.sessionID),
); err != nil {
return err
}
if err = c.client.SendPacket(pkt); err != nil {
return err
}
var respPkt libimobiledevice.Packet
if respPkt, err = c.client.ReceivePacket(); err != nil {
return fmt.Errorf("lockdown stop session: %w", err)
}
var reply libimobiledevice.LockdownBasicResponse
if err = respPkt.Unmarshal(&reply); err != nil {
return err
}
c.sessionID = ""
return
}
func (c *lockdown) startService(service string, escrowBag []byte) (dynamicPort int, enableSSL bool, err error) {
req := c.client.NewStartServiceRequest(service)
if escrowBag != nil {
req.EscrowBag = escrowBag
}
var pkt libimobiledevice.Packet
if pkt, err = c.client.NewXmlPacket(req); err != nil {
return 0, false, err
}
if err = c.client.SendPacket(pkt); err != nil {
return 0, false, err
}
respPkt, err := c.client.ReceivePacket()
if err != nil {
return 0, false, err
}
var reply libimobiledevice.LockdownStartServiceResponse
if err = respPkt.Unmarshal(&reply); err != nil {
return 0, false, err
}
if reply.Error != "" {
return 0, false, fmt.Errorf("lockdown start service: %s", reply.Error)
}
dynamicPort = reply.Port
enableSSL = reply.EnableServiceSSL
return
}
func (c *lockdown) ImageMounterService() (imageMounter ImageMounter, err error) {
var innerConn InnerConn
if innerConn, err = c._startService(libimobiledevice.ImageMounterServiceName, nil); err != nil {
return nil, err
}
imageMounterClient := libimobiledevice.NewImageMounterClient(innerConn)
imageMounter = newImageMounter(imageMounterClient)
return
}
func (c *lockdown) ScreenshotService() (screenshot Screenshot, err error) {
var innerConn InnerConn
if innerConn, err = c._startService(libimobiledevice.ScreenshotServiceName, nil); err != nil {
return nil, err
}
screenshotClient := libimobiledevice.NewScreenshotClient(innerConn)
screenshot = newScreenshot(screenshotClient)
return
}
func (c *lockdown) SimulateLocationService() (simulateLocation SimulateLocation, err error) {
var innerConn InnerConn
if innerConn, err = c._startService(libimobiledevice.SimulateLocationServiceName, nil); err != nil {
return nil, err
}
simulateLocationClient := libimobiledevice.NewSimulateLocationClient(innerConn)
simulateLocation = newSimulateLocation(simulateLocationClient)
return
}
func (c *lockdown) InstallationProxyService() (installationProxy InstallationProxy, err error) {
var innerConn InnerConn
if innerConn, err = c._startService(libimobiledevice.InstallationProxyServiceName, nil); err != nil {
return nil, err
}
installationProxyClient := libimobiledevice.NewInstallationProxyClient(innerConn)
installationProxy = newInstallationProxy(installationProxyClient)
return
}
func (c *lockdown) InstrumentsService() (instruments Instruments, err error) {
service := libimobiledevice.InstrumentsServiceName
if DeviceVersion(c.iOSVersion...) >= DeviceVersion(14, 0, 0) {
service = libimobiledevice.InstrumentsSecureProxyServiceName
}
var innerConn InnerConn
if innerConn, err = c._startService(service, nil); err != nil {
return nil, err
}
instrumentsClient := libimobiledevice.NewInstrumentsClient(innerConn)
instruments = newInstruments(instrumentsClient)
if service == libimobiledevice.InstrumentsServiceName {
_ = innerConn.DismissSSL()
}
if err = instruments.notifyOfPublishedCapabilities(); err != nil {
return nil, err
}
return
}
func (c *lockdown) TestmanagerdService() (testmanagerd Testmanagerd, err error) {
service := libimobiledevice.TestmanagerdServiceName
if DeviceVersion(c.iOSVersion...) >= DeviceVersion(14, 0, 0) {
service = libimobiledevice.TestmanagerdSecureServiceName
}
var innerConn InnerConn
if innerConn, err = c._startService(service, nil); err != nil {
return nil, err
}
testmanagerdClient := libimobiledevice.NewTestmanagerdClient(innerConn)
testmanagerd = newTestmanagerd(testmanagerdClient, c.iOSVersion)
if service == libimobiledevice.TestmanagerdServiceName {
_ = innerConn.DismissSSL()
}
if err = testmanagerd.notifyOfPublishedCapabilities(); err != nil {
return nil, err
}
return
}
func (c *lockdown) AfcService() (afc Afc, err error) {
var innerConn InnerConn
if innerConn, err = c._startService(libimobiledevice.AfcServiceName, nil); err != nil {
return nil, err
}
afcClient := libimobiledevice.NewAfcClient(innerConn)
afc = newAfc(afcClient)
return
}
func (c *lockdown) HouseArrestService() (houseArrest HouseArrest, err error) {
var innerConn InnerConn
if innerConn, err = c._startService(libimobiledevice.HouseArrestServiceName, nil); err != nil {
return nil, err
}
houseArrestClient := libimobiledevice.NewHouseArrestClient(innerConn)
houseArrest = newHouseArrest(houseArrestClient)
return
}
func (c *lockdown) SyslogRelayService() (syslogRelay SyslogRelay, err error) {
var innerConn InnerConn
if innerConn, err = c._startService(libimobiledevice.SyslogRelayServiceName, nil); err != nil {
return nil, err
}
syslogRelayClient := libimobiledevice.NewSyslogRelayClient(innerConn)
syslogRelay = newSyslogRelay(syslogRelayClient)
return
}
func (c *lockdown) PcapdService(targetPID int, targetProcName string) (pcapd Pcapd, err error) {
var innerConn InnerConn
if innerConn, err = c._startService(libimobiledevice.PcapdServiceName, nil); err != nil {
return nil, err
}
pcapdClient := libimobiledevice.NewPcapdClient(innerConn, targetPID, targetProcName)
return newPcapdClient(pcapdClient), nil
}
func (c *lockdown) DiagnosticsRelayService() (diagnostics DiagnosticsRelay, err error) {
var innerConn InnerConn
if innerConn, err = c._startService(libimobiledevice.DiagnosticsRelayServiceName, nil); err != nil {
return nil, err
}
diagnosticsRelayClient := libimobiledevice.NewDiagnosticsRelayClient(innerConn)
diagnostics = newDiagnosticsRelay(diagnosticsRelayClient)
return
}
func (c *lockdown) SpringBoardService() (springboard SpringBoard, err error) {
var innerConn InnerConn
if innerConn, err = c._startService(libimobiledevice.SpringBoardServiceName, nil); err != nil {
return nil, err
}
springBoardServiceClient := libimobiledevice.NewSpringBoardClient(innerConn)
springboard = newSpringBoard(springBoardServiceClient)
return
}
func (c *lockdown) CrashReportMoverService() (crashReportMover CrashReportMover, err error) {
var innerConn InnerConn
if innerConn, err = c._startService(libimobiledevice.CrashReportMoverServiceName, nil); err != nil {
return nil, err
}
mover := newCrashReportMover(libimobiledevice.NewCrashReportMoverClient(innerConn))
if err = mover.readPing(); err != nil {
return nil, err
}
if innerConn, err = c._startService(libimobiledevice.CrashReportCopyMobileServiceName, nil); err != nil {
return nil, err
}
mover.afc = newAfc(libimobiledevice.NewAfcClient(innerConn))
crashReportMover = mover
return
}
func (c *lockdown) _startService(serviceName string, escrowBag []byte) (innerConn InnerConn, err error) {
if err = c.handshake(); err != nil {
return nil, err
}
if err = c.startSession(c.pairRecord); err != nil {
return nil, err
}
dynamicPort, enableSSL, err := c.startService(serviceName, escrowBag)
if err != nil {
return nil, err
}
if err = c.stopSession(); err != nil {
return nil, err
}
if innerConn, err = c.dev.NewConnect(dynamicPort, 0); err != nil {
return nil, err
}
// clean deadline
innerConn.Timeout(0)
if enableSSL {
if err = innerConn.Handshake(c.iOSVersion, c.pairRecord); err != nil {
return nil, err
}
}
return
}
func (c *lockdown) _getProductVersion() (version []int, err error) {
if c.iOSVersion != nil {
return c.iOSVersion, nil
}
var devProductVersion []string
if lockdownValue, err := c.GetValue("", "ProductVersion"); err != nil {
return nil, err
} else {
devProductVersion = strings.Split(lockdownValue.(string), ".")
}
version = make([]int, len(devProductVersion))
for i, v := range devProductVersion {
version[i], _ = strconv.Atoi(v)
}
// if len(version) == 2 {
// version = append(version, 0)
// }
c.iOSVersion = version
return
}
func generatePairRecord(devPublicKeyPem []byte) (pairRecord *PairRecord, err error) {
block, _ := pem.Decode(devPublicKeyPem)
var deviceKey *rsa.PublicKey
if deviceKey, err = x509.ParsePKCS1PublicKey(block.Bytes); err != nil {
return nil, err
}
var rootKey, hostKey *rsa.PrivateKey
if rootKey, err = rsa.GenerateKey(rand.Reader, 2048); err != nil {
return nil, err
}
if hostKey, err = rsa.GenerateKey(rand.Reader, 2048); err != nil {
return nil, err
}
serialNumber := big.NewInt(0)
notBefore := time.Now()
notAfter := notBefore.Add(time.Hour * (24 * 365) * 10)
rootTemplate := x509.Certificate{
IsCA: true,
SerialNumber: serialNumber,
Version: 2,
SignatureAlgorithm: x509.SHA1WithRSA,
PublicKeyAlgorithm: x509.RSA,
NotBefore: notBefore,
NotAfter: notAfter,
BasicConstraintsValid: true,
}
var caCert, cert []byte
if caCert, err = x509.CreateCertificate(rand.Reader, &rootTemplate, &rootTemplate, rootKey.Public(), rootKey); err != nil {
return nil, err
}
hostTemplate := x509.Certificate{
SerialNumber: serialNumber,
Version: 2,
SignatureAlgorithm: x509.SHA1WithRSA,
PublicKeyAlgorithm: x509.RSA,
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
BasicConstraintsValid: true,
}
if cert, err = x509.CreateCertificate(rand.Reader, &hostTemplate, &rootTemplate, hostKey.Public(), rootKey); err != nil {
return nil, err
}
h := sha1.New()
if _, err = h.Write(rootKey.N.Bytes()); err != nil {
return nil, err
}
subjectKeyId := h.Sum(nil)
deviceTemplate := x509.Certificate{
SerialNumber: serialNumber,
Version: 2,
SignatureAlgorithm: x509.SHA1WithRSA,
PublicKeyAlgorithm: x509.RSA,
NotBefore: notBefore,
NotAfter: notAfter,
BasicConstraintsValid: true,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
SubjectKeyId: subjectKeyId,
}
var deviceCert []byte
if deviceCert, err = x509.CreateCertificate(rand.Reader, &deviceTemplate, &rootTemplate, deviceKey, rootKey); err != nil {
return nil, err
}
var deviceCertPEM []byte
if deviceCertPEM, err = encodePemCertificate(deviceCert); err != nil {
return nil, err
}
var caPEM, caPrivatePEM []byte
if caPEM, caPrivatePEM, err = encodePairPemFormat(caCert, rootKey); err != nil {
return nil, err
}
var certPEM, certPrivatePEM []byte
if certPEM, certPrivatePEM, err = encodePairPemFormat(cert, hostKey); err != nil {
return nil, err
}
pairRecord = new(PairRecord)
pairRecord.DeviceCertificate = deviceCertPEM
pairRecord.HostCertificate = certPEM
pairRecord.HostPrivateKey = certPrivatePEM
pairRecord.RootCertificate = caPEM
pairRecord.RootPrivateKey = caPrivatePEM
return
}
func encodePairPemFormat(cert []byte, key *rsa.PrivateKey) ([]byte, []byte, error) {
p, err := encodePemCertificate(cert)
if err != nil {
return nil, nil, err
}
buf := new(bytes.Buffer)
if err := pem.Encode(buf, &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(key),
}); err != nil {
return nil, nil, err
}
privy := buf.Bytes()
return p, privy, nil
}
func encodePemCertificate(cert []byte) ([]byte, error) {
buf := new(bytes.Buffer)
if err := pem.Encode(buf, &pem.Block{
Type: "CERTIFICATE",
Bytes: cert,
}); err != nil {
return nil, err
}
return buf.Bytes(), nil
}