A custom way to get inaccessible information on iOS



    Following the steps of my presentation at Positive Hack Days, I would like to share with you the results of researching the configd daemon at the MACH level in iOS 6. As you know, in iOS there is not much information about the Wi-Fi connection status. In general, the Public API does not allow you to find out anything but the SSID, BSSID and network settings of the adapter. What about encryption mode? signal strength? Under the cut, I’ll show you how to learn all this without using the Private API and Jailbreak.

    I apologize in advance, but in this article I will upload a lot of source codes. First, let's recall how this was done earlier, on iOS 5. * firmware. Used Apple System Log facility: it was possible to receive system messages that the OS displays at the time of connection to the network. They featured an encryption mode and signal strength. And we got them like this:

            aslmsg asl, message;
            aslresponse searchResult;
            int i;
            const char *key, *val;
            NSMutableArray *result_dicts = [NSMutableArray array];
            asl = asl_new(ASL_TYPE_QUERY);
            if (!asl)
            {
                DDLogCError(@"Failed creating ASL query");
            }
            asl_set_query(asl, "Sender", "kernel", ASL_QUERY_OP_EQUAL);
            asl_set_query(asl, "Message", "AppleBCMWLAN Joined BSS:", ASL_QUERY_OP_PREFIX|ASL_QUERY_OP_EQUAL);
            searchResult = asl_search(NULL, asl);
            while (NULL != (message = aslresponse_next(searchResult)))
            {
                NSMutableDictionary *tmpDict = [NSMutableDictionary dictionary];
                for (i = 0; (NULL != (key = asl_key(message, i))); i++)
                {
                    NSString *keyString = [NSString stringWithUTF8String:(char *)key];
                    val = asl_get(message, key);
                    NSString *string = [NSString stringWithUTF8String:val];
                    [tmpDict setObject:string forKey:keyString];
                }
                [result_dicts addObject:tmpDict];
            }
            aslresponse_free(searchResult);
            asl_free(asl);
    


    But, as is usual with Apple, upon learning about this, they blocked access to system messages in ASL. I had to look for a new way to get this data. Then the question was posed differently: how can one get this data on Mac OS and iOS?

    First of all, using the scutil utility , which allows you to get data on the system configuration, including the ones we need. A test iPhone with iOS 6 and Jailbreak showed that the utility also works properly on it. For me, this became a hint, a guiding thread, and I began to look for how else I could “reach out” to SystemConfiguration on iOS.

    The path turned out to be simple and trivial to madness - the SystemConfiguration.framework library . Using it on Mac OS, you can programmatically connect to a value store and receiveproperty list with data on wireless networks.

    But if you look at the header files of this library on iOS, it becomes sad: the use of the required method is prohibited.

    CFPropertyListRef
    SCDynamicStoreCopyValue			(
    					SCDynamicStoreRef		store,
    					CFStringRef			key
    					)				__OSX_AVAILABLE_STARTING(__MAC_10_1,__IPHONE_NA);
    


    First, make sure that the method is generally workable.

        void *handle = dlopen("/System/Library/Frameworks/SystemConfiguration.framework/SystemConfiguration", RTLD_LAZY);
        CFArrayRef (*_SCDynamicStoreCopyKeyList)(int store, CFStringRef pattern) = dlsym(handle, "SCDynamicStoreCopyKeyList");
        NSLog(@"Lib handle: %u", handle);
        NSString *key = @"State:/Network/Global/DNS";
        CFArrayRef testarrray =  _SCDynamicStoreCopyKeyList(0, CFSTR("State:/Network/Interface/en0/AirPort"));
        NSLog(@"Tested array res: %@", testarrray);
    


    Everything is fine, the result is returning. So, we do not have any software locks, except for a formal ban on Apple, which will not allow us to pass validation in the AppStore. However, why don't we write a piece of this library ourselves?

    Finding the source turned out to be very simple: this is part of the configd daemon . The most interesting part starts when you read the description of the SCDynamicStoreCopyValue function .

    #include "config.h"		/* MiG generated file */
    ...
            /* send the key & fetch the associated data from the server */
    	status = configget(storePrivate->server,
    			   myKeyRef,
    			   myKeyLen,
    			   &xmlDataRef,
    			   (int *)&xmlDataLen,
    			   &newInstance,
    			   (int *)&sc_status);
    


    Okay A file is being generated using the MACH Interface Generator file. Accordingly, we have a description in the MIG language that lies in the file nearby .

    routine configget	(	server		: mach_port_t;
    				key		: xmlData;
    			 out	data		: xmlDataOut, dealloc;
    			 out	newInstance	: int;
    			 out	status		: int);
    


    After that, you have two paths - the path of a normal person and the path of a Jedi. You could run the mig utility on the config.defs file and get the codes to insert into the project. But, unfortunately, at the time of the study, we did not find this file and had to do reverse engineering :) As a result, Dima Sklyarov , who was able to restore the process of accessing the MACH port configd, was restored. With its help, it turned out to restore the whole method.

    #define kMachPortConfigd "com.apple.SystemConfiguration.configd"
    -(NSDictionary *)getSCdata:(NSString *)key
    {
        if(SYSTEM_VERSION_LESS_THAN(@"6.0"))
        {
            // It does not work on iOS 5.*
            return nil;
        }
        struct send_body {mach_msg_header_t header; int count; UInt8 *addr; CFIndex size0; int flags; NDR_record_t ndr; CFIndex size; int retB; int rcB; int f24; int f28;};
        mach_port_t bootstrapport = MACH_PORT_NULL;
        mach_port_t configport = MACH_PORT_NULL;
        mach_msg_header_t *msg;
        mach_msg_return_t msg_return;
        struct send_body send_msg;
        // Make request
        CFDataRef  extRepr;
        extRepr = CFStringCreateExternalRepresentation(NULL, (__bridge CFStringRef)(key), kCFStringEncodingUTF8, 0);
        // Connect to Mach MIG port of configd
        task_get_bootstrap_port(mach_task_self(), &bootstrapport);
        bootstrap_look_up2(bootstrapport, kMachPortConfigd, &configport, 0, 8LL);
        // Make request
        send_msg.count = 1;
        send_msg.addr = (UInt8*)CFDataGetBytePtr(extRepr);
        send_msg.size0 = CFDataGetLength(extRepr);
        send_msg.size = CFDataGetLength(extRepr);
        send_msg.flags = 0x1000100u;
        send_msg.ndr = NDR_record;
        // Make message header
        msg = &(send_msg.header);
        msg->msgh_bits = 0x80001513u;
        msg->msgh_remote_port = configport;
        msg->msgh_local_port = mig_get_reply_port();
        msg->msgh_id = 20010;
        // Request server
        msg_return = mach_msg(msg, 3, 0x34u, 0x44u, msg->msgh_local_port, 0, 0);
        if(msg_return)
        {
            if (msg_return - 0x10000002u >= 2 && msg_return != 0x10000010 )
            {
                mig_dealloc_reply_port(msg->msgh_local_port);
            }
            else
            {
                mig_put_reply_port(msg->msgh_local_port);
            }
        }
        else if ( msg->msgh_id != 71 && msg->msgh_id == 20110 && msg->msgh_bits <= -1 )
        {
            if ((send_msg.flags & 0xFF000000) == 0x1000000)
            {
                CFDataRef deserializedData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, send_msg.addr,send_msg.size0, kCFAllocatorNull);
                CFPropertyListRef proplist = CFPropertyListCreateWithData(kCFAllocatorDefault, deserializedData, kCFPropertyListImmutable, NULL, NULL);
                mig_dealloc_reply_port(msg->msgh_local_port);
                mach_port_deallocate(mach_task_self(), bootstrapport);
                mach_port_deallocate(mach_task_self(), configport);
                mach_msg_destroy(msg);
                NSDictionary *property_list = (__bridge NSDictionary*)proplist;
                if(proplist)
                    CFRelease(proplist);
                CFRelease(deserializedData);
                CFRelease(extRepr);
                return property_list;
            }
        }
        mig_dealloc_reply_port(msg->msgh_local_port);
        mach_port_deallocate(mach_task_self(), bootstrapport);
        mach_port_deallocate(mach_task_self(), configport);
        mach_msg_destroy(msg);
        CFRelease(extRepr);
        return nil;
    }
    


    The values ​​of interest to us are located by the key @ “Setup: / Network / Interface / en0 / AirPort”.

    So, we implemented part of SystemConfiguration.framework on our own and received the necessary data without resorting to Jailbreak or illegal use of libraries. Curiously, in iOS 6 there are more than 100 MACH ports with a wide variety of names open for connection. It seems to me that this provides a rather rich ground for various studies. Unfortunately, so far I can’t say whether similar code passes in the AppStore, but it’s definitely worth a try.

    Thanks for attention.

    Links:

    - MACH Kernel programming guide

    - iOS Hackers handbook

    - Mac OS X internals

    Also popular now: