[
  {
    "path": ".gitignore",
    "content": "*.swp\nobj/*\nios_instruments_client\nios_instruments_client.dSYM\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2018 Hex-Rays\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# ios\\_instruments\\_client\n\nThis is a command-line tool that can communicate with the iOS Instruments Server.\n\nIt was inspired by my work on the [dtxmsg](https://github.com/troybowman/dtxmsg) IDA plugin.\n\nThe app is implemented in C++. It should build/run on any recent version of OSX,\nand it can work with any recent version of iOS.\n\n## build\n\n$ make\n\n## run\n\n$ ./ios\\_instruments\\_client -h\n"
  },
  {
    "path": "cftypes.cpp",
    "content": "#include <Foundation/Foundation.h>\n#include \"cftypes.h\"\n\n//------------------------------------------------------------------------------\nstring_t to_stlstr(CFStringRef ref)\n{\n  if ( ref == NULL )\n    return \"\";\n\n  CFIndex length = CFStringGetLength(ref);\n  if ( length <= 0 )\n    return \"\";\n\n  CFIndex bufsize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;\n  char *buf = (char *)calloc(bufsize, 1);\n\n  string_t ret;\n  if ( CFStringGetCString(ref, buf, bufsize, kCFStringEncodingUTF8) )\n    ret = buf;\n\n  free(buf);\n  return ret;\n}\n\n//------------------------------------------------------------------------------\nstring_t get_description(CFTypeRef ref)\n{\n  CFStringRef desc = CFCopyDescription(ref);\n  string_t ret = to_stlstr(desc);\n  CFRelease(desc);\n  return ret;\n}\n\n//-----------------------------------------------------------------------------\nvoid archive(bytevec_t *buf, CFTypeRef ref)\n{\n  @autoreleasepool\n  {\n    id object = (__bridge id)ref;\n    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:object];\n    const void *bytes = [data bytes];\n    int length = [data length];\n    append_v(*buf, bytes, length);\n  }\n}\n\n//-----------------------------------------------------------------------------\nCFTypeRef unarchive(const uint8_t *buf, size_t bufsize)\n{\n  @autoreleasepool\n  {\n    NSData *data = [NSData dataWithBytesNoCopy:(void *)buf length:bufsize freeWhenDone:false];\n    id object = [NSKeyedUnarchiver unarchiveObjectWithData:data];\n    return (__bridge CFTypeRef)[object retain];\n  }\n}\n\n//-----------------------------------------------------------------------------\nCFArrayRef deserialize(\n        const uint8_t *buf,\n        size_t bufsize,\n        string_t *errbuf)\n{\n  if ( bufsize < 16 )\n  {\n    sprnt(errbuf, \"Error: buffer of size 0x%lx is too small for a serialized array\", bufsize);\n    return NULL;\n  }\n\n  uint64_t size = *((uint64_t *)buf+1);\n  if ( size > bufsize )\n  {\n    sprnt(errbuf, \"size of array object (%llx) is larger than total length of data (%lx)\", size, bufsize);\n    return NULL;\n  }\n\n  CFMutableArrayRef array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);\n\n  uint64_t off = sizeof(uint64_t) * 2;\n  uint64_t end = off + size;\n\n  while ( off < end )\n  {\n    int length = 0;\n    int type = *((int *)(buf+off));\n    off += sizeof(int);\n\n    CFTypeRef ref = NULL;\n\n    switch ( type )\n    {\n      case 2:\n        // archived object\n        length = *((int *)(buf+off));\n        off += sizeof(int);\n        ref = unarchive(buf+off, length);\n        break;\n\n      case 3:\n      case 5:\n        // 32-bit int\n        ref = CFNumberCreate(NULL, kCFNumberSInt32Type, buf+off);\n        length = 4;\n        break;\n\n      case 4:\n      case 6:\n        // 64-bit int\n        ref = CFNumberCreate(NULL, kCFNumberSInt64Type, buf+off);\n        length = 8;\n        break;\n\n      case 10:\n        // dictionary key. for arrays, the keys are empty and we ignore them\n        continue;\n\n      default:\n        // there are more. we will deal with them as necessary\n        break;\n    }\n\n    if ( ref == NULL )\n    {\n      sprnt(errbuf, \"invalid object at offset %llx, type: %d\\n\", off, type);\n      return NULL;\n    }\n\n    CFArrayAppendValue(array, ref);\n    CFRelease(ref);\n    off += length;\n  }\n\n  return (CFArrayRef)array;\n}\n"
  },
  {
    "path": "cftypes.h",
    "content": "#ifndef CFTYPES_H\n#define CFTYPES_H\n\n#include <CoreFoundation/CoreFoundation.h>\n#include \"common.h\"\n\n//------------------------------------------------------------------------------\n// convert the given CFString to an STL string\nstring_t to_stlstr(CFStringRef ref);\n\n//------------------------------------------------------------------------------\n// get a human readable description of the given CF object\nstring_t get_description(CFTypeRef ref);\n\n//------------------------------------------------------------------------------\n// serialize a CF object\nvoid archive(bytevec_t *buf, CFTypeRef ref);\n\n//------------------------------------------------------------------------------\n// deserialize a CF object\nCFTypeRef unarchive(const uint8_t *buf, size_t bufsize);\n\n//------------------------------------------------------------------------------\n// deserialize an array of CF objects\nCFArrayRef deserialize(const uint8_t *buf, size_t bufsize, string_t *errbuf = NULL);\n\n#endif // CFTYPES_H\n"
  },
  {
    "path": "common.cpp",
    "content": "#include \"common.h\"\n\n#define MAXSTR 1024\n\n//-----------------------------------------------------------------------------\nvoid append_v(bytevec_t &out, const void *v, size_t len)\n{\n  const uint8_t *begin = (const uint8_t *)v;\n  const uint8_t *end   = begin + len;\n  out.insert(out.end(), begin, end);\n}\n\n//-----------------------------------------------------------------------------\nvoid append_d(bytevec_t &out, uint32_t num)\n{\n  append_v(out, &num, sizeof(num));\n}\n\n//-----------------------------------------------------------------------------\nvoid append_q(bytevec_t &out, uint64_t num)\n{\n  append_v(out, &num, sizeof(num));\n}\n\n//-----------------------------------------------------------------------------\nvoid append_b(bytevec_t &out, const bytevec_t &bv)\n{\n  append_v(out, bv.data(), bv.size());\n}\n\n//-----------------------------------------------------------------------------\nvoid vsprnt(string_t *out, const char *format, va_list va)\n{\n  char buf[MAXSTR];\n  if ( out != NULL && vsnprintf(buf, sizeof(buf), format, va) > 0 )\n    *out = buf;\n}\n\n//-----------------------------------------------------------------------------\nvoid sprnt(string_t *out, const char *format, ...)\n{\n  va_list va;\n  va_start(va, format);\n  vsprnt(out, format, va);\n  va_end(va);\n}\n"
  },
  {
    "path": "common.h",
    "content": "#ifndef COMMON_H\n#define COMMON_H\n\n#include <stdio.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <string>\n#include <vector>\n\ntypedef std::string string_t;\ntypedef std::vector<uint8_t> bytevec_t;\n\nvoid append_d(bytevec_t &out, uint32_t num);\nvoid append_q(bytevec_t &out, uint64_t num);\nvoid append_b(bytevec_t &out, const bytevec_t &bv);\nvoid append_v(bytevec_t &out, const void *v, size_t len);\n\nvoid sprnt(string_t *out, const char *format, ...);\nvoid vsprnt(string_t *out, const char *format, va_list va);\n\n#endif // COMMON_H\n"
  },
  {
    "path": "ios_instruments_client.cpp",
    "content": "#include \"ios_instruments_client.h\"\n#include \"mobile_device.h\"\n#include <unistd.h>\n\nstatic string_t device_id;              // user's preferred device (-d option)\nstatic bool found_device = false;       // has the user's preferred device been detected?\nstatic bool verbose = false;            // verbose mode (-v option)\nstatic int cur_message = 0;             // current message id\nstatic int cur_channel = 0;             // current channel id\nstatic CFDictionaryRef channels = NULL; // list of available channels published by the instruments server\nstatic int pid2kill = -1;               // process id to kill (\"kill\" option)\nstatic const char *bid2launch = NULL;   // bundle id of app to launch (\"launch\" option)\nstatic bool proclist = false;           // print the process list (\"proclist\" option)\nstatic bool applist = false;            // print the application list (\"applist\" option)\nstatic bool ssl_enabled = false;        // does the instruments server use SSL?\n\n//-----------------------------------------------------------------------------\nvoid message_aux_t::append_int(int32_t val)\n{\n  append_d(buf, 10);  // empty dictionary key\n  append_d(buf, 3);   // 32-bit int\n  append_d(buf, val);\n}\n\n//-----------------------------------------------------------------------------\nvoid message_aux_t::append_long(int64_t val)\n{\n  append_d(buf, 10);  // empty dictionary key\n  append_d(buf, 4);   // 64-bit int\n  append_q(buf, val);\n}\n\n//-----------------------------------------------------------------------------\nvoid message_aux_t::append_obj(CFTypeRef obj)\n{\n  append_d(buf, 10);  // empty dictionary key\n  append_d(buf, 2);   // archived object\n\n  bytevec_t tmp;\n  archive(&tmp, obj);\n\n  append_d(buf, tmp.size());\n  append_b(buf, tmp);\n}\n\n//-----------------------------------------------------------------------------\nvoid message_aux_t::get_bytes(bytevec_t *out) const\n{\n  if ( !buf.empty() )\n  {\n    // the final serialized array must start with a magic qword,\n    // followed by the total length of the array data as a qword,\n    // followed by the array data itself.\n    append_q(*out, 0x1F0);\n    append_q(*out, buf.size());\n    append_b(*out, buf);\n  }\n}\n\n//-----------------------------------------------------------------------------\n// callback that handles device notifications. called once for each connected device.\nstatic void device_callback(am_device_notification_callback_info *cbi, void *arg)\n{\n  if ( cbi->code != ADNCI_MSG_CONNECTED )\n    return;\n\n  CFStringRef id = MobileDevice.AMDeviceCopyDeviceIdentifier(cbi->dev);\n  string_t _device_id = to_stlstr(id);\n  CFRelease(id);\n\n  if ( !device_id.empty() && device_id != _device_id )\n    return;\n\n  found_device = true;\n\n  if ( verbose )\n    printf(\"found device: %s\\n\", _device_id.c_str());\n\n  do\n  {\n    // start a session on the device\n    if ( MobileDevice.AMDeviceConnect(cbi->dev) != kAMDSuccess\n      || MobileDevice.AMDeviceIsPaired(cbi->dev) == 0\n      || MobileDevice.AMDeviceValidatePairing(cbi->dev) != kAMDSuccess\n      || MobileDevice.AMDeviceStartSession(cbi->dev) != kAMDSuccess )\n    {\n      fprintf(stderr, \"Error: failed to start a session on the device\\n\");\n      break;\n    }\n\n    am_device_service_connection **connptr = (am_device_service_connection **)arg;\n\n    // launch the instruments server\n    mach_error_t err = MobileDevice.AMDeviceSecureStartService(\n            cbi->dev,\n            CFSTR(\"com.apple.instruments.remoteserver\"),\n            NULL,\n            connptr);\n\n    if ( err != kAMDSuccess )\n    {\n      // try again with an SSL-enabled service, commonly used after iOS 14\n      err = MobileDevice.AMDeviceSecureStartService(\n              cbi->dev,\n              CFSTR(\"com.apple.instruments.remoteserver.DVTSecureSocketProxy\"),\n              NULL,\n              connptr);\n\n      if ( err != kAMDSuccess )\n      {\n        fprintf(stderr, \"Failed to start the instruments server (0x%x). \"\n                 \"Perhaps DeveloperDiskImage.dmg is not installed on the device?\\n\", err);\n        break;\n      }\n\n      ssl_enabled = true;\n    }\n\n    if ( verbose )\n      printf(\"successfully launched instruments server\\n\");\n  }\n  while ( false );\n\n  MobileDevice.AMDeviceStopSession(cbi->dev);\n  MobileDevice.AMDeviceDisconnect(cbi->dev);\n\n  CFRunLoopStop(CFRunLoopGetCurrent());\n}\n\n//-----------------------------------------------------------------------------\n// launch the instruments server on the user's device.\n// returns a handle that can be used to send/receive data to/from the server.\nstatic am_device_service_connection *start_server(void)\n{\n  am_device_notification *notify_handle = NULL;\n  am_device_service_connection *conn = NULL;\n\n  mach_error_t err = MobileDevice.AMDeviceNotificationSubscribe(\n          device_callback,\n          0,\n          0,\n          &conn,\n          &notify_handle);\n\n  if ( err != kAMDSuccess )\n  {\n    fprintf(stderr, \"failed to register device notifier: 0x%x\\n\", err);\n    return NULL;\n  }\n\n  // start a run loop, and wait for the device notifier to call our callback function.\n  // if no device was detected within 3 seconds, we bail out.\n  CFRunLoopRunInMode(kCFRunLoopDefaultMode, 3, false);\n\n  MobileDevice.AMDeviceNotificationUnsubscribe(notify_handle);\n\n  if ( conn == NULL && !found_device )\n  {\n    if ( device_id.empty() )\n      fprintf(stderr, \"Failed to find a connected device\\n\");\n    else\n      fprintf(stderr, \"Failed to find device with id = %s\\n\", device_id.c_str());\n    return NULL;\n  }\n\n  return conn;\n}\n\n//-----------------------------------------------------------------------------\n// \"call\" an Objective-C method in the instruments server process\n//   conn           server handle\n//   channel        determines the object that will receive the message,\n//                  obtained by a previous call to make_channel()\n//   selector       method name\n//   args           serialized list of arguments for the method\n//   expects_reply  do we expect a return value from the method?\n//                  the return value can be obtained by a subsequent call to recv_message()\nstatic bool send_message(\n        am_device_service_connection *conn,\n        int channel,\n        CFStringRef selector,\n        const message_aux_t *args,\n        bool expects_reply = true)\n{\n  uint32_t id = ++cur_message;\n\n  bytevec_t aux;\n  if ( args != NULL )\n    args->get_bytes(&aux);\n\n  bytevec_t sel;\n  if ( selector != NULL )\n    archive(&sel, selector);\n\n  DTXMessagePayloadHeader pheader;\n  // the low byte of the payload flags represents the message type.\n  // so far it seems that all requests to the instruments server have message type 2.\n  pheader.flags = 0x2 | (expects_reply ? 0x1000 : 0);\n  pheader.auxiliaryLength = aux.size();\n  pheader.totalLength = aux.size() + sel.size();\n\n  DTXMessageHeader mheader;\n  mheader.magic = 0x1F3D5B79;\n  mheader.cb = sizeof(DTXMessageHeader);\n  mheader.fragmentId = 0;\n  mheader.fragmentCount = 1;\n  mheader.length = sizeof(pheader) + pheader.totalLength;\n  mheader.identifier = id;\n  mheader.conversationIndex = 0;\n  mheader.channelCode = channel;\n  mheader.expectsReply = (expects_reply ? 1 : 0);\n\n  bytevec_t msg;\n  append_v(msg, &mheader, sizeof(mheader));\n  append_v(msg, &pheader, sizeof(pheader));\n  append_b(msg, aux);\n  append_b(msg, sel);\n\n  size_t msglen = msg.size();\n  ssize_t nsent = ssl_enabled\n                ? MobileDevice.AMDServiceConnectionSend(conn, msg.data(), msglen)\n                : write(MobileDevice.AMDServiceConnectionGetSocket(conn), msg.data(), msglen);\n  if ( nsent != msglen )\n  {\n    fprintf(stderr, \"Failed to send 0x%lx bytes of message: %s\\n\", msglen, strerror(errno));\n    return false;\n  }\n\n  return true;\n}\n\n//-----------------------------------------------------------------------------\n// handle a response from the server.\n//   conn    server handle\n//   retobj  contains the return value for the method invoked by send_message()\n//   aux     usually empty, except in specific situations (see _notifyOfPublishedCapabilities)\nstatic bool recv_message(\n        am_device_service_connection *conn,\n        CFTypeRef *retobj,\n        CFArrayRef *aux)\n{\n  uint32_t id = 0;\n  bytevec_t payload;\n\n  int sock = MobileDevice.AMDServiceConnectionGetSocket(conn);\n\n  while ( true )\n  {\n    DTXMessageHeader mheader;\n    ssize_t nrecv = ssl_enabled\n                  ? MobileDevice.AMDServiceConnectionReceive(conn, &mheader, sizeof(mheader))\n                  : read(sock, &mheader, sizeof(mheader));\n    if ( nrecv != sizeof(mheader) )\n    {\n      fprintf(stderr, \"failed to read message header: %s, nrecv = %lx\\n\", strerror(errno), nrecv);\n      return false;\n    }\n\n    if ( mheader.magic != 0x1F3D5B79 )\n    {\n      fprintf(stderr, \"bad header magic: %x\\n\", mheader.magic);\n      return false;\n    }\n\n    if ( mheader.conversationIndex == 1 )\n    {\n      // the message is a response to a previous request, so it should have the same id as the request\n      if ( mheader.identifier != cur_message )\n      {\n        fprintf(stderr, \"expected response to message id=%d, got a new message with id=%d\\n\", cur_message, mheader.identifier);\n        return false;\n      }\n    }\n    else if ( mheader.conversationIndex == 0 )\n    {\n      // the message is not a response to a previous request. in this case, different iOS versions produce different results.\n      // on iOS 9, the incoming message can have the same message ID has the previous message we sent to the server.\n      // on later versions, the incoming message will have a new message ID. we must be aware of both situations.\n      if ( mheader.identifier > cur_message )\n      {\n        // new message id, we must update the count on our side\n        cur_message = mheader.identifier;\n      }\n      else if ( mheader.identifier < cur_message )\n      {\n        // the id must match the previous request, anything else doesn't really make sense\n        fprintf(stderr, \"unexpected message ID: %d\\n\", mheader.identifier);\n        return false;\n      }\n    }\n    else\n    {\n      fprintf(stderr, \"invalid conversation index: %d\\n\", mheader.conversationIndex);\n      return false;\n    }\n\n    if ( mheader.fragmentId == 0 )\n    {\n      id = mheader.identifier;\n      // when reading multiple message fragments, the 0th fragment contains only a message header\n      if ( mheader.fragmentCount > 1 )\n        continue;\n    }\n\n    // read the entire payload in the current fragment\n    bytevec_t frag;\n    append_v(frag, &mheader, sizeof(mheader));\n    frag.resize(frag.size() + mheader.length);\n\n    uint8_t *data = frag.data() + sizeof(mheader);\n\n    uint32_t nbytes = 0;\n    while ( nbytes < mheader.length )\n    {\n      uint8_t *curptr = data + nbytes;\n      size_t curlen = mheader.length - nbytes;\n      nrecv = ssl_enabled\n            ? MobileDevice.AMDServiceConnectionReceive(conn, curptr, curlen)\n            : read(sock, curptr, curlen);\n      if ( nrecv <= 0 )\n      {\n        fprintf(stderr, \"failed reading from socket: %s\\n\", strerror(errno));\n        return false;\n      }\n      nbytes += nrecv;\n    }\n\n    // append to the incremental payload\n    append_v(payload, data, mheader.length);\n\n    // done reading message fragments?\n    if ( mheader.fragmentId == mheader.fragmentCount - 1 )\n      break;\n  }\n\n  const DTXMessagePayloadHeader *pheader = (const DTXMessagePayloadHeader *)payload.data();\n\n  // we don't know how to decompress messages yet\n  uint8_t compression = (pheader->flags & 0xFF000) >> 12;\n  if ( compression != 0 )\n  {\n    fprintf(stderr, \"message is compressed (compression type %d)\\n\", compression);\n    return false;\n  }\n\n  // serialized object array is located just after payload header\n  const uint8_t *auxptr = payload.data() + sizeof(DTXMessagePayloadHeader);\n  uint32_t auxlen = pheader->auxiliaryLength;\n\n  // archived payload object appears after the auxiliary array\n  const uint8_t *objptr = auxptr + auxlen;\n  uint64_t objlen = pheader->totalLength - auxlen;\n\n  if ( auxlen != 0 && aux != NULL )\n  {\n    string_t errbuf;\n    CFArrayRef _aux = deserialize(auxptr, auxlen, &errbuf);\n    if ( _aux == NULL )\n    {\n      fprintf(stderr, \"Error: %s\\n\", errbuf.c_str());\n      return false;\n    }\n    *aux = _aux;\n  }\n\n  if ( objlen != 0 && retobj != NULL )\n    *retobj = unarchive(objptr, objlen);\n\n  return true;\n}\n\n//-----------------------------------------------------------------------------\n// perform the initial client-server handshake.\n// here we retrieve the list of available channels published by the instruments server.\n// we can open a given channel with make_channel().\nstatic bool perform_handshake(am_device_service_connection *conn)\n{\n  // I'm not sure if this argument is necessary - but Xcode uses it, so I'm using it too.\n  CFMutableDictionaryRef capabilities = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);\n\n  int64_t _v1 = 1;\n  int64_t _v2 = 2;\n\n  CFNumberRef v1 = CFNumberCreate(NULL, kCFNumberSInt64Type, &_v1);\n  CFNumberRef v2 = CFNumberCreate(NULL, kCFNumberSInt64Type, &_v2);\n\n  CFDictionaryAddValue(capabilities, CFSTR(\"com.apple.private.DTXBlockCompression\"), v2);\n  CFDictionaryAddValue(capabilities, CFSTR(\"com.apple.private.DTXConnection\"), v1);\n\n  // serialize the dictionary\n  message_aux_t args;\n  args.append_obj(capabilities);\n\n  CFRelease(capabilities);\n  CFRelease(v1);\n  CFRelease(v2);\n\n  if ( !send_message(conn, 0, CFSTR(\"_notifyOfPublishedCapabilities:\"), &args, false) )\n    return false;\n\n  CFTypeRef obj = NULL;\n  CFArrayRef aux = NULL;\n\n  // we are now expecting the server to reply with the same message.\n  // a description of all available channels will be provided in the arguments list.\n  if ( !recv_message(conn, &obj, &aux) || obj == NULL || aux == NULL )\n  {\n    fprintf(stderr, \"Error: failed to receive response from _notifyOfPublishedCapabilities:\\n\");\n    return false;\n  }\n\n  bool ok = false;\n  do\n  {\n    if ( CFGetTypeID(obj) != CFStringGetTypeID()\n      || to_stlstr((CFStringRef)obj) != \"_notifyOfPublishedCapabilities:\" )\n    {\n      fprintf(stderr, \"Error: unexpected message selector: %s\\n\", get_description(obj).c_str());\n      break;\n    }\n\n    CFDictionaryRef _channels;\n\n    // extract the channel list from the arguments\n    if ( CFArrayGetCount(aux) != 1\n      || (_channels = (CFDictionaryRef)CFArrayGetValueAtIndex(aux, 0)) == NULL\n      || CFGetTypeID(_channels) != CFDictionaryGetTypeID()\n      || CFDictionaryGetCount(_channels) == 0 )\n    {\n      fprintf(stderr, \"channel list has an unexpected format:\\n%s\\n\", get_description(aux).c_str());\n      break;\n    }\n\n    channels = (CFDictionaryRef)CFRetain(_channels);\n\n    if ( verbose )\n      printf(\"channel list:\\n%s\\n\", get_description(channels).c_str());\n\n    ok = true;\n  }\n  while ( false );\n\n  CFRelease(obj);\n  CFRelease(aux);\n\n  return ok;\n}\n\n//-----------------------------------------------------------------------------\n// establish a connection to a service in the instruments server process.\n// the channel identifier should be in the list of channels returned by the server\n// in perform_handshake(). after a channel is established, you can use send_message()\n// to remotely invoke Objective-C methods.\nstatic int make_channel(am_device_service_connection *conn, CFStringRef identifier)\n{\n  if ( !CFDictionaryContainsKey(channels, identifier) )\n  {\n    fprintf(stderr, \"channel %s is not supported by the server\\n\", to_stlstr(identifier).c_str());\n    return -1;\n  }\n\n  int code = ++cur_channel;\n\n  message_aux_t args;\n  args.append_int(code);\n  args.append_obj(identifier);\n\n  CFTypeRef retobj = NULL;\n\n  // request to open the channel, expect an empty reply\n  if ( !send_message(conn, 0, CFSTR(\"_requestChannelWithCode:identifier:\"), &args)\n    || !recv_message(conn, &retobj, NULL) )\n  {\n    return -1;\n  }\n\n  if ( retobj != NULL )\n  {\n    fprintf(stderr, \"Error: _requestChannelWithCode:identifier: returned %s\\n\", get_description(retobj).c_str());\n    CFRelease(retobj);\n    return -1;\n  }\n\n  return code;\n}\n\n//-----------------------------------------------------------------------------\n// invoke method -[DTDeviceInfoService runningProcesses]\n//   args:    none\n//   returns: CFArrayRef procs\nstatic bool print_proclist(am_device_service_connection *conn)\n{\n  int channel = make_channel(conn, CFSTR(\"com.apple.instruments.server.services.deviceinfo\"));\n  if ( channel < 0 )\n    return false;\n\n  CFTypeRef retobj = NULL;\n\n  if ( !send_message(conn, channel, CFSTR(\"runningProcesses\"), NULL)\n    || !recv_message(conn, &retobj, NULL)\n    || retobj == NULL )\n  {\n    fprintf(stderr, \"Error: failed to retrieve return value for runningProcesses\\n\");\n    return false;\n  }\n\n  bool ok = true;\n  if ( CFGetTypeID(retobj) == CFArrayGetTypeID() )\n  {\n    CFArrayRef array = (CFArrayRef)retobj;\n\n    printf(\"proclist:\\n\");\n    for ( size_t i = 0, size = CFArrayGetCount(array); i < size; i++ )\n    {\n      CFDictionaryRef dict = (CFDictionaryRef)CFArrayGetValueAtIndex(array, i);\n\n      CFStringRef _name = (CFStringRef)CFDictionaryGetValue(dict, CFSTR(\"name\"));\n      string_t name = to_stlstr(_name);\n\n      CFNumberRef _pid = (CFNumberRef)CFDictionaryGetValue(dict, CFSTR(\"pid\"));\n      int pid = 0;\n      CFNumberGetValue(_pid, kCFNumberSInt32Type, &pid);\n\n      printf(\"%6d %s\\n\", pid, name.c_str());\n    }\n  }\n  else\n  {\n    fprintf(stderr, \"Error: process list is not in the expected format: %s\\n\", get_description(retobj).c_str());\n    ok = false;\n  }\n\n  CFRelease(retobj);\n  return ok;\n}\n\n//-----------------------------------------------------------------------------\n// invoke method -[DTApplicationListingService installedApplicationsMatching:registerUpdateToken:]\n//   args:   CFDictionaryRef dict\n//           CFStringRef token\n//   returns CFArrayRef apps\nstatic bool print_applist(am_device_service_connection *conn)\n{\n  int channel = make_channel(conn, CFSTR(\"com.apple.instruments.server.services.device.applictionListing\"));\n  if ( channel < 0 )\n    return false;\n\n  // the method expects a dictionary and a string argument.\n  // pass empty values so we get descriptions for all known applications.\n  CFDictionaryRef dict = CFDictionaryCreate(NULL, NULL, NULL, 0, NULL, NULL);\n\n  message_aux_t args;\n  args.append_obj(dict);\n  args.append_obj(CFSTR(\"\"));\n\n  CFRelease(dict);\n\n  CFTypeRef retobj = NULL;\n\n  if ( !send_message(conn, channel, CFSTR(\"installedApplicationsMatching:registerUpdateToken:\"), &args)\n    || !recv_message(conn, &retobj, NULL)\n    || retobj == NULL )\n  {\n    fprintf(stderr, \"Error: failed to retrieve applist\\n\");\n    return false;\n  }\n\n  bool ok = true;\n  if ( CFGetTypeID(retobj) == CFArrayGetTypeID() )\n  {\n    CFArrayRef array = (CFArrayRef)retobj;\n    for ( size_t i = 0, size = CFArrayGetCount(array); i < size; i++ )\n    {\n      CFDictionaryRef app_desc = (CFDictionaryRef)CFArrayGetValueAtIndex(array, i);\n      printf(\"%s\\n\", get_description(app_desc).c_str());\n    }\n  }\n  else\n  {\n    fprintf(stderr, \"apps list has an unexpected format: %s\\n\", get_description(retobj).c_str());\n    ok = false;\n  }\n\n  CFRelease(retobj);\n  return ok;\n}\n\n//-----------------------------------------------------------------------------\n// invoke method -[DTProcessControlService killPid:]\n//   args:    CFNumberRef process_id\n//   returns: void\nstatic bool kill(am_device_service_connection *conn, int pid)\n{\n  int channel = make_channel(conn, CFSTR(\"com.apple.instruments.server.services.processcontrol\"));\n  if ( channel < 0 )\n    return false;\n\n  CFNumberRef _pid = CFNumberCreate(NULL, kCFNumberSInt32Type, &pid);\n\n  message_aux_t args;\n  args.append_obj(_pid);\n\n  CFRelease(_pid);\n\n  return send_message(conn, channel, CFSTR(\"killPid:\"), &args, false);\n}\n\n//-----------------------------------------------------------------------------\n// invoke method -[DTProcessControlService launchSuspendedProcessWithDevicePath:bundleIdentifier:environment:arguments:]\n//   args:    CFStringRef app_path\n//            CFStringRef bundle_id\n//            CFArrayRef args_for_app\n//            CFDictionaryRef environment_vars\n//            CFDictionaryRef launch_options\n//   returns: CFNumberRef pid\nstatic bool launch(am_device_service_connection *conn, const char *_bid)\n{\n  int channel = make_channel(conn, CFSTR(\"com.apple.instruments.server.services.processcontrol\"));\n  if ( channel < 0 )\n    return false;\n\n  // app path: not used, just pass empty string\n  CFStringRef path = CFStringCreateWithCString(NULL, \"\", kCFStringEncodingUTF8);\n  // bundle id\n  CFStringRef bid = CFStringCreateWithCString(NULL, _bid, kCFStringEncodingUTF8);\n  // args for app: not used, just pass empty array\n  CFArrayRef appargs = CFArrayCreate(NULL, NULL, 0, NULL);\n  // environment variables: not used, just pass empty dictionary\n  CFDictionaryRef env = CFDictionaryCreate(NULL, NULL, NULL, 0, NULL, NULL);\n\n  // launch options\n  int _v0 = 0; // don't suspend the process after starting it\n  int _v1 = 1; // kill the application if it is already running\n\n  CFNumberRef v0 = CFNumberCreate(NULL, kCFNumberSInt32Type, &_v0);\n  CFNumberRef v1 = CFNumberCreate(NULL, kCFNumberSInt32Type, &_v1);\n\n  const void *keys[] =\n  {\n    CFSTR(\"StartSuspendedKey\"),\n    CFSTR(\"KillExisting\")\n  };\n  const void *values[] = { v0, v1 };\n  CFDictionaryRef options = CFDictionaryCreate(\n        NULL,\n        keys,\n        values,\n        2,\n        NULL,\n        NULL);\n\n  message_aux_t args;\n  args.append_obj(path);\n  args.append_obj(bid);\n  args.append_obj(env);\n  args.append_obj(appargs);\n  args.append_obj(options);\n\n  CFRelease(v1);\n  CFRelease(v0);\n  CFRelease(options);\n  CFRelease(env);\n  CFRelease(appargs);\n  CFRelease(bid);\n  CFRelease(path);\n\n  CFTypeRef retobj = NULL;\n\n  if ( !send_message(conn, channel, CFSTR(\"launchSuspendedProcessWithDevicePath:bundleIdentifier:environment:arguments:options:\"), &args)\n    || !recv_message(conn, &retobj, NULL)\n    || retobj == NULL )\n  {\n    fprintf(stderr, \"Error: failed to launch %s\\n\", _bid);\n    return false;\n  }\n\n  bool ok = true;\n  if ( CFGetTypeID(retobj) == CFNumberGetTypeID() )\n  {\n    CFNumberRef _pid = (CFNumberRef)retobj;\n    int pid = 0;\n    CFNumberGetValue(_pid, kCFNumberSInt32Type, &pid);\n    printf(\"pid: %d\\n\", pid);\n  }\n  else\n  {\n    fprintf(stderr, \"failed to retrieve the process ID: %s\\n\", get_description(retobj).c_str());\n    ok = false;\n  }\n\n  CFRelease(retobj);\n  return ok;\n}\n\n//-----------------------------------------------------------------------------\nstatic void usage(const char *prog)\n{\n  fprintf(stderr, \"usage: %s [-v] [-d <device id>] TASK <task args>\\n\"\n           \"\\n\"\n           \"This is a sample client application for the iOS Instruments server.\\n\"\n           \"It is capable of rudimentary communication with the server and can\\n\"\n           \"ask it to perform some interesting tasks.\\n\"\n           \"\\n\"\n           \"TASK can be one of the following:\\n\"\n           \"  proclist  - print a list of running processes\\n\"\n           \"  applist   - print a list of installed applications\\n\"\n           \"  launch    - launch a given app. provide the bundle id of the app to launch\\n\"\n           \"  kill      - kill a given process. provide the pid of the process to kill\\n\"\n           \"\\n\"\n           \"other args:\\n\"\n           \"  -v  more verbose output\\n\"\n           \"  -d  device ID. if empty, this app will use the first device it finds\\n\", prog);\n}\n\n//-----------------------------------------------------------------------------\nstatic bool parse_args(int argc, const char **argv)\n{\n  if ( argc > 1 )\n  {\n    for ( int i = 1; i < argc; )\n    {\n      if ( strcmp(\"-v\", argv[i]) == 0 )\n      {\n        verbose = true;\n        i++;\n        continue;\n      }\n      else if ( strcmp(\"-d\", argv[i]) == 0 )\n      {\n        if ( i == argc - 1 )\n        {\n          fprintf(stderr, \"Error: -d option requires a device id string\\n\");\n          break;\n        }\n        device_id = argv[i+1];\n        i += 2;\n        continue;\n      }\n\n      string_t task = argv[i];\n\n      if ( task == \"proclist\" )\n      {\n        proclist = true;\n        return true;\n      }\n      else if ( task == \"applist\" )\n      {\n        applist = true;\n        return true;\n      }\n      else if ( task == \"kill\" )\n      {\n        if ( i == argc - 1 )\n        {\n          fprintf(stderr, \"Error: \\\"kill\\\" requires a process id\\n\");\n          break;\n        }\n        pid2kill = atoi(argv[i+1]);\n        return true;\n      }\n      else if ( task == \"launch\" )\n      {\n        if ( i == argc - 1 )\n        {\n          fprintf(stderr, \"Error: \\\"launch\\\" requires a bundle id\\n\");\n          break;\n        }\n        bid2launch = argv[i+1];\n        return true;\n      }\n\n      fprintf(stderr, \"Error, invalid task: %s\\n\", task.c_str());\n      break;\n    }\n  }\n\n  usage(argv[0]);\n  return false;\n}\n\n//-----------------------------------------------------------------------------\nint main(int argc, const char **argv)\n{\n  if ( !parse_args(argc, argv) )\n    return EXIT_FAILURE;\n\n  if ( !MobileDevice.load() )\n    return EXIT_FAILURE;\n\n  am_device_service_connection *conn = start_server();\n  if ( conn == NULL )\n    return EXIT_FAILURE;\n\n  bool ok = false;\n  if ( perform_handshake(conn) )\n  {\n    if ( proclist )\n      ok = print_proclist(conn);\n    else if ( applist )\n      ok = print_applist(conn);\n    else if ( pid2kill > 0 )\n      ok = kill(conn, pid2kill);\n    else if ( bid2launch != NULL )\n      ok = launch(conn, bid2launch);\n    else\n      ok = true;\n\n    CFRelease(channels);\n  }\n\n  MobileDevice.AMDServiceConnectionInvalidate(conn);\n  CFRelease(conn);\n\n  return ok ? EXIT_SUCCESS : EXIT_FAILURE;\n}\n"
  },
  {
    "path": "ios_instruments_client.h",
    "content": "#ifndef IOS_INSTRUMENTS_CLIENT\n#define IOS_INSTRUMENTS_CLIENT\n\n#include \"cftypes.h\"\n\n//-----------------------------------------------------------------------------\nstruct DTXMessageHeader\n{\n  uint32_t magic;\n  uint32_t cb;\n  uint16_t fragmentId;\n  uint16_t fragmentCount;\n  uint32_t length;\n  uint32_t identifier;\n  uint32_t conversationIndex;\n  uint32_t channelCode;\n  uint32_t expectsReply;\n};\n\n//-----------------------------------------------------------------------------\nstruct DTXMessagePayloadHeader\n{\n  uint32_t flags;\n  uint32_t auxiliaryLength;\n  uint64_t totalLength;\n};\n\n//------------------------------------------------------------------------------\n// helper class for serializing method arguments\nclass message_aux_t\n{\n  bytevec_t buf;\n\npublic:\n  void append_int(int32_t val);\n  void append_long(int64_t val);\n  void append_obj(CFTypeRef obj);\n\n  void get_bytes(bytevec_t *out) const;\n};\n\n#endif // IOS_INSTRUMENTS_CLIENT\n"
  },
  {
    "path": "makefile",
    "content": "T = ios_instruments_client\nO = obj\nall: $(T)\n.PHONY: all clean\n\nCFLAGS = -g -O0 -mmacosx-version-min=10.9\n\nifeq ($(wildcard $(O)/.),)\n  $(shell mkdir -p 2>/dev/null $(O))\nendif\n\nMAIN    = $(O)/$(T).o\nCOMMON  = $(O)/common.o\nCFTYPES = $(O)/cftypes.o\nMD      = $(O)/mobile_device.o\n\n$(T): $(MAIN) $(COMMON) $(CFTYPES) $(MD)\n\tg++ $(CFLAGS) -o $@ $^ -lobjc -framework Foundation -framework CoreFoundation\n\n$(MAIN): $(T).cpp $(T).h cftypes.h common.h mobile_device.h\n\tg++ $(CFLAGS) -c -o $@ $<\n\n$(COMMON): common.cpp common.h\n\tg++ $(CFLAGS) -c -o $@ $<\n\n$(CFTYPES): cftypes.cpp cftypes.h common.h\n\tg++ $(CFLAGS) -c -o $@ -x objective-c++ $<\n\n$(MD): mobile_device.cpp mobile_device.h cftypes.h common.h\n\tg++ $(CFLAGS) -c -o $@ $<\n\nclean:\n\trm -rf $(T) $(T).dSYM $(O)/*\n"
  },
  {
    "path": "mobile_device.cpp",
    "content": "#include \"mobile_device.h\"\n#include <err.h>\n#include <dlfcn.h>\n\nmobile_device_lib_t MobileDevice;\n\n//------------------------------------------------------------------------------\nbool mobile_device_lib_t::load(void)\n{\n  if ( dhandle != NULL )\n    return true;\n\n  const char *md_path = \"/System/Library/PrivateFrameworks/MobileDevice.framework/MobileDevice\";\n  dhandle = dlopen(md_path, RTLD_NOW);\n  if ( dhandle == NULL )\n  {\n    fprintf(stderr, \"dlopen() failed for %s: %s\", md_path, dlerror());\n    return false;\n  }\n\n#define BINDFUN(name, type)                                              \\\n  _##name = reinterpret_cast<type>(dlsym(dhandle, #name));               \\\n  if ( _##name == NULL )                                                 \\\n  {                                                                      \\\n    unload();                                                            \\\n    fprintf(stderr, \"Could not find function \" #name \" in %s\", md_path); \\\n    return false;                                                        \\\n  }\n\n  BINDFUN(AMDeviceNotificationSubscribe, mach_error_t (*)(am_device_notification_callback_t *, int, int, void *, am_device_notification **));\n  BINDFUN(AMDeviceNotificationUnsubscribe, mach_error_t (*)(am_device_notification *));\n  BINDFUN(AMDeviceCopyDeviceIdentifier, CFStringRef (*)(am_device *));\n  BINDFUN(AMDeviceConnect, mach_error_t (*)(am_device *));\n  BINDFUN(AMDeviceIsPaired, int (*)(am_device *));\n  BINDFUN(AMDeviceValidatePairing, mach_error_t (*)(am_device *));\n  BINDFUN(AMDeviceStartSession, mach_error_t (*)(am_device *));\n  BINDFUN(AMDeviceStopSession, mach_error_t (*)(am_device *));\n  BINDFUN(AMDeviceDisconnect, mach_error_t (*)(am_device *));\n  BINDFUN(AMDeviceSecureStartService, mach_error_t (*)(am_device *, CFStringRef, int *, am_device_service_connection **));\n  BINDFUN(AMDServiceConnectionInvalidate, void (*)(am_device_service_connection *));\n  BINDFUN(AMDServiceConnectionGetSocket, int (*)(am_device_service_connection *));\n  BINDFUN(AMDServiceConnectionSend, ssize_t (*)(am_device_service_connection *, const void *, size_t));\n  BINDFUN(AMDServiceConnectionReceive, ssize_t (*)(am_device_service_connection *, void *, size_t));\n\n#undef BINDFUN\n\n  return true;\n}\n\n//------------------------------------------------------------------------------\nvoid mobile_device_lib_t::unload()\n{\n  if ( dhandle != NULL )\n  {\n    dlclose(dhandle);\n    reset();\n  }\n}\n\n//------------------------------------------------------------------------------\nmach_error_t mobile_device_lib_t::AMDeviceNotificationSubscribe(\n        am_device_notification_callback_t *callback,\n        int unused1,\n        int unused2,\n        void *arg,\n        am_device_notification **hptr) const\n{\n  return _AMDeviceNotificationSubscribe(callback, unused1, unused2, arg, hptr);\n}\n\n//------------------------------------------------------------------------------\nmach_error_t mobile_device_lib_t::AMDeviceNotificationUnsubscribe(am_device_notification *handle) const\n{\n  return _AMDeviceNotificationUnsubscribe(handle);\n}\n\n//------------------------------------------------------------------------------\nCFStringRef mobile_device_lib_t::AMDeviceCopyDeviceIdentifier(am_device *device) const\n{\n  return _AMDeviceCopyDeviceIdentifier(device);\n}\n\n//------------------------------------------------------------------------------\nmach_error_t mobile_device_lib_t::AMDeviceConnect(am_device *device) const\n{\n  return _AMDeviceConnect(device);\n}\n\n//------------------------------------------------------------------------------\nint mobile_device_lib_t::AMDeviceIsPaired(am_device *device) const\n{\n  return _AMDeviceIsPaired(device);\n}\n\n//------------------------------------------------------------------------------\nmach_error_t mobile_device_lib_t::AMDeviceValidatePairing(am_device *device) const\n{\n  return _AMDeviceValidatePairing(device);\n}\n\n//------------------------------------------------------------------------------\nmach_error_t mobile_device_lib_t::AMDeviceStartSession(am_device *device) const\n{\n  return _AMDeviceStartSession(device);\n}\n\n//------------------------------------------------------------------------------\nmach_error_t mobile_device_lib_t::AMDeviceStopSession(am_device *device) const\n{\n  return _AMDeviceStopSession(device);\n}\n\n//------------------------------------------------------------------------------\nmach_error_t mobile_device_lib_t::AMDeviceDisconnect(am_device *device) const\n{\n  return _AMDeviceDisconnect(device);\n}\n\n//------------------------------------------------------------------------------\nmach_error_t mobile_device_lib_t::AMDeviceSecureStartService(\n        am_device *device,\n        CFStringRef name,\n        int *unused,\n        am_device_service_connection **hptr) const\n{\n  return _AMDeviceSecureStartService(device, name, unused, hptr);\n}\n\n//------------------------------------------------------------------------------\nvoid mobile_device_lib_t::AMDServiceConnectionInvalidate(am_device_service_connection *handle) const\n{\n  _AMDServiceConnectionInvalidate(handle);\n}\n\n//------------------------------------------------------------------------------\nint mobile_device_lib_t::AMDServiceConnectionGetSocket(am_device_service_connection *handle) const\n{\n  return _AMDServiceConnectionGetSocket(handle);\n}\n\n//------------------------------------------------------------------------------\nssize_t mobile_device_lib_t::AMDServiceConnectionSend(\n        am_device_service_connection *handle,\n        const void *buf,\n        size_t len) const\n{\n  return _AMDServiceConnectionSend(handle, buf, len);\n}\n\n//------------------------------------------------------------------------------\nssize_t mobile_device_lib_t::AMDServiceConnectionReceive(\n        am_device_service_connection *handle,\n        void *buf,\n        size_t size) const\n{\n  return _AMDServiceConnectionReceive(handle, buf, size);\n}\n"
  },
  {
    "path": "mobile_device.h",
    "content": "#ifndef MOBILE_DEVICE_H\n#define MOBILE_DEVICE_H\n\n#include \"cftypes.h\"\n#include <mach/error.h>\n\n#define kAMDSuccess ERR_SUCCESS\n\n// opaque structures\nstruct am_device;\nstruct am_device_notification;\nstruct am_device_service_connection;\n\n#define ADNCI_MSG_CONNECTED    1\n#define ADNCI_MSG_DISCONNECTED 2\n#define ADNCI_MSG_UNKNOWN      3\n\n// callback info for AMDeviceNotificationSubscribe()\nstruct am_device_notification_callback_info\n{\n  am_device *dev; // device handle\n  uint32_t code;  // one of ADNCI_MSG_...\n};\ntypedef void am_device_notification_callback_t(am_device_notification_callback_info *cbi, void *arg);\n\n// manage access to the MobileDevice library\nclass mobile_device_lib_t\n{\n  // dll handle\n  void *dhandle;\n\n  // pointers to functions in MobileDevice. not to be used directly\n  mach_error_t (*_AMDeviceNotificationSubscribe)(am_device_notification_callback_t *, int, int, void *, am_device_notification **);\n  mach_error_t (*_AMDeviceNotificationUnsubscribe)(am_device_notification *);\n  CFStringRef (*_AMDeviceCopyDeviceIdentifier)(am_device *);\n  mach_error_t (*_AMDeviceConnect)(am_device *);\n  int (*_AMDeviceIsPaired)(am_device *);\n  mach_error_t (*_AMDeviceValidatePairing)(am_device *);\n  mach_error_t (*_AMDeviceStartSession)(am_device *);\n  mach_error_t (*_AMDeviceStopSession)(am_device *);\n  mach_error_t (*_AMDeviceDisconnect)(am_device *);\n  mach_error_t (*_AMDeviceSecureStartService)(am_device *, CFStringRef, int *, am_device_service_connection **);\n  void (*_AMDServiceConnectionInvalidate)(am_device_service_connection *);\n  int (*_AMDServiceConnectionGetSocket)(am_device_service_connection *);\n  ssize_t (*_AMDServiceConnectionSend)(am_device_service_connection *, const void *, size_t);\n  ssize_t (*_AMDServiceConnectionReceive)(am_device_service_connection *, void *, size_t);\n\npublic:\n  mobile_device_lib_t(void) { reset(); }\n  ~mobile_device_lib_t(void) { unload(); }\n\n  void reset(void) { memset(this, 0, sizeof(*this)); }\n  bool load(void);\n  void unload(void);\n\n  mach_error_t AMDeviceNotificationSubscribe(am_device_notification_callback_t *, int, int, void *, am_device_notification **) const;\n  mach_error_t AMDeviceNotificationUnsubscribe(am_device_notification *) const;\n  CFStringRef AMDeviceCopyDeviceIdentifier(am_device *) const;\n  mach_error_t AMDeviceConnect(am_device *) const;\n  int AMDeviceIsPaired(am_device *) const;\n  mach_error_t AMDeviceValidatePairing(am_device *) const;\n  mach_error_t AMDeviceStartSession(am_device *) const;\n  mach_error_t AMDeviceStopSession(am_device *) const;\n  mach_error_t AMDeviceDisconnect(am_device *) const;\n  mach_error_t AMDeviceSecureStartService(am_device *, CFStringRef, int *, am_device_service_connection **) const;\n  void AMDServiceConnectionInvalidate(am_device_service_connection *) const;\n  int AMDServiceConnectionGetSocket(am_device_service_connection *) const;\n  ssize_t AMDServiceConnectionSend(am_device_service_connection *handle, const void *buf, size_t len) const;\n  ssize_t AMDServiceConnectionReceive(am_device_service_connection *handle, void *buf, size_t size) const;\n};\n\nextern mobile_device_lib_t MobileDevice;\n\n#endif // MOBILE_DEVICE_H\n"
  }
]