├── .gitignore ├── LICENSE ├── README.md ├── cftypes.cpp ├── cftypes.h ├── common.cpp ├── common.h ├── ios_instruments_client.cpp ├── ios_instruments_client.h ├── makefile ├── mobile_device.cpp └── mobile_device.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | obj/* 3 | ios_instruments_client 4 | ios_instruments_client.dSYM 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Hex-Rays 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ios\_instruments\_client 2 | 3 | This is a command-line tool that can communicate with the iOS Instruments Server. 4 | 5 | It was inspired by my work on the [dtxmsg](https://github.com/troybowman/dtxmsg) IDA plugin. 6 | 7 | The app is implemented in C++. It should build/run on any recent version of OSX, 8 | and it can work with any recent version of iOS. 9 | 10 | ## build 11 | 12 | $ make 13 | 14 | ## run 15 | 16 | $ ./ios\_instruments\_client -h 17 | -------------------------------------------------------------------------------- /cftypes.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "cftypes.h" 3 | 4 | //------------------------------------------------------------------------------ 5 | string_t to_stlstr(CFStringRef ref) 6 | { 7 | if ( ref == NULL ) 8 | return ""; 9 | 10 | CFIndex length = CFStringGetLength(ref); 11 | if ( length <= 0 ) 12 | return ""; 13 | 14 | CFIndex bufsize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1; 15 | char *buf = (char *)calloc(bufsize, 1); 16 | 17 | string_t ret; 18 | if ( CFStringGetCString(ref, buf, bufsize, kCFStringEncodingUTF8) ) 19 | ret = buf; 20 | 21 | free(buf); 22 | return ret; 23 | } 24 | 25 | //------------------------------------------------------------------------------ 26 | string_t get_description(CFTypeRef ref) 27 | { 28 | CFStringRef desc = CFCopyDescription(ref); 29 | string_t ret = to_stlstr(desc); 30 | CFRelease(desc); 31 | return ret; 32 | } 33 | 34 | //----------------------------------------------------------------------------- 35 | void archive(bytevec_t *buf, CFTypeRef ref) 36 | { 37 | @autoreleasepool 38 | { 39 | id object = (__bridge id)ref; 40 | NSData *data = [NSKeyedArchiver archivedDataWithRootObject:object]; 41 | const void *bytes = [data bytes]; 42 | int length = [data length]; 43 | append_v(*buf, bytes, length); 44 | } 45 | } 46 | 47 | //----------------------------------------------------------------------------- 48 | CFTypeRef unarchive(const uint8_t *buf, size_t bufsize) 49 | { 50 | @autoreleasepool 51 | { 52 | NSData *data = [NSData dataWithBytesNoCopy:(void *)buf length:bufsize freeWhenDone:false]; 53 | id object = [NSKeyedUnarchiver unarchiveObjectWithData:data]; 54 | return (__bridge CFTypeRef)[object retain]; 55 | } 56 | } 57 | 58 | //----------------------------------------------------------------------------- 59 | CFArrayRef deserialize( 60 | const uint8_t *buf, 61 | size_t bufsize, 62 | string_t *errbuf) 63 | { 64 | if ( bufsize < 16 ) 65 | { 66 | sprnt(errbuf, "Error: buffer of size 0x%lx is too small for a serialized array", bufsize); 67 | return NULL; 68 | } 69 | 70 | uint64_t size = *((uint64_t *)buf+1); 71 | if ( size > bufsize ) 72 | { 73 | sprnt(errbuf, "size of array object (%llx) is larger than total length of data (%lx)", size, bufsize); 74 | return NULL; 75 | } 76 | 77 | CFMutableArrayRef array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 78 | 79 | uint64_t off = sizeof(uint64_t) * 2; 80 | uint64_t end = off + size; 81 | 82 | while ( off < end ) 83 | { 84 | int length = 0; 85 | int type = *((int *)(buf+off)); 86 | off += sizeof(int); 87 | 88 | CFTypeRef ref = NULL; 89 | 90 | switch ( type ) 91 | { 92 | case 2: 93 | // archived object 94 | length = *((int *)(buf+off)); 95 | off += sizeof(int); 96 | ref = unarchive(buf+off, length); 97 | break; 98 | 99 | case 3: 100 | case 5: 101 | // 32-bit int 102 | ref = CFNumberCreate(NULL, kCFNumberSInt32Type, buf+off); 103 | length = 4; 104 | break; 105 | 106 | case 4: 107 | case 6: 108 | // 64-bit int 109 | ref = CFNumberCreate(NULL, kCFNumberSInt64Type, buf+off); 110 | length = 8; 111 | break; 112 | 113 | case 10: 114 | // dictionary key. for arrays, the keys are empty and we ignore them 115 | continue; 116 | 117 | default: 118 | // there are more. we will deal with them as necessary 119 | break; 120 | } 121 | 122 | if ( ref == NULL ) 123 | { 124 | sprnt(errbuf, "invalid object at offset %llx, type: %d\n", off, type); 125 | return NULL; 126 | } 127 | 128 | CFArrayAppendValue(array, ref); 129 | CFRelease(ref); 130 | off += length; 131 | } 132 | 133 | return (CFArrayRef)array; 134 | } 135 | -------------------------------------------------------------------------------- /cftypes.h: -------------------------------------------------------------------------------- 1 | #ifndef CFTYPES_H 2 | #define CFTYPES_H 3 | 4 | #include 5 | #include "common.h" 6 | 7 | //------------------------------------------------------------------------------ 8 | // convert the given CFString to an STL string 9 | string_t to_stlstr(CFStringRef ref); 10 | 11 | //------------------------------------------------------------------------------ 12 | // get a human readable description of the given CF object 13 | string_t get_description(CFTypeRef ref); 14 | 15 | //------------------------------------------------------------------------------ 16 | // serialize a CF object 17 | void archive(bytevec_t *buf, CFTypeRef ref); 18 | 19 | //------------------------------------------------------------------------------ 20 | // deserialize a CF object 21 | CFTypeRef unarchive(const uint8_t *buf, size_t bufsize); 22 | 23 | //------------------------------------------------------------------------------ 24 | // deserialize an array of CF objects 25 | CFArrayRef deserialize(const uint8_t *buf, size_t bufsize, string_t *errbuf = NULL); 26 | 27 | #endif // CFTYPES_H 28 | -------------------------------------------------------------------------------- /common.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | #define MAXSTR 1024 4 | 5 | //----------------------------------------------------------------------------- 6 | void append_v(bytevec_t &out, const void *v, size_t len) 7 | { 8 | const uint8_t *begin = (const uint8_t *)v; 9 | const uint8_t *end = begin + len; 10 | out.insert(out.end(), begin, end); 11 | } 12 | 13 | //----------------------------------------------------------------------------- 14 | void append_d(bytevec_t &out, uint32_t num) 15 | { 16 | append_v(out, &num, sizeof(num)); 17 | } 18 | 19 | //----------------------------------------------------------------------------- 20 | void append_q(bytevec_t &out, uint64_t num) 21 | { 22 | append_v(out, &num, sizeof(num)); 23 | } 24 | 25 | //----------------------------------------------------------------------------- 26 | void append_b(bytevec_t &out, const bytevec_t &bv) 27 | { 28 | append_v(out, bv.data(), bv.size()); 29 | } 30 | 31 | //----------------------------------------------------------------------------- 32 | void vsprnt(string_t *out, const char *format, va_list va) 33 | { 34 | char buf[MAXSTR]; 35 | if ( out != NULL && vsnprintf(buf, sizeof(buf), format, va) > 0 ) 36 | *out = buf; 37 | } 38 | 39 | //----------------------------------------------------------------------------- 40 | void sprnt(string_t *out, const char *format, ...) 41 | { 42 | va_list va; 43 | va_start(va, format); 44 | vsprnt(out, format, va); 45 | va_end(va); 46 | } 47 | -------------------------------------------------------------------------------- /common.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_H 2 | #define COMMON_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | typedef std::string string_t; 11 | typedef std::vector bytevec_t; 12 | 13 | void append_d(bytevec_t &out, uint32_t num); 14 | void append_q(bytevec_t &out, uint64_t num); 15 | void append_b(bytevec_t &out, const bytevec_t &bv); 16 | void append_v(bytevec_t &out, const void *v, size_t len); 17 | 18 | void sprnt(string_t *out, const char *format, ...); 19 | void vsprnt(string_t *out, const char *format, va_list va); 20 | 21 | #endif // COMMON_H 22 | -------------------------------------------------------------------------------- /ios_instruments_client.cpp: -------------------------------------------------------------------------------- 1 | #include "ios_instruments_client.h" 2 | #include "mobile_device.h" 3 | #include 4 | 5 | static string_t device_id; // user's preferred device (-d option) 6 | static bool found_device = false; // has the user's preferred device been detected? 7 | static bool verbose = false; // verbose mode (-v option) 8 | static int cur_message = 0; // current message id 9 | static int cur_channel = 0; // current channel id 10 | static CFDictionaryRef channels = NULL; // list of available channels published by the instruments server 11 | static int pid2kill = -1; // process id to kill ("kill" option) 12 | static const char *bid2launch = NULL; // bundle id of app to launch ("launch" option) 13 | static bool proclist = false; // print the process list ("proclist" option) 14 | static bool applist = false; // print the application list ("applist" option) 15 | static bool ssl_enabled = false; // does the instruments server use SSL? 16 | 17 | //----------------------------------------------------------------------------- 18 | void message_aux_t::append_int(int32_t val) 19 | { 20 | append_d(buf, 10); // empty dictionary key 21 | append_d(buf, 3); // 32-bit int 22 | append_d(buf, val); 23 | } 24 | 25 | //----------------------------------------------------------------------------- 26 | void message_aux_t::append_long(int64_t val) 27 | { 28 | append_d(buf, 10); // empty dictionary key 29 | append_d(buf, 4); // 64-bit int 30 | append_q(buf, val); 31 | } 32 | 33 | //----------------------------------------------------------------------------- 34 | void message_aux_t::append_obj(CFTypeRef obj) 35 | { 36 | append_d(buf, 10); // empty dictionary key 37 | append_d(buf, 2); // archived object 38 | 39 | bytevec_t tmp; 40 | archive(&tmp, obj); 41 | 42 | append_d(buf, tmp.size()); 43 | append_b(buf, tmp); 44 | } 45 | 46 | //----------------------------------------------------------------------------- 47 | void message_aux_t::get_bytes(bytevec_t *out) const 48 | { 49 | if ( !buf.empty() ) 50 | { 51 | // the final serialized array must start with a magic qword, 52 | // followed by the total length of the array data as a qword, 53 | // followed by the array data itself. 54 | append_q(*out, 0x1F0); 55 | append_q(*out, buf.size()); 56 | append_b(*out, buf); 57 | } 58 | } 59 | 60 | //----------------------------------------------------------------------------- 61 | // callback that handles device notifications. called once for each connected device. 62 | static void device_callback(am_device_notification_callback_info *cbi, void *arg) 63 | { 64 | if ( cbi->code != ADNCI_MSG_CONNECTED ) 65 | return; 66 | 67 | CFStringRef id = MobileDevice.AMDeviceCopyDeviceIdentifier(cbi->dev); 68 | string_t _device_id = to_stlstr(id); 69 | CFRelease(id); 70 | 71 | if ( !device_id.empty() && device_id != _device_id ) 72 | return; 73 | 74 | found_device = true; 75 | 76 | if ( verbose ) 77 | printf("found device: %s\n", _device_id.c_str()); 78 | 79 | do 80 | { 81 | // start a session on the device 82 | if ( MobileDevice.AMDeviceConnect(cbi->dev) != kAMDSuccess 83 | || MobileDevice.AMDeviceIsPaired(cbi->dev) == 0 84 | || MobileDevice.AMDeviceValidatePairing(cbi->dev) != kAMDSuccess 85 | || MobileDevice.AMDeviceStartSession(cbi->dev) != kAMDSuccess ) 86 | { 87 | fprintf(stderr, "Error: failed to start a session on the device\n"); 88 | break; 89 | } 90 | 91 | am_device_service_connection **connptr = (am_device_service_connection **)arg; 92 | 93 | // launch the instruments server 94 | mach_error_t err = MobileDevice.AMDeviceSecureStartService( 95 | cbi->dev, 96 | CFSTR("com.apple.instruments.remoteserver"), 97 | NULL, 98 | connptr); 99 | 100 | if ( err != kAMDSuccess ) 101 | { 102 | // try again with an SSL-enabled service, commonly used after iOS 14 103 | err = MobileDevice.AMDeviceSecureStartService( 104 | cbi->dev, 105 | CFSTR("com.apple.instruments.remoteserver.DVTSecureSocketProxy"), 106 | NULL, 107 | connptr); 108 | 109 | if ( err != kAMDSuccess ) 110 | { 111 | fprintf(stderr, "Failed to start the instruments server (0x%x). " 112 | "Perhaps DeveloperDiskImage.dmg is not installed on the device?\n", err); 113 | break; 114 | } 115 | 116 | ssl_enabled = true; 117 | } 118 | 119 | if ( verbose ) 120 | printf("successfully launched instruments server\n"); 121 | } 122 | while ( false ); 123 | 124 | MobileDevice.AMDeviceStopSession(cbi->dev); 125 | MobileDevice.AMDeviceDisconnect(cbi->dev); 126 | 127 | CFRunLoopStop(CFRunLoopGetCurrent()); 128 | } 129 | 130 | //----------------------------------------------------------------------------- 131 | // launch the instruments server on the user's device. 132 | // returns a handle that can be used to send/receive data to/from the server. 133 | static am_device_service_connection *start_server(void) 134 | { 135 | am_device_notification *notify_handle = NULL; 136 | am_device_service_connection *conn = NULL; 137 | 138 | mach_error_t err = MobileDevice.AMDeviceNotificationSubscribe( 139 | device_callback, 140 | 0, 141 | 0, 142 | &conn, 143 | ¬ify_handle); 144 | 145 | if ( err != kAMDSuccess ) 146 | { 147 | fprintf(stderr, "failed to register device notifier: 0x%x\n", err); 148 | return NULL; 149 | } 150 | 151 | // start a run loop, and wait for the device notifier to call our callback function. 152 | // if no device was detected within 3 seconds, we bail out. 153 | CFRunLoopRunInMode(kCFRunLoopDefaultMode, 3, false); 154 | 155 | MobileDevice.AMDeviceNotificationUnsubscribe(notify_handle); 156 | 157 | if ( conn == NULL && !found_device ) 158 | { 159 | if ( device_id.empty() ) 160 | fprintf(stderr, "Failed to find a connected device\n"); 161 | else 162 | fprintf(stderr, "Failed to find device with id = %s\n", device_id.c_str()); 163 | return NULL; 164 | } 165 | 166 | return conn; 167 | } 168 | 169 | //----------------------------------------------------------------------------- 170 | // "call" an Objective-C method in the instruments server process 171 | // conn server handle 172 | // channel determines the object that will receive the message, 173 | // obtained by a previous call to make_channel() 174 | // selector method name 175 | // args serialized list of arguments for the method 176 | // expects_reply do we expect a return value from the method? 177 | // the return value can be obtained by a subsequent call to recv_message() 178 | static bool send_message( 179 | am_device_service_connection *conn, 180 | int channel, 181 | CFStringRef selector, 182 | const message_aux_t *args, 183 | bool expects_reply = true) 184 | { 185 | uint32_t id = ++cur_message; 186 | 187 | bytevec_t aux; 188 | if ( args != NULL ) 189 | args->get_bytes(&aux); 190 | 191 | bytevec_t sel; 192 | if ( selector != NULL ) 193 | archive(&sel, selector); 194 | 195 | DTXMessagePayloadHeader pheader; 196 | // the low byte of the payload flags represents the message type. 197 | // so far it seems that all requests to the instruments server have message type 2. 198 | pheader.flags = 0x2 | (expects_reply ? 0x1000 : 0); 199 | pheader.auxiliaryLength = aux.size(); 200 | pheader.totalLength = aux.size() + sel.size(); 201 | 202 | DTXMessageHeader mheader; 203 | mheader.magic = 0x1F3D5B79; 204 | mheader.cb = sizeof(DTXMessageHeader); 205 | mheader.fragmentId = 0; 206 | mheader.fragmentCount = 1; 207 | mheader.length = sizeof(pheader) + pheader.totalLength; 208 | mheader.identifier = id; 209 | mheader.conversationIndex = 0; 210 | mheader.channelCode = channel; 211 | mheader.expectsReply = (expects_reply ? 1 : 0); 212 | 213 | bytevec_t msg; 214 | append_v(msg, &mheader, sizeof(mheader)); 215 | append_v(msg, &pheader, sizeof(pheader)); 216 | append_b(msg, aux); 217 | append_b(msg, sel); 218 | 219 | size_t msglen = msg.size(); 220 | ssize_t nsent = ssl_enabled 221 | ? MobileDevice.AMDServiceConnectionSend(conn, msg.data(), msglen) 222 | : write(MobileDevice.AMDServiceConnectionGetSocket(conn), msg.data(), msglen); 223 | if ( nsent != msglen ) 224 | { 225 | fprintf(stderr, "Failed to send 0x%lx bytes of message: %s\n", msglen, strerror(errno)); 226 | return false; 227 | } 228 | 229 | return true; 230 | } 231 | 232 | //----------------------------------------------------------------------------- 233 | // handle a response from the server. 234 | // conn server handle 235 | // retobj contains the return value for the method invoked by send_message() 236 | // aux usually empty, except in specific situations (see _notifyOfPublishedCapabilities) 237 | static bool recv_message( 238 | am_device_service_connection *conn, 239 | CFTypeRef *retobj, 240 | CFArrayRef *aux) 241 | { 242 | uint32_t id = 0; 243 | bytevec_t payload; 244 | 245 | int sock = MobileDevice.AMDServiceConnectionGetSocket(conn); 246 | 247 | while ( true ) 248 | { 249 | DTXMessageHeader mheader; 250 | ssize_t nrecv = ssl_enabled 251 | ? MobileDevice.AMDServiceConnectionReceive(conn, &mheader, sizeof(mheader)) 252 | : read(sock, &mheader, sizeof(mheader)); 253 | if ( nrecv != sizeof(mheader) ) 254 | { 255 | fprintf(stderr, "failed to read message header: %s, nrecv = %lx\n", strerror(errno), nrecv); 256 | return false; 257 | } 258 | 259 | if ( mheader.magic != 0x1F3D5B79 ) 260 | { 261 | fprintf(stderr, "bad header magic: %x\n", mheader.magic); 262 | return false; 263 | } 264 | 265 | if ( mheader.conversationIndex == 1 ) 266 | { 267 | // the message is a response to a previous request, so it should have the same id as the request 268 | if ( mheader.identifier != cur_message ) 269 | { 270 | fprintf(stderr, "expected response to message id=%d, got a new message with id=%d\n", cur_message, mheader.identifier); 271 | return false; 272 | } 273 | } 274 | else if ( mheader.conversationIndex == 0 ) 275 | { 276 | // the message is not a response to a previous request. in this case, different iOS versions produce different results. 277 | // on iOS 9, the incoming message can have the same message ID has the previous message we sent to the server. 278 | // on later versions, the incoming message will have a new message ID. we must be aware of both situations. 279 | if ( mheader.identifier > cur_message ) 280 | { 281 | // new message id, we must update the count on our side 282 | cur_message = mheader.identifier; 283 | } 284 | else if ( mheader.identifier < cur_message ) 285 | { 286 | // the id must match the previous request, anything else doesn't really make sense 287 | fprintf(stderr, "unexpected message ID: %d\n", mheader.identifier); 288 | return false; 289 | } 290 | } 291 | else 292 | { 293 | fprintf(stderr, "invalid conversation index: %d\n", mheader.conversationIndex); 294 | return false; 295 | } 296 | 297 | if ( mheader.fragmentId == 0 ) 298 | { 299 | id = mheader.identifier; 300 | // when reading multiple message fragments, the 0th fragment contains only a message header 301 | if ( mheader.fragmentCount > 1 ) 302 | continue; 303 | } 304 | 305 | // read the entire payload in the current fragment 306 | bytevec_t frag; 307 | append_v(frag, &mheader, sizeof(mheader)); 308 | frag.resize(frag.size() + mheader.length); 309 | 310 | uint8_t *data = frag.data() + sizeof(mheader); 311 | 312 | uint32_t nbytes = 0; 313 | while ( nbytes < mheader.length ) 314 | { 315 | uint8_t *curptr = data + nbytes; 316 | size_t curlen = mheader.length - nbytes; 317 | nrecv = ssl_enabled 318 | ? MobileDevice.AMDServiceConnectionReceive(conn, curptr, curlen) 319 | : read(sock, curptr, curlen); 320 | if ( nrecv <= 0 ) 321 | { 322 | fprintf(stderr, "failed reading from socket: %s\n", strerror(errno)); 323 | return false; 324 | } 325 | nbytes += nrecv; 326 | } 327 | 328 | // append to the incremental payload 329 | append_v(payload, data, mheader.length); 330 | 331 | // done reading message fragments? 332 | if ( mheader.fragmentId == mheader.fragmentCount - 1 ) 333 | break; 334 | } 335 | 336 | const DTXMessagePayloadHeader *pheader = (const DTXMessagePayloadHeader *)payload.data(); 337 | 338 | // we don't know how to decompress messages yet 339 | uint8_t compression = (pheader->flags & 0xFF000) >> 12; 340 | if ( compression != 0 ) 341 | { 342 | fprintf(stderr, "message is compressed (compression type %d)\n", compression); 343 | return false; 344 | } 345 | 346 | // serialized object array is located just after payload header 347 | const uint8_t *auxptr = payload.data() + sizeof(DTXMessagePayloadHeader); 348 | uint32_t auxlen = pheader->auxiliaryLength; 349 | 350 | // archived payload object appears after the auxiliary array 351 | const uint8_t *objptr = auxptr + auxlen; 352 | uint64_t objlen = pheader->totalLength - auxlen; 353 | 354 | if ( auxlen != 0 && aux != NULL ) 355 | { 356 | string_t errbuf; 357 | CFArrayRef _aux = deserialize(auxptr, auxlen, &errbuf); 358 | if ( _aux == NULL ) 359 | { 360 | fprintf(stderr, "Error: %s\n", errbuf.c_str()); 361 | return false; 362 | } 363 | *aux = _aux; 364 | } 365 | 366 | if ( objlen != 0 && retobj != NULL ) 367 | *retobj = unarchive(objptr, objlen); 368 | 369 | return true; 370 | } 371 | 372 | //----------------------------------------------------------------------------- 373 | // perform the initial client-server handshake. 374 | // here we retrieve the list of available channels published by the instruments server. 375 | // we can open a given channel with make_channel(). 376 | static bool perform_handshake(am_device_service_connection *conn) 377 | { 378 | // I'm not sure if this argument is necessary - but Xcode uses it, so I'm using it too. 379 | CFMutableDictionaryRef capabilities = CFDictionaryCreateMutable(NULL, 0, NULL, NULL); 380 | 381 | int64_t _v1 = 1; 382 | int64_t _v2 = 2; 383 | 384 | CFNumberRef v1 = CFNumberCreate(NULL, kCFNumberSInt64Type, &_v1); 385 | CFNumberRef v2 = CFNumberCreate(NULL, kCFNumberSInt64Type, &_v2); 386 | 387 | CFDictionaryAddValue(capabilities, CFSTR("com.apple.private.DTXBlockCompression"), v2); 388 | CFDictionaryAddValue(capabilities, CFSTR("com.apple.private.DTXConnection"), v1); 389 | 390 | // serialize the dictionary 391 | message_aux_t args; 392 | args.append_obj(capabilities); 393 | 394 | CFRelease(capabilities); 395 | CFRelease(v1); 396 | CFRelease(v2); 397 | 398 | if ( !send_message(conn, 0, CFSTR("_notifyOfPublishedCapabilities:"), &args, false) ) 399 | return false; 400 | 401 | CFTypeRef obj = NULL; 402 | CFArrayRef aux = NULL; 403 | 404 | // we are now expecting the server to reply with the same message. 405 | // a description of all available channels will be provided in the arguments list. 406 | if ( !recv_message(conn, &obj, &aux) || obj == NULL || aux == NULL ) 407 | { 408 | fprintf(stderr, "Error: failed to receive response from _notifyOfPublishedCapabilities:\n"); 409 | return false; 410 | } 411 | 412 | bool ok = false; 413 | do 414 | { 415 | if ( CFGetTypeID(obj) != CFStringGetTypeID() 416 | || to_stlstr((CFStringRef)obj) != "_notifyOfPublishedCapabilities:" ) 417 | { 418 | fprintf(stderr, "Error: unexpected message selector: %s\n", get_description(obj).c_str()); 419 | break; 420 | } 421 | 422 | CFDictionaryRef _channels; 423 | 424 | // extract the channel list from the arguments 425 | if ( CFArrayGetCount(aux) != 1 426 | || (_channels = (CFDictionaryRef)CFArrayGetValueAtIndex(aux, 0)) == NULL 427 | || CFGetTypeID(_channels) != CFDictionaryGetTypeID() 428 | || CFDictionaryGetCount(_channels) == 0 ) 429 | { 430 | fprintf(stderr, "channel list has an unexpected format:\n%s\n", get_description(aux).c_str()); 431 | break; 432 | } 433 | 434 | channels = (CFDictionaryRef)CFRetain(_channels); 435 | 436 | if ( verbose ) 437 | printf("channel list:\n%s\n", get_description(channels).c_str()); 438 | 439 | ok = true; 440 | } 441 | while ( false ); 442 | 443 | CFRelease(obj); 444 | CFRelease(aux); 445 | 446 | return ok; 447 | } 448 | 449 | //----------------------------------------------------------------------------- 450 | // establish a connection to a service in the instruments server process. 451 | // the channel identifier should be in the list of channels returned by the server 452 | // in perform_handshake(). after a channel is established, you can use send_message() 453 | // to remotely invoke Objective-C methods. 454 | static int make_channel(am_device_service_connection *conn, CFStringRef identifier) 455 | { 456 | if ( !CFDictionaryContainsKey(channels, identifier) ) 457 | { 458 | fprintf(stderr, "channel %s is not supported by the server\n", to_stlstr(identifier).c_str()); 459 | return -1; 460 | } 461 | 462 | int code = ++cur_channel; 463 | 464 | message_aux_t args; 465 | args.append_int(code); 466 | args.append_obj(identifier); 467 | 468 | CFTypeRef retobj = NULL; 469 | 470 | // request to open the channel, expect an empty reply 471 | if ( !send_message(conn, 0, CFSTR("_requestChannelWithCode:identifier:"), &args) 472 | || !recv_message(conn, &retobj, NULL) ) 473 | { 474 | return -1; 475 | } 476 | 477 | if ( retobj != NULL ) 478 | { 479 | fprintf(stderr, "Error: _requestChannelWithCode:identifier: returned %s\n", get_description(retobj).c_str()); 480 | CFRelease(retobj); 481 | return -1; 482 | } 483 | 484 | return code; 485 | } 486 | 487 | //----------------------------------------------------------------------------- 488 | // invoke method -[DTDeviceInfoService runningProcesses] 489 | // args: none 490 | // returns: CFArrayRef procs 491 | static bool print_proclist(am_device_service_connection *conn) 492 | { 493 | int channel = make_channel(conn, CFSTR("com.apple.instruments.server.services.deviceinfo")); 494 | if ( channel < 0 ) 495 | return false; 496 | 497 | CFTypeRef retobj = NULL; 498 | 499 | if ( !send_message(conn, channel, CFSTR("runningProcesses"), NULL) 500 | || !recv_message(conn, &retobj, NULL) 501 | || retobj == NULL ) 502 | { 503 | fprintf(stderr, "Error: failed to retrieve return value for runningProcesses\n"); 504 | return false; 505 | } 506 | 507 | bool ok = true; 508 | if ( CFGetTypeID(retobj) == CFArrayGetTypeID() ) 509 | { 510 | CFArrayRef array = (CFArrayRef)retobj; 511 | 512 | printf("proclist:\n"); 513 | for ( size_t i = 0, size = CFArrayGetCount(array); i < size; i++ ) 514 | { 515 | CFDictionaryRef dict = (CFDictionaryRef)CFArrayGetValueAtIndex(array, i); 516 | 517 | CFStringRef _name = (CFStringRef)CFDictionaryGetValue(dict, CFSTR("name")); 518 | string_t name = to_stlstr(_name); 519 | 520 | CFNumberRef _pid = (CFNumberRef)CFDictionaryGetValue(dict, CFSTR("pid")); 521 | int pid = 0; 522 | CFNumberGetValue(_pid, kCFNumberSInt32Type, &pid); 523 | 524 | printf("%6d %s\n", pid, name.c_str()); 525 | } 526 | } 527 | else 528 | { 529 | fprintf(stderr, "Error: process list is not in the expected format: %s\n", get_description(retobj).c_str()); 530 | ok = false; 531 | } 532 | 533 | CFRelease(retobj); 534 | return ok; 535 | } 536 | 537 | //----------------------------------------------------------------------------- 538 | // invoke method -[DTApplicationListingService installedApplicationsMatching:registerUpdateToken:] 539 | // args: CFDictionaryRef dict 540 | // CFStringRef token 541 | // returns CFArrayRef apps 542 | static bool print_applist(am_device_service_connection *conn) 543 | { 544 | int channel = make_channel(conn, CFSTR("com.apple.instruments.server.services.device.applictionListing")); 545 | if ( channel < 0 ) 546 | return false; 547 | 548 | // the method expects a dictionary and a string argument. 549 | // pass empty values so we get descriptions for all known applications. 550 | CFDictionaryRef dict = CFDictionaryCreate(NULL, NULL, NULL, 0, NULL, NULL); 551 | 552 | message_aux_t args; 553 | args.append_obj(dict); 554 | args.append_obj(CFSTR("")); 555 | 556 | CFRelease(dict); 557 | 558 | CFTypeRef retobj = NULL; 559 | 560 | if ( !send_message(conn, channel, CFSTR("installedApplicationsMatching:registerUpdateToken:"), &args) 561 | || !recv_message(conn, &retobj, NULL) 562 | || retobj == NULL ) 563 | { 564 | fprintf(stderr, "Error: failed to retrieve applist\n"); 565 | return false; 566 | } 567 | 568 | bool ok = true; 569 | if ( CFGetTypeID(retobj) == CFArrayGetTypeID() ) 570 | { 571 | CFArrayRef array = (CFArrayRef)retobj; 572 | for ( size_t i = 0, size = CFArrayGetCount(array); i < size; i++ ) 573 | { 574 | CFDictionaryRef app_desc = (CFDictionaryRef)CFArrayGetValueAtIndex(array, i); 575 | printf("%s\n", get_description(app_desc).c_str()); 576 | } 577 | } 578 | else 579 | { 580 | fprintf(stderr, "apps list has an unexpected format: %s\n", get_description(retobj).c_str()); 581 | ok = false; 582 | } 583 | 584 | CFRelease(retobj); 585 | return ok; 586 | } 587 | 588 | //----------------------------------------------------------------------------- 589 | // invoke method -[DTProcessControlService killPid:] 590 | // args: CFNumberRef process_id 591 | // returns: void 592 | static bool kill(am_device_service_connection *conn, int pid) 593 | { 594 | int channel = make_channel(conn, CFSTR("com.apple.instruments.server.services.processcontrol")); 595 | if ( channel < 0 ) 596 | return false; 597 | 598 | CFNumberRef _pid = CFNumberCreate(NULL, kCFNumberSInt32Type, &pid); 599 | 600 | message_aux_t args; 601 | args.append_obj(_pid); 602 | 603 | CFRelease(_pid); 604 | 605 | return send_message(conn, channel, CFSTR("killPid:"), &args, false); 606 | } 607 | 608 | //----------------------------------------------------------------------------- 609 | // invoke method -[DTProcessControlService launchSuspendedProcessWithDevicePath:bundleIdentifier:environment:arguments:] 610 | // args: CFStringRef app_path 611 | // CFStringRef bundle_id 612 | // CFArrayRef args_for_app 613 | // CFDictionaryRef environment_vars 614 | // CFDictionaryRef launch_options 615 | // returns: CFNumberRef pid 616 | static bool launch(am_device_service_connection *conn, const char *_bid) 617 | { 618 | int channel = make_channel(conn, CFSTR("com.apple.instruments.server.services.processcontrol")); 619 | if ( channel < 0 ) 620 | return false; 621 | 622 | // app path: not used, just pass empty string 623 | CFStringRef path = CFStringCreateWithCString(NULL, "", kCFStringEncodingUTF8); 624 | // bundle id 625 | CFStringRef bid = CFStringCreateWithCString(NULL, _bid, kCFStringEncodingUTF8); 626 | // args for app: not used, just pass empty array 627 | CFArrayRef appargs = CFArrayCreate(NULL, NULL, 0, NULL); 628 | // environment variables: not used, just pass empty dictionary 629 | CFDictionaryRef env = CFDictionaryCreate(NULL, NULL, NULL, 0, NULL, NULL); 630 | 631 | // launch options 632 | int _v0 = 0; // don't suspend the process after starting it 633 | int _v1 = 1; // kill the application if it is already running 634 | 635 | CFNumberRef v0 = CFNumberCreate(NULL, kCFNumberSInt32Type, &_v0); 636 | CFNumberRef v1 = CFNumberCreate(NULL, kCFNumberSInt32Type, &_v1); 637 | 638 | const void *keys[] = 639 | { 640 | CFSTR("StartSuspendedKey"), 641 | CFSTR("KillExisting") 642 | }; 643 | const void *values[] = { v0, v1 }; 644 | CFDictionaryRef options = CFDictionaryCreate( 645 | NULL, 646 | keys, 647 | values, 648 | 2, 649 | NULL, 650 | NULL); 651 | 652 | message_aux_t args; 653 | args.append_obj(path); 654 | args.append_obj(bid); 655 | args.append_obj(env); 656 | args.append_obj(appargs); 657 | args.append_obj(options); 658 | 659 | CFRelease(v1); 660 | CFRelease(v0); 661 | CFRelease(options); 662 | CFRelease(env); 663 | CFRelease(appargs); 664 | CFRelease(bid); 665 | CFRelease(path); 666 | 667 | CFTypeRef retobj = NULL; 668 | 669 | if ( !send_message(conn, channel, CFSTR("launchSuspendedProcessWithDevicePath:bundleIdentifier:environment:arguments:options:"), &args) 670 | || !recv_message(conn, &retobj, NULL) 671 | || retobj == NULL ) 672 | { 673 | fprintf(stderr, "Error: failed to launch %s\n", _bid); 674 | return false; 675 | } 676 | 677 | bool ok = true; 678 | if ( CFGetTypeID(retobj) == CFNumberGetTypeID() ) 679 | { 680 | CFNumberRef _pid = (CFNumberRef)retobj; 681 | int pid = 0; 682 | CFNumberGetValue(_pid, kCFNumberSInt32Type, &pid); 683 | printf("pid: %d\n", pid); 684 | } 685 | else 686 | { 687 | fprintf(stderr, "failed to retrieve the process ID: %s\n", get_description(retobj).c_str()); 688 | ok = false; 689 | } 690 | 691 | CFRelease(retobj); 692 | return ok; 693 | } 694 | 695 | //----------------------------------------------------------------------------- 696 | static void usage(const char *prog) 697 | { 698 | fprintf(stderr, "usage: %s [-v] [-d ] TASK \n" 699 | "\n" 700 | "This is a sample client application for the iOS Instruments server.\n" 701 | "It is capable of rudimentary communication with the server and can\n" 702 | "ask it to perform some interesting tasks.\n" 703 | "\n" 704 | "TASK can be one of the following:\n" 705 | " proclist - print a list of running processes\n" 706 | " applist - print a list of installed applications\n" 707 | " launch - launch a given app. provide the bundle id of the app to launch\n" 708 | " kill - kill a given process. provide the pid of the process to kill\n" 709 | "\n" 710 | "other args:\n" 711 | " -v more verbose output\n" 712 | " -d device ID. if empty, this app will use the first device it finds\n", prog); 713 | } 714 | 715 | //----------------------------------------------------------------------------- 716 | static bool parse_args(int argc, const char **argv) 717 | { 718 | if ( argc > 1 ) 719 | { 720 | for ( int i = 1; i < argc; ) 721 | { 722 | if ( strcmp("-v", argv[i]) == 0 ) 723 | { 724 | verbose = true; 725 | i++; 726 | continue; 727 | } 728 | else if ( strcmp("-d", argv[i]) == 0 ) 729 | { 730 | if ( i == argc - 1 ) 731 | { 732 | fprintf(stderr, "Error: -d option requires a device id string\n"); 733 | break; 734 | } 735 | device_id = argv[i+1]; 736 | i += 2; 737 | continue; 738 | } 739 | 740 | string_t task = argv[i]; 741 | 742 | if ( task == "proclist" ) 743 | { 744 | proclist = true; 745 | return true; 746 | } 747 | else if ( task == "applist" ) 748 | { 749 | applist = true; 750 | return true; 751 | } 752 | else if ( task == "kill" ) 753 | { 754 | if ( i == argc - 1 ) 755 | { 756 | fprintf(stderr, "Error: \"kill\" requires a process id\n"); 757 | break; 758 | } 759 | pid2kill = atoi(argv[i+1]); 760 | return true; 761 | } 762 | else if ( task == "launch" ) 763 | { 764 | if ( i == argc - 1 ) 765 | { 766 | fprintf(stderr, "Error: \"launch\" requires a bundle id\n"); 767 | break; 768 | } 769 | bid2launch = argv[i+1]; 770 | return true; 771 | } 772 | 773 | fprintf(stderr, "Error, invalid task: %s\n", task.c_str()); 774 | break; 775 | } 776 | } 777 | 778 | usage(argv[0]); 779 | return false; 780 | } 781 | 782 | //----------------------------------------------------------------------------- 783 | int main(int argc, const char **argv) 784 | { 785 | if ( !parse_args(argc, argv) ) 786 | return EXIT_FAILURE; 787 | 788 | if ( !MobileDevice.load() ) 789 | return EXIT_FAILURE; 790 | 791 | am_device_service_connection *conn = start_server(); 792 | if ( conn == NULL ) 793 | return EXIT_FAILURE; 794 | 795 | bool ok = false; 796 | if ( perform_handshake(conn) ) 797 | { 798 | if ( proclist ) 799 | ok = print_proclist(conn); 800 | else if ( applist ) 801 | ok = print_applist(conn); 802 | else if ( pid2kill > 0 ) 803 | ok = kill(conn, pid2kill); 804 | else if ( bid2launch != NULL ) 805 | ok = launch(conn, bid2launch); 806 | else 807 | ok = true; 808 | 809 | CFRelease(channels); 810 | } 811 | 812 | MobileDevice.AMDServiceConnectionInvalidate(conn); 813 | CFRelease(conn); 814 | 815 | return ok ? EXIT_SUCCESS : EXIT_FAILURE; 816 | } 817 | -------------------------------------------------------------------------------- /ios_instruments_client.h: -------------------------------------------------------------------------------- 1 | #ifndef IOS_INSTRUMENTS_CLIENT 2 | #define IOS_INSTRUMENTS_CLIENT 3 | 4 | #include "cftypes.h" 5 | 6 | //----------------------------------------------------------------------------- 7 | struct DTXMessageHeader 8 | { 9 | uint32_t magic; 10 | uint32_t cb; 11 | uint16_t fragmentId; 12 | uint16_t fragmentCount; 13 | uint32_t length; 14 | uint32_t identifier; 15 | uint32_t conversationIndex; 16 | uint32_t channelCode; 17 | uint32_t expectsReply; 18 | }; 19 | 20 | //----------------------------------------------------------------------------- 21 | struct DTXMessagePayloadHeader 22 | { 23 | uint32_t flags; 24 | uint32_t auxiliaryLength; 25 | uint64_t totalLength; 26 | }; 27 | 28 | //------------------------------------------------------------------------------ 29 | // helper class for serializing method arguments 30 | class message_aux_t 31 | { 32 | bytevec_t buf; 33 | 34 | public: 35 | void append_int(int32_t val); 36 | void append_long(int64_t val); 37 | void append_obj(CFTypeRef obj); 38 | 39 | void get_bytes(bytevec_t *out) const; 40 | }; 41 | 42 | #endif // IOS_INSTRUMENTS_CLIENT 43 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | T = ios_instruments_client 2 | O = obj 3 | all: $(T) 4 | .PHONY: all clean 5 | 6 | CFLAGS = -g -O0 -mmacosx-version-min=10.9 7 | 8 | ifeq ($(wildcard $(O)/.),) 9 | $(shell mkdir -p 2>/dev/null $(O)) 10 | endif 11 | 12 | MAIN = $(O)/$(T).o 13 | COMMON = $(O)/common.o 14 | CFTYPES = $(O)/cftypes.o 15 | MD = $(O)/mobile_device.o 16 | 17 | $(T): $(MAIN) $(COMMON) $(CFTYPES) $(MD) 18 | g++ $(CFLAGS) -o $@ $^ -lobjc -framework Foundation -framework CoreFoundation 19 | 20 | $(MAIN): $(T).cpp $(T).h cftypes.h common.h mobile_device.h 21 | g++ $(CFLAGS) -c -o $@ $< 22 | 23 | $(COMMON): common.cpp common.h 24 | g++ $(CFLAGS) -c -o $@ $< 25 | 26 | $(CFTYPES): cftypes.cpp cftypes.h common.h 27 | g++ $(CFLAGS) -c -o $@ -x objective-c++ $< 28 | 29 | $(MD): mobile_device.cpp mobile_device.h cftypes.h common.h 30 | g++ $(CFLAGS) -c -o $@ $< 31 | 32 | clean: 33 | rm -rf $(T) $(T).dSYM $(O)/* 34 | -------------------------------------------------------------------------------- /mobile_device.cpp: -------------------------------------------------------------------------------- 1 | #include "mobile_device.h" 2 | #include 3 | #include 4 | 5 | mobile_device_lib_t MobileDevice; 6 | 7 | //------------------------------------------------------------------------------ 8 | bool mobile_device_lib_t::load(void) 9 | { 10 | if ( dhandle != NULL ) 11 | return true; 12 | 13 | const char *md_path = "/System/Library/PrivateFrameworks/MobileDevice.framework/MobileDevice"; 14 | dhandle = dlopen(md_path, RTLD_NOW); 15 | if ( dhandle == NULL ) 16 | { 17 | fprintf(stderr, "dlopen() failed for %s: %s", md_path, dlerror()); 18 | return false; 19 | } 20 | 21 | #define BINDFUN(name, type) \ 22 | _##name = reinterpret_cast(dlsym(dhandle, #name)); \ 23 | if ( _##name == NULL ) \ 24 | { \ 25 | unload(); \ 26 | fprintf(stderr, "Could not find function " #name " in %s", md_path); \ 27 | return false; \ 28 | } 29 | 30 | BINDFUN(AMDeviceNotificationSubscribe, mach_error_t (*)(am_device_notification_callback_t *, int, int, void *, am_device_notification **)); 31 | BINDFUN(AMDeviceNotificationUnsubscribe, mach_error_t (*)(am_device_notification *)); 32 | BINDFUN(AMDeviceCopyDeviceIdentifier, CFStringRef (*)(am_device *)); 33 | BINDFUN(AMDeviceConnect, mach_error_t (*)(am_device *)); 34 | BINDFUN(AMDeviceIsPaired, int (*)(am_device *)); 35 | BINDFUN(AMDeviceValidatePairing, mach_error_t (*)(am_device *)); 36 | BINDFUN(AMDeviceStartSession, mach_error_t (*)(am_device *)); 37 | BINDFUN(AMDeviceStopSession, mach_error_t (*)(am_device *)); 38 | BINDFUN(AMDeviceDisconnect, mach_error_t (*)(am_device *)); 39 | BINDFUN(AMDeviceSecureStartService, mach_error_t (*)(am_device *, CFStringRef, int *, am_device_service_connection **)); 40 | BINDFUN(AMDServiceConnectionInvalidate, void (*)(am_device_service_connection *)); 41 | BINDFUN(AMDServiceConnectionGetSocket, int (*)(am_device_service_connection *)); 42 | BINDFUN(AMDServiceConnectionSend, ssize_t (*)(am_device_service_connection *, const void *, size_t)); 43 | BINDFUN(AMDServiceConnectionReceive, ssize_t (*)(am_device_service_connection *, void *, size_t)); 44 | 45 | #undef BINDFUN 46 | 47 | return true; 48 | } 49 | 50 | //------------------------------------------------------------------------------ 51 | void mobile_device_lib_t::unload() 52 | { 53 | if ( dhandle != NULL ) 54 | { 55 | dlclose(dhandle); 56 | reset(); 57 | } 58 | } 59 | 60 | //------------------------------------------------------------------------------ 61 | mach_error_t mobile_device_lib_t::AMDeviceNotificationSubscribe( 62 | am_device_notification_callback_t *callback, 63 | int unused1, 64 | int unused2, 65 | void *arg, 66 | am_device_notification **hptr) const 67 | { 68 | return _AMDeviceNotificationSubscribe(callback, unused1, unused2, arg, hptr); 69 | } 70 | 71 | //------------------------------------------------------------------------------ 72 | mach_error_t mobile_device_lib_t::AMDeviceNotificationUnsubscribe(am_device_notification *handle) const 73 | { 74 | return _AMDeviceNotificationUnsubscribe(handle); 75 | } 76 | 77 | //------------------------------------------------------------------------------ 78 | CFStringRef mobile_device_lib_t::AMDeviceCopyDeviceIdentifier(am_device *device) const 79 | { 80 | return _AMDeviceCopyDeviceIdentifier(device); 81 | } 82 | 83 | //------------------------------------------------------------------------------ 84 | mach_error_t mobile_device_lib_t::AMDeviceConnect(am_device *device) const 85 | { 86 | return _AMDeviceConnect(device); 87 | } 88 | 89 | //------------------------------------------------------------------------------ 90 | int mobile_device_lib_t::AMDeviceIsPaired(am_device *device) const 91 | { 92 | return _AMDeviceIsPaired(device); 93 | } 94 | 95 | //------------------------------------------------------------------------------ 96 | mach_error_t mobile_device_lib_t::AMDeviceValidatePairing(am_device *device) const 97 | { 98 | return _AMDeviceValidatePairing(device); 99 | } 100 | 101 | //------------------------------------------------------------------------------ 102 | mach_error_t mobile_device_lib_t::AMDeviceStartSession(am_device *device) const 103 | { 104 | return _AMDeviceStartSession(device); 105 | } 106 | 107 | //------------------------------------------------------------------------------ 108 | mach_error_t mobile_device_lib_t::AMDeviceStopSession(am_device *device) const 109 | { 110 | return _AMDeviceStopSession(device); 111 | } 112 | 113 | //------------------------------------------------------------------------------ 114 | mach_error_t mobile_device_lib_t::AMDeviceDisconnect(am_device *device) const 115 | { 116 | return _AMDeviceDisconnect(device); 117 | } 118 | 119 | //------------------------------------------------------------------------------ 120 | mach_error_t mobile_device_lib_t::AMDeviceSecureStartService( 121 | am_device *device, 122 | CFStringRef name, 123 | int *unused, 124 | am_device_service_connection **hptr) const 125 | { 126 | return _AMDeviceSecureStartService(device, name, unused, hptr); 127 | } 128 | 129 | //------------------------------------------------------------------------------ 130 | void mobile_device_lib_t::AMDServiceConnectionInvalidate(am_device_service_connection *handle) const 131 | { 132 | _AMDServiceConnectionInvalidate(handle); 133 | } 134 | 135 | //------------------------------------------------------------------------------ 136 | int mobile_device_lib_t::AMDServiceConnectionGetSocket(am_device_service_connection *handle) const 137 | { 138 | return _AMDServiceConnectionGetSocket(handle); 139 | } 140 | 141 | //------------------------------------------------------------------------------ 142 | ssize_t mobile_device_lib_t::AMDServiceConnectionSend( 143 | am_device_service_connection *handle, 144 | const void *buf, 145 | size_t len) const 146 | { 147 | return _AMDServiceConnectionSend(handle, buf, len); 148 | } 149 | 150 | //------------------------------------------------------------------------------ 151 | ssize_t mobile_device_lib_t::AMDServiceConnectionReceive( 152 | am_device_service_connection *handle, 153 | void *buf, 154 | size_t size) const 155 | { 156 | return _AMDServiceConnectionReceive(handle, buf, size); 157 | } 158 | -------------------------------------------------------------------------------- /mobile_device.h: -------------------------------------------------------------------------------- 1 | #ifndef MOBILE_DEVICE_H 2 | #define MOBILE_DEVICE_H 3 | 4 | #include "cftypes.h" 5 | #include 6 | 7 | #define kAMDSuccess ERR_SUCCESS 8 | 9 | // opaque structures 10 | struct am_device; 11 | struct am_device_notification; 12 | struct am_device_service_connection; 13 | 14 | #define ADNCI_MSG_CONNECTED 1 15 | #define ADNCI_MSG_DISCONNECTED 2 16 | #define ADNCI_MSG_UNKNOWN 3 17 | 18 | // callback info for AMDeviceNotificationSubscribe() 19 | struct am_device_notification_callback_info 20 | { 21 | am_device *dev; // device handle 22 | uint32_t code; // one of ADNCI_MSG_... 23 | }; 24 | typedef void am_device_notification_callback_t(am_device_notification_callback_info *cbi, void *arg); 25 | 26 | // manage access to the MobileDevice library 27 | class mobile_device_lib_t 28 | { 29 | // dll handle 30 | void *dhandle; 31 | 32 | // pointers to functions in MobileDevice. not to be used directly 33 | mach_error_t (*_AMDeviceNotificationSubscribe)(am_device_notification_callback_t *, int, int, void *, am_device_notification **); 34 | mach_error_t (*_AMDeviceNotificationUnsubscribe)(am_device_notification *); 35 | CFStringRef (*_AMDeviceCopyDeviceIdentifier)(am_device *); 36 | mach_error_t (*_AMDeviceConnect)(am_device *); 37 | int (*_AMDeviceIsPaired)(am_device *); 38 | mach_error_t (*_AMDeviceValidatePairing)(am_device *); 39 | mach_error_t (*_AMDeviceStartSession)(am_device *); 40 | mach_error_t (*_AMDeviceStopSession)(am_device *); 41 | mach_error_t (*_AMDeviceDisconnect)(am_device *); 42 | mach_error_t (*_AMDeviceSecureStartService)(am_device *, CFStringRef, int *, am_device_service_connection **); 43 | void (*_AMDServiceConnectionInvalidate)(am_device_service_connection *); 44 | int (*_AMDServiceConnectionGetSocket)(am_device_service_connection *); 45 | ssize_t (*_AMDServiceConnectionSend)(am_device_service_connection *, const void *, size_t); 46 | ssize_t (*_AMDServiceConnectionReceive)(am_device_service_connection *, void *, size_t); 47 | 48 | public: 49 | mobile_device_lib_t(void) { reset(); } 50 | ~mobile_device_lib_t(void) { unload(); } 51 | 52 | void reset(void) { memset(this, 0, sizeof(*this)); } 53 | bool load(void); 54 | void unload(void); 55 | 56 | mach_error_t AMDeviceNotificationSubscribe(am_device_notification_callback_t *, int, int, void *, am_device_notification **) const; 57 | mach_error_t AMDeviceNotificationUnsubscribe(am_device_notification *) const; 58 | CFStringRef AMDeviceCopyDeviceIdentifier(am_device *) const; 59 | mach_error_t AMDeviceConnect(am_device *) const; 60 | int AMDeviceIsPaired(am_device *) const; 61 | mach_error_t AMDeviceValidatePairing(am_device *) const; 62 | mach_error_t AMDeviceStartSession(am_device *) const; 63 | mach_error_t AMDeviceStopSession(am_device *) const; 64 | mach_error_t AMDeviceDisconnect(am_device *) const; 65 | mach_error_t AMDeviceSecureStartService(am_device *, CFStringRef, int *, am_device_service_connection **) const; 66 | void AMDServiceConnectionInvalidate(am_device_service_connection *) const; 67 | int AMDServiceConnectionGetSocket(am_device_service_connection *) const; 68 | ssize_t AMDServiceConnectionSend(am_device_service_connection *handle, const void *buf, size_t len) const; 69 | ssize_t AMDServiceConnectionReceive(am_device_service_connection *handle, void *buf, size_t size) const; 70 | }; 71 | 72 | extern mobile_device_lib_t MobileDevice; 73 | 74 | #endif // MOBILE_DEVICE_H 75 | --------------------------------------------------------------------------------