diff --git a/afc/afc.go b/afc/afc.go new file mode 100644 index 0000000..3bb75dc --- /dev/null +++ b/afc/afc.go @@ -0,0 +1,103 @@ +package afc + +// #cgo pkg-config: libimobiledevice-1.0 +// #include +// #include +// int afc_length(char **arr) +// { +// int length = 0; +// int k = 0; +// for (k = 0; arr[k] != NULL; k++) { +// length = length + 1; +// } +// return length; +// } +import "C" +import ( + "unsafe" + + "github.com/nowsecure/goidevice/idevice" + "github.com/nowsecure/goidevice/lockdown" +) + +type AFC struct { + a C.afc_client_t +} + +func NewClient(device idevice.Device, svc *lockdown.Service) (*AFC, error) { + var a C.afc_client_t + err := resultToError( + C.afc_client_new( + (C.idevice_t)(idevice.GetPointer(device)), + (C.lockdownd_service_descriptor_t)(svc.GetDescriptor()), + &a, + ), + ) + return &AFC{a}, err +} + +func (a *AFC) WalkDirectory(path string) ([]SourceFile, error) { + dir, err := a.ReadDirectory(path) + if err != nil { + return []SourceFile{}, err + } + + files := []SourceFile{} + for _, f := range dir { + if f.format == "S_IFDIR" { + childDir, err := a.WalkDirectory(f.Name) + if err != nil { + return []SourceFile{}, err + } + files = append(files, childDir...) + continue + } + files = append(files, f) + } + return files, nil +} + +type SourceFile struct { + Name string + + format string +} + +func (a *AFC) ReadDirectory(path string) ([]SourceFile, error) { + var directoryC **C.char + defer C.afc_dictionary_free(directoryC) + + sourceFiles := []SourceFile{} + pathC := C.CString(path) + defer C.free(unsafe.Pointer(pathC)) + + err := resultToError(C.afc_read_directory(a.a, pathC, &directoryC)) + if err != nil { + return []SourceFile{}, err + } + + directory := unsafe.Slice(directoryC, C.afc_length(directoryC)) + for i := range directory { + file := SourceFile{ + Name: C.GoString(directory[i]), + } + if file.Name == ".." || file.Name == "." { + continue + } + + var fileInfoC **C.char + C.afc_get_file_info(a.a, directory[i], &fileInfoC) + if fileInfoC != nil { + fileInfo := unsafe.Slice(fileInfoC, C.afc_length(fileInfoC)) + + for j := 0; j < len(fileInfo); j += 2 { + if C.GoString(fileInfo[j]) == "st_ifmt" { + file.format = C.GoString(fileInfo[j+1]) + } + } + } + C.afc_dictionary_free(fileInfoC) + sourceFiles = append(sourceFiles, file) + } + return sourceFiles, nil +} diff --git a/afc/errors.go b/afc/errors.go new file mode 100644 index 0000000..62d8d21 --- /dev/null +++ b/afc/errors.go @@ -0,0 +1,20 @@ +package afc + +// #cgo pkg-config: libimobiledevice-1.0 +// #include +// #include +import "C" +import ( + "errors" + "fmt" +) + +func resultToError(result C.afc_error_t) error { + switch result { + case 0: + return nil + default: + fmt.Println(result) + return errors.New("unknown") + } +} diff --git a/installation/installation.go b/installation/installation.go index 54a8565..e89dcc4 100644 --- a/installation/installation.go +++ b/installation/installation.go @@ -7,7 +7,7 @@ package installation // // void instproxy_client_options_add_pair(plist_t client_opts, char * key, char * value) // { -// instproxy_client_options_add(client_opts, key, value, NULL); +// instproxy_client_options_add(client_opts, key, value, NULL); // } import "C" import ( diff --git a/lockdown/errors.go b/lockdown/errors.go index 4c2b940..457ef7a 100644 --- a/lockdown/errors.go +++ b/lockdown/errors.go @@ -2,6 +2,7 @@ package lockdown // #cgo pkg-config: libimobiledevice-1.0 // #include +// #include import "C" import ( "errors" @@ -174,3 +175,24 @@ func resultToError(result C.lockdownd_error_t) error { return ErrUnknown } } + +func serviceResultToError(result C.service_error_t) error { + switch result { + case 0: + return nil + case -1: + return errors.New("invalid args") + case -3: + return errors.New("mux error") + case -4: + return errors.New("ssl error") + case -5: + return errors.New("start service error") + case -6: + return errors.New("not enough data error") + case -7: + return errors.New("timeout") + default: + return errors.New("unknown") + } +} diff --git a/lockdown/lockdown.go b/lockdown/lockdown.go index 51e604b..ed80a6e 100644 --- a/lockdown/lockdown.go +++ b/lockdown/lockdown.go @@ -2,9 +2,12 @@ package lockdown // #cgo pkg-config: libimobiledevice-1.0 // #include +// #include // #include +// #include import "C" import ( + "errors" "unsafe" "github.com/nowsecure/goidevice/idevice" @@ -18,7 +21,10 @@ type Client interface { ValidatePair() error DeviceName() (string, error) PList(domain string) (*plist.PList, error) - Close() error + Free() error + GetClient() unsafe.Pointer + StartService(d idevice.Device, serviceName string) (*Service, error) + StartServiceClient(d idevice.Device, serviceName string) (*Service, error) } type client struct { @@ -95,10 +101,83 @@ func (s *client) PList(domain string) (*plist.PList, error) { return list, nil } -func (s *client) Close() error { - err := resultToError(C.lockdownd_client_free(s.p)) - if err == nil { - s.p = nil +func (s *client) Free() error { + return resultToError(C.lockdownd_client_free(s.p)) +} + +func (s *client) GetClient() unsafe.Pointer { + return unsafe.Pointer(s.p) +} + +// GetDescriptor gets the lockdown descriptor for the service +func (s *Service) GetDescriptor() unsafe.Pointer { + return unsafe.Pointer(s.descriptor) +} + +func (s *Service) FreeDescriptor() { + C.lockdownd_service_descriptor_free(s.descriptor) +} + +func (s *Service) Free() { + C.lockdownd_service_descriptor_free(s.descriptor) + C.service_client_free(s.s) +} + +func (s *client) StartService(d idevice.Device, serviceName string) (*Service, error) { + var p C.lockdownd_service_descriptor_t + svc := C.CString(serviceName) + defer C.free(unsafe.Pointer(svc)) + err := resultToError(C.lockdownd_start_service(s.p, svc, &p)) + if err != nil { + return nil, err + } + + return &Service{descriptor: p}, nil +} + +type Service struct { + s C.service_client_t + descriptor C.lockdownd_service_descriptor_t +} + +const ( + CRASH_REPORT_MOVER_SERVICE = "com.apple.crashreportmover" + CRASH_REPORT_COPY_MOBILE_SERVICE = "com.apple.crashreportcopymobile" +) + +func (s *client) StartServiceClient(d idevice.Device, serviceName string) (*Service, error) { + svc, err := s.StartService(d, serviceName) + if err != nil { + return nil, err + } + + err = serviceResultToError( + C.service_client_new((C.idevice_t)(idevice.GetPointer(d)), + svc.descriptor, + &svc.s, + ), + ) + return svc, err +} + +func (s *Service) ReadPing() error { + var msg [4]int8 + var n C.uint32_t + + var attempts = 0 + for { + res := C.service_receive_with_timeout(s.s, (*C.char)(&msg[0]), 4, &n, 2000) + switch res { + case 0: + return nil + case -7: + attempts++ + if attempts == 10 { + return errors.New("failed 10 attempts to ping") + } + continue + default: + return errors.New(":(((") + } } - return err } diff --git a/thing/main.go b/thing/main.go new file mode 100644 index 0000000..1500316 --- /dev/null +++ b/thing/main.go @@ -0,0 +1,47 @@ +package main + +import ( + "fmt" + "log" + + "github.com/nowsecure/goidevice/afc" + "github.com/nowsecure/goidevice/idevice" + "github.com/nowsecure/goidevice/lockdown" +) + +func main() { + device, err := idevice.New("bd133240a37062e545bbbbf664f0011c9f45895d") + if err != nil { + log.Fatal(err) + } + lock, err := lockdown.NewClientWithHandshake(device, "thingy") + if err != nil { + log.Fatal(err) + } + client, err := lock.StartServiceClient(device, lockdown.CRASH_REPORT_MOVER_SERVICE) + if err != nil { + log.Fatal(err) + } + defer client.Free() + err = client.ReadPing() + if err != nil { + log.Fatal(err) + } + + service, err := lock.StartService(device, lockdown.CRASH_REPORT_COPY_MOBILE_SERVICE) + if err != nil { + log.Fatal(err) + } + defer service.Free() + + afc, err := afc.NewClient(device, service) + if err != nil { + log.Fatal(err) + } + k, _ := afc.WalkDirectory(".") + for _, v := range k { + fmt.Println(v) + } + + log.Println("yay we did it") +} diff --git a/thing/main_test.go b/thing/main_test.go new file mode 100644 index 0000000..87a1775 --- /dev/null +++ b/thing/main_test.go @@ -0,0 +1,7 @@ +package main + +import "testing" + +func Test_ke(t *testing.T) { + main() +}