├── README.md ├── afc.go ├── arrest.go ├── devices.go ├── frames ├── frames.go ├── lockdown.go └── package.go ├── go.mod ├── instrument.go ├── main.go ├── mount.go ├── ns ├── dtxmessage.go └── keyed_archiver.go ├── screenshot.go ├── services ├── afc.go ├── arrest.go ├── common.go ├── installation.go ├── instrument.go ├── mount.go ├── screenshot.go ├── simulate.go └── syslog.go ├── sim.go ├── syslog.go ├── transport.go ├── tunnel ├── connUnix.go ├── connWindows.go ├── lockdown.go ├── mixConn.go ├── service.go └── tunnel.go ├── value.go └── wifiSync.go /README.md: -------------------------------------------------------------------------------- 1 | # iConsole 2 | 3 | ## About 4 | 5 | pure golang communication 6 | 7 | this project just for learn `iOS` `iTunes` communication 8 | 9 | reference: 10 | 11 | [libimobiledevice](https://github.com/libimobiledevice) 12 | 13 | ## Tools 14 | 15 | ### devices 16 | 17 | list all iOS devices 18 | 19 | ```bash 20 | ./iconsole devices 21 | 22 | iPhone AnonymousPhone 13.3 23 | ConnectionType: Network 24 | UDID: XXXXXXXX-XXXXXXXXXXXXXXXX 25 | iPad AnonymousResearch 13.2.3 26 | ConnectionType: USB 27 | UDID: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 28 | ``` 29 | 30 | ### syslog 31 | 32 | show all device system log like `idevicesyslog` 33 | 34 | ```base 35 | ./iconsole syslog -u XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 36 | 37 | Jan 9 18:18:00 AnonymousResearch backboardd(CoreBrightness)[67] : Lcurrent=101.0476 Lr=0.3557 DR=200.0000 factor=0.0000 38 | Jan 9 18:18:00 AnonymousResearch backboardd(CoreBrightness)[67] : Lcurrent=101.0476 Lr=0.3557 DR=200.0000 factor=0.0000 39 | Jan 9 18:18:00 AnonymousResearch trustd[118] : cert[0]: MissingIntermediate =(leaf)[force]> 0 40 | Jan 9 18:18:00 AnonymousResearch trustd[118] : cert[0]: NonEmptySubject =(path)[]> 0 41 | Jan 9 18:18:00 AnonymousResearch trustd[118] : cert[0]: MissingIntermediate =(leaf)[force]> 0 42 | Jan 9 18:18:00 AnonymousResearch trustd[118] : cert[0]: NonEmptySubject =(path)[]> 0 43 | ... 44 | ``` 45 | 46 | ### simlocation 47 | 48 | Simulate device location include convert coordinate u can go anywhere 49 | 50 | stander coordinate wgs84 51 | 52 | default coordinate gcj02 53 | 54 | > Remember: that you have to mount the Developer disk image on your device, if you want to use the `DTSimulateLocation` service. 55 | 56 | #### start 57 | ```bash 58 | ./iconsole simlocation start -u XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -lat xx.xxx -lon xx.xxx 59 | ``` 60 | 61 | #### stop 62 | ```bash 63 | ./iconsole simlocation stop -u XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 64 | ``` 65 | 66 | ### screenshot 67 | 68 | capture device screen file format `tiff` auto save work path. 69 | 70 | format `ScreenShot 2006-01-02 15.04.05.tiff` 71 | 72 | ```bash 73 | ./iconsole screenshot -u XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 74 | ``` 75 | 76 | ### sync 77 | 78 | enable disable get Wi-Fi communication 79 | 80 | ```bash 81 | ./iconsole sync -u XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 82 | 83 | Device enable WiFi connections 84 | ``` 85 | 86 | ```bash 87 | ./iconsole sync enable -u XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 88 | 89 | Successd 90 | ``` 91 | 92 | ```bash 93 | ./iconsole sync disable -u XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 94 | 95 | Successd 96 | ``` 97 | 98 | ### relay 99 | 100 | relay device port to local normal usage for `debugserver` 101 | 102 | transport communication 103 | 104 | examples: 105 | 106 | ```bash 107 | ./iconsole relay -u XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX tcp :1234 108 | ``` 109 | 110 | ```bash 111 | ./iconsole relay -u XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX unix /opt/xx 112 | ``` 113 | 114 | ```bash 115 | ./iconsole relay -u XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX tcp 127.0.0.1:1234 116 | ``` 117 | 118 | ### mount 119 | 120 | mount Developer image 121 | 122 | Developer image can be found in 123 | 124 | `/Application/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport//` 125 | 126 | examples: 127 | 128 | ```bash 129 | ./iconsole mount list 130 | ``` 131 | 132 | ```bash 133 | ./iconsole mount 134 | ``` 135 | 136 | ### afc 137 | 138 | support fully apple file conduit 139 | 140 | detail see program help 141 | -------------------------------------------------------------------------------- /afc.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "fmt" 7 | "iconsole/services" 8 | "io" 9 | "os" 10 | "path" 11 | 12 | "github.com/urfave/cli" 13 | ) 14 | 15 | func byteCountDecimal(b int64) string { 16 | const unit = 1000 17 | if b < unit { 18 | return fmt.Sprintf("%dB", b) 19 | } 20 | div, exp := int64(unit), 0 21 | for n := b / unit; n >= unit; n /= unit { 22 | div *= unit 23 | exp++ 24 | } 25 | return fmt.Sprintf("%.1f%cB", float64(b)/float64(div), "kMGTPE"[exp]) 26 | } 27 | 28 | func afcSpaceAction(ctx *cli.Context) error { 29 | udid := ctx.String("UDID") 30 | 31 | device, err := getDevice(udid) 32 | if err != nil { 33 | return err 34 | } 35 | 36 | afc, err := services.NewAFCService(device) 37 | if err != nil { 38 | return err 39 | } 40 | defer afc.Close() 41 | 42 | info, err := afc.GetDeviceInfo() 43 | if err != nil { 44 | return err 45 | } 46 | 47 | fmt.Printf(" Model: %s\n", info.Model) 48 | fmt.Printf(" BlockSize: %d\n", info.BlockSize/8) 49 | fmt.Printf(" FreeSpace: %s\n", byteCountDecimal(int64(info.FreeBytes))) 50 | fmt.Printf(" UsedSpace: %s\n", byteCountDecimal(int64(info.TotalBytes-info.FreeBytes))) 51 | fmt.Printf(" TotalSpace: %s\n", byteCountDecimal(int64(info.TotalBytes))) 52 | 53 | return nil 54 | } 55 | 56 | func afcLsAction(ctx *cli.Context) error { 57 | udid := ctx.String("UDID") 58 | 59 | args := ctx.Args() 60 | if len(args) <= 0 { 61 | return cli.ShowSubcommandHelp(ctx) 62 | } 63 | 64 | device, err := getDevice(udid) 65 | if err != nil { 66 | return err 67 | } 68 | 69 | afc, err := services.NewAFCService(device) 70 | if err != nil { 71 | return err 72 | } 73 | defer afc.Close() 74 | 75 | if p, err := afc.ReadDirectory(args[0]); err != nil { 76 | return err 77 | } else { 78 | for _, v := range p { 79 | if v != "." && v != ".." { 80 | if i, err := afc.GetFileInfo(path.Join(args[0], v)); err != nil { 81 | return err 82 | } else if i.IsDir() { 83 | fmt.Printf("%7s %s \x1B[1;34m%s\x1B[0m\n", byteCountDecimal(i.Size()), i.ModTime().Format("2006-01-02 15:04:05"), i.Name()) 84 | } else { 85 | fmt.Printf("%7s %s %s\n", byteCountDecimal(i.Size()), i.ModTime().Format("2006-01-02 15:04:05"), i.Name()) 86 | } 87 | } 88 | } 89 | } 90 | 91 | return nil 92 | } 93 | 94 | func printTree(afc *services.AFCService, raw string, hasNexts []bool) error { 95 | if p, err := afc.ReadDirectory(raw); err != nil { 96 | return err 97 | } else { 98 | for i, v := range p { 99 | if v == "." || v == ".." { 100 | continue 101 | } 102 | b := &bytes.Buffer{} 103 | for _, hasNext := range hasNexts { 104 | if hasNext { 105 | b.WriteString("│ ") 106 | } else { 107 | b.WriteString(" ") 108 | } 109 | } 110 | var name string 111 | fullPath := path.Join(raw, v) 112 | info, err := afc.GetFileInfo(fullPath) 113 | if err != nil { 114 | return err 115 | } else if info.IsDir() { 116 | name = fmt.Sprintf("\x1B[1;34m%s\x1B[0m", info.Name()) 117 | //printTree(afc, v) 118 | } else { 119 | name = fmt.Sprintf("%s", info.Name()) 120 | } 121 | 122 | lastIndex := len(p) - 1 123 | 124 | // print tree 125 | if i == lastIndex { 126 | b.WriteString("└──") 127 | b.WriteString(name) 128 | } else { 129 | b.WriteString("├──") 130 | b.WriteString(name) 131 | } 132 | 133 | fmt.Println(string(b.Bytes())) 134 | 135 | if info.IsDir() { 136 | if i == lastIndex { 137 | hasNexts = append(hasNexts, false) 138 | } else { 139 | hasNexts = append(hasNexts, true) 140 | } 141 | err := printTree(afc, fullPath, hasNexts) 142 | hasNexts = hasNexts[:len(hasNexts)-1] 143 | if err != nil { 144 | return err 145 | } 146 | } 147 | } 148 | } 149 | 150 | return nil 151 | } 152 | 153 | func afcTreeAction(ctx *cli.Context) error { 154 | udid := ctx.String("UDID") 155 | 156 | args := ctx.Args() 157 | if len(args) <= 0 { 158 | return cli.ShowSubcommandHelp(ctx) 159 | } 160 | 161 | device, err := getDevice(udid) 162 | if err != nil { 163 | return err 164 | } 165 | 166 | afc, err := services.NewAFCService(device) 167 | if err != nil { 168 | return err 169 | } 170 | defer afc.Close() 171 | 172 | return printTree(afc, args[0], []bool{}) 173 | } 174 | 175 | func afcRemoveAction(ctx *cli.Context) error { 176 | udid := ctx.String("UDID") 177 | 178 | args := ctx.Args() 179 | if len(args) <= 0 { 180 | return cli.ShowSubcommandHelp(ctx) 181 | } 182 | 183 | device, err := getDevice(udid) 184 | if err != nil { 185 | return err 186 | } 187 | 188 | afc, err := services.NewAFCService(device) 189 | if err != nil { 190 | return err 191 | } 192 | defer afc.Close() 193 | 194 | return afc.RemoveAll(args[0]) 195 | } 196 | 197 | func afcUploadAction(ctx *cli.Context) error { 198 | udid := ctx.String("UDID") 199 | 200 | args := ctx.Args() 201 | if len(args) < 2 { 202 | return cli.ShowSubcommandHelp(ctx) 203 | } 204 | 205 | src := args[0] 206 | dst := args[1] 207 | 208 | if fi, err := os.Stat(src); err != nil { 209 | return err 210 | } else if fi.IsDir() { 211 | return errors.New("for now only support file not directory") 212 | } 213 | 214 | device, err := getDevice(udid) 215 | if err != nil { 216 | return err 217 | } 218 | 219 | afc, err := services.NewAFCService(device) 220 | if err != nil { 221 | return err 222 | } 223 | defer afc.Close() 224 | 225 | f, err := afc.FileOpen(dst, services.AFC_RW) 226 | if err != nil { 227 | return err 228 | } 229 | defer f.Close() 230 | 231 | srcFile, err := os.Open(src) 232 | if err != nil { 233 | return err 234 | } 235 | defer srcFile.Close() 236 | 237 | if _, err := io.Copy(f, srcFile); err != nil { 238 | return err 239 | } 240 | 241 | return nil 242 | } 243 | 244 | func afcDownloadAction(ctx *cli.Context) error { 245 | udid := ctx.String("UDID") 246 | 247 | args := ctx.Args() 248 | if len(args) < 2 { 249 | return cli.ShowSubcommandHelp(ctx) 250 | } 251 | 252 | src := args[1] 253 | dst := args[0] 254 | 255 | device, err := getDevice(udid) 256 | if err != nil { 257 | return err 258 | } 259 | 260 | afc, err := services.NewAFCService(device) 261 | if err != nil { 262 | return err 263 | } 264 | defer afc.Close() 265 | 266 | fi, err := afc.GetFileInfo(dst) 267 | if err != nil { 268 | return err 269 | } else if fi.IsDir() { 270 | return errors.New("for now only support file not directory") 271 | } 272 | 273 | f, err := afc.FileOpen(dst, services.AFC_RDONLY) 274 | if err != nil { 275 | return err 276 | } 277 | defer f.Close() 278 | 279 | srcFile, err := os.Create(src) 280 | if err != nil { 281 | return err 282 | } 283 | defer srcFile.Close() 284 | 285 | if _, err := io.Copy(srcFile, f); err != nil { 286 | return err 287 | } 288 | 289 | return nil 290 | } 291 | 292 | func initAFCCommand() cli.Command { 293 | return cli.Command{ 294 | Name: "afc", 295 | Usage: "Apple file conduit", 296 | Flags: globalFlags, 297 | Subcommands: []cli.Command{ 298 | { 299 | Name: "space", 300 | ShortName: "s", 301 | Usage: "Device space usage detail", 302 | Action: afcSpaceAction, 303 | Flags: globalFlags, 304 | }, 305 | { 306 | Name: "dir", 307 | Usage: "dir ", 308 | Action: afcLsAction, 309 | Flags: globalFlags, 310 | }, 311 | { 312 | Name: "tree", 313 | Usage: "tree ", 314 | Action: afcTreeAction, 315 | Flags: globalFlags, 316 | }, 317 | { 318 | Name: "upload", 319 | Usage: "Upload ", 320 | Action: afcUploadAction, 321 | Flags: globalFlags, 322 | }, 323 | { 324 | Name: "download", 325 | Usage: "download ", 326 | Action: afcDownloadAction, 327 | Flags: globalFlags, 328 | }, 329 | { 330 | Name: "remove", 331 | Usage: "remove ", 332 | Action: afcRemoveAction, 333 | Flags: globalFlags, 334 | }, 335 | }, 336 | } 337 | } 338 | -------------------------------------------------------------------------------- /arrest.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "iconsole/services" 6 | "path" 7 | 8 | "github.com/urfave/cli" 9 | ) 10 | 11 | func arrestAction(ctx *cli.Context) error { 12 | udid := ctx.String("UDID") 13 | 14 | device, err := getDevice(udid) 15 | if err != nil { 16 | return err 17 | } 18 | 19 | args := ctx.Args() 20 | if len(args) <= 0 { 21 | return cli.ShowSubcommandHelp(ctx) 22 | } 23 | 24 | a, err := services.NewHouseArrestService(device) 25 | if err != nil { 26 | return err 27 | } 28 | afc, err := a.Documents(args[0]) 29 | if err != nil { 30 | return err 31 | } 32 | 33 | base := "Documents" 34 | 35 | if p, err := afc.ReadDirectory(base); err != nil { 36 | return err 37 | } else { 38 | for _, v := range p { 39 | if v != "." && v != ".." { 40 | if i, err := afc.GetFileInfo(path.Join(base, v)); err != nil { 41 | return err 42 | } else if i.IsDir() { 43 | fmt.Printf("%7s %s \x1B[1;34m%s\x1B[0m\n", byteCountDecimal(i.Size()), i.ModTime().Format("2006-01-02 15:04:05"), i.Name()) 44 | } else { 45 | fmt.Printf("%7s %s %s\n", byteCountDecimal(i.Size()), i.ModTime().Format("2006-01-02 15:04:05"), i.Name()) 46 | } 47 | } 48 | } 49 | } 50 | 51 | return nil 52 | } 53 | 54 | func initArrest() cli.Command { 55 | return cli.Command{ 56 | Name: "arrest", 57 | Usage: "House arrest", 58 | UsageText: "iconsole arrest ", 59 | Action: arrestAction, 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /devices.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "iconsole/tunnel" 6 | 7 | "github.com/urfave/cli" 8 | ) 9 | 10 | func devicesAction(ctx *cli.Context) error { 11 | devices, err := tunnel.Devices() 12 | if err != nil { 13 | return err 14 | } 15 | for _, d := range devices { 16 | conn, err := tunnel.LockdownDial(d) 17 | if err != nil { 18 | return err 19 | } 20 | 21 | if err := conn.StartSession(); err != nil { 22 | return err 23 | } 24 | 25 | deviceName, err := conn.DeviceName() 26 | if err != nil { 27 | return err 28 | } 29 | deviceType, err := conn.DeviceClass() 30 | if err != nil { 31 | return err 32 | } 33 | version, err := conn.ProductVersion() 34 | if err != nil { 35 | return err 36 | } 37 | 38 | fmt.Printf("%s %s %s\n\tConnectionType: %s\n\tUDID: %s\n", deviceType, deviceName, version, d.GetConnectionType(), d.GetSerialNumber()) 39 | 40 | if err := conn.StopSession(); err != nil { 41 | return err 42 | } 43 | 44 | conn.Close() 45 | } 46 | return nil 47 | } 48 | 49 | func initDevices() cli.Command { 50 | return cli.Command{ 51 | Name: "devices", 52 | ShortName: "dev", 53 | Usage: "List all connect devices", 54 | Action: devicesAction, 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /frames/frames.go: -------------------------------------------------------------------------------- 1 | package frames 2 | 3 | const ( 4 | ProgramName = "iConsole" 5 | ClientVersion = "iConsole-Beta" 6 | BundleID = "anonymous5l.iConsole" 7 | LibUSBMuxVersion = 3 8 | ) 9 | 10 | const ( 11 | Listen = "Listen" 12 | Connect = "Connect" 13 | ListDevices = "ListDevices" 14 | ) 15 | 16 | type Device interface { 17 | GetConnectionType() string 18 | GetDeviceID() int 19 | GetSerialNumber() string 20 | } 21 | 22 | type Response interface { 23 | GetMessageType() string 24 | } 25 | 26 | type ( 27 | BaseResponse struct { 28 | MessageType string `plist:"MessageType"` 29 | } 30 | 31 | BaseRequest struct { 32 | MessageType string `plist:"MessageType"` 33 | BundleID string `plist:"BundleID"` 34 | LibUSBMuxVersion int `plist:"kLibUSBMuxVersion,omitempty"` 35 | ClientVersionString string `plist:"ClientVersionString"` 36 | ProgramName string `plist:"ProgName"` 37 | } 38 | 39 | ConnectRequest struct { 40 | BaseRequest 41 | DeviceID int `plist:"DeviceID"` 42 | PortNumber int `plist:"PortNumber"` 43 | } 44 | 45 | DeviceModel struct { 46 | ConnectionType string `plist:"ConnectionType"` 47 | DeviceID int `plist:"DeviceID"` 48 | SerialNumber string `plist:"SerialNumber"` 49 | } 50 | 51 | NetworkDevice struct { 52 | DeviceModel 53 | EscapedFullServiceName string `plist:"EscapedFullServiceName"` 54 | InterfaceIndex int `plist:"InterfaceIndex"` 55 | NetworkAddress []byte `plist:"NetworkAddress"` 56 | } 57 | 58 | USBDevice struct { 59 | DeviceModel 60 | ConnectionSpeed int `plist:"ConnectionSpeed"` 61 | LocationID int `plist:"LocationID"` 62 | ProductID int `plist:"ProductID"` 63 | UDID string `plist:"UDID"` 64 | USBSerialNumber string `plist:"USBSerialNumber"` 65 | } 66 | 67 | DeviceAttached struct { 68 | BaseResponse 69 | DeviceID int `plist:"DeviceID"` 70 | Properties Device `plist:"Properties"` 71 | } 72 | 73 | DeviceDetached struct { 74 | BaseResponse 75 | DeviceID int `plist:"DeviceID"` 76 | } 77 | 78 | Result struct { 79 | BaseResponse 80 | Number int `plist:"Number"` 81 | } 82 | 83 | PairRecordRequest struct { 84 | BaseRequest 85 | PairRecordID string `plist:"PairRecordID"` 86 | } 87 | 88 | PairRecordResponse struct { 89 | Result 90 | PairRecordData []byte `plist:"PairRecordData"` 91 | } 92 | 93 | SavePairRecordRequest struct { 94 | BaseRequest 95 | PairRecordID string `plist:"PairRecordID"` 96 | PairRecordData []byte `plist:"PairRecordData"` 97 | DeviceID int `plist:"DeviceID"` 98 | } 99 | 100 | DeletePairRecordRequest struct { 101 | BaseRequest 102 | PairRecordID string `plist:"PairRecordID"` 103 | } 104 | ) 105 | 106 | func (this *DeviceModel) GetConnectionType() string { 107 | return this.ConnectionType 108 | } 109 | 110 | func (this *DeviceModel) GetDeviceID() int { 111 | return this.DeviceID 112 | } 113 | 114 | func (this *DeviceModel) GetSerialNumber() string { 115 | return this.SerialNumber 116 | } 117 | 118 | func (this *BaseResponse) GetMessageType() string { 119 | return this.MessageType 120 | } 121 | 122 | func CreateBaseRequest(mt string) *BaseRequest { 123 | return &BaseRequest{ 124 | MessageType: mt, 125 | BundleID: BundleID, 126 | ClientVersionString: ClientVersion, 127 | ProgramName: ProgramName, 128 | LibUSBMuxVersion: LibUSBMuxVersion, 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /frames/lockdown.go: -------------------------------------------------------------------------------- 1 | package frames 2 | 3 | const ( 4 | ProtocolVersion = "2" 5 | ) 6 | 7 | type LockdownRequest struct { 8 | Label string `plist:"Label"` 9 | ProtocolVersion string `plist:"ProtocolVersion"` 10 | Request string `plist:"Request"` 11 | } 12 | 13 | type ValueRequest struct { 14 | LockdownRequest 15 | Domain string `plist:"Domain,omitempty"` 16 | Key string `plist:"Key,omitempty"` 17 | Value interface{} `plist:"Value,omitempty"` 18 | } 19 | 20 | type ValueResponse struct { 21 | LockdownRequest 22 | Domain string `plist:"Domain,omitempty"` 23 | Key string `plist:"Key,omitempty"` 24 | } 25 | 26 | type LockdownResponse struct { 27 | Request string `plist:"Request"` 28 | Error string `plist:"Error"` 29 | } 30 | 31 | type LockdownTypeResponse struct { 32 | LockdownResponse 33 | Type string `plist:"Type"` 34 | } 35 | 36 | type LockdownValueResponse struct { 37 | LockdownResponse 38 | Key string `plist:"Key"` 39 | Value interface{} `plist:"Value"` 40 | } 41 | 42 | func CreateLockdownRequest(request string) *LockdownRequest { 43 | return &LockdownRequest{ 44 | Label: BundleID, 45 | ProtocolVersion: ProtocolVersion, 46 | Request: request, 47 | } 48 | } 49 | 50 | type StartSessionRequest struct { 51 | LockdownRequest 52 | SystemBUID string `plist:"SystemBUID"` 53 | HostID string `plist:"HostID"` 54 | } 55 | 56 | type StopSessionRequest struct { 57 | LockdownRequest 58 | SessionID string `plist:"SessionID"` 59 | } 60 | 61 | type StartSessionResponse struct { 62 | LockdownResponse 63 | EnableSessionSSL bool `plist:"EnableSessionSSL"` 64 | SessionID string `plist:"SessionID"` 65 | } 66 | 67 | type PairRequest struct { 68 | LockdownRequest 69 | PairRecord *PairRecord `plist:"PairRecord"` 70 | PairingOptions map[string]interface{} `plist:"PairingOptions"` 71 | } 72 | 73 | type PairResponse struct { 74 | LockdownResponse 75 | EscrowBag []byte `plist:"EscrowBag"` 76 | } 77 | 78 | type PairRecord struct { 79 | DeviceCertificate []byte `plist:"DeviceCertificate"` 80 | EscrowBag []byte `plist:"EscrowBag,omitempty"` 81 | HostCertificate []byte `plist:"HostCertificate"` 82 | HostPrivateKey []byte `plist:"HostPrivateKey,omitempty"` 83 | HostID string `plist:"HostID"` 84 | RootCertificate []byte `plist:"RootCertificate"` 85 | RootPrivateKey []byte `plist:"RootPrivateKey,omitempty"` 86 | SystemBUID string `plist:"SystemBUID"` 87 | WiFiMACAddress string `plist:"WiFiMACAddress,omitempty"` 88 | } 89 | 90 | type StartServiceRequest struct { 91 | LockdownRequest 92 | Service string `plist:"Service"` 93 | EscrowBag []byte `plist:"EscrowBag,omitempty"` 94 | } 95 | 96 | type StartServiceResponse struct { 97 | LockdownResponse 98 | EnableServiceSSL bool `plist:"EnableServiceSSL"` 99 | Port int `plist:"Port"` 100 | Service string `plist:"Service"` 101 | } 102 | -------------------------------------------------------------------------------- /frames/package.go: -------------------------------------------------------------------------------- 1 | package frames 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "errors" 7 | "fmt" 8 | 9 | "howett.net/plist" 10 | ) 11 | 12 | type ServicePackage struct { 13 | Length uint32 14 | Body []byte 15 | } 16 | 17 | func (this *ServicePackage) Pack(body interface{}, format int) ([]byte, error) { 18 | frameXml, err := plist.MarshalIndent(body, format, "\t") 19 | if err != nil { 20 | return nil, err 21 | } 22 | 23 | var buf bytes.Buffer 24 | l := make([]byte, 4) 25 | binary.BigEndian.PutUint32(l, uint32(len(frameXml))) 26 | buf.Write(l) 27 | buf.Write(frameXml) 28 | 29 | return buf.Bytes(), nil 30 | } 31 | 32 | func (this *ServicePackage) UnmarshalBody(pkg interface{}) error { 33 | _, err := plist.Unmarshal(this.Body, pkg) 34 | return err 35 | } 36 | 37 | func (this *ServicePackage) String() string { 38 | return string(this.Body) 39 | } 40 | 41 | func UnpackLockdown(rawBytes []byte) (*ServicePackage, error) { 42 | pkg := &ServicePackage{} 43 | pkg.Length = binary.BigEndian.Uint32(rawBytes[:4]) 44 | if len(rawBytes[4:]) != int(pkg.Length) { 45 | return nil, errors.New("buffer not enough") 46 | } 47 | pkg.Body = rawBytes[4:] 48 | return pkg, nil 49 | } 50 | 51 | type Package struct { 52 | Length uint32 53 | Version uint32 54 | Type uint32 55 | Tag uint32 56 | Body []byte 57 | } 58 | 59 | func (this *Package) Pack(body interface{}) ([]byte, error) { 60 | frameXml, err := plist.MarshalIndent(body, plist.XMLFormat, "\t") 61 | if err != nil { 62 | return nil, err 63 | } 64 | 65 | var buf bytes.Buffer 66 | 67 | l := make([]byte, 4) 68 | binary.LittleEndian.PutUint32(l, uint32(len(frameXml)+16)) 69 | buf.Write(l) 70 | binary.LittleEndian.PutUint32(l, this.Version) 71 | buf.Write(l) 72 | binary.LittleEndian.PutUint32(l, this.Type) // xml plist 73 | buf.Write(l) 74 | binary.LittleEndian.PutUint32(l, this.Tag) // pkg len 75 | buf.Write(l) 76 | buf.Write(frameXml) 77 | 78 | return buf.Bytes(), nil 79 | } 80 | 81 | func (this *Package) String() string { 82 | return fmt.Sprintf("Length: %d Version: %d Type: %d Tag: %d\nBody: %s", 83 | this.Length, this.Version, this.Type, this.Tag, 84 | this.Body) 85 | } 86 | 87 | func (this *Package) UnmarshalBody(pkg interface{}) error { 88 | _, err := plist.Unmarshal(this.Body, pkg) 89 | return err 90 | } 91 | 92 | func Unpack(rawBytes []byte) (*Package, error) { 93 | pkg := &Package{} 94 | pkg.Length = binary.LittleEndian.Uint32(rawBytes[:4]) 95 | if len(rawBytes) != int(pkg.Length) { 96 | return nil, errors.New("buffer not enough") 97 | } 98 | pkg.Version = binary.LittleEndian.Uint32(rawBytes[4:8]) 99 | pkg.Type = binary.LittleEndian.Uint32(rawBytes[8:12]) 100 | pkg.Tag = binary.LittleEndian.Uint32(rawBytes[12:16]) 101 | pkg.Body = rawBytes[16:] 102 | return pkg, nil 103 | } 104 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module iconsole 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/olekukonko/tablewriter v0.0.4 7 | github.com/satori/go.uuid v1.2.0 8 | github.com/urfave/cli v1.22.2 9 | howett.net/plist v0.0.0-20181124034731-591f970eefbb 10 | ) 11 | -------------------------------------------------------------------------------- /instrument.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "iconsole/services" 7 | "os" 8 | "strconv" 9 | "strings" 10 | 11 | "github.com/olekukonko/tablewriter" 12 | 13 | "github.com/urfave/cli" 14 | ) 15 | 16 | func actionProcessList(ctx *cli.Context) error { 17 | udid := ctx.String("UDID") 18 | 19 | device, err := getDevice(udid) 20 | if err != nil { 21 | return err 22 | } 23 | 24 | s, err := services.NewInstrumentService(device) 25 | if err != nil { 26 | return err 27 | } 28 | 29 | if err := s.Handshake(); err != nil { 30 | return err 31 | } 32 | 33 | p, err := s.ProcessList() 34 | if err != nil { 35 | return err 36 | } 37 | 38 | writer := tablewriter.NewWriter(os.Stdout) 39 | writer.SetHeader([]string{"PID", "ProcessName", "StartDate"}) 40 | for _, v := range p { 41 | writer.Append([]string{ 42 | strconv.Itoa(v.Pid), 43 | v.Name, 44 | v.StartDate.Format("2006-01-02 15:04:05"), 45 | }) 46 | } 47 | writer.Render() 48 | 49 | return nil 50 | } 51 | 52 | func actionAppList(ctx *cli.Context) error { 53 | udid := ctx.String("UDID") 54 | 55 | device, err := getDevice(udid) 56 | if err != nil { 57 | return err 58 | } 59 | 60 | s, err := services.NewInstrumentService(device) 61 | if err != nil { 62 | return err 63 | } 64 | 65 | if err := s.Handshake(); err != nil { 66 | return err 67 | } 68 | 69 | p, err := s.AppList() 70 | if err != nil { 71 | return err 72 | } 73 | 74 | writer := tablewriter.NewWriter(os.Stdout) 75 | writer.SetHeader([]string{"Name", "BundleID"}) 76 | for _, a := range p { 77 | writer.Append([]string{ 78 | a.DisplayName, 79 | a.CFBundleIdentifier, 80 | }) 81 | } 82 | writer.Render() 83 | 84 | return nil 85 | } 86 | 87 | func actionKill(ctx *cli.Context) error { 88 | udid := ctx.String("UDID") 89 | pid := ctx.Int("pid") 90 | 91 | if pid <= 0 { 92 | return errors.New("argument error") 93 | } 94 | 95 | device, err := getDevice(udid) 96 | if err != nil { 97 | return err 98 | } 99 | 100 | s, err := services.NewInstrumentService(device) 101 | if err != nil { 102 | return err 103 | } 104 | 105 | if err := s.Handshake(); err != nil { 106 | return err 107 | } 108 | 109 | if err := s.Kill(pid); err != nil { 110 | return err 111 | } 112 | 113 | return nil 114 | } 115 | 116 | func actionLaunch(ctx *cli.Context) error { 117 | udid := ctx.String("UDID") 118 | bundleId := ctx.String("bundleid") 119 | 120 | if strings.Trim(bundleId, "") == "" { 121 | return errors.New("argument error") 122 | } 123 | 124 | device, err := getDevice(udid) 125 | if err != nil { 126 | return err 127 | } 128 | 129 | s, err := services.NewInstrumentService(device) 130 | if err != nil { 131 | return err 132 | } 133 | 134 | if err := s.Handshake(); err != nil { 135 | return err 136 | } 137 | 138 | pid, err := s.Launch(bundleId) 139 | if err != nil { 140 | return err 141 | } 142 | 143 | fmt.Printf("Launch successful pid: %d\n", pid) 144 | 145 | return nil 146 | } 147 | 148 | func initProcessCommond() cli.Command { 149 | return cli.Command{ 150 | Name: "instrument", 151 | Usage: "Instrument tools", 152 | UsageText: "iconsole instrument [-u serial_number|UDID] ", 153 | Flags: globalFlags, 154 | Subcommands: []cli.Command{ 155 | { 156 | Name: "proclist", 157 | ShortName: "pl", 158 | Usage: "List all process", 159 | Action: actionProcessList, 160 | Flags: globalFlags, 161 | }, 162 | { 163 | Name: "applist", 164 | ShortName: "al", 165 | Usage: "List all installed app", 166 | Action: actionAppList, 167 | Flags: globalFlags, 168 | }, 169 | { 170 | Name: "kill", 171 | ShortName: "k", 172 | Usage: "Kill application", 173 | Action: actionKill, 174 | Flags: append(globalFlags, cli.IntFlag{ 175 | Name: "pid, p", 176 | Usage: "Process id", 177 | EnvVar: "PROCESS_PID", 178 | Required: true, 179 | }), 180 | }, 181 | { 182 | Name: "launch", 183 | ShortName: "s", 184 | Usage: "Launch application", 185 | Action: actionLaunch, 186 | Flags: append(globalFlags, cli.StringFlag{ 187 | Name: "bundleid, i", 188 | Usage: "Application bundle id", 189 | EnvVar: "BUNDLE_ID", 190 | Required: true, 191 | }), 192 | }, 193 | }, 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "iconsole/frames" 6 | "iconsole/tunnel" 7 | "os" 8 | 9 | "github.com/urfave/cli" 10 | ) 11 | 12 | var globalFlags = []cli.Flag{ 13 | cli.StringFlag{ 14 | Name: "UDID, u", 15 | Usage: "device serialNumber UDID", 16 | EnvVar: "DEVICE_UDID", 17 | Value: "", 18 | }, 19 | } 20 | 21 | func session(udid string, cb func(*tunnel.LockdownConnection) error) error { 22 | device, err := getDevice(udid) 23 | if err != nil { 24 | return err 25 | } 26 | 27 | conn, err := tunnel.LockdownDial(device) 28 | if err != nil { 29 | return err 30 | } 31 | 32 | defer conn.Close() 33 | 34 | if err := conn.StartSession(); err != nil { 35 | return err 36 | } 37 | 38 | defer conn.StopSession() 39 | 40 | return cb(conn) 41 | } 42 | 43 | func getDevice(udid string) (frames.Device, error) { 44 | devices, err := tunnel.Devices() 45 | if err != nil { 46 | return nil, err 47 | } 48 | 49 | var ds []frames.Device 50 | 51 | for i, d := range devices { 52 | if udid == "" && d.GetConnectionType() == "USB" { 53 | return d, nil 54 | } 55 | if d.GetSerialNumber() == udid { 56 | ds = append(ds, devices[i]) 57 | } 58 | } 59 | 60 | if len(ds) > 0 { 61 | for _, d := range ds { 62 | if d.GetConnectionType() == "USB" { 63 | return d, nil 64 | } 65 | } 66 | return ds[0], nil 67 | } 68 | 69 | return nil, fmt.Errorf("device %s was not found", udid) 70 | } 71 | 72 | func main() { 73 | app := cli.NewApp() 74 | app.Name = "iConsole" 75 | app.Usage = "iOS device tools" 76 | app.Version = "1.0.0" 77 | app.Authors = []cli.Author{ 78 | { 79 | Name: "anonymous5l", 80 | Email: "wxdxfg@hotmail.com", 81 | }, 82 | } 83 | app.Commands = []cli.Command{ 84 | initDevices(), 85 | initSyslogCommond(), 86 | initSimCommond(), 87 | initScreenShotCommond(), 88 | initSyncCommond(), 89 | initValueCommond(), 90 | initTransportCommand(), 91 | initMountCommand(), 92 | initAFCCommand(), 93 | initArrest(), 94 | initProcessCommond(), 95 | } 96 | 97 | if err := app.Run(os.Args); err != nil { 98 | fmt.Println(err.Error()) 99 | return 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /mount.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/base64" 5 | "fmt" 6 | "iconsole/services" 7 | "strings" 8 | 9 | "github.com/urfave/cli" 10 | ) 11 | 12 | func actionList(ctx *cli.Context) error { 13 | args := ctx.Args() 14 | udid := ctx.String("UDID") 15 | imageType := ctx.String("type") 16 | 17 | if len(args) > 0 { 18 | if strings.ToLower(args[0]) == "help" { 19 | return cli.ShowSubcommandHelp(ctx) 20 | } 21 | } 22 | 23 | if device, err := getDevice(udid); err != nil { 24 | return err 25 | } else if ms, err := services.NewMountService(device); err != nil { 26 | return err 27 | } else if images, err := ms.Images(imageType); err != nil { 28 | return err 29 | } else { 30 | fmt.Printf("ImageSignatures[%d]:\n", len(images.ImageSignature)) 31 | 32 | for i, is := range images.ImageSignature { 33 | fmt.Printf("%2d: %s\n", i, base64.StdEncoding.EncodeToString(is)) 34 | } 35 | } 36 | 37 | return nil 38 | } 39 | 40 | func actionMount(ctx *cli.Context) error { 41 | udid := ctx.String("UDID") 42 | imageType := ctx.String("type") 43 | 44 | args := ctx.Args() 45 | 46 | var dmgFile, dmgFileSignature string 47 | 48 | if len(args) == 2 { 49 | dmgFile = args[0] 50 | dmgFileSignature = args[1] 51 | } else { 52 | return cli.ShowSubcommandHelp(ctx) 53 | } 54 | 55 | path := "/private/var/mobile/Media/PublicStaging/staging.dimage" 56 | 57 | if device, err := getDevice(udid); err != nil { 58 | return err 59 | } else if ms, err := services.NewMountService(device); err != nil { 60 | return err 61 | } else if err := ms.UploadImage(dmgFile, dmgFileSignature, imageType); err != nil { 62 | return err 63 | } else if err := ms.Mount(path, imageType, dmgFileSignature); err != nil { 64 | return err 65 | } 66 | 67 | return nil 68 | } 69 | 70 | func initMountCommand() cli.Command { 71 | flags := append(globalFlags, cli.StringFlag{ 72 | Name: "type, t", 73 | Usage: "Image type default Developer", 74 | EnvVar: "IMAGE_TYPE", 75 | Value: "Developer", 76 | }) 77 | 78 | return cli.Command{ 79 | Name: "mount", 80 | Usage: "Mount developer image", 81 | UsageText: "iconsole mount [-u serial_number|udid] ", 82 | Action: actionMount, 83 | Flags: flags, 84 | Subcommands: []cli.Command{ 85 | { 86 | Name: "list", 87 | ShortName: "l", 88 | Usage: "Show developer lists", 89 | Action: actionList, 90 | Flags: flags, 91 | }, 92 | }, 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /ns/dtxmessage.go: -------------------------------------------------------------------------------- 1 | package ns 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "errors" 7 | "fmt" 8 | ) 9 | 10 | type DTXMessage struct { 11 | msgbuf *bytes.Buffer 12 | } 13 | 14 | func NewDTXMessage() *DTXMessage { 15 | return &DTXMessage{msgbuf: &bytes.Buffer{}} 16 | } 17 | 18 | func (this *DTXMessage) AppendObject(obj interface{}) error { 19 | archiver := NewNSKeyedArchiver() 20 | 21 | b, err := archiver.Marshal(obj) 22 | if err != nil { 23 | return err 24 | } 25 | 26 | this.AppendUInt32(10) 27 | this.AppendUInt32(2) 28 | this.AppendUInt32(uint32(len(b))) 29 | this.msgbuf.Write(b) 30 | 31 | return nil 32 | } 33 | 34 | func (this *DTXMessage) AppendInt64(v int64) { 35 | this.AppendUInt32(10) 36 | this.AppendUInt32(4) 37 | this.AppendUInt64(uint64(v)) 38 | } 39 | 40 | func (this *DTXMessage) AppendInt32(v int32) { 41 | this.AppendUInt32(10) 42 | this.AppendUInt32(3) 43 | this.AppendUInt32(uint32(v)) 44 | } 45 | 46 | func (this *DTXMessage) AppendUInt32(v uint32) { 47 | _ = binary.Write(this.msgbuf, binary.LittleEndian, v) 48 | } 49 | 50 | func (this *DTXMessage) AppendUInt64(v uint64) { 51 | _ = binary.Write(this.msgbuf, binary.LittleEndian, v) 52 | } 53 | 54 | func (this *DTXMessage) AppendBytes(b []byte) { 55 | this.msgbuf.Write(b) 56 | } 57 | 58 | func (this *DTXMessage) Len() int { 59 | return this.msgbuf.Len() 60 | } 61 | 62 | func (this *DTXMessage) ToBytes() []byte { 63 | dup := this.msgbuf.Bytes() 64 | b := make([]byte, 16) 65 | binary.LittleEndian.PutUint64(b, 0x1f0) 66 | binary.LittleEndian.PutUint64(b[8:], uint64(this.Len())) 67 | return append(b, dup...) 68 | } 69 | 70 | func UnmarshalDTXMessage(b []byte) ([]interface{}, error) { 71 | r := bytes.NewReader(b) 72 | var magic uint64 73 | var pkgLen uint64 74 | if err := binary.Read(r, binary.LittleEndian, &magic); err != nil { 75 | return nil, err 76 | } else if err := binary.Read(r, binary.LittleEndian, &pkgLen); err != nil { 77 | return nil, err 78 | } 79 | 80 | if magic != 0x1df0 { 81 | return nil, errors.New("magic not equal 0x1df0") 82 | } 83 | 84 | if pkgLen > uint64(len(b)-16) { 85 | return nil, errors.New("package length not enough") 86 | } 87 | 88 | var ret []interface{} 89 | 90 | for r.Len() > 0 { 91 | var flag uint32 92 | var typ uint32 93 | if err := binary.Read(r, binary.LittleEndian, &flag); err != nil { 94 | return nil, err 95 | } else if err := binary.Read(r, binary.LittleEndian, &typ); err != nil { 96 | return nil, err 97 | } 98 | switch typ { 99 | case 2: 100 | var l uint32 101 | if err := binary.Read(r, binary.LittleEndian, &l); err != nil { 102 | return nil, err 103 | } 104 | plistBuf := make([]byte, l) 105 | if _, err := r.Read(plistBuf); err != nil { 106 | return nil, err 107 | } 108 | archiver := NewNSKeyedArchiver() 109 | d, err := archiver.Unmarshal(plistBuf) 110 | if err != nil { 111 | return nil, err 112 | } 113 | ret = append(ret, d) 114 | case 3, 5: 115 | var i int32 116 | if err := binary.Read(r, binary.LittleEndian, &i); err != nil { 117 | return nil, err 118 | } 119 | ret = append(ret, i) 120 | case 4, 6: 121 | var i int64 122 | if err := binary.Read(r, binary.LittleEndian, &i); err != nil { 123 | return nil, err 124 | } 125 | ret = append(ret, i) 126 | case 10: 127 | // debug 128 | fmt.Println("Dictionary key!") 129 | continue 130 | default: 131 | // debug 132 | fmt.Printf("Unknow type %d\n", typ) 133 | break 134 | } 135 | } 136 | 137 | return ret, nil 138 | } 139 | -------------------------------------------------------------------------------- /ns/keyed_archiver.go: -------------------------------------------------------------------------------- 1 | package ns 2 | 3 | import ( 4 | "reflect" 5 | "time" 6 | 7 | "howett.net/plist" 8 | ) 9 | 10 | const NSNull = "$null" 11 | 12 | type ArchiverRoot struct { 13 | Root plist.UID `plist:"root"` 14 | } 15 | 16 | type KeyedArchiver struct { 17 | Archiver string `plist:"$archiver"` 18 | Objects []interface{} `plist:"$objects"` 19 | Top ArchiverRoot `plist:"$top"` 20 | Version int `plist:"$version"` 21 | } 22 | 23 | func (this KeyedArchiver) UID() plist.UID { 24 | return plist.UID(len(this.Objects)) 25 | } 26 | 27 | func NewKeyedArchiver() *KeyedArchiver { 28 | return &KeyedArchiver{ 29 | Archiver: "NSKeyedArchiver", 30 | Version: 100000, 31 | } 32 | } 33 | 34 | type ArchiverClasses struct { 35 | Classes []string `plist:"$classes"` 36 | ClassName string `plist:"$classname"` 37 | } 38 | 39 | var ( 40 | NSMutableDictionaryClass = &ArchiverClasses{ 41 | Classes: []string{"NSMutableDictionary", "NSDictionary", "NSObject"}, 42 | ClassName: "NSMutableDictionary", 43 | } 44 | NSDictionaryClass = &ArchiverClasses{ 45 | Classes: []string{"NSDictionary", "NSObject"}, 46 | ClassName: "NSDictionary", 47 | } 48 | NSMutableArrayClass = &ArchiverClasses{ 49 | Classes: []string{"NSMutableArray", "NSArray", "NSObject"}, 50 | ClassName: "NSMutableArray", 51 | } 52 | NSArrayClass = &ArchiverClasses{ 53 | Classes: []string{"NSArray", "NSObject"}, 54 | ClassName: "NSArray", 55 | } 56 | NSMutableDataClass = &ArchiverClasses{ 57 | Classes: []string{"NSMutableArray", "NSArray", "NSObject"}, 58 | ClassName: "NSMutableArray", 59 | } 60 | NSDataClass = &ArchiverClasses{ 61 | Classes: []string{"NSData", "NSObject"}, 62 | ClassName: "NSData", 63 | } 64 | NSDateClass = &ArchiverClasses{ 65 | Classes: []string{"NSDate", "NSObject"}, 66 | ClassName: "NSDate", 67 | } 68 | NSErrorClass = &ArchiverClasses{ 69 | Classes: []string{"NSError", "NSObject"}, 70 | ClassName: "NSError", 71 | } 72 | ) 73 | 74 | type NSObject struct { 75 | Class plist.UID `plist:"$class"` 76 | } 77 | 78 | type NSArray struct { 79 | NSObject 80 | Values []plist.UID `plist:"NS.objects"` 81 | } 82 | 83 | type NSDictionary struct { 84 | NSArray 85 | Keys []plist.UID `plist:"NS.keys"` 86 | } 87 | 88 | type NSData struct { 89 | NSObject 90 | Data []byte `plist:"NS.data"` 91 | } 92 | 93 | type GoNSError struct { 94 | NSCode int 95 | NSDomain string 96 | NSUserInfo interface{} 97 | } 98 | 99 | type NSKeyedArchiver struct { 100 | objRefVal []interface{} 101 | objRef map[interface{}]plist.UID 102 | } 103 | 104 | func NewNSKeyedArchiver() *NSKeyedArchiver { 105 | a := &NSKeyedArchiver{ 106 | objRef: make(map[interface{}]plist.UID), 107 | } 108 | 109 | return a 110 | } 111 | 112 | func (this *NSKeyedArchiver) id(v interface{}) plist.UID { 113 | var ref plist.UID 114 | if id, ok := this.objRef[v]; !ok { 115 | ref = plist.UID(len(this.objRef)) 116 | this.objRefVal = append(this.objRefVal, v) 117 | this.objRef[v] = ref 118 | } else { 119 | ref = id 120 | } 121 | return ref 122 | } 123 | 124 | func (this *NSKeyedArchiver) flushToStruct(root *KeyedArchiver) { 125 | for i := 0; i < len(this.objRefVal); i++ { 126 | val := this.objRefVal[i] 127 | vt := reflect.ValueOf(val) 128 | if vt.Kind() == reflect.Ptr { 129 | val = vt.Elem().Interface() 130 | } 131 | root.Objects = append(root.Objects, val) 132 | } 133 | } 134 | 135 | func (this *NSKeyedArchiver) clear() { 136 | this.objRef = make(map[interface{}]plist.UID) 137 | this.objRefVal = []interface{}{} 138 | } 139 | 140 | func (this *NSKeyedArchiver) Marshal(obj interface{}) ([]byte, error) { 141 | val := reflect.ValueOf(obj) 142 | typ := val.Type() 143 | 144 | root := NewKeyedArchiver() 145 | 146 | var tmpTop plist.UID 147 | 148 | this.id(NSNull) 149 | 150 | switch typ.Kind() { 151 | case reflect.Map: 152 | m := &NSDictionary{} 153 | m.Class = this.id(NSDictionaryClass) 154 | keys := val.MapKeys() 155 | for _, v := range keys { 156 | m.Keys = append(m.Keys, this.id(v.Interface())) 157 | m.Values = append(m.Values, this.id(val.MapIndex(v).Interface())) 158 | } 159 | tmpTop = this.id(m) 160 | case reflect.Slice, reflect.Array: 161 | if typ.Elem().Kind() == reflect.Uint8 { 162 | d := &NSData{} 163 | d.Class = this.id(NSDataClass) 164 | var w []byte 165 | for i := 0; i < val.Len(); i++ { 166 | w = append(w, uint8(val.Index(i).Uint())) 167 | } 168 | d.Data = w 169 | } 170 | a := &NSArray{} 171 | a.Class = this.id(NSArrayClass) 172 | for i := 0; i < val.Len(); i++ { 173 | a.Values = append(a.Values, this.id(val.Index(i).Interface())) 174 | } 175 | tmpTop = this.id(a) 176 | case reflect.String: 177 | tmpTop = this.id(obj) 178 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, 179 | reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 180 | tmpTop = this.id(obj) 181 | } 182 | 183 | root.Top.Root = tmpTop 184 | 185 | this.flushToStruct(root) 186 | 187 | this.clear() 188 | 189 | return plist.Marshal(root, plist.BinaryFormat) 190 | } 191 | 192 | func (this *NSKeyedArchiver) convertValue(v interface{}) interface{} { 193 | if m, ok := v.(map[string]interface{}); ok { 194 | className := this.objRefVal[m["$class"].(plist.UID)].(map[string]interface{})["$classname"] 195 | 196 | switch className { 197 | case NSMutableDictionaryClass.Classes[0], NSDictionaryClass.Classes[0]: 198 | ret := make(map[string]interface{}) 199 | keys := m["NS.keys"].([]interface{}) 200 | values := m["NS.objects"].([]interface{}) 201 | 202 | for i := 0; i < len(keys); i++ { 203 | key := this.objRefVal[keys[i].(plist.UID)].(string) 204 | val := this.convertValue(this.objRefVal[values[i].(plist.UID)]) 205 | ret[key] = val 206 | } 207 | return ret 208 | case NSMutableArrayClass.Classes[0], NSArrayClass.Classes[0]: 209 | ret := make([]interface{}, 0) 210 | values := m["NS.objects"].([]interface{}) 211 | for i := 0; i < len(values); i++ { 212 | ret = append(ret, this.convertValue(values[i])) 213 | } 214 | return ret 215 | case NSMutableDataClass.Classes[0], NSDataClass.Classes[0]: 216 | return m["NS.data"].([]byte) 217 | case NSDateClass.Classes[0]: 218 | return time.Date(2001, 1, 1, 0, 0, 0, 0, time.UTC). 219 | Add(time.Duration(m["NS.time"].(float64)) * time.Second) 220 | case NSErrorClass.Classes[0]: 221 | err := &GoNSError{} 222 | err.NSCode = int(m["NSCode"].(uint64)) 223 | err.NSDomain = this.objRefVal[m["NSDomain"].(plist.UID)].(string) 224 | err.NSUserInfo = this.convertValue(this.objRefVal[m["NSUserInfo"].(plist.UID)]) 225 | return *err 226 | } 227 | } else if uid, ok := v.(plist.UID); ok { 228 | return this.convertValue(this.objRefVal[uid]) 229 | } 230 | return v 231 | } 232 | 233 | func (this *NSKeyedArchiver) Unmarshal(b []byte) (interface{}, error) { 234 | archiver := &KeyedArchiver{} 235 | 236 | _, err := plist.Unmarshal(b, archiver) 237 | if err != nil { 238 | return nil, err 239 | } 240 | 241 | for _, v := range archiver.Objects { 242 | this.objRefVal = append(this.objRefVal, v) 243 | } 244 | 245 | ret := this.convertValue(this.objRefVal[archiver.Top.Root]) 246 | 247 | this.clear() 248 | 249 | return ret, nil 250 | } 251 | -------------------------------------------------------------------------------- /screenshot.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "iconsole/services" 6 | "os" 7 | "time" 8 | 9 | "github.com/urfave/cli" 10 | ) 11 | 12 | func screenShotCommand(ctx *cli.Context) error { 13 | udid := ctx.String("UDID") 14 | 15 | device, err := getDevice(udid) 16 | if err != nil { 17 | return err 18 | } 19 | 20 | ss, err := services.NewScreenshotService(device) 21 | if err != nil { 22 | return err 23 | } 24 | defer ss.Close() 25 | 26 | fs, err := os.Create(fmt.Sprintf("Screenshot-%s.png", time.Now().Format("2006-01-02 15.04.05"))) 27 | if err != nil { 28 | return err 29 | } 30 | defer fs.Close() 31 | if err := ss.Shot(fs); err != nil { 32 | return err 33 | } 34 | 35 | return nil 36 | } 37 | 38 | func initScreenShotCommond() cli.Command { 39 | return cli.Command{ 40 | Name: "screenshot", 41 | ShortName: "screen", 42 | Usage: "Capture screen.", 43 | Action: screenShotCommand, 44 | Flags: globalFlags, 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /services/afc.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "errors" 7 | "fmt" 8 | "iconsole/frames" 9 | "iconsole/tunnel" 10 | "io" 11 | "os" 12 | "path" 13 | "strconv" 14 | "time" 15 | ) 16 | 17 | var ( 18 | afcHeader = []byte{0x43, 0x46, 0x41, 0x36, 0x4C, 0x50, 0x41, 0x41} 19 | ) 20 | 21 | const ( 22 | AFCOperationInvalid = 0x00000000 /* Invalid */ 23 | AFCOperationStatus = 0x00000001 /* Status */ 24 | AFCOperationData = 0x00000002 /* Data */ 25 | AFCOperationReadDir = 0x00000003 /* ReadDir */ 26 | AFCOperationReadFile = 0x00000004 /* ReadFile */ 27 | AFCOperationWriteFile = 0x00000005 /* WriteFile */ 28 | AFCOperationWritePart = 0x00000006 /* WritePart */ 29 | AFCOperationTruncateFile = 0x00000007 /* TruncateFile */ 30 | AFCOperationRemovePath = 0x00000008 /* RemovePath */ 31 | AFCOperationMakeDir = 0x00000009 /* MakeDir */ 32 | AFCOperationGetFileInfo = 0x0000000A /* GetFileInfo */ 33 | AFCOperationGetDeviceInfo = 0x0000000B /* GetDeviceInfo */ 34 | AFCOperationWriteFileAtomic = 0x0000000C /* WriteFileAtomic (tmp file+rename) */ 35 | AFCOperationFileOpen = 0x0000000D /* FileRefOpen */ 36 | AFCOperationFileOpenResult = 0x0000000E /* FileRefOpenResult */ 37 | AFCOperationFileRead = 0x0000000F /* FileRefRead */ 38 | AFCOperationFileWrite = 0x00000010 /* FileRefWrite */ 39 | AFCOperationFileSeek = 0x00000011 /* FileRefSeek */ 40 | AFCOperationFileTell = 0x00000012 /* FileRefTell */ 41 | AFCOperationFileTellResult = 0x00000013 /* FileRefTellResult */ 42 | AFCOperationFileClose = 0x00000014 /* FileRefClose */ 43 | AFCOperationFileSetSize = 0x00000015 /* FileRefSetFileSize (ftruncate) */ 44 | AFCOperationGetConnectionInfo = 0x00000016 /* GetConnectionInfo */ 45 | AFCOperationSetConnectionOptions = 0x00000017 /* SetConnectionOptions */ 46 | AFCOperationRenamePath = 0x00000018 /* RenamePath */ 47 | AFCOperationSetFSBlockSize = 0x00000019 /* SetFSBlockSize (0x800000) */ 48 | AFCOperationSetSocketBlockSize = 0x0000001A /* SetSocketBlockSize (0x800000) */ 49 | AFCOperationFileRefLock = 0x0000001B /* FileRefLock */ 50 | AFCOperationMakeLink = 0x0000001C /* MakeLink */ 51 | AFCOperationGetFileHash = 0x0000001D /* GetFileHash */ 52 | AFCOperationSetFileModTime = 0x0000001E /* SetModTime */ 53 | AFCOperationGetFileHashRange = 0x0000001F /* GetFileHashWithRange */ 54 | /* iOS 6+ */ 55 | AFCOperationFileSetImmutableHint = 0x00000020 /* FileRefSetImmutableHint */ 56 | AFCOperationGetSizeOfPathContents = 0x00000021 /* GetSizeOfPathContents */ 57 | AFCOperationRemovePathAndContents = 0x00000022 /* RemovePathAndContents */ 58 | AFCOperationDirectoryEnumeratorRefOpen = 0x00000023 /* DirectoryEnumeratorRefOpen */ 59 | AFCOperationDirectoryEnumeratorRefOpenResult = 0x00000024 /* DirectoryEnumeratorRefOpenResult */ 60 | AFCOperationDirectoryEnumeratorRefRead = 0x00000025 /* DirectoryEnumeratorRefRead */ 61 | AFCOperationDirectoryEnumeratorRefClose = 0x00000026 /* DirectoryEnumeratorRefClose */ 62 | /* iOS 7+ */ 63 | AFCOperationFileRefReadWithOffset = 0x00000027 /* FileRefReadWithOffset */ 64 | AFCOperationFileRefWriteWithOffset = 0x00000028 /* FileRefWriteWithOffset */ 65 | ) 66 | 67 | type AFCFileMode int 68 | 69 | const ( 70 | AFC_RDONLY AFCFileMode = 0x00000001 71 | AFC_RW AFCFileMode = 0x00000002 72 | AFC_WRONLY AFCFileMode = 0x00000003 73 | AFC_WR AFCFileMode = 0x00000004 74 | AFC_APPEND AFCFileMode = 0x00000005 75 | AFC_RDAPPEND AFCFileMode = 0x00000006 76 | ) 77 | 78 | const ( 79 | AFCErrSuccess = 0 80 | AFCErrUnknownError = 1 81 | AFCErrOperationHeaderInvalid = 2 82 | AFCErrNoResources = 3 83 | AFCErrReadError = 4 84 | AFCErrWriteError = 5 85 | AFCErrUnknownPacketType = 6 86 | AFCErrInvalidArgument = 7 87 | AFCErrObjectNotFound = 8 88 | AFCErrObjectIsDir = 9 89 | AFCErrPermDenied = 10 90 | AFCErrServiceNotConnected = 11 91 | AFCErrOperationTimeout = 12 92 | AFCErrTooMuchData = 13 93 | AFCErrEndOfData = 14 94 | AFCErrOperationNotSupported = 15 95 | AFCErrObjectExists = 16 96 | AFCErrObjectBusy = 17 97 | AFCErrNoSpaceLeft = 18 98 | AFCErrOperationWouldBlock = 19 99 | AFCErrIoError = 20 100 | AFCErrOperationInterrupted = 21 101 | AFCErrOperationInProgress = 22 102 | AFCErrInternalError = 23 103 | AFCErrMuxError = 30 104 | AFCErrNoMemory = 31 105 | AFCErrNotEnoughData = 32 106 | AFCErrDirNotEmpty = 33 107 | ) 108 | 109 | type AFCLockType int 110 | 111 | const ( 112 | AFCLockSharedLock AFCLockType = 1 | 4 113 | AFCLockExclusiveLock AFCLockType = 2 | 4 114 | AFCLockUnlock AFCLockType = 8 | 4 115 | ) 116 | 117 | type AFCLinkType int 118 | 119 | const ( 120 | AFCHardLink AFCLinkType = 1 121 | AFCSymLink AFCLinkType = 2 122 | ) 123 | 124 | func getCStr(strs ...string) []byte { 125 | b := &bytes.Buffer{} 126 | for _, v := range strs { 127 | b.WriteString(v) 128 | b.WriteByte(0) 129 | } 130 | return b.Bytes() 131 | } 132 | 133 | func getError(status uint64) error { 134 | switch status { 135 | case AFCErrUnknownError: 136 | return errors.New("UnknownError") 137 | case AFCErrOperationHeaderInvalid: 138 | return errors.New("OperationHeaderInvalid") 139 | case AFCErrNoResources: 140 | return errors.New("NoResources") 141 | case AFCErrReadError: 142 | return errors.New("ReadError") 143 | case AFCErrWriteError: 144 | return errors.New("WriteError") 145 | case AFCErrUnknownPacketType: 146 | return errors.New("UnknownPacketType") 147 | case AFCErrInvalidArgument: 148 | return errors.New("InvalidArgument") 149 | case AFCErrObjectNotFound: 150 | return errors.New("ObjectNotFound") 151 | case AFCErrObjectIsDir: 152 | return errors.New("ObjectIsDir") 153 | case AFCErrPermDenied: 154 | return errors.New("PermDenied") 155 | case AFCErrServiceNotConnected: 156 | return errors.New("ServiceNotConnected") 157 | case AFCErrOperationTimeout: 158 | return errors.New("OperationTimeout") 159 | case AFCErrTooMuchData: 160 | return errors.New("TooMuchData") 161 | case AFCErrEndOfData: 162 | return errors.New("EndOfData") 163 | case AFCErrOperationNotSupported: 164 | return errors.New("OperationNotSupported") 165 | case AFCErrObjectExists: 166 | return errors.New("ObjectExists") 167 | case AFCErrObjectBusy: 168 | return errors.New("ObjectBusy") 169 | case AFCErrNoSpaceLeft: 170 | return errors.New("NoSpaceLeft") 171 | case AFCErrOperationWouldBlock: 172 | return errors.New("OperationWouldBlock") 173 | case AFCErrIoError: 174 | return errors.New("IoError") 175 | case AFCErrOperationInterrupted: 176 | return errors.New("OperationInterrupted") 177 | case AFCErrOperationInProgress: 178 | return errors.New("OperationInProgress") 179 | case AFCErrInternalError: 180 | return errors.New("InternalError") 181 | case AFCErrMuxError: 182 | return errors.New("MuxError") 183 | case AFCErrNoMemory: 184 | return errors.New("NoMemory") 185 | case AFCErrNotEnoughData: 186 | return errors.New("NotEnoughData") 187 | case AFCErrDirNotEmpty: 188 | return errors.New("DirNotEmpty") 189 | } 190 | return nil 191 | } 192 | 193 | type AFCPacket struct { 194 | EntireLen uint64 195 | ThisLen uint64 196 | PacketNum uint64 197 | Operation uint64 198 | Data []byte 199 | Payload []byte 200 | } 201 | 202 | func (this *AFCPacket) Map() map[string]string { 203 | m := make(map[string]string) 204 | strs := this.Array() 205 | if strs != nil { 206 | for i := 0; i < len(strs); i += 2 { 207 | m[strs[i]] = strs[i+1] 208 | } 209 | } 210 | return m 211 | } 212 | 213 | func (this *AFCPacket) Array() []string { 214 | if this.Operation == AFCOperationData { 215 | bs := bytes.Split(this.Payload, []byte{0}) 216 | strs := make([]string, len(bs)-1) 217 | for i := 0; i < len(strs); i++ { 218 | strs[i] = string(bs[i]) 219 | } 220 | return strs 221 | } 222 | return nil 223 | } 224 | 225 | func (this *AFCPacket) Uint64() uint64 { 226 | return binary.LittleEndian.Uint64(this.Data) 227 | } 228 | 229 | func (this *AFCPacket) Error() error { 230 | if this.Operation == AFCOperationStatus { 231 | status := this.Uint64() 232 | if status != AFCErrSuccess { 233 | return getError(status) 234 | } 235 | } 236 | return nil 237 | } 238 | 239 | type AFCService struct { 240 | service *tunnel.Service 241 | packetNum uint64 242 | } 243 | 244 | func NewAFCService(device frames.Device) (*AFCService, error) { 245 | serv, err := startService(AFCServiceName, device) 246 | if err != nil { 247 | return nil, err 248 | } 249 | 250 | return &AFCService{service: serv}, nil 251 | } 252 | 253 | func (this *AFCService) send(operation uint64, data, payload []byte) error { 254 | this.packetNum++ 255 | 256 | entireLen := uint64(40) 257 | 258 | if data != nil { 259 | entireLen += uint64(len(data)) 260 | } 261 | 262 | if payload != nil { 263 | entireLen += uint64(len(payload)) 264 | } 265 | 266 | tLen := uint64(40) 267 | if data != nil { 268 | tLen += uint64(len(data)) 269 | } 270 | 271 | buf := bytes.NewBuffer([]byte{}) 272 | buf.Write(afcHeader) 273 | if err := binary.Write(buf, binary.LittleEndian, entireLen); err != nil { 274 | return err 275 | } 276 | if err := binary.Write(buf, binary.LittleEndian, tLen); err != nil { 277 | return err 278 | } 279 | if err := binary.Write(buf, binary.LittleEndian, this.packetNum); err != nil { 280 | return err 281 | } 282 | if err := binary.Write(buf, binary.LittleEndian, operation); err != nil { 283 | return err 284 | } 285 | 286 | if data != nil { 287 | buf.Write(data) 288 | } 289 | 290 | sB := buf.Bytes() 291 | 292 | conn := this.service.GetConnection() 293 | 294 | sent := 0 295 | for sent < len(sB) { 296 | if n, err := conn.Write(sB[sent:]); err != nil { 297 | return err 298 | } else { 299 | sent += n 300 | } 301 | } 302 | 303 | sent = 0 304 | if payload != nil { 305 | for sent < len(payload) { 306 | if n, err := conn.Write(payload[sent:]); err != nil { 307 | return err 308 | } else { 309 | sent += n 310 | } 311 | } 312 | } 313 | 314 | return nil 315 | } 316 | 317 | func (this *AFCService) recv() (*AFCPacket, error) { 318 | offset := uint64(0x28) 319 | 320 | conn := this.service.GetConnection() 321 | 322 | header := make([]byte, offset) 323 | n, err := conn.Read(header) 324 | if err != nil && n == 0 { 325 | return nil, err 326 | } 327 | if n < 0x28 { 328 | return nil, errors.New("recv: header") 329 | } 330 | if bytes.Compare(header[:8], afcHeader) != 0 { 331 | return nil, errors.New("recv: header not match") 332 | } 333 | 334 | packet := &AFCPacket{} 335 | 336 | packet.EntireLen = binary.LittleEndian.Uint64(header[8:16]) 337 | packet.ThisLen = binary.LittleEndian.Uint64(header[16:24]) 338 | packet.PacketNum = binary.LittleEndian.Uint64(header[24:32]) 339 | packet.Operation = binary.LittleEndian.Uint64(header[32:]) 340 | 341 | buf := &bytes.Buffer{} 342 | pkgBuf := make([]byte, 0xffff) 343 | 344 | for offset < packet.EntireLen { 345 | if n, err := conn.Read(pkgBuf); err != nil && n <= 0 { 346 | return nil, err 347 | } else { 348 | buf.Write(pkgBuf[:n]) 349 | offset += uint64(n) 350 | } 351 | } 352 | 353 | dataAndPayload := buf.Bytes() 354 | 355 | packet.Data = dataAndPayload[:int(packet.ThisLen-40)] 356 | packet.Payload = dataAndPayload[int(packet.ThisLen-40):] 357 | 358 | if err := packet.Error(); err != nil { 359 | return nil, err 360 | } 361 | 362 | return packet, nil 363 | } 364 | 365 | type AFCDeviceInfo struct { 366 | Model string 367 | TotalBytes uint64 368 | FreeBytes uint64 369 | BlockSize uint64 370 | } 371 | 372 | func (this *AFCService) GetDeviceInfo() (*AFCDeviceInfo, error) { 373 | if err := this.send(AFCOperationGetDeviceInfo, nil, nil); err != nil { 374 | return nil, err 375 | } 376 | 377 | if b, err := this.recv(); err != nil { 378 | return nil, err 379 | } else { 380 | m := b.Map() 381 | totalBytes, err := strconv.ParseUint(m["FSTotalBytes"], 10, 64) 382 | if err != nil { 383 | return nil, err 384 | } 385 | freeBytes, err := strconv.ParseUint(m["FSFreeBytes"], 10, 64) 386 | if err != nil { 387 | return nil, err 388 | } 389 | blockSize, err := strconv.ParseUint(m["FSBlockSize"], 10, 64) 390 | if err != nil { 391 | return nil, err 392 | } 393 | 394 | return &AFCDeviceInfo{ 395 | Model: m["Model"], 396 | TotalBytes: totalBytes, 397 | FreeBytes: freeBytes, 398 | BlockSize: blockSize, 399 | }, nil 400 | } 401 | } 402 | 403 | func (this *AFCService) ReadDirectory(p string) ([]string, error) { 404 | if err := this.send(AFCOperationReadDir, getCStr(p), nil); err != nil { 405 | return nil, err 406 | } 407 | 408 | if b, err := this.recv(); err != nil { 409 | return nil, err 410 | } else { 411 | return b.Array(), nil 412 | } 413 | } 414 | 415 | type afcFileInfo struct { 416 | name string 417 | size uint64 418 | mtime uint64 419 | ifmt string 420 | source map[string]string 421 | } 422 | 423 | func (this *afcFileInfo) Name() string { 424 | return this.name 425 | } 426 | 427 | /* 428 | for get physical size 429 | use st_blocks * (FSBlockSize / 8) 430 | */ 431 | func (this *afcFileInfo) Size() int64 { 432 | return int64(this.size) 433 | } 434 | func (this *afcFileInfo) Mode() os.FileMode { 435 | return os.ModeType 436 | } 437 | func (this *afcFileInfo) ModTime() time.Time { 438 | return time.Unix(0, int64(this.mtime)) 439 | } 440 | func (this *afcFileInfo) IsDir() bool { 441 | return this.ifmt == "S_IFDIR" 442 | } 443 | func (this *afcFileInfo) Sys() interface{} { 444 | return this.source 445 | } 446 | 447 | func (this *AFCService) GetFileInfo(filename string) (os.FileInfo, error) { 448 | if err := this.send(AFCOperationGetFileInfo, getCStr(filename), nil); err != nil { 449 | return nil, err 450 | } 451 | 452 | if b, err := this.recv(); err != nil { 453 | return nil, err 454 | } else { 455 | m := b.Map() 456 | 457 | st_size, err := strconv.ParseUint(m["st_size"], 10, 64) 458 | if err != nil { 459 | return nil, err 460 | } 461 | st_mtime, err := strconv.ParseUint(m["st_mtime"], 10, 64) 462 | if err != nil { 463 | return nil, err 464 | } 465 | 466 | info := &afcFileInfo{ 467 | name: path.Base(filename), 468 | size: st_size, 469 | mtime: st_mtime, 470 | ifmt: m["st_ifmt"], 471 | source: m, 472 | } 473 | 474 | return info, nil 475 | } 476 | } 477 | 478 | type AFCFile struct { 479 | service *AFCService 480 | fd uint64 481 | } 482 | 483 | func (this *AFCService) FileOpen(filename string, filemode AFCFileMode) (*AFCFile, error) { 484 | b := getCStr(filename) 485 | buf := make([]byte, len(b)+8) 486 | copy(buf[8:], b) 487 | binary.LittleEndian.PutUint64(buf[:8], uint64(filemode)) 488 | 489 | if err := this.send(AFCOperationFileOpen, buf, nil); err != nil { 490 | return nil, err 491 | } 492 | 493 | if b, err := this.recv(); err != nil { 494 | return nil, err 495 | } else if b.Operation == AFCOperationFileOpenResult { 496 | return &AFCFile{service: this, fd: b.Uint64()}, nil 497 | } else { 498 | return nil, fmt.Errorf("operation %d", b.Operation) 499 | } 500 | } 501 | 502 | func (this *AFCFile) op(o ...uint64) []byte { 503 | lo := 1 504 | if o != nil { 505 | lo = len(o) + 1 506 | } 507 | buf := make([]byte, lo*8) 508 | binary.LittleEndian.PutUint64(buf, this.fd) 509 | for i := 1; i < lo; i++ { 510 | binary.LittleEndian.PutUint64(buf[i*8:], o[i-1]) 511 | } 512 | return buf 513 | } 514 | 515 | func (this *AFCFile) Lock(mode AFCLockType) error { 516 | if err := this.service.send(AFCOperationFileRefLock, this.op(uint64(mode)), nil); err != nil { 517 | return err 518 | } 519 | 520 | if b, err := this.service.recv(); err != nil { 521 | return err 522 | } else if err := b.Error(); err != nil { 523 | return err 524 | } 525 | 526 | return nil 527 | } 528 | 529 | func (this *AFCFile) Unlock() error { 530 | return this.Lock(AFCLockUnlock) 531 | } 532 | 533 | func (this *AFCFile) Read(p []byte) (int, error) { 534 | if err := this.service.send(AFCOperationFileRead, this.op(uint64(len(p))), nil); err != nil { 535 | return -1, err 536 | } 537 | 538 | if b, err := this.service.recv(); err != nil { 539 | return -1, err 540 | } else if err := b.Error(); err != nil { 541 | return -1, err 542 | } else { 543 | if b.Payload == nil { 544 | return 0, io.EOF 545 | } 546 | copy(p, b.Payload) 547 | return len(b.Payload), nil 548 | } 549 | } 550 | 551 | func (this *AFCFile) Write(p []byte) (int, error) { 552 | if err := this.service.send(AFCOperationFileWrite, this.op(), p); err != nil { 553 | return -1, err 554 | } 555 | 556 | if b, err := this.service.recv(); err != nil { 557 | return -1, err 558 | } else if err := b.Error(); err != nil { 559 | return -1, err 560 | } else { 561 | return len(p), nil 562 | } 563 | } 564 | 565 | func (this *AFCFile) Tell() (uint64, error) { 566 | if err := this.service.send(AFCOperationFileTell, this.op(), nil); err != nil { 567 | return 0, err 568 | } else if b, err := this.service.recv(); err != nil { 569 | return 0, err 570 | } else if err := b.Error(); err != nil { 571 | return 0, err 572 | } else if b.Operation == AFCOperationFileTellResult { 573 | return b.Uint64(), nil 574 | } else { 575 | return 0, fmt.Errorf("operation %d", b.Operation) 576 | } 577 | } 578 | 579 | func (this *AFCFile) Seek(offset int64, whence int) (int64, error) { 580 | if err := this.service.send(AFCOperationFileSeek, this.op(uint64(whence), uint64(offset)), nil); err != nil { 581 | return -1, err 582 | } else if b, err := this.service.recv(); err != nil { 583 | return -1, err 584 | } else if err := b.Error(); err != nil { 585 | return -1, err 586 | } else if t, err := this.Tell(); err != nil { 587 | return -1, err 588 | } else { 589 | return int64(t), nil 590 | } 591 | } 592 | 593 | func (this *AFCFile) Truncate(size int64) error { 594 | if err := this.service.send(AFCOperationFileSetSize, this.op(uint64(size)), nil); err != nil { 595 | return err 596 | } else if b, err := this.service.recv(); err != nil { 597 | return err 598 | } else if err := b.Error(); err != nil { 599 | return err 600 | } 601 | return nil 602 | } 603 | 604 | func (this *AFCFile) Close() error { 605 | b := make([]byte, 8) 606 | binary.LittleEndian.PutUint64(b, this.fd) 607 | if err := this.service.send(AFCOperationFileClose, b, nil); err != nil { 608 | return err 609 | } else if _, err := this.service.recv(); err != nil { 610 | return err 611 | } 612 | return nil 613 | } 614 | 615 | func (this *AFCService) Remove(path string) error { 616 | if err := this.send(AFCOperationRemovePath, getCStr(path), nil); err != nil { 617 | return err 618 | } else if b, err := this.recv(); err != nil { 619 | return err 620 | } else if err := b.Error(); err != nil { 621 | return err 622 | } 623 | return nil 624 | } 625 | 626 | func (this *AFCService) Rename(oldpath, newpath string) error { 627 | if err := this.send(AFCOperationRenamePath, getCStr(oldpath, newpath), nil); err != nil { 628 | return err 629 | } else if b, err := this.recv(); err != nil { 630 | return err 631 | } else if err := b.Error(); err != nil { 632 | return err 633 | } 634 | return nil 635 | } 636 | 637 | func (this *AFCService) Mkdir(path string) error { 638 | if err := this.send(AFCOperationMakeDir, getCStr(path), nil); err != nil { 639 | return err 640 | } else if b, err := this.recv(); err != nil { 641 | return err 642 | } else if err := b.Error(); err != nil { 643 | return err 644 | } 645 | return nil 646 | } 647 | 648 | func (this *AFCService) Link(linkType AFCLinkType, oldname, newname string) error { 649 | b := make([]byte, 8) 650 | binary.LittleEndian.PutUint64(b, uint64(linkType)) 651 | b = append(b, getCStr(oldname, newname)...) 652 | if err := this.send(AFCOperationMakeLink, b, nil); err != nil { 653 | return err 654 | } else if b, err := this.recv(); err != nil { 655 | return err 656 | } else if err := b.Error(); err != nil { 657 | return err 658 | } 659 | return nil 660 | } 661 | 662 | func (this *AFCService) Truncate(path string, newsize uint64) error { 663 | b := make([]byte, 8) 664 | binary.LittleEndian.PutUint64(b, newsize) 665 | b = append(b, getCStr(path)...) 666 | if err := this.send(AFCOperationTruncateFile, b, nil); err != nil { 667 | return err 668 | } else if b, err := this.recv(); err != nil { 669 | return err 670 | } else if err := b.Error(); err != nil { 671 | return err 672 | } 673 | return nil 674 | } 675 | 676 | func (this *AFCService) SetFileTime(mtime uint64, path string) error { 677 | b := make([]byte, 8) 678 | binary.LittleEndian.PutUint64(b, mtime) 679 | b = append(b, getCStr(path)...) 680 | if err := this.send(AFCOperationSetFileModTime, b, nil); err != nil { 681 | return err 682 | } else if b, err := this.recv(); err != nil { 683 | return err 684 | } else if err := b.Error(); err != nil { 685 | return err 686 | } 687 | return nil 688 | } 689 | 690 | /* sha1 algorithm */ 691 | func (this *AFCService) Hash(path string) ([]byte, error) { 692 | if err := this.send(AFCOperationGetFileHash, getCStr(path), nil); err != nil { 693 | return nil, err 694 | } else if b, err := this.recv(); err != nil { 695 | return nil, err 696 | } else if err := b.Error(); err != nil { 697 | return nil, err 698 | } else { 699 | return b.Payload, nil 700 | } 701 | } 702 | 703 | /* sha1 algorithm with file range */ 704 | func (this *AFCService) HashWithRange(start, end uint64, path string) ([]byte, error) { 705 | b := make([]byte, 16) 706 | binary.LittleEndian.PutUint64(b, start) 707 | binary.LittleEndian.PutUint64(b[8:], end) 708 | b = append(b, getCStr(path)...) 709 | 710 | if err := this.send(AFCOperationGetFileHashRange, b, nil); err != nil { 711 | return nil, err 712 | } else if b, err := this.recv(); err != nil { 713 | return nil, err 714 | } else if err := b.Error(); err != nil { 715 | return nil, err 716 | } else { 717 | return b.Payload, nil 718 | } 719 | } 720 | 721 | /* since iOS6+ */ 722 | func (this *AFCService) RemoveAll(path string) error { 723 | if err := this.send(AFCOperationRemovePathAndContents, getCStr(path), nil); err != nil { 724 | return err 725 | } else if b, err := this.recv(); err != nil { 726 | return err 727 | } else if err := b.Error(); err != nil { 728 | return err 729 | } 730 | return nil 731 | } 732 | 733 | func (this *AFCService) Close() error { 734 | return this.service.GetConnection().Close() 735 | } 736 | -------------------------------------------------------------------------------- /services/arrest.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "iconsole/frames" 7 | "iconsole/tunnel" 8 | ) 9 | 10 | type HouseArrestService struct { 11 | service *tunnel.Service 12 | afc bool 13 | } 14 | 15 | func NewHouseArrestService(device frames.Device) (*HouseArrestService, error) { 16 | serv, err := startService(HouseArrestServiceName, device) 17 | if err != nil { 18 | return nil, err 19 | } 20 | 21 | return &HouseArrestService{service: serv}, nil 22 | } 23 | 24 | func (this *HouseArrestService) command(cmd, id string) error { 25 | m := map[string]string{ 26 | "Command": cmd, 27 | "Identifier": id, 28 | } 29 | 30 | if err := this.service.SendXML(m); err != nil { 31 | return err 32 | } else if pkg, err := this.service.Sync(); err != nil { 33 | return err 34 | } else { 35 | var resp map[string]interface{} 36 | if err := pkg.UnmarshalBody(&resp); err != nil { 37 | return err 38 | } else if e, ok := resp["Error"].(string); ok { 39 | return errors.New(e) 40 | } else if s, ok := resp["Status"].(string); !ok { 41 | return errors.New("unknown error") 42 | } else if s != "Complete" { 43 | return fmt.Errorf("status: %s", s) 44 | } 45 | } 46 | 47 | return nil 48 | } 49 | 50 | func (this *HouseArrestService) Documents(id string) (*AFCService, error) { 51 | if this.afc { 52 | return nil, errors.New("please use `AFCService`") 53 | } 54 | 55 | if err := this.command("VendDocuments", id); err != nil { 56 | return nil, err 57 | } else { 58 | this.afc = true 59 | return &AFCService{service: this.service}, nil 60 | } 61 | } 62 | 63 | func (this *HouseArrestService) Container(id string) (*AFCService, error) { 64 | if this.afc { 65 | return nil, errors.New("please use `AFCService`") 66 | } 67 | 68 | if err := this.command("VendContainer", id); err != nil { 69 | return nil, err 70 | } else { 71 | this.afc = true 72 | return &AFCService{service: this.service}, nil 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /services/common.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "iconsole/frames" 5 | "iconsole/tunnel" 6 | ) 7 | 8 | const ( 9 | MountServiceName = "com.apple.mobile.mobile_image_mounter" 10 | ScreenshotServiceName = "com.apple.mobile.screenshotr" 11 | SimulateLocationServiceName = "com.apple.dt.simulatelocation" 12 | SyslogRelayServiceName = "com.apple.syslog_relay" 13 | AFCServiceName = "com.apple.afc" 14 | HouseArrestServiceName = "com.apple.mobile.house_arrest" 15 | InstallationProxyServiceName = "com.apple.mobile.installation_proxy" 16 | InstrumentsServiceName = "com.apple.instruments.remoteserver" 17 | ) 18 | 19 | // the LockdownConnection must start session 20 | func startService(name string, device frames.Device) (*tunnel.Service, error) { 21 | lockdown, err := tunnel.LockdownDial(device) 22 | if err != nil { 23 | return nil, err 24 | } 25 | defer lockdown.Close() 26 | 27 | if err := lockdown.StartSession(); err != nil { 28 | return nil, err 29 | } 30 | 31 | dynamicPort, err := lockdown.StartService(name) 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | if err := lockdown.StopSession(); err != nil { 37 | return nil, err 38 | } 39 | 40 | baseConn, err := lockdown.GenerateConnection(dynamicPort.Port, dynamicPort.EnableServiceSSL) 41 | if err != nil { 42 | return nil, err 43 | } 44 | 45 | return tunnel.GenerateService(baseConn), nil 46 | } 47 | 48 | func syncServiceAndCheckError(service *tunnel.Service, resp interface{}) error { 49 | if pkg, err := service.Sync(); err != nil { 50 | return err 51 | } else if err := pkg.UnmarshalBody(resp); err != nil { 52 | return err 53 | } 54 | return nil 55 | } 56 | -------------------------------------------------------------------------------- /services/installation.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "iconsole/frames" 7 | "iconsole/tunnel" 8 | ) 9 | 10 | type ApplicationType string 11 | 12 | const ( 13 | System = ApplicationType("System") 14 | User = ApplicationType("User") 15 | Internal = ApplicationType("Internal") 16 | Any = ApplicationType("Any") 17 | ) 18 | 19 | type baseCommand struct { 20 | Command string `plist:"Command"` 21 | ClientOptions *InstallationProxyOption `plist:"ClientOptions,omitempty"` 22 | } 23 | 24 | type InstallationProxyOption struct { 25 | ApplicationType ApplicationType `plist:"ApplicationType,omitempty"` 26 | ReturnAttributes []string `plist:"ReturnAttributes,omitempty"` 27 | MetaData bool `plist:"com.apple.mobile_installation.metadata,omitempty"` 28 | BundleIDs []string `plist:"BundleIDs,omitempty"` /* for Lookup */ 29 | } 30 | 31 | type InstallationProxyService struct { 32 | service *tunnel.Service 33 | } 34 | 35 | func NewInstallationProxyService(device frames.Device) (*InstallationProxyService, error) { 36 | serv, err := startService(InstallationProxyServiceName, device) 37 | if err != nil { 38 | return nil, err 39 | } 40 | 41 | return &InstallationProxyService{service: serv}, nil 42 | } 43 | 44 | func (this *InstallationProxyService) Browse(opt *InstallationProxyOption) ([]map[string]interface{}, error) { 45 | m := baseCommand{ 46 | Command: "Browse", 47 | ClientOptions: opt, 48 | } 49 | 50 | if err := this.service.SendXML(m); err != nil { 51 | return nil, err 52 | } 53 | 54 | var apps []map[string]interface{} 55 | 56 | for { 57 | if pkg, err := this.service.Sync(); err != nil { 58 | return nil, err 59 | } else { 60 | r := map[string]interface{}{} 61 | if err := pkg.UnmarshalBody(&r); err != nil { 62 | return nil, err 63 | } 64 | if r["Status"].(string) == "Complete" { 65 | break 66 | } else if l, ok := r["CurrentList"].([]interface{}); ok { 67 | for _, v := range l { 68 | apps = append(apps, v.(map[string]interface{})) 69 | } 70 | } 71 | } 72 | } 73 | 74 | return apps, nil 75 | } 76 | 77 | func (this *InstallationProxyService) Lookup(opt *InstallationProxyOption) (map[string]interface{}, error) { 78 | req := baseCommand{ 79 | Command: "Lookup", 80 | ClientOptions: opt, 81 | } 82 | 83 | if err := this.service.SendXML(req); err != nil { 84 | return nil, err 85 | } 86 | 87 | if pkg, err := this.service.Sync(); err != nil { 88 | return nil, err 89 | } else { 90 | var r map[string]interface{} 91 | if err := pkg.UnmarshalBody(&r); err != nil { 92 | return nil, err 93 | } 94 | 95 | status, ok := r["Status"].(string) 96 | if !ok { 97 | return nil, errors.New("wrap object failed") 98 | } 99 | 100 | if status == "Complete" { 101 | if res, ok := r["LookupResult"].(map[string]interface{}); ok { 102 | return res, nil 103 | } else { 104 | return nil, errors.New("wrap object failed") 105 | } 106 | } else { 107 | return nil, fmt.Errorf("status: %s", status) 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /services/instrument.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "fmt" 7 | "iconsole/frames" 8 | "iconsole/ns" 9 | "iconsole/tunnel" 10 | "time" 11 | "unsafe" 12 | ) 13 | 14 | type DTXMessagePayloadHeader struct { 15 | Flags uint32 16 | AuxiliaryLength uint32 17 | TotalLength uint64 18 | } 19 | 20 | func (this DTXMessagePayloadHeader) Marshal() []byte { 21 | buf := &bytes.Buffer{} 22 | _ = binary.Write(buf, binary.LittleEndian, this.Flags) 23 | _ = binary.Write(buf, binary.LittleEndian, this.AuxiliaryLength) 24 | _ = binary.Write(buf, binary.LittleEndian, this.TotalLength) 25 | return buf.Bytes() 26 | } 27 | 28 | func (this *DTXMessagePayloadHeader) Unmarshal(buf []byte) error { 29 | r := bytes.NewReader(buf) 30 | if err := binary.Read(r, binary.LittleEndian, &this.Flags); err != nil { 31 | return err 32 | } else if err := binary.Read(r, binary.LittleEndian, &this.AuxiliaryLength); err != nil { 33 | return err 34 | } else if err := binary.Read(r, binary.LittleEndian, &this.TotalLength); err != nil { 35 | return err 36 | } 37 | return nil 38 | } 39 | 40 | type PrivateResponseMessage struct { 41 | Aux []interface{} 42 | Obj interface{} 43 | } 44 | 45 | type DTXMessageHeader struct { 46 | Magic uint32 47 | CB uint32 48 | FragmentId uint16 49 | FragmentCount uint16 50 | Length uint32 51 | Identifier uint32 52 | ConversationIndex uint32 53 | ChannelCode uint32 54 | ExpectsReply uint32 55 | } 56 | 57 | func (this *DTXMessageHeader) Unmarshal(data []byte) error { 58 | r := bytes.NewReader(data) 59 | if err := binary.Read(r, binary.LittleEndian, &this.Magic); err != nil { 60 | return err 61 | } else if err := binary.Read(r, binary.LittleEndian, &this.CB); err != nil { 62 | return err 63 | } else if err := binary.Read(r, binary.LittleEndian, &this.FragmentId); err != nil { 64 | return err 65 | } else if err := binary.Read(r, binary.LittleEndian, &this.FragmentCount); err != nil { 66 | return err 67 | } else if err := binary.Read(r, binary.LittleEndian, &this.Length); err != nil { 68 | return err 69 | } else if err := binary.Read(r, binary.LittleEndian, &this.Identifier); err != nil { 70 | return err 71 | } else if err := binary.Read(r, binary.LittleEndian, &this.ConversationIndex); err != nil { 72 | return err 73 | } else if err := binary.Read(r, binary.LittleEndian, &this.ChannelCode); err != nil { 74 | return err 75 | } else if err := binary.Read(r, binary.LittleEndian, &this.ExpectsReply); err != nil { 76 | return err 77 | } 78 | return nil 79 | } 80 | 81 | func (this DTXMessageHeader) Marshal() []byte { 82 | buf := &bytes.Buffer{} 83 | _ = binary.Write(buf, binary.LittleEndian, this.Magic) 84 | _ = binary.Write(buf, binary.LittleEndian, this.CB) 85 | _ = binary.Write(buf, binary.LittleEndian, this.FragmentId) 86 | _ = binary.Write(buf, binary.LittleEndian, this.FragmentCount) 87 | _ = binary.Write(buf, binary.LittleEndian, this.Length) 88 | _ = binary.Write(buf, binary.LittleEndian, this.Identifier) 89 | _ = binary.Write(buf, binary.LittleEndian, this.ConversationIndex) 90 | _ = binary.Write(buf, binary.LittleEndian, this.ChannelCode) 91 | _ = binary.Write(buf, binary.LittleEndian, this.ExpectsReply) 92 | return buf.Bytes() 93 | } 94 | 95 | type InstrumentService struct { 96 | service *tunnel.Service 97 | hs bool 98 | msgId uint32 99 | channels map[string]int32 100 | openChannel map[string]uint32 101 | } 102 | 103 | func NewInstrumentService(device frames.Device) (*InstrumentService, error) { 104 | service, err := startService(InstrumentsServiceName, device) 105 | if err != nil { 106 | return nil, err 107 | } 108 | 109 | if err := service.DismissSSL(); err != nil { 110 | return nil, err 111 | } 112 | 113 | return &InstrumentService{ 114 | service: service, 115 | channels: make(map[string]int32), 116 | openChannel: make(map[string]uint32), 117 | }, nil 118 | } 119 | 120 | func (this *InstrumentService) recvPrivateMessage() (*PrivateResponseMessage, error) { 121 | payloadBuf := &bytes.Buffer{} 122 | for { 123 | header := &DTXMessageHeader{} 124 | headerBuf := make([]byte, unsafe.Sizeof(*header)) 125 | if _, err := this.service.GetConnection().Read(headerBuf); err != nil { 126 | return nil, err 127 | } 128 | 129 | if err := header.Unmarshal(headerBuf); err != nil { 130 | return nil, err 131 | } 132 | 133 | if header.Magic != 0x1F3D5B79 { 134 | return nil, fmt.Errorf("bad magic %x", header.Magic) 135 | } 136 | 137 | if header.ConversationIndex == 1 { 138 | if header.Identifier != this.msgId { 139 | return nil, fmt.Errorf("except identifier %d new identifier %d", this.msgId, header.Identifier) 140 | } 141 | } else if header.ConversationIndex == 0 { 142 | if header.Identifier > this.msgId { 143 | this.msgId = header.Identifier 144 | } else if header.Identifier < this.msgId { 145 | return nil, fmt.Errorf("unexcept identifier %d", header.Identifier) 146 | } 147 | } else { 148 | return nil, fmt.Errorf("invalid conversationIndex %d", header.ConversationIndex) 149 | } 150 | 151 | if header.FragmentId == 0 { 152 | if header.FragmentCount > 1 { 153 | continue 154 | } 155 | } 156 | 157 | nRecv := 0 158 | for nRecv < int(header.Length) { 159 | _cap := 2048 160 | left := int(header.Length) - nRecv 161 | if left < _cap { 162 | _cap = left 163 | } 164 | recvBuf := make([]byte, _cap) 165 | n, err := this.service.GetConnection().Read(recvBuf) 166 | if err != nil { 167 | return nil, err 168 | } 169 | payloadBuf.Write(recvBuf[:n]) 170 | nRecv += n 171 | } 172 | 173 | if header.FragmentId == header.FragmentCount-1 { 174 | break 175 | } 176 | } 177 | 178 | payloadBytes := payloadBuf.Bytes() 179 | payload := &DTXMessagePayloadHeader{} 180 | if err := payload.Unmarshal(payloadBytes); err != nil { 181 | return nil, err 182 | } 183 | 184 | compress := (payload.Flags & 0xff000) >> 12 185 | if compress != 0 { 186 | return nil, fmt.Errorf("message is compressed type %d", compress) 187 | } 188 | 189 | payloadSize := uint32(unsafe.Sizeof(*payload)) 190 | objOffset := payloadSize + payload.AuxiliaryLength 191 | 192 | aux := payloadBytes[payloadSize : payloadSize+payload.AuxiliaryLength] 193 | obj := payloadBytes[objOffset : uint64(objOffset)+(payload.TotalLength-uint64(payload.AuxiliaryLength))] 194 | 195 | ret := &PrivateResponseMessage{} 196 | 197 | if len(aux) > 0 { 198 | if aux, err := ns.UnmarshalDTXMessage(aux); err != nil { 199 | return nil, err 200 | } else { 201 | ret.Aux = aux 202 | } 203 | } 204 | 205 | if len(obj) > 0 { 206 | if obj, err := ns.NewNSKeyedArchiver().Unmarshal(obj); err != nil { 207 | return nil, err 208 | } else { 209 | ret.Obj = obj 210 | } 211 | } 212 | 213 | return ret, nil 214 | } 215 | 216 | func (this *InstrumentService) sendPrivateMessage(selector string, args *ns.DTXMessage, channel uint32, expectReply bool) error { 217 | payload := &DTXMessagePayloadHeader{} 218 | header := &DTXMessageHeader{ 219 | ExpectsReply: 1, 220 | } 221 | 222 | er := 0x1000 223 | if !expectReply { 224 | er = 0 225 | header.ExpectsReply = 0 226 | } 227 | 228 | sel, err := ns.NewNSKeyedArchiver().Marshal(selector) 229 | if err != nil { 230 | return err 231 | } 232 | 233 | aux := make([]byte, 0) 234 | 235 | if args != nil { 236 | aux = args.ToBytes() 237 | } 238 | 239 | payload.Flags = uint32(0x2 | er) 240 | payload.AuxiliaryLength = uint32(len(aux)) 241 | payload.TotalLength = uint64(len(aux)) + uint64(len(sel)) 242 | 243 | header.Magic = 0x1F3D5B79 244 | header.CB = uint32(unsafe.Sizeof(*header)) 245 | header.FragmentId = 0 246 | header.FragmentCount = 1 247 | header.Length = uint32(unsafe.Sizeof(*payload)) + uint32(payload.TotalLength) 248 | this.msgId++ 249 | header.Identifier = this.msgId 250 | header.ConversationIndex = 0 251 | header.ChannelCode = channel 252 | 253 | msgBuf := &bytes.Buffer{} 254 | msgBuf.Write(header.Marshal()) 255 | msgBuf.Write(payload.Marshal()) 256 | msgBuf.Write(aux) 257 | msgBuf.Write(sel) 258 | 259 | _, err = this.service.GetConnection().Write(msgBuf.Bytes()) 260 | return err 261 | } 262 | 263 | func (this *InstrumentService) makeChannel(channel string) (uint32, error) { 264 | if _, ok := this.channels[channel]; !ok { 265 | return 0, fmt.Errorf("not support %s", channel) 266 | } else if c, ok := this.openChannel[channel]; ok { 267 | return c, nil 268 | } else { 269 | c := uint32(len(this.openChannel) + 1) 270 | msg := ns.NewDTXMessage() 271 | msg.AppendInt32(int32(c)) 272 | if err := msg.AppendObject(channel); err != nil { 273 | return 0, err 274 | } 275 | 276 | if err := this.sendPrivateMessage("_requestChannelWithCode:identifier:", msg, 0, true); err != nil { 277 | return 0, err 278 | } 279 | 280 | if _, err := this.recvPrivateMessage(); err != nil { 281 | return 0, err 282 | } 283 | 284 | return c, nil 285 | } 286 | } 287 | 288 | type Application struct { 289 | AppExtensionUUIDs []string 290 | BundlePath string 291 | CFBundleIdentifier string 292 | ContainerBundleIdentifier string 293 | ContainerBundlePath string 294 | PluginIdentifier string 295 | PluginUUID string 296 | DisplayName string 297 | ExecutableName string 298 | Placeholder string 299 | Restricted int 300 | Type string 301 | Version string 302 | } 303 | 304 | func (this *InstrumentService) AppList() ([]Application, error) { 305 | c, err := this.makeChannel("com.apple.instruments.server.services.device.applictionListing") 306 | if err != nil { 307 | return nil, err 308 | } 309 | 310 | // could use filter 311 | emptyMap := make(map[string]interface{}) 312 | msg := ns.NewDTXMessage() 313 | if err := msg.AppendObject(emptyMap); err != nil { 314 | return nil, err 315 | } 316 | if err := msg.AppendObject(""); err != nil { 317 | return nil, err 318 | } 319 | 320 | if err := this.sendPrivateMessage("installedApplicationsMatching:registerUpdateToken:", msg, c, true); err != nil { 321 | return nil, err 322 | } 323 | 324 | resp, err := this.recvPrivateMessage() 325 | if err != nil { 326 | return nil, err 327 | } 328 | 329 | var apps []Application 330 | rapps := resp.Obj.([]interface{}) 331 | for _, v := range rapps { 332 | m := v.(map[string]interface{}) 333 | a := &Application{} 334 | if uuids, ok := m["AppExtensionUUIDs"].([]interface{}); ok { 335 | for _, uuid := range uuids { 336 | a.AppExtensionUUIDs = append(a.AppExtensionUUIDs, uuid.(string)) 337 | } 338 | } 339 | a.BundlePath, _ = m["BundlePath"].(string) 340 | a.CFBundleIdentifier, _ = m["CFBundleIdentifier"].(string) 341 | a.DisplayName, _ = m["DisplayName"].(string) 342 | a.ExecutableName, _ = m["ExecutableName"].(string) 343 | a.Placeholder, _ = m["Placeholder"].(string) 344 | a.ContainerBundleIdentifier, _ = m["ContainerBundleIdentifier"].(string) 345 | a.ContainerBundlePath, _ = m["ContainerBundlePath"].(string) 346 | a.PluginIdentifier, _ = m["PluginIdentifier"].(string) 347 | a.PluginUUID, _ = m["PluginUUID"].(string) 348 | a.Restricted = int(m["Restricted"].(uint64)) 349 | a.Type, _ = m["Type"].(string) 350 | a.Version, _ = m["Version"].(string) 351 | apps = append(apps, *a) 352 | } 353 | 354 | return apps, nil 355 | } 356 | 357 | type Process struct { 358 | IsApplication bool 359 | Name string 360 | Pid int 361 | RealAppName string 362 | StartDate time.Time 363 | } 364 | 365 | func (this *InstrumentService) ProcessList() ([]Process, error) { 366 | c, err := this.makeChannel("com.apple.instruments.server.services.deviceinfo") 367 | if err != nil { 368 | return nil, err 369 | } 370 | 371 | if err := this.sendPrivateMessage("runningProcesses", nil, c, true); err != nil { 372 | return nil, err 373 | } 374 | 375 | resp, err := this.recvPrivateMessage() 376 | if err != nil { 377 | return nil, err 378 | } 379 | 380 | var p []Process 381 | 382 | objs := resp.Obj.([]interface{}) 383 | for _, v := range objs { 384 | m := v.(map[string]interface{}) 385 | tp := &Process{} 386 | if m["isApplication"].(bool) { 387 | tp.IsApplication = true 388 | } 389 | tp.Name = m["name"].(string) 390 | tp.Pid = int(m["pid"].(uint64)) 391 | tp.RealAppName = m["realAppName"].(string) 392 | if t, ok := m["startDate"].(time.Time); ok { 393 | tp.StartDate = t 394 | } 395 | 396 | p = append(p, *tp) 397 | } 398 | 399 | return p, nil 400 | } 401 | 402 | func (this *InstrumentService) Kill(pid int) error { 403 | c, err := this.makeChannel("com.apple.instruments.server.services.processcontrol") 404 | if err != nil { 405 | return err 406 | } 407 | 408 | msg := ns.NewDTXMessage() 409 | if err := msg.AppendObject(pid); err != nil { 410 | return err 411 | } 412 | 413 | if err := this.sendPrivateMessage("killPid:", msg, c, false); err != nil { 414 | return err 415 | } 416 | 417 | return nil 418 | } 419 | 420 | func (this *InstrumentService) Launch(bundleId string) (int, error) { 421 | c, err := this.makeChannel("com.apple.instruments.server.services.processcontrol") 422 | if err != nil { 423 | return 0, err 424 | } 425 | 426 | msg := ns.NewDTXMessage() 427 | // application path: not use through empty 428 | if err := msg.AppendObject(""); err != nil { 429 | return 0, err 430 | } 431 | // `CFBundleIdentifier` 432 | if err := msg.AppendObject(bundleId); err != nil { 433 | return 0, err 434 | } 435 | // launch app environment variables: not use 436 | if err := msg.AppendObject(map[string]interface{}{}); err != nil { 437 | return 0, err 438 | } 439 | // launch app start arguments: not use 440 | if err := msg.AppendObject([]interface{}{}); err != nil { 441 | return 0, err 442 | } 443 | // launch app options 444 | if err := msg.AppendObject(map[string]interface{}{ 445 | "StartSuspendedKey": 0, 446 | "KillExisting": 1, 447 | }); err != nil { 448 | return 0, err 449 | } 450 | 451 | if err := this.sendPrivateMessage("launchSuspendedProcessWithDevicePath:bundleIdentifier:environment:arguments:options:", msg, c, true); err != nil { 452 | return 0, err 453 | } 454 | 455 | resp, err := this.recvPrivateMessage() 456 | if err != nil { 457 | return 0, err 458 | } 459 | 460 | if err, ok := resp.Obj.(ns.GoNSError); ok { 461 | return 0, fmt.Errorf("%s", err.NSUserInfo.(map[string]interface{})["NSLocalizedDescription"]) 462 | } 463 | 464 | return int(resp.Obj.(uint64)), nil 465 | } 466 | 467 | func (this *InstrumentService) Handshake() error { 468 | if !this.hs { 469 | msg := ns.NewDTXMessage() 470 | if err := msg.AppendObject(map[string]interface{}{ 471 | "com.apple.private.DTXBlockCompression": 2, 472 | "com.apple.private.DTXConnection": 1, 473 | }); err != nil { 474 | return err 475 | } 476 | 477 | if err := this.sendPrivateMessage("_notifyOfPublishedCapabilities:", msg, 0, false); err != nil { 478 | return err 479 | } 480 | 481 | resp, err := this.recvPrivateMessage() 482 | if err != nil { 483 | return err 484 | } 485 | 486 | if resp.Obj.(string) != "_notifyOfPublishedCapabilities:" { 487 | return fmt.Errorf("response obj %s", resp.Obj) 488 | } 489 | 490 | aux := resp.Aux[0].(map[string]interface{}) 491 | for k, v := range aux { 492 | this.channels[k] = int32(v.(uint64)) 493 | } 494 | } 495 | 496 | return nil 497 | } 498 | -------------------------------------------------------------------------------- /services/mount.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "iconsole/frames" 7 | "iconsole/tunnel" 8 | "io" 9 | "io/ioutil" 10 | "os" 11 | ) 12 | 13 | type MountResponse struct { 14 | frames.LockdownResponse 15 | Status string `plist:"Status"` 16 | } 17 | 18 | type Images struct { 19 | MountResponse 20 | ImageSignature [][]byte `plist:"ImageSignature"` 21 | } 22 | 23 | type MountRequest struct { 24 | Command string `plist:"Command"` 25 | ImageType string `plist:"ImageType"` 26 | } 27 | 28 | type uploadImageRequest struct { 29 | MountRequest 30 | ImageSignature []byte `plist:"ImageSignature"` 31 | ImageSize uint32 `plist:"ImageSize"` 32 | } 33 | 34 | type mountImageRequest struct { 35 | MountRequest 36 | ImagePath string `plist:"ImagePath"` 37 | ImageSignature []byte `plist:"ImageSignature"` 38 | } 39 | 40 | type MountService struct { 41 | service *tunnel.Service 42 | } 43 | 44 | func NewMountService(device frames.Device) (*MountService, error) { 45 | serv, err := startService(MountServiceName, device) 46 | if err != nil { 47 | return nil, err 48 | } 49 | 50 | return &MountService{service: serv}, nil 51 | } 52 | 53 | func (this *MountService) Images(imageType string) (*Images, error) { 54 | req := MountRequest{ 55 | Command: "LookupImage", 56 | ImageType: imageType, 57 | } 58 | 59 | if err := this.service.SendXML(req); err != nil { 60 | return nil, err 61 | } 62 | 63 | var resp Images 64 | 65 | if err := syncServiceAndCheckError(this.service, &resp); err != nil { 66 | return nil, err 67 | } else if resp.Error != "" { 68 | return nil, errors.New(resp.Error) 69 | } 70 | 71 | return &resp, nil 72 | } 73 | 74 | func (this *MountService) readFileData(path string) ([]byte, error) { 75 | f, err := os.Open(path) 76 | if err != nil { 77 | return nil, err 78 | } 79 | defer f.Close() 80 | return ioutil.ReadAll(f) 81 | } 82 | 83 | func (this *MountService) UploadImage(dmg, signature, imageType string) error { 84 | dmgFile, err := os.Open(dmg) 85 | if err != nil { 86 | return err 87 | } 88 | defer func() { 89 | if dmgFile != nil { 90 | dmgFile.Close() 91 | } 92 | }() 93 | 94 | signatureData, err := this.readFileData(signature) 95 | if err != nil { 96 | return err 97 | } 98 | 99 | dmgFileSize, err := dmgFile.Seek(0, io.SeekEnd) 100 | if err != nil { 101 | return err 102 | } 103 | if _, err := dmgFile.Seek(0, io.SeekStart); err != nil { 104 | return err 105 | } 106 | 107 | req := &uploadImageRequest{ 108 | MountRequest: MountRequest{ 109 | Command: "ReceiveBytes", 110 | ImageType: imageType, 111 | }, 112 | ImageSize: uint32(dmgFileSize), 113 | ImageSignature: signatureData, 114 | } 115 | 116 | if err := this.service.SendXML(req); err != nil { 117 | return err 118 | } 119 | 120 | var resp MountResponse 121 | if err := syncServiceAndCheckError(this.service, &resp); err != nil { 122 | return err 123 | } else if resp.Error != "" { 124 | return errors.New(resp.Error) 125 | } 126 | 127 | if resp.Status != "ReceiveBytesAck" { 128 | return fmt.Errorf("status: %s", resp.Status) 129 | } 130 | 131 | b := make([]byte, 0xffff) 132 | baseConn := this.service.GetConnection() 133 | for { 134 | if n, err := dmgFile.Read(b); err != nil && err != io.EOF { 135 | return err 136 | } else if n > 0 { 137 | if _, err := baseConn.Write(b[:n]); err != nil { 138 | return err 139 | } 140 | } else { 141 | break 142 | } 143 | } 144 | dmgFile.Close() 145 | dmgFile = nil 146 | 147 | if err := syncServiceAndCheckError(this.service, &resp); err != nil { 148 | return err 149 | } else if resp.Error != "" { 150 | return errors.New(resp.Error) 151 | } 152 | 153 | if resp.Status != "Complete" { 154 | return fmt.Errorf("status: %s", resp.Status) 155 | } 156 | 157 | return nil 158 | } 159 | 160 | func (this *MountService) Mount(path, imageType, signature string) error { 161 | signatureData, err := this.readFileData(signature) 162 | if err != nil { 163 | return err 164 | } 165 | 166 | req := mountImageRequest{ 167 | MountRequest: MountRequest{ 168 | Command: "MountImage", 169 | ImageType: imageType, 170 | }, 171 | ImagePath: path, 172 | ImageSignature: signatureData, 173 | } 174 | 175 | var resp MountResponse 176 | 177 | if err := this.service.SendXML(req); err != nil { 178 | return err 179 | } else if err := syncServiceAndCheckError(this.service, &resp); err != nil { 180 | return err 181 | } else if resp.Error != "" { 182 | return errors.New(resp.Error) 183 | } else if resp.Status != "Complete" { 184 | return fmt.Errorf("status: %s", resp.Status) 185 | } 186 | 187 | return nil 188 | } 189 | 190 | func (this *MountService) Close() error { 191 | return this.service.GetConnection().Close() 192 | } 193 | -------------------------------------------------------------------------------- /services/screenshot.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "iconsole/frames" 7 | "iconsole/tunnel" 8 | "io" 9 | ) 10 | 11 | type ScreenshotService struct { 12 | service *tunnel.Service 13 | hs bool 14 | } 15 | 16 | func NewScreenshotService(device frames.Device) (*ScreenshotService, error) { 17 | serv, err := startService(ScreenshotServiceName, device) 18 | if err != nil { 19 | return nil, err 20 | } 21 | 22 | return &ScreenshotService{service: serv}, nil 23 | } 24 | 25 | func (this *ScreenshotService) Handshake() error { 26 | if !this.hs { 27 | firstMsg := []interface{}{ 28 | "DLMessageVersionExchange", 29 | "DLVersionsOk", 30 | } 31 | 32 | var f []interface{} 33 | if err := syncServiceAndCheckError(this.service, &f); err != nil { 34 | return err 35 | } 36 | firstMsg = append(firstMsg, f[1]) 37 | if err := this.service.SendBinary(firstMsg); err != nil { 38 | return err 39 | } else if err := syncServiceAndCheckError(this.service, &f); err != nil { 40 | return err 41 | } else if f[3].(string) != "DLMessageDeviceReady" { 42 | return fmt.Errorf("message device not ready %s", f[3]) 43 | } 44 | this.hs = true 45 | } 46 | return nil 47 | } 48 | 49 | func (this *ScreenshotService) Shot(w io.Writer) error { 50 | if err := this.Handshake(); err != nil { 51 | return err 52 | } 53 | 54 | var f []interface{} 55 | 56 | captureMsg := []interface{}{ 57 | "DLMessageProcessMessage", 58 | map[string]interface{}{ 59 | "MessageType": "ScreenShotRequest", 60 | }, 61 | } 62 | 63 | if err := this.service.SendBinary(captureMsg); err != nil { 64 | return err 65 | } else if err := syncServiceAndCheckError(this.service, &f); err != nil { 66 | return err 67 | } else if f[0] != "DLMessageProcessMessage" { 68 | return fmt.Errorf("message device not ready %s %s", f[3], f[4]) 69 | } 70 | 71 | screen := f[1].(map[string]interface{}) 72 | if data, ok := screen["ScreenShotData"].([]byte); !ok { 73 | return errors.New("`ScreenShotData` not ready") 74 | } else if _, err := w.Write(data); err != nil { 75 | return err 76 | } 77 | 78 | return nil 79 | } 80 | 81 | func (this *ScreenshotService) Close() error { 82 | return this.service.GetConnection().Close() 83 | } 84 | -------------------------------------------------------------------------------- /services/simulate.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "iconsole/frames" 7 | "iconsole/tunnel" 8 | "math" 9 | "strconv" 10 | ) 11 | 12 | const ( 13 | X_PI = math.Pi * 3000.0 / 180.0 14 | OFFSET = 0.00669342162296594323 15 | AXIS = 6378245.0 16 | ) 17 | 18 | func isOutOFChina(lon, lat float64) bool { 19 | return !(lon > 73.66 && lon < 135.05 && lat > 3.86 && lat < 53.55) 20 | } 21 | 22 | func delta(lon, lat float64) (float64, float64) { 23 | dlat := transformlat(lon-105.0, lat-35.0) 24 | dlon := transformlng(lon-105.0, lat-35.0) 25 | 26 | radlat := lat / 180.0 * math.Pi 27 | magic := math.Sin(radlat) 28 | magic = 1 - OFFSET*magic*magic 29 | sqrtmagic := math.Sqrt(magic) 30 | 31 | dlat = (dlat * 180.0) / ((AXIS * (1 - OFFSET)) / (magic * sqrtmagic) * math.Pi) 32 | dlon = (dlon * 180.0) / (AXIS / sqrtmagic * math.Cos(radlat) * math.Pi) 33 | 34 | mgLat := lat + dlat 35 | mgLon := lon + dlon 36 | 37 | return mgLon, mgLat 38 | } 39 | 40 | func transformlat(lon, lat float64) float64 { 41 | var ret = -100.0 + 2.0*lon + 3.0*lat + 0.2*lat*lat + 0.1*lon*lat + 0.2*math.Sqrt(math.Abs(lon)) 42 | ret += (20.0*math.Sin(6.0*lon*math.Pi) + 20.0*math.Sin(2.0*lon*math.Pi)) * 2.0 / 3.0 43 | ret += (20.0*math.Sin(lat*math.Pi) + 40.0*math.Sin(lat/3.0*math.Pi)) * 2.0 / 3.0 44 | ret += (160.0*math.Sin(lat/12.0*math.Pi) + 320*math.Sin(lat*math.Pi/30.0)) * 2.0 / 3.0 45 | return ret 46 | } 47 | 48 | func transformlng(lon, lat float64) float64 { 49 | var ret = 300.0 + lon + 2.0*lat + 0.1*lon*lon + 0.1*lon*lat + 0.1*math.Sqrt(math.Abs(lon)) 50 | ret += (20.0*math.Sin(6.0*lon*math.Pi) + 20.0*math.Sin(2.0*lon*math.Pi)) * 2.0 / 3.0 51 | ret += (20.0*math.Sin(lon*math.Pi) + 40.0*math.Sin(lon/3.0*math.Pi)) * 2.0 / 3.0 52 | ret += (150.0*math.Sin(lon/12.0*math.Pi) + 300.0*math.Sin(lon/30.0*math.Pi)) * 2.0 / 3.0 53 | return ret 54 | } 55 | 56 | func gcj02ToWGS84(lon, lat float64) (float64, float64) { 57 | if isOutOFChina(lon, lat) { 58 | return lon, lat 59 | } 60 | 61 | mgLon, mgLat := delta(lon, lat) 62 | 63 | return lon*2 - mgLon, lat*2 - mgLat 64 | } 65 | 66 | func bd09ToGCJ02(lon, lat float64) (float64, float64) { 67 | x := lon - 0.0065 68 | y := lat - 0.006 69 | 70 | z := math.Sqrt(x*x+y*y) - 0.00002*math.Sin(y*X_PI) 71 | theta := math.Atan2(y, x) - 0.000003*math.Cos(x*X_PI) 72 | 73 | gLon := z * math.Cos(theta) 74 | gLat := z * math.Sin(theta) 75 | 76 | return gLon, gLat 77 | } 78 | 79 | func bd09ToWGS84(lon, lat float64) (float64, float64) { 80 | lon, lat = bd09ToGCJ02(lon, lat) 81 | return gcj02ToWGS84(lon, lat) 82 | } 83 | 84 | type SimulateLocationService struct { 85 | service *tunnel.Service 86 | } 87 | 88 | func NewSimulateLocationService(device frames.Device) (*SimulateLocationService, error) { 89 | serv, err := startService(SimulateLocationServiceName, device) 90 | if err != nil { 91 | return nil, err 92 | } 93 | 94 | return &SimulateLocationService{service: serv}, nil 95 | } 96 | 97 | func (this *SimulateLocationService) Start(lon, lat float64, coor string) error { 98 | switch coor { 99 | case "gcj02": 100 | lon, lat = gcj02ToWGS84(lon, lat) 101 | case "bd09": 102 | lon, lat = bd09ToWGS84(lon, lat) 103 | } 104 | 105 | buf := new(bytes.Buffer) 106 | if err := binary.Write(buf, binary.BigEndian, uint32(0)); err != nil { 107 | return err 108 | } 109 | 110 | latS := []byte(strconv.FormatFloat(lat, 'E', -1, 64)) 111 | if err := binary.Write(buf, binary.BigEndian, uint32(len(latS))); err != nil { 112 | return err 113 | } else if err := binary.Write(buf, binary.BigEndian, latS); err != nil { 114 | return err 115 | } 116 | 117 | lonS := []byte(strconv.FormatFloat(lon, 'E', -1, 64)) 118 | if err := binary.Write(buf, binary.BigEndian, uint32(len(lonS))); err != nil { 119 | return err 120 | } else if err := binary.Write(buf, binary.BigEndian, lonS); err != nil { 121 | return err 122 | } 123 | 124 | if _, err := this.service.GetConnection().Write(buf.Bytes()); err != nil { 125 | return err 126 | } 127 | 128 | return nil 129 | } 130 | 131 | /* 132 | version test in 12.3.1 can't stop but 13 up can stop 133 | */ 134 | func (this *SimulateLocationService) Stop() error { 135 | if _, err := this.service.GetConnection().Write([]byte{0x00, 0x00, 0x00, 0x01}); err != nil { 136 | return err 137 | } 138 | return nil 139 | } 140 | 141 | func (this *SimulateLocationService) Close() error { 142 | return this.service.GetConnection().Close() 143 | } 144 | -------------------------------------------------------------------------------- /services/syslog.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "errors" 5 | "iconsole/frames" 6 | "iconsole/tunnel" 7 | ) 8 | 9 | type SyslogRelayService struct { 10 | service *tunnel.Service 11 | closed bool 12 | } 13 | 14 | func NewSyslogRelayService(device frames.Device) (*SyslogRelayService, error) { 15 | serv, err := startService(SyslogRelayServiceName, device) 16 | if err != nil { 17 | return nil, err 18 | } 19 | 20 | return &SyslogRelayService{service: serv}, nil 21 | } 22 | 23 | func (this *SyslogRelayService) IsClosed() bool { 24 | return this.closed 25 | } 26 | 27 | const ( 28 | kBackslash = 0x5c 29 | kM = 0x4d 30 | kDash = 0x2d 31 | kCaret = 0x5e 32 | kNum = 0x30 33 | ) 34 | 35 | func (this *SyslogRelayService) isDigit(d []byte) bool { 36 | for i := 0; i < len(d); i++ { 37 | if (d[i] & 0xf0) != kNum { 38 | return false 39 | } 40 | } 41 | return true 42 | } 43 | 44 | func (this *SyslogRelayService) unicode(data []byte) []byte { 45 | var out []byte 46 | for i := 0; i < len(data); { 47 | if data[i] != kBackslash || i > len(data)-4 { 48 | out = append(out, data[i]) 49 | i++ 50 | } else { 51 | if data[i+1] == kM && data[i+2] == kCaret { 52 | out = append(out, (data[i+3]&0x7f)+0x40) 53 | } else if data[i+1] == kCaret { 54 | // don't know is right 55 | out = append(out, (data[i+2]&0x7f)-0x40, data[i+3]) 56 | } else if data[i+1] == kM && data[i+2] == kDash { 57 | out = append(out, data[i+3]|0x80) 58 | } else if this.isDigit(data[i+1 : i+3]) { 59 | out = append(out, (data[i+1]&0x3)<<6|(data[i+2]&0x7)<<3|data[i+3]&0x07) 60 | } else { 61 | out = append(out, data[i:i+4]...) 62 | } 63 | i += 4 64 | } 65 | } 66 | return out 67 | } 68 | 69 | func (this *SyslogRelayService) Relay(cb func(*SyslogRelayService, []byte) bool) error { 70 | if this.IsClosed() { 71 | return errors.New("closed") 72 | } 73 | 74 | buf := make([]byte, 0xffff) 75 | 76 | for { 77 | n, err := this.service.GetConnection().Read(buf) 78 | if err != nil && n == 0 { 79 | return err 80 | } 81 | 82 | if !cb(this, this.unicode(buf[:n])) { 83 | break 84 | } 85 | } 86 | 87 | return this.Close() 88 | } 89 | 90 | func (this *SyslogRelayService) Close() error { 91 | this.closed = true 92 | return this.service.GetConnection().Close() 93 | } 94 | -------------------------------------------------------------------------------- /sim.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "iconsole/services" 5 | 6 | "github.com/urfave/cli" 7 | ) 8 | 9 | func startSimAction(ctx *cli.Context) error { 10 | udid := ctx.String("UDID") 11 | lon := ctx.Float64("lon") 12 | lat := ctx.Float64("lat") 13 | coor := ctx.String("coor") 14 | 15 | device, err := getDevice(udid) 16 | if err != nil { 17 | return err 18 | } else if sls, err := services.NewSimulateLocationService(device); err != nil { 19 | return err 20 | } else if err := sls.Start(lon, lat, coor); err != nil { 21 | return err 22 | } 23 | 24 | return err 25 | } 26 | 27 | func stopSimAction(ctx *cli.Context) error { 28 | udid := ctx.String("UDID") 29 | 30 | device, err := getDevice(udid) 31 | if err != nil { 32 | return err 33 | } else if sls, err := services.NewSimulateLocationService(device); err != nil { 34 | return err 35 | } else if err := sls.Stop(); err != nil { 36 | return err 37 | } 38 | 39 | return err 40 | } 41 | 42 | func initSimCommond() cli.Command { 43 | return cli.Command{ 44 | Name: "simlocation", 45 | ShortName: "sim", 46 | Usage: "A mounted developer disk image is required on the device.", 47 | Subcommands: []cli.Command{ 48 | { 49 | Name: "start", 50 | Action: startSimAction, 51 | Flags: append(globalFlags, cli.Float64Flag{ 52 | Name: "latitude, lat", 53 | EnvVar: "LATITUDE", 54 | Required: true, 55 | Value: 0, 56 | }, 57 | cli.Float64Flag{ 58 | Name: "longtitude, lon", 59 | EnvVar: "LONGTITUDE", 60 | Required: true, 61 | Value: 0, 62 | }, 63 | cli.StringFlag{ 64 | Name: "coordinate, coor", 65 | EnvVar: "COORDINATE", 66 | Usage: "coordinate name `gcj02` `wsg84` `bd09` default `gcj02`", 67 | Value: "gcj02", 68 | }), 69 | }, 70 | { 71 | Name: "stop", 72 | Action: stopSimAction, 73 | Flags: globalFlags, 74 | }, 75 | }, 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /syslog.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "iconsole/services" 6 | 7 | "github.com/urfave/cli" 8 | ) 9 | 10 | func logCb(_ *services.SyslogRelayService, log []byte) bool { 11 | fmt.Print(string(log)) 12 | return true 13 | } 14 | 15 | func syslogAction(ctx *cli.Context) error { 16 | udid := ctx.String("UDID") 17 | 18 | if device, err := getDevice(udid); err != nil { 19 | return err 20 | } else if srs, err := services.NewSyslogRelayService(device); err != nil { 21 | return err 22 | } else if err := srs.Relay(logCb); err != nil { 23 | return err 24 | } 25 | 26 | return nil 27 | } 28 | 29 | func initSyslogCommond() cli.Command { 30 | return cli.Command{ 31 | Name: "syslog", 32 | ShortName: "log", 33 | Usage: "Relay syslog of a connected device.", 34 | Action: syslogAction, 35 | Flags: globalFlags, 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /transport.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "iconsole/tunnel" 7 | "io" 8 | "net" 9 | "os" 10 | "os/signal" 11 | "strings" 12 | "syscall" 13 | "time" 14 | 15 | "github.com/urfave/cli" 16 | ) 17 | 18 | func transportAction(ctx *cli.Context) error { 19 | udid := ctx.String("UDID") 20 | port := ctx.Int64("port") 21 | args := ctx.Args() 22 | t := "tcp" 23 | a := "" 24 | if len(args) == 2 { 25 | t = strings.ToLower(args[0]) 26 | a = args[1] 27 | } else { 28 | return fmt.Errorf("Arguments not enough\nuseage example.\n\ticonsole relay -u -p 1234 tcp :1234\n\ticonsole relay -u -p 1234 tcp 127.0.0.1:443\n\ticonsole relay -u -p 1234 unix /opt/debugserver") 29 | } 30 | 31 | l, err := net.Listen(t, a) 32 | if err != nil { 33 | return err 34 | } 35 | 36 | if t == "unix" { 37 | if err := os.Chmod(a, 0777); err != nil { 38 | return err 39 | } 40 | 41 | sigc := make(chan os.Signal, 1) 42 | signal.Notify(sigc, os.Interrupt, os.Kill, syscall.SIGTERM) 43 | go func(c chan os.Signal) { 44 | <-c 45 | l.Close() 46 | }(sigc) 47 | } 48 | 49 | if udid == "" { 50 | return errors.New("Exec failed unset `UDID` argument") 51 | } 52 | 53 | if port < 3 { 54 | return errors.New("Port number range error") 55 | } 56 | 57 | device, err := getDevice(udid) 58 | if err != nil { 59 | return err 60 | } 61 | 62 | for { 63 | come, err := l.Accept() 64 | if err != nil { 65 | return nil 66 | } 67 | 68 | go func(front net.Conn) { 69 | back, err := tunnel.Connect(device, int(port)) 70 | if err != nil { 71 | front.Close() 72 | fmt.Printf("Dial device port `%d` error: %s\n", port, err) 73 | return 74 | } 75 | 76 | /* clean timeout */ 77 | if err := back.RawConn.SetDeadline(time.Time{}); err != nil { 78 | fmt.Printf("SetDeadline %s\n", err) 79 | return 80 | } 81 | 82 | fmt.Printf("Accept new connection %s\n", front.RemoteAddr()) 83 | go func(front, back net.Conn) { 84 | if _, err := io.Copy(front, back); err != nil { 85 | _ = front.Close() 86 | _ = back.Close() 87 | } 88 | }(front, back.RawConn) 89 | go func(front, back net.Conn) { 90 | if _, err := io.Copy(back, front); err != nil { 91 | _ = front.Close() 92 | _ = back.Close() 93 | } 94 | }(front, back.RawConn) 95 | }(come) 96 | } 97 | } 98 | 99 | func initTransportCommand() cli.Command { 100 | return cli.Command{ 101 | Name: "relay", 102 | Usage: "Transport communication wrap to local network", 103 | Action: transportAction, 104 | Flags: append(globalFlags, cli.Int64Flag{ 105 | Name: "port, p", 106 | Usage: "Connect to device port", 107 | EnvVar: "PORT", 108 | }), 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /tunnel/connUnix.go: -------------------------------------------------------------------------------- 1 | // +build linux darwin freebsd openbsd android netbsd solaris 2 | 3 | package tunnel 4 | 5 | import ( 6 | "net" 7 | "time" 8 | ) 9 | 10 | func RawDial(timeout time.Duration) (net.Conn, error) { 11 | dialer := net.Dialer{ 12 | Timeout: timeout, 13 | } 14 | 15 | return dialer.Dial("unix", "/var/run/usbmuxd") 16 | } 17 | -------------------------------------------------------------------------------- /tunnel/connWindows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package tunnel 4 | 5 | import ( 6 | "net" 7 | "time" 8 | ) 9 | 10 | func RawDial(timeout time.Duration) (net.Conn, error) { 11 | dialer := net.Dialer{ 12 | Timeout: timeout, 13 | } 14 | 15 | return dialer.Dial("tcp", "127.0.0.1:27015") 16 | } 17 | -------------------------------------------------------------------------------- /tunnel/lockdown.go: -------------------------------------------------------------------------------- 1 | package tunnel 2 | 3 | import ( 4 | "bytes" 5 | "crypto/rand" 6 | "crypto/rsa" 7 | "crypto/sha1" 8 | "crypto/x509" 9 | "encoding/pem" 10 | "errors" 11 | "fmt" 12 | "iconsole/frames" 13 | "math/big" 14 | "strconv" 15 | "strings" 16 | "time" 17 | 18 | uuid "github.com/satori/go.uuid" 19 | ) 20 | 21 | const ( 22 | LockdownPort = 62078 23 | ) 24 | 25 | type LockdownConnection struct { 26 | conn *Service 27 | Version []int 28 | device frames.Device 29 | pairRecord *frames.PairRecord 30 | sslSession *frames.StartSessionResponse 31 | } 32 | 33 | func getPemCertificate(cert []byte) ([]byte, error) { 34 | buf := new(bytes.Buffer) 35 | if err := pem.Encode(buf, &pem.Block{ 36 | Type: "CERTIFICATE", 37 | Bytes: cert, 38 | }); err != nil { 39 | return nil, err 40 | } 41 | return buf.Bytes(), nil 42 | } 43 | 44 | func getPairPemFormat(cert []byte, key *rsa.PrivateKey) ([]byte, []byte, error) { 45 | p, err := getPemCertificate(cert) 46 | if err != nil { 47 | return nil, nil, err 48 | } 49 | 50 | buf := new(bytes.Buffer) 51 | if err := pem.Encode(buf, &pem.Block{ 52 | Type: "RSA PRIVATE KEY", 53 | Bytes: x509.MarshalPKCS1PrivateKey(key), 54 | }); err != nil { 55 | return nil, nil, err 56 | } 57 | 58 | priv := buf.Bytes() 59 | 60 | return p, priv, nil 61 | } 62 | 63 | func (this *LockdownConnection) generatePairRecord(devicePubkeyPEM []byte) (*frames.PairRecord, error) { 64 | record := &frames.PairRecord{} 65 | 66 | if devicePubkeyPEM == nil { 67 | return nil, errors.New("DevicePublic nil") 68 | } 69 | 70 | block, _ := pem.Decode(devicePubkeyPEM) 71 | deviceKey, err := x509.ParsePKCS1PublicKey(block.Bytes) 72 | if err != nil { 73 | return nil, err 74 | } 75 | 76 | rootKey, err := rsa.GenerateKey(rand.Reader, 2048) 77 | if err != nil { 78 | return nil, err 79 | } 80 | 81 | hostKey, err := rsa.GenerateKey(rand.Reader, 2048) 82 | if err != nil { 83 | return nil, err 84 | } 85 | 86 | serialNumber := big.NewInt(0) 87 | 88 | notBefore := time.Now() 89 | notAfter := notBefore.Add(time.Hour * (24 * 365) * 10) 90 | 91 | rootTemplate := x509.Certificate{ 92 | IsCA: true, 93 | SerialNumber: serialNumber, 94 | Version: 2, 95 | SignatureAlgorithm: x509.SHA1WithRSA, 96 | PublicKeyAlgorithm: x509.RSA, 97 | NotBefore: notBefore, 98 | NotAfter: notAfter, 99 | BasicConstraintsValid: true, 100 | } 101 | 102 | caCert, err := x509.CreateCertificate(rand.Reader, &rootTemplate, &rootTemplate, rootKey.Public(), rootKey) 103 | if err != nil { 104 | return nil, err 105 | } 106 | 107 | hostTemplate := x509.Certificate{ 108 | SerialNumber: serialNumber, 109 | Version: 2, 110 | SignatureAlgorithm: x509.SHA1WithRSA, 111 | PublicKeyAlgorithm: x509.RSA, 112 | NotBefore: notBefore, 113 | NotAfter: notAfter, 114 | KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, 115 | BasicConstraintsValid: true, 116 | } 117 | 118 | cert, err := x509.CreateCertificate(rand.Reader, &hostTemplate, &rootTemplate, hostKey.Public(), rootKey) 119 | if err != nil { 120 | return nil, err 121 | } 122 | 123 | caPEM, caPrivPEM, err := getPairPemFormat(caCert, rootKey) 124 | 125 | certPEM, certPrivPEM, err := getPairPemFormat(cert, hostKey) 126 | 127 | h := sha1.New() 128 | h.Write(rootKey.N.Bytes()) 129 | subjectKeyId := h.Sum(nil) 130 | 131 | deviceTemplate := x509.Certificate{ 132 | SerialNumber: serialNumber, 133 | Version: 2, 134 | SignatureAlgorithm: x509.SHA1WithRSA, 135 | PublicKeyAlgorithm: x509.RSA, 136 | NotBefore: notBefore, 137 | NotAfter: notAfter, 138 | BasicConstraintsValid: true, 139 | KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, 140 | SubjectKeyId: subjectKeyId, 141 | } 142 | 143 | deviceCert, err := x509.CreateCertificate(rand.Reader, &deviceTemplate, &rootTemplate, deviceKey, rootKey) 144 | if err != nil { 145 | return nil, err 146 | } 147 | 148 | deviceCertPEM, err := getPemCertificate(deviceCert) 149 | if err != nil { 150 | return nil, err 151 | } 152 | 153 | record.DeviceCertificate = deviceCertPEM 154 | record.HostCertificate = certPEM 155 | record.HostPrivateKey = certPrivPEM 156 | record.RootCertificate = caPEM 157 | record.RootPrivateKey = caPrivPEM 158 | 159 | return record, nil 160 | } 161 | 162 | func LockdownDial(device frames.Device) (*LockdownConnection, error) { 163 | c, err := Connect(device, LockdownPort) 164 | if err != nil { 165 | return nil, err 166 | } 167 | 168 | s := &Service{conn: MixConnectionClient(c.RawConn)} 169 | 170 | return &LockdownConnection{conn: s, device: device}, nil 171 | } 172 | 173 | func (this *LockdownConnection) Pair() (*frames.PairRecord, error) { 174 | valueResp, err := this.GetValue("", "DevicePublicKey") 175 | if err != nil { 176 | return nil, err 177 | } 178 | 179 | var devicePub []byte 180 | 181 | if valueResp.Value == nil { 182 | return nil, fmt.Errorf("%s", valueResp.Error) 183 | } else if d, ok := valueResp.Value.([]byte); ok { 184 | devicePub = d 185 | } 186 | 187 | buid, err := ReadBUID() 188 | if err != nil { 189 | return nil, err 190 | } 191 | 192 | wifiAddress := "" 193 | 194 | valueResp, err = this.GetValue("", "WiFiAddress") 195 | if err == nil && valueResp.Value != nil { 196 | wifiAddress = valueResp.Value.(string) 197 | } 198 | 199 | record, err := this.generatePairRecord(devicePub) 200 | if err != nil { 201 | return nil, err 202 | } 203 | 204 | record.SystemBUID = buid 205 | record.HostID = strings.ToUpper(uuid.NewV4().String()) 206 | hostPrivKey := record.HostPrivateKey 207 | record.HostPrivateKey = nil 208 | rootPrivKey := record.RootPrivateKey 209 | record.RootPrivateKey = nil 210 | 211 | request := &frames.PairRequest{ 212 | LockdownRequest: *frames.CreateLockdownRequest("Pair"), 213 | PairRecord: record, 214 | PairingOptions: map[string]interface{}{ 215 | "ExtendedPairingErrors": true, 216 | }, 217 | } 218 | 219 | if err := this.conn.SendXML(request); err != nil { 220 | return nil, err 221 | } 222 | pkg, err := this.conn.Sync() 223 | if err != nil { 224 | return nil, err 225 | } 226 | 227 | var resp frames.PairResponse 228 | if err := pkg.UnmarshalBody(&resp); err != nil { 229 | return nil, err 230 | } 231 | 232 | if resp.Error != "" { 233 | return nil, fmt.Errorf("%s", resp.Error) 234 | } 235 | 236 | record.EscrowBag = resp.EscrowBag 237 | record.WiFiMACAddress = wifiAddress 238 | record.HostPrivateKey = hostPrivKey 239 | record.RootPrivateKey = rootPrivKey 240 | 241 | return record, nil 242 | } 243 | 244 | func (this *LockdownConnection) StopSession() error { 245 | if this.sslSession == nil { 246 | return nil 247 | } 248 | 249 | request := &frames.StopSessionRequest{ 250 | LockdownRequest: *frames.CreateLockdownRequest("StopSession"), 251 | SessionID: this.sslSession.SessionID, 252 | } 253 | 254 | if err := this.conn.SendXML(request); err != nil { 255 | return err 256 | } 257 | pkg, err := this.conn.Sync() 258 | if err != nil { 259 | return err 260 | } 261 | 262 | var resp frames.LockdownResponse 263 | if err := pkg.UnmarshalBody(&resp); err != nil { 264 | return err 265 | } 266 | 267 | if resp.Error != "" { 268 | return fmt.Errorf("%s", resp.Error) 269 | } 270 | 271 | this.sslSession = nil 272 | return nil 273 | } 274 | 275 | func (this *LockdownConnection) IsSessionStart() bool { 276 | return this.sslSession != nil 277 | } 278 | 279 | func (this *LockdownConnection) StartSession() error { 280 | if this.IsSessionStart() { 281 | if err := this.StopSession(); err != nil { 282 | return err 283 | } 284 | } 285 | 286 | if this.pairRecord == nil { 287 | if err := this.Handshake(); err != nil { 288 | return err 289 | } 290 | } 291 | 292 | buid, err := ReadBUID() 293 | if err != nil { 294 | return err 295 | } 296 | 297 | request := &frames.StartSessionRequest{ 298 | LockdownRequest: *frames.CreateLockdownRequest("StartSession"), 299 | SystemBUID: buid, 300 | HostID: this.pairRecord.HostID, 301 | } 302 | 303 | if err := this.conn.SendXML(request); err != nil { 304 | return err 305 | } 306 | pkg, err := this.conn.Sync() 307 | if err != nil { 308 | return err 309 | } 310 | 311 | var resp frames.StartSessionResponse 312 | 313 | if err := pkg.UnmarshalBody(&resp); err != nil { 314 | return err 315 | } 316 | 317 | if resp.Error != "" { 318 | if resp.Error == "InvalidHostID" { 319 | /* try repair device */ 320 | this.pairRecord = nil 321 | if err := DeletePairRecord(this.device); err != nil { 322 | return err 323 | } 324 | return this.Handshake() 325 | } 326 | return fmt.Errorf("%s", resp.Error) 327 | } 328 | 329 | this.sslSession = &resp 330 | 331 | if resp.EnableSessionSSL { 332 | if err := this.conn.conn.Handshake(this.Version, this.pairRecord); err != nil { 333 | return err 334 | } 335 | } 336 | 337 | return nil 338 | } 339 | 340 | func (this *LockdownConnection) Handshake() error { 341 | qtResp, err := this.QueryType() 342 | if err != nil { 343 | return err 344 | } 345 | if qtResp.Type != "com.apple.mobile.lockdown" { 346 | return errors.New("queryType not mobile lockdown") 347 | } 348 | 349 | pvResp, err := this.GetValue("", "ProductVersion") 350 | if err != nil { 351 | return err 352 | } 353 | if pvResp.Error != "" { 354 | return fmt.Errorf("%s", pvResp.Error) 355 | } 356 | 357 | version := strings.Split(pvResp.Value.(string), ".") 358 | this.Version = make([]int, len(version)) 359 | for i, v := range version { 360 | this.Version[i], _ = strconv.Atoi(v) 361 | } 362 | 363 | if fResp, err := ReadPairRecord(this.device); err != nil { 364 | // try pair device 365 | if record, err := this.Pair(); err != nil { 366 | return err 367 | } else if resp, err := ReadPairRecord(this.device); err != nil { 368 | if err := SavePairRecord(this.device, record); err != nil { 369 | return err 370 | } else { 371 | this.pairRecord = record 372 | return nil 373 | } 374 | } else { 375 | this.pairRecord = resp 376 | } 377 | } else { 378 | this.pairRecord = fResp 379 | } 380 | 381 | return nil 382 | } 383 | 384 | func (this *LockdownConnection) StartServiceWithEscroBag(service string) (*frames.StartServiceResponse, error) { 385 | return this.rawStartService(service, true) 386 | } 387 | 388 | func (this *LockdownConnection) StartService(service string) (*frames.StartServiceResponse, error) { 389 | return this.rawStartService(service, false) 390 | } 391 | 392 | func (this *LockdownConnection) rawStartService(service string, escrowBag bool) (*frames.StartServiceResponse, error) { 393 | request := &frames.StartServiceRequest{ 394 | LockdownRequest: *frames.CreateLockdownRequest("StartService"), 395 | Service: service, 396 | } 397 | 398 | if this.pairRecord != nil && escrowBag { 399 | request.EscrowBag = this.pairRecord.EscrowBag 400 | } 401 | 402 | if err := this.conn.SendXML(request); err != nil { 403 | return nil, err 404 | } 405 | 406 | pkg, err := this.conn.Sync() 407 | if err != nil { 408 | return nil, err 409 | } 410 | 411 | var resp frames.StartServiceResponse 412 | if err := pkg.UnmarshalBody(&resp); err != nil { 413 | return nil, err 414 | } 415 | 416 | if resp.Error != "" { 417 | return nil, errors.New(resp.Error) 418 | } 419 | 420 | return &resp, nil 421 | } 422 | 423 | func (this *LockdownConnection) QueryType() (*frames.LockdownTypeResponse, error) { 424 | req := frames.CreateLockdownRequest("QueryType") 425 | if err := this.conn.SendXML(req); err != nil { 426 | return nil, err 427 | } 428 | pkg, err := this.conn.Sync() 429 | if err != nil { 430 | return nil, err 431 | } 432 | var resp frames.LockdownTypeResponse 433 | 434 | if err := pkg.UnmarshalBody(&resp); err != nil { 435 | return nil, err 436 | } 437 | 438 | if resp.Error != "" { 439 | return nil, fmt.Errorf("%s", resp.Error) 440 | } 441 | 442 | return &resp, nil 443 | } 444 | 445 | func (this *LockdownConnection) GetValue(domain string, key string) (*frames.LockdownValueResponse, error) { 446 | req := frames.ValueRequest{ 447 | LockdownRequest: *frames.CreateLockdownRequest("GetValue"), 448 | Domain: domain, 449 | Key: key, 450 | } 451 | 452 | if err := this.conn.SendXML(req); err != nil { 453 | return nil, err 454 | } 455 | 456 | pkg, err := this.conn.Sync() 457 | if err != nil { 458 | return nil, err 459 | } 460 | 461 | var resp frames.LockdownValueResponse 462 | if err := pkg.UnmarshalBody(&resp); err != nil { 463 | return nil, err 464 | } 465 | 466 | if resp.Error != "" { 467 | return nil, fmt.Errorf("%s", resp.Error) 468 | } 469 | 470 | return &resp, nil 471 | } 472 | 473 | func (this *LockdownConnection) SetValue(domain string, key string, value interface{}) (*frames.LockdownValueResponse, error) { 474 | req := frames.ValueRequest{ 475 | LockdownRequest: *frames.CreateLockdownRequest("SetValue"), 476 | Domain: domain, 477 | Key: key, 478 | Value: value, 479 | } 480 | 481 | if err := this.conn.SendXML(req); err != nil { 482 | return nil, err 483 | } 484 | pkg, err := this.conn.Sync() 485 | if err != nil { 486 | return nil, err 487 | } 488 | var resp frames.LockdownValueResponse 489 | if err := pkg.UnmarshalBody(&resp); err != nil { 490 | return nil, err 491 | } 492 | 493 | if resp.Error != "" { 494 | return nil, fmt.Errorf("%s", resp.Error) 495 | } 496 | 497 | return &resp, nil 498 | } 499 | 500 | func (this *LockdownConnection) GetStringValue(key string) (string, error) { 501 | resp, err := this.GetValue("", key) 502 | 503 | if err != nil { 504 | return "", err 505 | } 506 | 507 | if resp.Error != "" { 508 | return "", fmt.Errorf("%s", resp.Error) 509 | } 510 | 511 | return resp.Value.(string), nil 512 | } 513 | 514 | func (this *LockdownConnection) EnterRecovery() error { 515 | req := frames.CreateLockdownRequest("EnterRecovery") 516 | 517 | if err := this.conn.SendXML(req); err != nil { 518 | return err 519 | } 520 | pkg, err := this.conn.Sync() 521 | if err != nil { 522 | return err 523 | } 524 | var resp frames.LockdownResponse 525 | if err := pkg.UnmarshalBody(&resp); err != nil { 526 | return err 527 | } 528 | 529 | if resp.Error != "" { 530 | return fmt.Errorf("%s", resp.Error) 531 | } 532 | 533 | return nil 534 | } 535 | 536 | func (this *LockdownConnection) UniqueDeviceID() (string, error) { 537 | return this.GetStringValue("UniqueDeviceID") 538 | } 539 | 540 | func (this *LockdownConnection) DeviceName() (string, error) { 541 | return this.GetStringValue("DeviceName") 542 | } 543 | 544 | func (this *LockdownConnection) HardwareModel() (string, error) { 545 | return this.GetStringValue("HardwareModel") 546 | } 547 | 548 | func (this *LockdownConnection) DeviceClass() (string, error) { 549 | return this.GetStringValue("DeviceClass") 550 | } 551 | 552 | func (this *LockdownConnection) ProductVersion() (string, error) { 553 | return this.GetStringValue("ProductVersion") 554 | } 555 | 556 | func (this *LockdownConnection) ProductName() (string, error) { 557 | return this.GetStringValue("ProductName") 558 | } 559 | 560 | func (this *LockdownConnection) GenerateConnection(port int, enableSSL bool) (*MixConnection, error) { 561 | if enableSSL && (this.pairRecord == nil || this.Version == nil) { 562 | if err := this.Handshake(); err != nil { 563 | return nil, err 564 | } 565 | } 566 | 567 | base, err := Connect(this.device, port) 568 | if err != nil { 569 | return nil, err 570 | } 571 | 572 | /* clean deadline */ 573 | if err := base.RawConn.SetDeadline(time.Time{}); err != nil { 574 | return nil, err 575 | } 576 | 577 | client := MixConnectionClient(base.RawConn) 578 | 579 | if enableSSL { 580 | if err := client.Handshake(this.Version, this.pairRecord); err != nil { 581 | return nil, err 582 | } 583 | } 584 | 585 | return client, nil 586 | } 587 | 588 | func GenerateService(c *MixConnection) *Service { 589 | return &Service{conn: c} 590 | } 591 | 592 | func (this *LockdownConnection) Close() { 593 | if this.sslSession != nil { 594 | this.StopSession() 595 | } 596 | 597 | if this.conn != nil { 598 | this.conn.conn.Close() 599 | } 600 | 601 | this.pairRecord = nil 602 | this.Version = nil 603 | } 604 | -------------------------------------------------------------------------------- /tunnel/mixConn.go: -------------------------------------------------------------------------------- 1 | package tunnel 2 | 3 | import ( 4 | "crypto/tls" 5 | "errors" 6 | "iconsole/frames" 7 | "net" 8 | "time" 9 | ) 10 | 11 | type MixConnection struct { 12 | conn net.Conn 13 | ssl *tls.Conn 14 | } 15 | 16 | func MixConnectionClient(conn net.Conn) *MixConnection { 17 | return &MixConnection{ 18 | conn: conn, 19 | } 20 | } 21 | 22 | func (this *MixConnection) DismissSSL() error { 23 | // in openssl should be shutdown ssl connection 24 | // but in there i don't know how to dismiss that 25 | 26 | //if err := this.ssl.CloseWrite(); err != nil { 27 | // return err 28 | //} 29 | this.ssl = nil 30 | return nil 31 | } 32 | 33 | func (this *MixConnection) Handshake(version []int, record *frames.PairRecord) error { 34 | if record == nil { 35 | return errors.New("record nil") 36 | } 37 | 38 | minVersion := uint16(tls.VersionTLS11) 39 | maxVersion := uint16(tls.VersionTLS11) 40 | 41 | if version[0] > 10 { 42 | minVersion = tls.VersionTLS11 43 | maxVersion = tls.VersionTLS13 44 | } 45 | 46 | cert, err := tls.X509KeyPair(record.RootCertificate, record.RootPrivateKey) 47 | if err != nil { 48 | return err 49 | } 50 | 51 | cfg := &tls.Config{ 52 | Certificates: []tls.Certificate{cert}, 53 | InsecureSkipVerify: true, 54 | MinVersion: minVersion, 55 | MaxVersion: maxVersion, 56 | } 57 | 58 | this.ssl = tls.Client(this.conn, cfg) 59 | 60 | if err := this.ssl.Handshake(); err != nil { 61 | return err 62 | } 63 | 64 | return nil 65 | } 66 | 67 | func (this *MixConnection) getConn() net.Conn { 68 | if this.ssl != nil { 69 | return this.ssl 70 | } 71 | return this.conn 72 | } 73 | 74 | func (this *MixConnection) Read(b []byte) (n int, err error) { 75 | return this.getConn().Read(b) 76 | } 77 | 78 | func (this *MixConnection) Write(b []byte) (n int, err error) { 79 | return this.getConn().Write(b) 80 | } 81 | 82 | func (this *MixConnection) Close() error { 83 | this.ssl = nil 84 | return this.getConn().Close() 85 | } 86 | 87 | func (this *MixConnection) LocalAddr() net.Addr { 88 | return this.getConn().LocalAddr() 89 | } 90 | 91 | func (this *MixConnection) RemoteAddr() net.Addr { 92 | return this.getConn().RemoteAddr() 93 | } 94 | 95 | func (this *MixConnection) SetDeadline(t time.Time) error { 96 | return this.getConn().SetDeadline(t) 97 | } 98 | 99 | func (this *MixConnection) SetReadDeadline(t time.Time) error { 100 | return this.getConn().SetReadDeadline(t) 101 | } 102 | 103 | func (this *MixConnection) SetWriteDeadline(t time.Time) error { 104 | return this.getConn().SetWriteDeadline(t) 105 | } 106 | -------------------------------------------------------------------------------- /tunnel/service.go: -------------------------------------------------------------------------------- 1 | package tunnel 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "iconsole/frames" 7 | "net" 8 | 9 | "howett.net/plist" 10 | ) 11 | 12 | type Service struct { 13 | conn *MixConnection 14 | } 15 | 16 | func (this *Service) DismissSSL() error { 17 | return this.conn.DismissSSL() 18 | } 19 | 20 | func (this *Service) GetConnection() net.Conn { 21 | return this.conn 22 | } 23 | 24 | func (this *Service) Send(frame interface{}, format int) error { 25 | if this.conn == nil { 26 | return ErrNoConnection 27 | } 28 | 29 | pkg := &frames.ServicePackage{} 30 | 31 | packageBuf, err := pkg.Pack(frame, format) 32 | if err != nil { 33 | return err 34 | } 35 | 36 | _, err = this.conn.Write(packageBuf) 37 | return err 38 | } 39 | 40 | func (this *Service) SendXML(frame interface{}) error { 41 | return this.Send(frame, plist.XMLFormat) 42 | } 43 | 44 | func (this *Service) SendBinary(frame interface{}) error { 45 | return this.Send(frame, plist.BinaryFormat) 46 | } 47 | 48 | func (this *Service) Sync() (*frames.ServicePackage, error) { 49 | if this.conn == nil { 50 | return nil, ErrNoConnection 51 | } 52 | 53 | buf := bytes.NewBuffer([]byte{}) 54 | 55 | var err error 56 | var n int 57 | var pkg *frames.ServicePackage 58 | 59 | offset := 0 60 | pkgLen := 0 61 | 62 | pkgBuf := make([]byte, 0x4) 63 | for { 64 | n, err = this.conn.Read(pkgBuf) 65 | 66 | if err != nil && n == 0 { 67 | return nil, err 68 | } 69 | 70 | buf.Write(pkgBuf[:n]) 71 | 72 | offset += n 73 | 74 | if pkgLen == 0 { 75 | pkgLen = int(binary.BigEndian.Uint32(pkgBuf[:4])) + 4 76 | pkgBuf = make([]byte, pkgLen-4) 77 | } 78 | 79 | if offset >= pkgLen { 80 | break 81 | } 82 | } 83 | 84 | pkg, err = frames.UnpackLockdown(buf.Bytes()) 85 | if err != nil { 86 | return nil, err 87 | } 88 | 89 | return pkg, nil 90 | } 91 | -------------------------------------------------------------------------------- /tunnel/tunnel.go: -------------------------------------------------------------------------------- 1 | package tunnel 2 | 3 | import ( 4 | "context" 5 | "encoding/binary" 6 | "errors" 7 | "fmt" 8 | "iconsole/frames" 9 | "net" 10 | "time" 11 | 12 | "howett.net/plist" 13 | ) 14 | 15 | var ( 16 | ErrNoConnection = errors.New("not connection") 17 | ) 18 | 19 | const ( 20 | ResultOk = 0 21 | ResultBadCommand = 1 22 | ResultBadDev = 2 23 | ResultCommRefused = 3 24 | // ??? 25 | // ??? 26 | ResultBadVersion = 6 27 | ResultUnknown = 100 28 | ) 29 | 30 | func getError(num uint64) error { 31 | switch num { 32 | case ResultOk: 33 | return nil 34 | case ResultBadCommand: 35 | return errors.New("BadCommand") 36 | case ResultBadDev: 37 | return errors.New("BadDev") 38 | case ResultCommRefused: 39 | return errors.New("CommRefused") 40 | case ResultBadVersion: 41 | return errors.New("BadVersion") 42 | default: 43 | return fmt.Errorf("ErrorCode %d", num) 44 | } 45 | } 46 | 47 | type PlistConnection struct { 48 | RawConn net.Conn 49 | version uint32 50 | Timeout time.Duration 51 | } 52 | 53 | func NewPlistConnection() *PlistConnection { 54 | /* socket deadline 30 seconds */ 55 | return &PlistConnection{ 56 | version: 1, 57 | Timeout: 30 * time.Second, 58 | } 59 | } 60 | 61 | func (this *PlistConnection) Close() { 62 | if this.RawConn != nil { 63 | _ = this.RawConn.Close() 64 | this.RawConn = nil 65 | } 66 | } 67 | 68 | func (this *PlistConnection) Sync() (*frames.Package, error) { 69 | var err error 70 | var n int 71 | var pkg *frames.Package 72 | 73 | var pkgLen uint32 74 | if err = binary.Read(this.RawConn, binary.LittleEndian, &pkgLen); err != nil { 75 | return nil, err 76 | } 77 | 78 | pkgBuf := make([]byte, pkgLen) 79 | binary.LittleEndian.PutUint32(pkgBuf, pkgLen) 80 | 81 | offset := 4 82 | 83 | if err := this.RawConn.SetDeadline(time.Now().Add(this.Timeout)); err != nil { 84 | return nil, err 85 | } 86 | 87 | for { 88 | n, err = this.RawConn.Read(pkgBuf[offset:]) 89 | if err != nil { 90 | return nil, err 91 | } 92 | if offset+n >= len(pkgBuf) { 93 | break 94 | } 95 | offset += n 96 | } 97 | 98 | pkg, err = frames.Unpack(pkgBuf) 99 | if err != nil { 100 | return nil, err 101 | } 102 | 103 | return pkg, nil 104 | } 105 | 106 | func (this *PlistConnection) Dial() error { 107 | if conn, err := RawDial(this.Timeout); err != nil { 108 | return err 109 | } else { 110 | this.RawConn = conn 111 | } 112 | return nil 113 | } 114 | 115 | func (this *PlistConnection) Send(frame interface{}) error { 116 | if this.RawConn == nil { 117 | return ErrNoConnection 118 | } 119 | 120 | pkg := &frames.Package{ 121 | Version: this.version, 122 | Type: 8, 123 | Tag: 0, 124 | } 125 | 126 | packageBuf, err := pkg.Pack(frame) 127 | if err != nil { 128 | return err 129 | } 130 | 131 | if err := this.RawConn.SetDeadline(time.Now().Add(this.Timeout)); err != nil { 132 | return err 133 | } 134 | 135 | if _, err := this.RawConn.Write(packageBuf); err != nil { 136 | return err 137 | } 138 | 139 | return nil 140 | } 141 | 142 | func analyzeDevice(properties map[string]interface{}) (frames.Device, error) { 143 | ct := properties["ConnectionType"].(string) 144 | 145 | var device frames.Device 146 | 147 | model := frames.DeviceModel{ 148 | ConnectionType: ct, 149 | DeviceID: int(properties["DeviceID"].(uint64)), 150 | SerialNumber: properties["SerialNumber"].(string), 151 | } 152 | 153 | serialNumber := "" 154 | if sn, ok := properties["USBSerialNumber"].(string); ok { 155 | serialNumber = sn 156 | } else if sn, ok := properties["SerialNumber"].(string); ok { 157 | serialNumber = sn 158 | } 159 | 160 | udid := "" 161 | if u, ok := properties["UDID"].(string); ok { 162 | udid = u 163 | } 164 | 165 | switch ct { 166 | case "USB": 167 | device = &frames.USBDevice{ 168 | DeviceModel: model, 169 | ConnectionSpeed: int(properties["ConnectionSpeed"].(uint64)), 170 | LocationID: int(properties["LocationID"].(uint64)), 171 | ProductID: int(properties["ProductID"].(uint64)), 172 | UDID: udid, 173 | USBSerialNumber: serialNumber, 174 | } 175 | case "Network": 176 | device = &frames.NetworkDevice{ 177 | DeviceModel: model, 178 | EscapedFullServiceName: properties["EscapedFullServiceName"].(string), 179 | InterfaceIndex: int(properties["InterfaceIndex"].(uint64)), 180 | NetworkAddress: properties["NetworkAddress"].([]uint8), 181 | } 182 | } 183 | 184 | return device, nil 185 | } 186 | 187 | // just for once call 188 | func Devices() ([]frames.Device, error) { 189 | conn := NewPlistConnection() 190 | if err := conn.Dial(); err != nil { 191 | return nil, err 192 | } 193 | defer conn.Close() 194 | 195 | frame := frames.CreateBaseRequest(frames.ListDevices) 196 | 197 | if err := conn.Send(frame); err != nil { 198 | return nil, err 199 | } 200 | 201 | var devices []frames.Device 202 | var m map[string]interface{} 203 | 204 | respPkg, err := conn.Sync() 205 | if err != nil { 206 | return nil, err 207 | } 208 | if err := respPkg.UnmarshalBody(&m); err != nil { 209 | return nil, err 210 | } 211 | 212 | deviceList, ok := m["DeviceList"].([]interface{}) 213 | if ok { 214 | for _, v := range deviceList { 215 | item := v.(map[string]interface{}) 216 | properties := item["Properties"].(map[string]interface{}) 217 | device, err := analyzeDevice(properties) 218 | if err != nil { 219 | return nil, err 220 | } 221 | devices = append(devices, device) 222 | } 223 | } else if n, ok := m["Number"].(uint64); ok { 224 | return nil, getError(n) 225 | } else { 226 | return nil, getError(ResultUnknown) 227 | } 228 | 229 | return devices, nil 230 | } 231 | 232 | func ReadBUID() (string, error) { 233 | conn := NewPlistConnection() 234 | if err := conn.Dial(); err != nil { 235 | return "", err 236 | } 237 | defer conn.Close() 238 | 239 | frame := frames.CreateBaseRequest("ReadBUID") 240 | 241 | if err := conn.Send(frame); err != nil { 242 | return "", err 243 | } 244 | 245 | pkg, err := conn.Sync() 246 | if err != nil { 247 | return "", err 248 | } 249 | 250 | var m map[string]interface{} 251 | if err := pkg.UnmarshalBody(&m); err != nil { 252 | return "", err 253 | } 254 | 255 | if buid, ok := m["BUID"].(string); ok { 256 | return buid, nil 257 | } else if n, ok := m["Number"].(uint64); ok { 258 | return "", getError(n) 259 | } 260 | 261 | return "", getError(ResultUnknown) 262 | } 263 | 264 | func Listen(msgNotifyer chan frames.Response) (context.CancelFunc, error) { 265 | conn := NewPlistConnection() 266 | if err := conn.Dial(); err != nil { 267 | return nil, err 268 | } 269 | 270 | frame := frames.CreateBaseRequest(frames.Listen) 271 | frame.LibUSBMuxVersion = frames.LibUSBMuxVersion 272 | 273 | if err := conn.Send(frame); err != nil { 274 | return nil, err 275 | } 276 | 277 | ctx, cancelFunc := context.WithCancel(context.Background()) 278 | 279 | go func() { 280 | defer conn.Close() 281 | defer close(msgNotifyer) 282 | for { 283 | select { 284 | case <-ctx.Done(): 285 | return 286 | default: 287 | pkg, err := conn.Sync() 288 | if err != nil { 289 | return 290 | } 291 | 292 | var m map[string]interface{} 293 | if err := pkg.UnmarshalBody(&m); err != nil { 294 | return 295 | } 296 | 297 | if mt, ok := m["MessageType"].(string); ok { 298 | switch mt { 299 | case "Attached": 300 | device, err := analyzeDevice(m["Properties"].(map[string]interface{})) 301 | if err != nil { 302 | return 303 | } 304 | msgNotifyer <- &frames.DeviceAttached{ 305 | BaseResponse: frames.BaseResponse{MessageType: mt}, 306 | DeviceID: int(m["DeviceID"].(uint64)), 307 | Properties: device, 308 | } 309 | case "Detached": 310 | msgNotifyer <- &frames.DeviceDetached{ 311 | BaseResponse: frames.BaseResponse{MessageType: mt}, 312 | DeviceID: int(m["DeviceID"].(uint64)), 313 | } 314 | case "Result": 315 | msgNotifyer <- &frames.Result{ 316 | BaseResponse: frames.BaseResponse{MessageType: mt}, 317 | Number: int(m["Number"].(uint64)), 318 | } 319 | } 320 | } 321 | } 322 | } 323 | }() 324 | 325 | return cancelFunc, nil 326 | } 327 | 328 | func connectRaw(deviceId int, port int) (conn *PlistConnection, err error) { 329 | conn = NewPlistConnection() 330 | 331 | if err = conn.Dial(); err != nil { 332 | return 333 | } 334 | 335 | defer func() { 336 | if err != nil { 337 | if conn != nil { 338 | conn.Close() 339 | conn = nil 340 | } 341 | } 342 | }() 343 | 344 | connRequest := &frames.ConnectRequest{ 345 | BaseRequest: *frames.CreateBaseRequest(frames.Connect), 346 | DeviceID: deviceId, 347 | PortNumber: ((port << 8) & 0xFF00) | (port >> 8), 348 | } 349 | 350 | if err = conn.Send(connRequest); err != nil { 351 | return 352 | } 353 | 354 | var pkg *frames.Package 355 | pkg, err = conn.Sync() 356 | if err != nil { 357 | return 358 | } 359 | 360 | var result frames.Result 361 | 362 | if err = pkg.UnmarshalBody(&result); err != nil { 363 | return 364 | } 365 | 366 | if result.Number == ResultOk { 367 | return 368 | } 369 | 370 | return nil, getError(uint64(result.Number)) 371 | } 372 | 373 | func Connect(device frames.Device, port int) (*PlistConnection, error) { 374 | return connectRaw(device.GetDeviceID(), port) 375 | } 376 | 377 | func readPairRecordRaw(udid string) (*frames.PairRecord, error) { 378 | conn := NewPlistConnection() 379 | if err := conn.Dial(); err != nil { 380 | return nil, err 381 | } 382 | defer conn.Close() 383 | 384 | req := &frames.PairRecordRequest{ 385 | BaseRequest: *frames.CreateBaseRequest("ReadPairRecord"), 386 | PairRecordID: udid, 387 | } 388 | if err := conn.Send(req); err != nil { 389 | return nil, err 390 | } 391 | 392 | pkg, err := conn.Sync() 393 | if err != nil { 394 | return nil, err 395 | } 396 | 397 | var m frames.PairRecordResponse 398 | if err := pkg.UnmarshalBody(&m); err != nil { 399 | return nil, err 400 | } 401 | 402 | if m.Number != 0 { 403 | return nil, getError(uint64(m.Number)) 404 | } 405 | 406 | var resp frames.PairRecord 407 | if _, err := plist.Unmarshal(m.PairRecordData, &resp); err != nil { 408 | return nil, err 409 | } 410 | 411 | return &resp, nil 412 | } 413 | 414 | func ReadPairRecord(device frames.Device) (*frames.PairRecord, error) { 415 | return readPairRecordRaw(device.GetSerialNumber()) 416 | } 417 | 418 | func SavePairRecord(device frames.Device, record *frames.PairRecord) error { 419 | conn := NewPlistConnection() 420 | if err := conn.Dial(); err != nil { 421 | return err 422 | } 423 | defer conn.Close() 424 | 425 | data, err := plist.Marshal(record, plist.XMLFormat) 426 | if err != nil { 427 | return err 428 | } 429 | 430 | req := &frames.SavePairRecordRequest{ 431 | BaseRequest: *frames.CreateBaseRequest("SavePairRecord"), 432 | PairRecordID: device.GetSerialNumber(), 433 | PairRecordData: data, 434 | DeviceID: device.GetDeviceID(), 435 | } 436 | 437 | var resp frames.Result 438 | 439 | if err := conn.Send(req); err != nil { 440 | return err 441 | } 442 | 443 | pkg, err := conn.Sync() 444 | if err != nil { 445 | return err 446 | } 447 | 448 | if err := pkg.UnmarshalBody(&resp); err != nil { 449 | return err 450 | } 451 | 452 | if resp.Number != 0 { 453 | return getError(uint64(resp.Number)) 454 | } 455 | 456 | return nil 457 | } 458 | 459 | func DeletePairRecord(device frames.Device) error { 460 | conn := NewPlistConnection() 461 | if err := conn.Dial(); err != nil { 462 | return err 463 | } 464 | defer conn.Close() 465 | 466 | req := &frames.DeletePairRecordRequest{ 467 | BaseRequest: *frames.CreateBaseRequest("DeletePairRecord"), 468 | PairRecordID: device.GetSerialNumber(), 469 | } 470 | 471 | var resp frames.Result 472 | 473 | if err := conn.Send(req); err != nil { 474 | return err 475 | } 476 | 477 | pkg, err := conn.Sync() 478 | if err != nil { 479 | return err 480 | } 481 | 482 | if err := pkg.UnmarshalBody(&resp); err != nil { 483 | return err 484 | } 485 | 486 | if resp.Number != 0 { 487 | return getError(uint64(resp.Number)) 488 | } 489 | 490 | return nil 491 | } 492 | -------------------------------------------------------------------------------- /value.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "iconsole/tunnel" 7 | "strings" 8 | 9 | "github.com/urfave/cli" 10 | ) 11 | 12 | func getAction(ctx *cli.Context) error { 13 | udid := ctx.String("UDID") 14 | 15 | args := ctx.Args() 16 | if len(args) == 0 { 17 | return nil 18 | } 19 | 20 | return session(udid, func(conn *tunnel.LockdownConnection) error { 21 | for _, s := range args { 22 | domain := "" 23 | key := "" 24 | a := strings.Split(s, ":") 25 | if len(a) == 2 { 26 | domain = a[0] 27 | key = a[1] 28 | } else if len(a) == 1 { 29 | key = a[0] 30 | } else { 31 | return fmt.Errorf("Arguments too many `%s`", s) 32 | } 33 | 34 | if resp, err := conn.GetValue(domain, key); err != nil { 35 | return err 36 | } else { 37 | if b, err := json.MarshalIndent(resp, "", "\t"); err != nil { 38 | return err 39 | } else { 40 | fmt.Println(string(b)) 41 | } 42 | } 43 | } 44 | return nil 45 | }) 46 | } 47 | 48 | func initValueCommond() cli.Command { 49 | return cli.Command{ 50 | Name: "get", 51 | Usage: "Get session value", 52 | Action: getAction, 53 | Flags: globalFlags, 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /wifiSync.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "iconsole/tunnel" 6 | 7 | "github.com/urfave/cli" 8 | ) 9 | 10 | func edAction(ctx *cli.Context, ed bool) error { 11 | udid := ctx.String("UDID") 12 | 13 | return session(udid, func(conn *tunnel.LockdownConnection) error { 14 | if resp, err := conn.SetValue("com.apple.mobile.wireless_lockdown", "EnableWifiConnections", ed); err != nil { 15 | return err 16 | } else if resp.Value.(bool) == ed { 17 | fmt.Println("Succeed") 18 | } else { 19 | fmt.Println("Failed") 20 | } 21 | 22 | return nil 23 | }) 24 | } 25 | 26 | func syncEnableAction(ctx *cli.Context) error { 27 | return edAction(ctx, true) 28 | } 29 | 30 | func syncDisableAction(ctx *cli.Context) error { 31 | return edAction(ctx, false) 32 | } 33 | 34 | func syncAction(ctx *cli.Context) error { 35 | udid := ctx.String("UDID") 36 | 37 | return session(udid, func(conn *tunnel.LockdownConnection) error { 38 | if resp, err := conn.GetValue("com.apple.mobile.wireless_lockdown", "EnableWifiConnections"); err != nil { 39 | return err 40 | } else if resp.Value.(bool) { 41 | fmt.Println("Device enable WiFi connections") 42 | } else { 43 | fmt.Println("Device disable WiFi connections") 44 | } 45 | 46 | return nil 47 | }) 48 | } 49 | 50 | func initSyncCommond() cli.Command { 51 | return cli.Command{ 52 | Name: "sync", 53 | Usage: "Enable Wi-Fi sync or disable", 54 | Action: syncAction, 55 | Flags: globalFlags, 56 | Subcommands: []cli.Command{ 57 | { 58 | Name: "enable", 59 | ShortName: "e", 60 | Action: syncEnableAction, 61 | Flags: globalFlags, 62 | }, 63 | { 64 | Name: "disable", 65 | ShortName: "d", 66 | Action: syncDisableAction, 67 | Flags: globalFlags, 68 | }, 69 | }, 70 | } 71 | } 72 | --------------------------------------------------------------------------------