├── .gitignore ├── LICENSE ├── demo.c ├── Entitlements.plist ├── ResourceRules.plist ├── Info.plist ├── Makefile ├── README.md ├── MobileDevice.h └── fruitstrap.c /.gitignore: -------------------------------------------------------------------------------- 1 | demo 2 | demo.app 3 | fruitstrap 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | fruitstrap is available under the provisions of the GNU General Public License, 2 | version 3 (or later), available here: http://www.gnu.org/licenses/gpl-3.0.html -------------------------------------------------------------------------------- /demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, const char* argv[]) { 4 | int i; 5 | for (i = 0; i < argc; i++) { 6 | printf("argv[%d] = %s\n", i, argv[i]); 7 | } 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /Entitlements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | get-task-allow 6 | 7 | 8 | -------------------------------------------------------------------------------- /ResourceRules.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | rules 6 | 7 | .* 8 | 9 | Info.plist 10 | 11 | omit 12 | 13 | weight 14 | 10 15 | 16 | ResourceRules.plist 17 | 18 | omit 19 | 20 | weight 21 | 100 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleName 6 | demo 7 | CFBundleSupportedPlatforms 8 | 9 | iPhoneOS 10 | 11 | CFBundleExecutable 12 | demo 13 | CFBundleIdentifier 14 | demo 15 | CFBundleResourceSpecification 16 | ResourceRules.plist 17 | LSRequiresIPhoneOS 18 | 19 | CFBundleDisplayName 20 | demo 21 | 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | IOS_CC = /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/gcc 2 | 3 | all: demo.app fruitstrap 4 | 5 | demo.app: demo Info.plist 6 | mkdir -p demo.app 7 | cp demo demo.app/ 8 | cp Info.plist ResourceRules.plist demo.app/ 9 | codesign -f -s "iPhone Developer" --entitlements Entitlements.plist demo.app 10 | 11 | demo: demo.c 12 | $(IOS_CC) -arch armv7 -isysroot /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk -framework CoreFoundation -o demo demo.c 13 | 14 | fruitstrap: fruitstrap.c 15 | gcc -o fruitstrap -framework CoreFoundation -framework MobileDevice -F/System/Library/PrivateFrameworks fruitstrap.c 16 | 17 | install: all 18 | ./fruitstrap demo.app 19 | 20 | debug: all 21 | ./fruitstrap -d demo.app 22 | 23 | clean: 24 | rm -rf *.app demo fruitstrap -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## This project is no longer maintained. 2 | 3 | fruitstrap 4 | ========== 5 | Install and debug iPhone apps without using Xcode. Designed to work on unjailbroken devices. 6 | 7 | ## Requirements 8 | 9 | * Mac OS X. Tested on Snow Leopard only. 10 | * You need to have a valid iPhone development certificate installed. 11 | * Xcode must be installed, along with the SDK for your iOS version. 12 | 13 | ## Usage 14 | 15 | * `fruitstrap [-d] -b [device_id]` 16 | * Optional `-d` flag launches a remote GDB session after the app has been installed. 17 | * `` must be an iPhone application bundle, *not* an IPA. 18 | * Optional `device_id`; useful when you have more than one iPhone/iPad connected. 19 | 20 | ## Demo 21 | 22 | * The included demo.app represents the minimum required to get code running on iOS. 23 | * `make install` will install demo.app to the device. 24 | * `make debug` will install demo.app and launch a GDB session. 25 | 26 | ## Notes 27 | 28 | * With some modifications, it may be possible to use this without Xcode installed; however, you would need a copy of the relevant DeveloperDiskImage.dmg (included with Xcode). GDB would also run slower as symbols would be downloaded from the device on-the-fly. 29 | -------------------------------------------------------------------------------- /MobileDevice.h: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------- 2 | * MobileDevice.h - interface to MobileDevice.framework 3 | * $LastChangedDate: 2007-07-09 18:59:29 -0700 (Mon, 09 Jul 2007) $ 4 | * 5 | * Copied from http://iphonesvn.halifrag.com/svn/iPhone/ 6 | * With modifications from Allen Porter and Scott Turner 7 | * 8 | * ------------------------------------------------------------------------- */ 9 | 10 | #ifndef MOBILEDEVICE_H 11 | #define MOBILEDEVICE_H 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | #if defined(WIN32) 18 | #include 19 | typedef unsigned int mach_error_t; 20 | #elif defined(__APPLE__) 21 | #include 22 | #include 23 | #endif 24 | 25 | /* Error codes */ 26 | #define MDERR_APPLE_MOBILE (err_system(0x3a)) 27 | #define MDERR_IPHONE (err_sub(0)) 28 | 29 | /* Apple Mobile (AM*) errors */ 30 | #define MDERR_OK ERR_SUCCESS 31 | #define MDERR_SYSCALL (ERR_MOBILE_DEVICE | 0x01) 32 | #define MDERR_OUT_OF_MEMORY (ERR_MOBILE_DEVICE | 0x03) 33 | #define MDERR_QUERY_FAILED (ERR_MOBILE_DEVICE | 0x04) 34 | #define MDERR_INVALID_ARGUMENT (ERR_MOBILE_DEVICE | 0x0b) 35 | #define MDERR_DICT_NOT_LOADED (ERR_MOBILE_DEVICE | 0x25) 36 | 37 | /* Apple File Connection (AFC*) errors */ 38 | #define MDERR_AFC_OUT_OF_MEMORY 0x03 39 | 40 | /* USBMux errors */ 41 | #define MDERR_USBMUX_ARG_NULL 0x16 42 | #define MDERR_USBMUX_FAILED 0xffffffff 43 | 44 | /* Messages passed to device notification callbacks: passed as part of 45 | * am_device_notification_callback_info. */ 46 | #define ADNCI_MSG_CONNECTED 1 47 | #define ADNCI_MSG_DISCONNECTED 2 48 | #define ADNCI_MSG_UNKNOWN 3 49 | 50 | #define AMD_IPHONE_PRODUCT_ID 0x1290 51 | #define AMD_IPHONE_SERIAL "3391002d9c804d105e2c8c7d94fc35b6f3d214a3" 52 | 53 | /* Services, found in /System/Library/Lockdown/Services.plist */ 54 | #define AMSVC_AFC CFSTR("com.apple.afc") 55 | #define AMSVC_BACKUP CFSTR("com.apple.mobilebackup") 56 | #define AMSVC_CRASH_REPORT_COPY CFSTR("com.apple.crashreportcopy") 57 | #define AMSVC_DEBUG_IMAGE_MOUNT CFSTR("com.apple.mobile.debug_image_mount") 58 | #define AMSVC_NOTIFICATION_PROXY CFSTR("com.apple.mobile.notification_proxy") 59 | #define AMSVC_PURPLE_TEST CFSTR("com.apple.purpletestr") 60 | #define AMSVC_SOFTWARE_UPDATE CFSTR("com.apple.mobile.software_update") 61 | #define AMSVC_SYNC CFSTR("com.apple.mobilesync") 62 | #define AMSVC_SCREENSHOT CFSTR("com.apple.screenshotr") 63 | #define AMSVC_SYSLOG_RELAY CFSTR("com.apple.syslog_relay") 64 | #define AMSVC_SYSTEM_PROFILER CFSTR("com.apple.mobile.system_profiler") 65 | 66 | typedef unsigned int afc_error_t; 67 | typedef unsigned int usbmux_error_t; 68 | typedef unsigned int service_conn_t; 69 | 70 | struct am_recovery_device; 71 | 72 | typedef struct am_device_notification_callback_info { 73 | struct am_device *dev; /* 0 device */ 74 | unsigned int msg; /* 4 one of ADNCI_MSG_* */ 75 | } __attribute__ ((packed)) am_device_notification_callback_info; 76 | 77 | /* The type of the device restore notification callback functions. 78 | * TODO: change to correct type. */ 79 | typedef void (*am_restore_device_notification_callback)(struct 80 | am_recovery_device *); 81 | 82 | /* This is a CoreFoundation object of class AMRecoveryModeDevice. */ 83 | typedef struct am_recovery_device { 84 | unsigned char unknown0[8]; /* 0 */ 85 | am_restore_device_notification_callback callback; /* 8 */ 86 | void *user_info; /* 12 */ 87 | unsigned char unknown1[12]; /* 16 */ 88 | unsigned int readwrite_pipe; /* 28 */ 89 | unsigned char read_pipe; /* 32 */ 90 | unsigned char write_ctrl_pipe; /* 33 */ 91 | unsigned char read_unknown_pipe; /* 34 */ 92 | unsigned char write_file_pipe; /* 35 */ 93 | unsigned char write_input_pipe; /* 36 */ 94 | } __attribute__ ((packed)) am_recovery_device; 95 | 96 | /* A CoreFoundation object of class AMRestoreModeDevice. */ 97 | typedef struct am_restore_device { 98 | unsigned char unknown[32]; 99 | int port; 100 | } __attribute__ ((packed)) am_restore_device; 101 | 102 | /* The type of the device notification callback function. */ 103 | typedef void(*am_device_notification_callback)(struct 104 | am_device_notification_callback_info *, void* arg); 105 | 106 | /* The type of the _AMDDeviceAttached function. 107 | * TODO: change to correct type. */ 108 | typedef void *amd_device_attached_callback; 109 | 110 | 111 | typedef struct am_device { 112 | unsigned char unknown0[16]; /* 0 - zero */ 113 | unsigned int device_id; /* 16 */ 114 | unsigned int product_id; /* 20 - set to AMD_IPHONE_PRODUCT_ID */ 115 | char *serial; /* 24 - set to AMD_IPHONE_SERIAL */ 116 | unsigned int unknown1; /* 28 */ 117 | unsigned char unknown2[4]; /* 32 */ 118 | unsigned int lockdown_conn; /* 36 */ 119 | unsigned char unknown3[8]; /* 40 */ 120 | } __attribute__ ((packed)) am_device; 121 | 122 | typedef struct am_device_notification { 123 | unsigned int unknown0; /* 0 */ 124 | unsigned int unknown1; /* 4 */ 125 | unsigned int unknown2; /* 8 */ 126 | am_device_notification_callback callback; /* 12 */ 127 | unsigned int unknown3; /* 16 */ 128 | } __attribute__ ((packed)) am_device_notification; 129 | 130 | typedef struct afc_connection { 131 | unsigned int handle; /* 0 */ 132 | unsigned int unknown0; /* 4 */ 133 | unsigned char unknown1; /* 8 */ 134 | unsigned char padding[3]; /* 9 */ 135 | unsigned int unknown2; /* 12 */ 136 | unsigned int unknown3; /* 16 */ 137 | unsigned int unknown4; /* 20 */ 138 | unsigned int fs_block_size; /* 24 */ 139 | unsigned int sock_block_size; /* 28: always 0x3c */ 140 | unsigned int io_timeout; /* 32: from AFCConnectionOpen, usu. 0 */ 141 | void *afc_lock; /* 36 */ 142 | unsigned int context; /* 40 */ 143 | } __attribute__ ((packed)) afc_connection; 144 | 145 | typedef struct afc_directory { 146 | unsigned char unknown[0]; /* size unknown */ 147 | } __attribute__ ((packed)) afc_directory; 148 | 149 | typedef struct afc_dictionary { 150 | unsigned char unknown[0]; /* size unknown */ 151 | } __attribute__ ((packed)) afc_dictionary; 152 | 153 | typedef unsigned long long afc_file_ref; 154 | 155 | typedef struct usbmux_listener_1 { /* offset value in iTunes */ 156 | unsigned int unknown0; /* 0 1 */ 157 | unsigned char *unknown1; /* 4 ptr, maybe device? */ 158 | amd_device_attached_callback callback; /* 8 _AMDDeviceAttached */ 159 | unsigned int unknown3; /* 12 */ 160 | unsigned int unknown4; /* 16 */ 161 | unsigned int unknown5; /* 20 */ 162 | } __attribute__ ((packed)) usbmux_listener_1; 163 | 164 | typedef struct usbmux_listener_2 { 165 | unsigned char unknown0[4144]; 166 | } __attribute__ ((packed)) usbmux_listener_2; 167 | 168 | typedef struct am_bootloader_control_packet { 169 | unsigned char opcode; /* 0 */ 170 | unsigned char length; /* 1 */ 171 | unsigned char magic[2]; /* 2: 0x34, 0x12 */ 172 | unsigned char payload[0]; /* 4 */ 173 | } __attribute__ ((packed)) am_bootloader_control_packet; 174 | 175 | /* ---------------------------------------------------------------------------- 176 | * Public routines 177 | * ------------------------------------------------------------------------- */ 178 | 179 | void AMDSetLogLevel(int level); 180 | 181 | /* Registers a notification with the current run loop. The callback gets 182 | * copied into the notification struct, as well as being registered with the 183 | * current run loop. dn_unknown3 gets copied into unknown3 in the same. 184 | * (Maybe dn_unknown3 is a user info parameter that gets passed as an arg to 185 | * the callback?) unused0 and unused1 are both 0 when iTunes calls this. 186 | * In iTunes the callback is located from $3db78e-$3dbbaf. 187 | * 188 | * Returns: 189 | * MDERR_OK if successful 190 | * MDERR_SYSCALL if CFRunLoopAddSource() failed 191 | * MDERR_OUT_OF_MEMORY if we ran out of memory 192 | */ 193 | 194 | mach_error_t AMDeviceNotificationSubscribe(am_device_notification_callback 195 | callback, unsigned int unused0, unsigned int unused1, void* //unsigned int 196 | dn_unknown3, struct am_device_notification **notification); 197 | 198 | /* Connects to the iPhone. Pass in the am_device structure that the 199 | * notification callback will give to you. 200 | * 201 | * Returns: 202 | * MDERR_OK if successfully connected 203 | * MDERR_SYSCALL if setsockopt() failed 204 | * MDERR_QUERY_FAILED if the daemon query failed 205 | * MDERR_INVALID_ARGUMENT if USBMuxConnectByPort returned 0xffffffff 206 | */ 207 | 208 | mach_error_t AMDeviceConnect(struct am_device *device); 209 | 210 | /* Calls PairingRecordPath() on the given device, than tests whether the path 211 | * which that function returns exists. During the initial connect, the path 212 | * returned by that function is '/', and so this returns 1. 213 | * 214 | * Returns: 215 | * 0 if the path did not exist 216 | * 1 if it did 217 | */ 218 | 219 | int AMDeviceIsPaired(struct am_device *device); 220 | 221 | /* iTunes calls this function immediately after testing whether the device is 222 | * paired. It creates a pairing file and establishes a Lockdown connection. 223 | * 224 | * Returns: 225 | * MDERR_OK if successful 226 | * MDERR_INVALID_ARGUMENT if the supplied device is null 227 | * MDERR_DICT_NOT_LOADED if the load_dict() call failed 228 | */ 229 | 230 | mach_error_t AMDeviceValidatePairing(struct am_device *device); 231 | 232 | /* Creates a Lockdown session and adjusts the device structure appropriately 233 | * to indicate that the session has been started. iTunes calls this function 234 | * after validating pairing. 235 | * 236 | * Returns: 237 | * MDERR_OK if successful 238 | * MDERR_INVALID_ARGUMENT if the Lockdown conn has not been established 239 | * MDERR_DICT_NOT_LOADED if the load_dict() call failed 240 | */ 241 | 242 | mach_error_t AMDeviceStartSession(struct am_device *device); 243 | 244 | /* Starts a service and returns a handle that can be used in order to further 245 | * access the service. You should stop the session and disconnect before using 246 | * the service. iTunes calls this function after starting a session. It starts 247 | * the service and the SSL connection. unknown may safely be 248 | * NULL (it is when iTunes calls this), but if it is not, then it will be 249 | * filled upon function exit. service_name should be one of the AMSVC_* 250 | * constants. If the service is AFC (AMSVC_AFC), then the handle is the handle 251 | * that will be used for further AFC* calls. 252 | * 253 | * Returns: 254 | * MDERR_OK if successful 255 | * MDERR_SYSCALL if the setsockopt() call failed 256 | * MDERR_INVALID_ARGUMENT if the Lockdown conn has not been established 257 | */ 258 | 259 | mach_error_t AMDeviceStartService(struct am_device *device, CFStringRef 260 | service_name, service_conn_t *handle, unsigned int * 261 | unknown); 262 | 263 | mach_error_t AMDeviceStartHouseArrestService(struct am_device *device, CFStringRef identifier, void *unknown, service_conn_t *handle, unsigned int *what); 264 | 265 | /* Stops a session. You should do this before accessing services. 266 | * 267 | * Returns: 268 | * MDERR_OK if successful 269 | * MDERR_INVALID_ARGUMENT if the Lockdown conn has not been established 270 | */ 271 | 272 | mach_error_t AMDeviceStopSession(struct am_device *device); 273 | 274 | /* Opens an Apple File Connection. You must start the appropriate service 275 | * first with AMDeviceStartService(). In iTunes, io_timeout is 0. 276 | * 277 | * Returns: 278 | * MDERR_OK if successful 279 | * MDERR_AFC_OUT_OF_MEMORY if malloc() failed 280 | */ 281 | 282 | afc_error_t AFCConnectionOpen(service_conn_t handle, unsigned int io_timeout, 283 | struct afc_connection **conn); 284 | 285 | /* Pass in a pointer to an afc_device_info structure. It will be filled. */ 286 | afc_error_t AFCDeviceInfoOpen(afc_connection *conn, struct 287 | afc_dictionary **info); 288 | 289 | /* Turns debug mode on if the environment variable AFCDEBUG is set to a numeric 290 | * value, or if the file '/AFCDEBUG' is present and contains a value. */ 291 | void AFCPlatformInit(); 292 | 293 | /* Opens a directory on the iPhone. Pass in a pointer in dir to be filled in. 294 | * Note that this normally only accesses the iTunes sandbox/partition as the 295 | * root, which is /var/root/Media. Pathnames are specified with '/' delimiters 296 | * as in Unix style. 297 | * 298 | * Returns: 299 | * MDERR_OK if successful 300 | */ 301 | 302 | afc_error_t AFCDirectoryOpen(afc_connection *conn, const char *path, 303 | struct afc_directory **dir); 304 | 305 | /* Acquires the next entry in a directory previously opened with 306 | * AFCDirectoryOpen(). When dirent is filled with a NULL value, then the end 307 | * of the directory has been reached. '.' and '..' will be returned as the 308 | * first two entries in each directory except the root; you may want to skip 309 | * over them. 310 | * 311 | * Returns: 312 | * MDERR_OK if successful, even if no entries remain 313 | */ 314 | 315 | afc_error_t AFCDirectoryRead(afc_connection *conn/*unsigned int unused*/, struct afc_directory *dir, 316 | char **dirent); 317 | 318 | afc_error_t AFCDirectoryClose(afc_connection *conn, struct afc_directory *dir); 319 | afc_error_t AFCDirectoryCreate(afc_connection *conn, const char *dirname); 320 | afc_error_t AFCRemovePath(afc_connection *conn, const char *dirname); 321 | afc_error_t AFCRenamePath(afc_connection *conn, const char *from, const char *to); 322 | afc_error_t AFCLinkPath(afc_connection *conn, long long int linktype, const char *target, const char *linkname); 323 | 324 | /* Returns the context field of the given AFC connection. */ 325 | unsigned int AFCConnectionGetContext(afc_connection *conn); 326 | 327 | /* Returns the fs_block_size field of the given AFC connection. */ 328 | unsigned int AFCConnectionGetFSBlockSize(afc_connection *conn); 329 | 330 | /* Returns the io_timeout field of the given AFC connection. In iTunes this is 331 | * 0. */ 332 | unsigned int AFCConnectionGetIOTimeout(afc_connection *conn); 333 | 334 | /* Returns the sock_block_size field of the given AFC connection. */ 335 | unsigned int AFCConnectionGetSocketBlockSize(afc_connection *conn); 336 | 337 | /* Closes the given AFC connection. */ 338 | afc_error_t AFCConnectionClose(afc_connection *conn); 339 | 340 | /* Registers for device notifications related to the restore process. unknown0 341 | * is zero when iTunes calls this. In iTunes, 342 | * the callbacks are located at: 343 | * 1: $3ac68e-$3ac6b1, calls $3ac542(unknown1, arg, 0) 344 | * 2: $3ac66a-$3ac68d, calls $3ac542(unknown1, 0, arg) 345 | * 3: $3ac762-$3ac785, calls $3ac6b2(unknown1, arg, 0) 346 | * 4: $3ac73e-$3ac761, calls $3ac6b2(unknown1, 0, arg) 347 | */ 348 | 349 | unsigned int AMRestoreRegisterForDeviceNotifications( 350 | am_restore_device_notification_callback dfu_connect_callback, 351 | am_restore_device_notification_callback recovery_connect_callback, 352 | am_restore_device_notification_callback dfu_disconnect_callback, 353 | am_restore_device_notification_callback recovery_disconnect_callback, 354 | unsigned int unknown0, 355 | void *user_info); 356 | 357 | /* Causes the restore functions to spit out (unhelpful) progress messages to 358 | * the file specified by the given path. iTunes always calls this right before 359 | * restoring with a path of 360 | * "$HOME/Library/Logs/iPhone Updater Logs/iPhoneUpdater X.log", where X is an 361 | * unused number. 362 | */ 363 | 364 | unsigned int AMRestoreEnableFileLogging(char *path); 365 | 366 | /* Initializes a new option dictionary to default values. Pass the constant 367 | * kCFAllocatorDefault as the allocator. The option dictionary looks as 368 | * follows: 369 | * { 370 | * NORImageType => 'production', 371 | * AutoBootDelay => 0, 372 | * KernelCacheType => 'Release', 373 | * UpdateBaseband => true, 374 | * DFUFileType => 'RELEASE', 375 | * SystemImageType => 'User', 376 | * CreateFilesystemPartitions => true, 377 | * FlashNOR => true, 378 | * RestoreBootArgs => 'rd=md0 nand-enable-reformat=1 -progress' 379 | * BootImageType => 'User' 380 | * } 381 | * 382 | * Returns: 383 | * the option dictionary if successful 384 | * NULL if out of memory 385 | */ 386 | 387 | CFMutableDictionaryRef AMRestoreCreateDefaultOptions(CFAllocatorRef allocator); 388 | 389 | /* ---------------------------------------------------------------------------- 390 | * Less-documented public routines 391 | * ------------------------------------------------------------------------- */ 392 | 393 | /* mode 2 = read, mode 3 = write */ 394 | afc_error_t AFCFileRefOpen(afc_connection *conn, const char *path, 395 | unsigned long long mode, afc_file_ref *ref); 396 | afc_error_t AFCFileRefSeek(afc_connection *conn, afc_file_ref ref, 397 | unsigned long long offset1, unsigned long long offset2); 398 | afc_error_t AFCFileRefRead(afc_connection *conn, afc_file_ref ref, 399 | void *buf, unsigned int *len); 400 | afc_error_t AFCFileRefSetFileSize(afc_connection *conn, afc_file_ref ref, 401 | unsigned long long offset); 402 | afc_error_t AFCFileRefWrite(afc_connection *conn, afc_file_ref ref, 403 | const void *buf, unsigned int len); 404 | afc_error_t AFCFileRefClose(afc_connection *conn, afc_file_ref ref); 405 | 406 | afc_error_t AFCFileInfoOpen(afc_connection *conn, const char *path, struct 407 | afc_dictionary **info); 408 | afc_error_t AFCKeyValueRead(struct afc_dictionary *dict, char **key, char ** 409 | val); 410 | afc_error_t AFCKeyValueClose(struct afc_dictionary *dict); 411 | 412 | unsigned int AMRestorePerformRecoveryModeRestore(struct am_recovery_device * 413 | rdev, CFDictionaryRef opts, void *callback, void *user_info); 414 | unsigned int AMRestorePerformRestoreModeRestore(struct am_restore_device * 415 | rdev, CFDictionaryRef opts, void *callback, void *user_info); 416 | 417 | struct am_restore_device *AMRestoreModeDeviceCreate(unsigned int unknown0, 418 | unsigned int connection_id, unsigned int unknown1); 419 | 420 | unsigned int AMRestoreCreatePathsForBundle(CFStringRef restore_bundle_path, 421 | CFStringRef kernel_cache_type, CFStringRef boot_image_type, unsigned int 422 | unknown0, CFStringRef *firmware_dir_path, CFStringRef * 423 | kernelcache_restore_path, unsigned int unknown1, CFStringRef * 424 | ramdisk_path); 425 | 426 | unsigned int AMDeviceGetConnectionID(struct am_device *device); 427 | mach_error_t AMDeviceEnterRecovery(struct am_device *device); 428 | mach_error_t AMDeviceDisconnect(struct am_device *device); 429 | mach_error_t AMDeviceRetain(struct am_device *device); 430 | mach_error_t AMDeviceRelease(struct am_device *device); 431 | CFStringRef AMDeviceCopyValue(struct am_device *device, unsigned int, CFStringRef cfstring); 432 | CFStringRef AMDeviceCopyDeviceIdentifier(struct am_device *device); 433 | 434 | typedef void (*notify_callback)(CFStringRef notification, void *data); 435 | 436 | mach_error_t AMDPostNotification(service_conn_t socket, CFStringRef notification, CFStringRef userinfo); 437 | mach_error_t AMDObserveNotification(void *socket, CFStringRef notification); 438 | mach_error_t AMDListenForNotifications(void *socket, notify_callback cb, void *data); 439 | mach_error_t AMDShutdownNotificationProxy(void *socket); 440 | 441 | /*edits by geohot*/ 442 | mach_error_t AMDeviceDeactivate(struct am_device *device); 443 | mach_error_t AMDeviceActivate(struct am_device *device, CFMutableDictionaryRef); 444 | /*end*/ 445 | 446 | void *AMDeviceSerialize(struct am_device *device); 447 | void AMDAddLogFileDescriptor(int fd); 448 | //kern_return_t AMDeviceSendMessage(service_conn_t socket, void *unused, CFPropertyListRef plist); 449 | //kern_return_t AMDeviceReceiveMessage(service_conn_t socket, CFDictionaryRef options, CFPropertyListRef * result); 450 | 451 | /* ---------------------------------------------------------------------------- 452 | * Semi-private routines 453 | * ------------------------------------------------------------------------- */ 454 | 455 | /* Pass in a usbmux_listener_1 structure and a usbmux_listener_2 structure 456 | * pointer, which will be filled with the resulting usbmux_listener_2. 457 | * 458 | * Returns: 459 | * MDERR_OK if completed successfully 460 | * MDERR_USBMUX_ARG_NULL if one of the arguments was NULL 461 | * MDERR_USBMUX_FAILED if the listener was not created successfully 462 | */ 463 | 464 | usbmux_error_t USBMuxListenerCreate(struct usbmux_listener_1 *esi_fp8, struct 465 | usbmux_listener_2 **eax_fp12); 466 | 467 | /* ---------------------------------------------------------------------------- 468 | * Less-documented semi-private routines 469 | * ------------------------------------------------------------------------- */ 470 | 471 | usbmux_error_t USBMuxListenerHandleData(void *); 472 | 473 | /* ---------------------------------------------------------------------------- 474 | * Private routines - here be dragons 475 | * ------------------------------------------------------------------------- */ 476 | 477 | /* AMRestorePerformRestoreModeRestore() calls this function with a dictionary 478 | * in order to perform certain special restore operations 479 | * (RESTORED_OPERATION_*). It is thought that this function might enable 480 | * significant access to the phone. */ 481 | 482 | typedef unsigned int (*t_performOperation)(struct am_restore_device *rdev, 483 | CFDictionaryRef op); // __attribute__ ((regparm(2))); 484 | 485 | #ifdef __cplusplus 486 | } 487 | #endif 488 | 489 | #endif -------------------------------------------------------------------------------- /fruitstrap.c: -------------------------------------------------------------------------------- 1 | //TODO: don't copy/mount DeveloperDiskImage.dmg if it's already done - Xcode checks this somehow 2 | 3 | #import 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "MobileDevice.h" 11 | 12 | #define FDVENDOR_PATH "/tmp/fruitstrap-remote-debugserver" 13 | #define PREP_CMDS_PATH "/tmp/fruitstrap-gdb-prep-cmds" 14 | #define GDB_SHELL "/Developer/Platforms/iPhoneOS.platform/Developer/usr/libexec/gdb/gdb-arm-apple-darwin --arch armv7 -q -x " PREP_CMDS_PATH 15 | 16 | // approximation of what Xcode does: 17 | #define GDB_PREP_CMDS CFSTR("set mi-show-protections off\n\ 18 | set auto-raise-load-levels 1\n\ 19 | set shlib-path-substitutions /usr \"{ds_path}/Symbols/usr\" /System \"{ds_path}/Symbols/System\" \"{device_container}\" \"{disk_container}\" \"/private{device_container}\" \"{disk_container}\" /Developer \"{ds_path}/Symbols/Developer\"\n\ 20 | set remote max-packet-size 1024\n\ 21 | set sharedlibrary check-uuids on\n\ 22 | set env NSUnbufferedIO YES\n\ 23 | set minimal-signal-handling 1\n\ 24 | set sharedlibrary load-rules \\\".*\\\" \\\".*\\\" container\n\ 25 | set inferior-auto-start-dyld 0\n\ 26 | file \"{disk_app}\"\n\ 27 | set remote executable-directory {device_app}\n\ 28 | set remote noack-mode 1\n\ 29 | set trust-readonly-sections 1\n\ 30 | target remote-mobile " FDVENDOR_PATH "\n\ 31 | mem 0x1000 0x3fffffff cache\n\ 32 | mem 0x40000000 0xffffffff none\n\ 33 | mem 0x00000000 0x0fff none\n\ 34 | run {args}\n\ 35 | set minimal-signal-handling 0\n\ 36 | set inferior-auto-start-cfm off\n\ 37 | set sharedLibrary load-rules dyld \".*libobjc.*\" all dyld \".*CoreFoundation.*\" all dyld \".*Foundation.*\" all dyld \".*libSystem.*\" all dyld \".*AppKit.*\" all dyld \".*PBGDBIntrospectionSupport.*\" all dyld \".*/usr/lib/dyld.*\" all dyld \".*CarbonDataFormatters.*\" all dyld \".*libauto.*\" all dyld \".*CFDataFormatters.*\" all dyld \"/System/Library/Frameworks\\\\\\\\|/System/Library/PrivateFrameworks\\\\\\\\|/usr/lib\" extern dyld \".*\" all exec \".*\" all\n\ 38 | sharedlibrary apply-load-rules all\n\ 39 | set inferior-auto-start-dyld 1") 40 | 41 | typedef struct am_device * AMDeviceRef; 42 | int AMDeviceSecureTransferPath(int zero, AMDeviceRef device, CFURLRef url, CFDictionaryRef options, void *callback, int cbarg); 43 | int AMDeviceSecureInstallApplication(int zero, AMDeviceRef device, CFURLRef url, CFDictionaryRef options, void *callback, int cbarg); 44 | int AMDeviceMountImage(AMDeviceRef device, CFStringRef image, CFDictionaryRef options, void *callback, int cbarg); 45 | int AMDeviceLookupApplications(AMDeviceRef device, int zero, CFDictionaryRef* result); 46 | 47 | bool found_device = false, debug = false, verbose = false; 48 | char *app_path = NULL; 49 | char *device_id = NULL; 50 | char *args = NULL; 51 | int timeout = 0; 52 | CFStringRef last_path = NULL; 53 | service_conn_t gdbfd; 54 | 55 | Boolean path_exists(CFTypeRef path) { 56 | if (CFGetTypeID(path) == CFStringGetTypeID()) { 57 | CFURLRef url = CFURLCreateWithFileSystemPath(NULL, path, kCFURLPOSIXPathStyle, true); 58 | Boolean result = CFURLResourceIsReachable(url, NULL); 59 | CFRelease(url); 60 | return result; 61 | } else if (CFGetTypeID(path) == CFURLGetTypeID()) { 62 | return CFURLResourceIsReachable(path, NULL); 63 | } else { 64 | return false; 65 | } 66 | } 67 | 68 | CFStringRef copy_device_support_path(AMDeviceRef device) { 69 | CFStringRef version = AMDeviceCopyValue(device, 0, CFSTR("ProductVersion")); 70 | CFStringRef build = AMDeviceCopyValue(device, 0, CFSTR("BuildVersion")); 71 | const char* home = getenv("HOME"); 72 | CFStringRef path; 73 | bool found = false; 74 | 75 | path = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s/Library/Developer/Xcode/iOS DeviceSupport/%@ (%@)"), home, version, build); 76 | found = path_exists(path); 77 | 78 | if (!found) 79 | { 80 | path = CFStringCreateWithFormat(NULL, NULL, CFSTR("/Developer/Platforms/iPhoneOS.platform/DeviceSupport/%@ (%@)"), version, build); 81 | found = path_exists(path); 82 | } 83 | if (!found) 84 | { 85 | path = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s/Library/Developer/Xcode/iOS DeviceSupport/%@"), home, version); 86 | found = path_exists(path); 87 | } 88 | if (!found) 89 | { 90 | path = CFStringCreateWithFormat(NULL, NULL, CFSTR("/Developer/Platforms/iPhoneOS.platform/DeviceSupport/%@"), version); 91 | found = path_exists(path); 92 | } 93 | if (!found) 94 | { 95 | path = CFStringCreateWithFormat(NULL, NULL, CFSTR("/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/%@"), version); 96 | found = path_exists(path); 97 | } 98 | 99 | CFRelease(version); 100 | CFRelease(build); 101 | 102 | if (!found) 103 | { 104 | CFRelease(path); 105 | printf("[ !! ] Unable to locate DeviceSupport directory.\n"); 106 | exit(1); 107 | } 108 | 109 | return path; 110 | } 111 | 112 | CFStringRef copy_developer_disk_image_path(AMDeviceRef device) { 113 | CFStringRef version = AMDeviceCopyValue(device, 0, CFSTR("ProductVersion")); 114 | CFStringRef build = AMDeviceCopyValue(device, 0, CFSTR("BuildVersion")); 115 | const char *home = getenv("HOME"); 116 | CFStringRef path; 117 | bool found = false; 118 | 119 | path = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s/Library/Developer/Xcode/iOS DeviceSupport/%@ (%@)/DeveloperDiskImage.dmg"), home, version, build); 120 | found = path_exists(path); 121 | 122 | if (!found) { 123 | path = CFStringCreateWithFormat(NULL, NULL, CFSTR("/Developer/Platforms/iPhoneOS.platform/DeviceSupport/%@ (%@/DeveloperDiskImage.dmg)"), version, build); 124 | found = path_exists(path); 125 | } 126 | if (!found) { 127 | path = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s/Library/Developer/Xcode/iOS DeviceSupport/@%/DeveloperDiskImage.dmg"), home, version); 128 | found = path_exists(path); 129 | } 130 | if (!found) { 131 | path = CFStringCreateWithFormat(NULL, NULL, CFSTR("/Developer/Platforms/iPhoneOS.platform/DeviceSupport/@%/DeveloperDiskImage.dmg"), version); 132 | found = path_exists(path); 133 | } 134 | if (!found) { 135 | path = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s/Library/Developer/Xcode/iOS DeviceSupport/Latest/DeveloperDiskImage.dmg"), home); 136 | found = path_exists(path); 137 | } 138 | if (!found) { 139 | path = CFStringCreateWithFormat(NULL, NULL, CFSTR("/Developer/Platforms/iPhoneOS.platform/DeviceSupport/Latest/DeveloperDiskImage.dmg")); 140 | found = path_exists(path); 141 | } 142 | 143 | CFRelease(version); 144 | CFRelease(build); 145 | 146 | if (!found) { 147 | CFRelease(path); 148 | printf("[ !! ] Unable to locate DeviceSupport directory containing DeveloperDiskImage.dmg.\n"); 149 | exit(1); 150 | } 151 | 152 | return path; 153 | } 154 | 155 | void mount_callback(CFDictionaryRef dict, int arg) { 156 | CFStringRef status = CFDictionaryGetValue(dict, CFSTR("Status")); 157 | 158 | if (CFEqual(status, CFSTR("LookingUpImage"))) { 159 | printf("[ 0%%] Looking up developer disk image\n"); 160 | } else if (CFEqual(status, CFSTR("CopyingImage"))) { 161 | printf("[ 30%%] Copying DeveloperDiskImage.dmg to device\n"); 162 | } else if (CFEqual(status, CFSTR("MountingImage"))) { 163 | printf("[ 90%%] Mounting developer disk image\n"); 164 | } 165 | } 166 | 167 | void mount_developer_image(AMDeviceRef device) { 168 | CFStringRef ds_path = copy_device_support_path(device); 169 | CFStringRef image_path = copy_developer_disk_image_path(device); 170 | CFStringRef sig_path = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@.signature"), image_path); 171 | CFRelease(ds_path); 172 | 173 | if (verbose) { 174 | printf("Device support path: "); 175 | fflush(stdout); 176 | CFShow(ds_path); 177 | printf("Developer disk image: "); 178 | fflush(stdout); 179 | CFShow(image_path); 180 | } 181 | 182 | FILE* sig = fopen(CFStringGetCStringPtr(sig_path, kCFStringEncodingMacRoman), "rb"); 183 | void *sig_buf = malloc(128); 184 | assert(fread(sig_buf, 1, 128, sig) == 128); 185 | fclose(sig); 186 | CFDataRef sig_data = CFDataCreateWithBytesNoCopy(NULL, sig_buf, 128, NULL); 187 | CFRelease(sig_path); 188 | 189 | CFTypeRef keys[] = { CFSTR("ImageSignature"), CFSTR("ImageType") }; 190 | CFTypeRef values[] = { sig_data, CFSTR("Developer") }; 191 | CFDictionaryRef options = CFDictionaryCreate(NULL, (const void **)&keys, (const void **)&values, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 192 | CFRelease(sig_data); 193 | 194 | int result = AMDeviceMountImage(device, image_path, options, &mount_callback, 0); 195 | if (result == 0) { 196 | printf("[ 95%%] Developer disk image mounted successfully\n"); 197 | } else if (result == 0xe8000076 /* already mounted */) { 198 | printf("[ 95%%] Developer disk image already mounted\n"); 199 | } else { 200 | printf("[ !! ] Unable to mount developer disk image. (%x)\n", result); 201 | exit(1); 202 | } 203 | 204 | CFRelease(image_path); 205 | CFRelease(options); 206 | } 207 | 208 | void transfer_callback(CFDictionaryRef dict, int arg) { 209 | int percent; 210 | CFStringRef status = CFDictionaryGetValue(dict, CFSTR("Status")); 211 | CFNumberGetValue(CFDictionaryGetValue(dict, CFSTR("PercentComplete")), kCFNumberSInt32Type, &percent); 212 | 213 | if (CFEqual(status, CFSTR("CopyingFile"))) { 214 | CFStringRef path = CFDictionaryGetValue(dict, CFSTR("Path")); 215 | 216 | if ((last_path == NULL || !CFEqual(path, last_path)) && !CFStringHasSuffix(path, CFSTR(".ipa"))) { 217 | printf("[%3d%%] Copying %s to device\n", percent / 2, CFStringGetCStringPtr(path, kCFStringEncodingMacRoman)); 218 | } 219 | 220 | if (last_path != NULL) { 221 | CFRelease(last_path); 222 | } 223 | last_path = CFStringCreateCopy(NULL, path); 224 | } 225 | } 226 | 227 | void install_callback(CFDictionaryRef dict, int arg) { 228 | int percent; 229 | CFStringRef status = CFDictionaryGetValue(dict, CFSTR("Status")); 230 | CFNumberGetValue(CFDictionaryGetValue(dict, CFSTR("PercentComplete")), kCFNumberSInt32Type, &percent); 231 | 232 | printf("[%3d%%] %s\n", (percent / 2) + 50, CFStringGetCStringPtr(status, kCFStringEncodingMacRoman)); 233 | } 234 | 235 | void fdvendor_callback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, const void *data, void *info) { 236 | CFSocketNativeHandle socket = (CFSocketNativeHandle)(*((CFSocketNativeHandle *)data)); 237 | 238 | struct msghdr message; 239 | struct iovec iov[1]; 240 | struct cmsghdr *control_message = NULL; 241 | char ctrl_buf[CMSG_SPACE(sizeof(int))]; 242 | char dummy_data[1]; 243 | 244 | memset(&message, 0, sizeof(struct msghdr)); 245 | memset(ctrl_buf, 0, CMSG_SPACE(sizeof(int))); 246 | 247 | dummy_data[0] = ' '; 248 | iov[0].iov_base = dummy_data; 249 | iov[0].iov_len = sizeof(dummy_data); 250 | 251 | message.msg_name = NULL; 252 | message.msg_namelen = 0; 253 | message.msg_iov = iov; 254 | message.msg_iovlen = 1; 255 | message.msg_controllen = CMSG_SPACE(sizeof(int)); 256 | message.msg_control = ctrl_buf; 257 | 258 | control_message = CMSG_FIRSTHDR(&message); 259 | control_message->cmsg_level = SOL_SOCKET; 260 | control_message->cmsg_type = SCM_RIGHTS; 261 | control_message->cmsg_len = CMSG_LEN(sizeof(int)); 262 | 263 | *((int *) CMSG_DATA(control_message)) = gdbfd; 264 | 265 | sendmsg(socket, &message, 0); 266 | CFSocketInvalidate(s); 267 | CFRelease(s); 268 | } 269 | 270 | CFURLRef copy_device_app_url(AMDeviceRef device, CFStringRef identifier) { 271 | CFDictionaryRef result; 272 | assert(AMDeviceLookupApplications(device, 0, &result) == 0); 273 | 274 | CFDictionaryRef app_dict = CFDictionaryGetValue(result, identifier); 275 | assert(app_dict != NULL); 276 | 277 | CFStringRef app_path = CFDictionaryGetValue(app_dict, CFSTR("Path")); 278 | assert(app_path != NULL); 279 | 280 | CFURLRef url = CFURLCreateWithFileSystemPath(NULL, app_path, kCFURLPOSIXPathStyle, true); 281 | CFRelease(result); 282 | return url; 283 | } 284 | 285 | CFStringRef copy_disk_app_identifier(CFURLRef disk_app_url) { 286 | CFURLRef plist_url = CFURLCreateCopyAppendingPathComponent(NULL, disk_app_url, CFSTR("Info.plist"), false); 287 | CFReadStreamRef plist_stream = CFReadStreamCreateWithFile(NULL, plist_url); 288 | CFReadStreamOpen(plist_stream); 289 | CFPropertyListRef plist = CFPropertyListCreateWithStream(NULL, plist_stream, 0, kCFPropertyListImmutable, NULL, NULL); 290 | CFStringRef bundle_identifier = CFRetain(CFDictionaryGetValue(plist, CFSTR("CFBundleIdentifier"))); 291 | CFReadStreamClose(plist_stream); 292 | 293 | CFRelease(plist_url); 294 | CFRelease(plist_stream); 295 | CFRelease(plist); 296 | 297 | return bundle_identifier; 298 | } 299 | 300 | void write_gdb_prep_cmds(AMDeviceRef device, CFURLRef disk_app_url) { 301 | CFMutableStringRef cmds = CFStringCreateMutableCopy(NULL, 0, GDB_PREP_CMDS); 302 | CFRange range = { 0, CFStringGetLength(cmds) }; 303 | 304 | CFStringRef ds_path = copy_device_support_path(device); 305 | CFStringFindAndReplace(cmds, CFSTR("{ds_path}"), ds_path, range, 0); 306 | range.length = CFStringGetLength(cmds); 307 | 308 | if (args) { 309 | CFStringRef cf_args = CFStringCreateWithCString(NULL, args, kCFStringEncodingASCII); 310 | CFStringFindAndReplace(cmds, CFSTR("{args}"), cf_args, range, 0); 311 | CFRelease(cf_args); 312 | } else { 313 | CFStringFindAndReplace(cmds, CFSTR(" {args}"), CFSTR(""), range, 0); 314 | } 315 | range.length = CFStringGetLength(cmds); 316 | 317 | CFStringRef bundle_identifier = copy_disk_app_identifier(disk_app_url); 318 | CFURLRef device_app_url = copy_device_app_url(device, bundle_identifier); 319 | CFStringRef device_app_path = CFURLCopyFileSystemPath(device_app_url, kCFURLPOSIXPathStyle); 320 | CFStringFindAndReplace(cmds, CFSTR("{device_app}"), device_app_path, range, 0); 321 | range.length = CFStringGetLength(cmds); 322 | 323 | CFStringRef disk_app_path = CFURLCopyFileSystemPath(disk_app_url, kCFURLPOSIXPathStyle); 324 | CFStringFindAndReplace(cmds, CFSTR("{disk_app}"), disk_app_path, range, 0); 325 | range.length = CFStringGetLength(cmds); 326 | 327 | CFURLRef device_container_url = CFURLCreateCopyDeletingLastPathComponent(NULL, device_app_url); 328 | CFStringRef device_container_path = CFURLCopyFileSystemPath(device_container_url, kCFURLPOSIXPathStyle); 329 | CFMutableStringRef dcp_noprivate = CFStringCreateMutableCopy(NULL, 0, device_container_path); 330 | range.length = CFStringGetLength(dcp_noprivate); 331 | CFStringFindAndReplace(dcp_noprivate, CFSTR("/private/var/"), CFSTR("/var/"), range, 0); 332 | range.length = CFStringGetLength(cmds); 333 | CFStringFindAndReplace(cmds, CFSTR("{device_container}"), dcp_noprivate, range, 0); 334 | range.length = CFStringGetLength(cmds); 335 | 336 | CFURLRef disk_container_url = CFURLCreateCopyDeletingLastPathComponent(NULL, disk_app_url); 337 | CFStringRef disk_container_path = CFURLCopyFileSystemPath(disk_container_url, kCFURLPOSIXPathStyle); 338 | CFStringFindAndReplace(cmds, CFSTR("{disk_container}"), disk_container_path, range, 0); 339 | 340 | CFDataRef cmds_data = CFStringCreateExternalRepresentation(NULL, cmds, kCFStringEncodingASCII, 0); 341 | FILE *out = fopen(PREP_CMDS_PATH, "w"); 342 | fwrite(CFDataGetBytePtr(cmds_data), CFDataGetLength(cmds_data), 1, out); 343 | fclose(out); 344 | 345 | CFRelease(cmds); 346 | if (ds_path != NULL) CFRelease(ds_path); 347 | CFRelease(bundle_identifier); 348 | CFRelease(device_app_url); 349 | CFRelease(device_app_path); 350 | CFRelease(disk_app_path); 351 | CFRelease(device_container_url); 352 | CFRelease(device_container_path); 353 | CFRelease(dcp_noprivate); 354 | CFRelease(disk_container_url); 355 | CFRelease(disk_container_path); 356 | CFRelease(cmds_data); 357 | } 358 | 359 | void start_remote_debug_server(AMDeviceRef device) { 360 | assert(AMDeviceStartService(device, CFSTR("com.apple.debugserver"), &gdbfd, NULL) == 0); 361 | 362 | CFSocketRef fdvendor = CFSocketCreate(NULL, AF_UNIX, 0, 0, kCFSocketAcceptCallBack, &fdvendor_callback, NULL); 363 | 364 | int yes = 1; 365 | setsockopt(CFSocketGetNative(fdvendor), SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); 366 | 367 | struct sockaddr_un address; 368 | memset(&address, 0, sizeof(address)); 369 | address.sun_family = AF_UNIX; 370 | strcpy(address.sun_path, FDVENDOR_PATH); 371 | address.sun_len = SUN_LEN(&address); 372 | CFDataRef address_data = CFDataCreate(NULL, (const UInt8 *)&address, sizeof(address)); 373 | 374 | unlink(FDVENDOR_PATH); 375 | 376 | CFSocketSetAddress(fdvendor, address_data); 377 | CFRelease(address_data); 378 | CFRunLoopAddSource(CFRunLoopGetMain(), CFSocketCreateRunLoopSource(NULL, fdvendor, 0), kCFRunLoopCommonModes); 379 | } 380 | 381 | void gdb_ready_handler(int signum) 382 | { 383 | _exit(0); 384 | } 385 | 386 | void handle_device(AMDeviceRef device) { 387 | if (found_device) return; // handle one device only 388 | 389 | CFStringRef found_device_id = AMDeviceCopyDeviceIdentifier(device); 390 | 391 | if (device_id != NULL) { 392 | if(strcmp(device_id, CFStringGetCStringPtr(found_device_id, CFStringGetSystemEncoding())) == 0) { 393 | found_device = true; 394 | } else { 395 | return; 396 | } 397 | } else { 398 | found_device = true; 399 | } 400 | 401 | CFRetain(device); // don't know if this is necessary? 402 | 403 | printf("[ 0%%] Found device (%s), beginning install\n", CFStringGetCStringPtr(found_device_id, CFStringGetSystemEncoding())); 404 | 405 | AMDeviceConnect(device); 406 | assert(AMDeviceIsPaired(device)); 407 | assert(AMDeviceValidatePairing(device) == 0); 408 | assert(AMDeviceStartSession(device) == 0); 409 | 410 | CFStringRef path = CFStringCreateWithCString(NULL, app_path, kCFStringEncodingASCII); 411 | CFURLRef relative_url = CFURLCreateWithFileSystemPath(NULL, path, kCFURLPOSIXPathStyle, false); 412 | CFURLRef url = CFURLCopyAbsoluteURL(relative_url); 413 | 414 | CFRelease(relative_url); 415 | 416 | int afcFd; 417 | assert(AMDeviceStartService(device, CFSTR("com.apple.afc"), &afcFd, NULL) == 0); 418 | assert(AMDeviceStopSession(device) == 0); 419 | assert(AMDeviceDisconnect(device) == 0); 420 | assert(AMDeviceTransferApplication(afcFd, path, NULL, transfer_callback, NULL) == 0); 421 | 422 | close(afcFd); 423 | 424 | CFStringRef keys[] = { CFSTR("PackageType") }; 425 | CFStringRef values[] = { CFSTR("Developer") }; 426 | CFDictionaryRef options = CFDictionaryCreate(NULL, (const void **)&keys, (const void **)&values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 427 | 428 | AMDeviceConnect(device); 429 | assert(AMDeviceIsPaired(device)); 430 | assert(AMDeviceValidatePairing(device) == 0); 431 | assert(AMDeviceStartSession(device) == 0); 432 | 433 | int installFd; 434 | assert(AMDeviceStartService(device, CFSTR("com.apple.mobile.installation_proxy"), &installFd, NULL) == 0); 435 | 436 | assert(AMDeviceStopSession(device) == 0); 437 | assert(AMDeviceDisconnect(device) == 0); 438 | 439 | mach_error_t result = AMDeviceInstallApplication(installFd, path, options, install_callback, NULL); 440 | if (result != 0) 441 | { 442 | printf("AMDeviceInstallApplication failed: %d\n", result); 443 | exit(1); 444 | } 445 | 446 | close(installFd); 447 | 448 | CFRelease(path); 449 | CFRelease(options); 450 | 451 | printf("[100%%] Installed package %s\n", app_path); 452 | 453 | if (!debug) exit(0); // no debug phase 454 | 455 | AMDeviceConnect(device); 456 | assert(AMDeviceIsPaired(device)); 457 | assert(AMDeviceValidatePairing(device) == 0); 458 | assert(AMDeviceStartSession(device) == 0); 459 | 460 | printf("------ Debug phase ------\n"); 461 | 462 | mount_developer_image(device); // put debugserver on the device 463 | start_remote_debug_server(device); // start debugserver 464 | write_gdb_prep_cmds(device, url); // dump the necessary gdb commands into a file 465 | 466 | CFRelease(url); 467 | 468 | printf("[100%%] Connecting to remote debug server\n"); 469 | printf("-------------------------\n"); 470 | 471 | signal(SIGHUP, gdb_ready_handler); 472 | 473 | pid_t parent = getpid(); 474 | int pid = fork(); 475 | if (pid == 0) { 476 | system(GDB_SHELL); // launch gdb 477 | kill(parent, SIGHUP); // "No. I am your father." 478 | _exit(0); 479 | } 480 | } 481 | 482 | void device_callback(struct am_device_notification_callback_info *info, void *arg) { 483 | switch (info->msg) { 484 | case ADNCI_MSG_CONNECTED: 485 | handle_device(info->dev); 486 | default: 487 | break; 488 | } 489 | } 490 | 491 | void timeout_callback(CFRunLoopTimerRef timer, void *info) { 492 | if (!found_device) { 493 | printf("Timed out waiting for device.\n"); 494 | exit(1); 495 | } 496 | } 497 | 498 | void usage(const char* app) { 499 | printf("usage: %s [-d/--debug] [-i/--id device_id] -b/--bundle bundle.app [-a/--args arguments] [-t/--timeout timeout(seconds)]\n", app); 500 | } 501 | 502 | int main(int argc, char *argv[]) { 503 | static struct option longopts[] = { 504 | { "debug", no_argument, NULL, 'd' }, 505 | { "id", required_argument, NULL, 'i' }, 506 | { "bundle", required_argument, NULL, 'b' }, 507 | { "args", required_argument, NULL, 'a' }, 508 | { "verbose", no_argument, NULL, 'v' }, 509 | { "timeout", required_argument, NULL, 't' }, 510 | { NULL, 0, NULL, 0 }, 511 | }; 512 | char ch; 513 | 514 | while ((ch = getopt_long(argc, argv, "dvi:b:a:t:", longopts, NULL)) != -1) 515 | { 516 | switch (ch) { 517 | case 'd': 518 | debug = 1; 519 | break; 520 | case 'i': 521 | device_id = optarg; 522 | break; 523 | case 'b': 524 | app_path = optarg; 525 | break; 526 | case 'a': 527 | args = optarg; 528 | break; 529 | case 'v': 530 | verbose = 1; 531 | break; 532 | case 't': 533 | timeout = atoi(optarg); 534 | break; 535 | default: 536 | usage(argv[0]); 537 | return 1; 538 | } 539 | } 540 | 541 | if (!app_path) { 542 | usage(argv[0]); 543 | exit(0); 544 | } 545 | 546 | printf("------ Install phase ------\n"); 547 | 548 | assert(access(app_path, F_OK) == 0); 549 | 550 | AMDSetLogLevel(5); // otherwise syslog gets flooded with crap 551 | if (timeout > 0) 552 | { 553 | CFRunLoopTimerRef timer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent() + timeout, 0, 0, 0, timeout_callback, NULL); 554 | CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopCommonModes); 555 | printf("[....] Waiting up to %d seconds for iOS device to be connected\n", timeout); 556 | } 557 | else 558 | { 559 | printf("[....] Waiting for iOS device to be connected\n"); 560 | } 561 | 562 | struct am_device_notification *notify; 563 | AMDeviceNotificationSubscribe(&device_callback, 0, 0, NULL, ¬ify); 564 | CFRunLoopRun(); 565 | } 566 | --------------------------------------------------------------------------------