Introduction
In the wake of my speech at Positive Hack Days, I would like to share information I got exploring a daemon configd on iOS 6 MACH. As you know, iOS gives little information about Wi-Fi connection status. Basically,
the Public API allows getting SSID, BSSID, adapter network settings, and that's all. And what about encryption mode? Signal power? You can look under the cut for more information on how to get such data without Private API and
jail breaking.
Now I must apologize for posting so many source codes. To begin with, let us recall how it was earlier, in iOS 5.*. Then you could use Apple System Log facility to get the system messages that are displayed when connecting to a network. The encryption mode and signal power data appeared in the messages. And you could get them this way:
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 Apple usually does, the company closed access to system messages in ASL once it knew about them. So we had to find a new way to get these data.
The question was stated differently: how can you get these data in Mac OS and iOS?
First of all, you can use scutil, which allows getting the system configuration data including the information we need. Testing jailbroken iPhone on iOS 6 proved that
the tool works quite well. For me it was a clue, and I started to look for a way to reach
SystemConfiguration
on iOS.
It was as simple as pie: SystemConfiguration.framework
. It allows connecting to Mac OS value storage and getting a property list, which includes wireless network data.
However, when you look at the header files of the library, you will get upset: using the required method is restricted.
CFPropertyListRef
SCDynamicStoreCopyValue (
SCDynamicStoreRef store,
CFStringRef key
) __OSX_AVAILABLE_STARTING(__MAC_10_1,__IPHONE_NA);
First, make sure that the method is functional.
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's fine. The result returns. So there are no blocks, only formal Apple's restrictions, which won't allow passing validation in
the App Store.
Anyway, why don't we write a piece of the library by our own? The source code was easy to be found: it was a part of the daemon configd.
The most interesting stuff begins when reading the description of SCDynamicStoreCopyValue
.
#include "config.h" /* MiG generated file */
...
status = configget(storePrivate->server,
myKeyRef,
myKeyLen,
&xmlDataRef,
(int *)&xmlDataLen,
&newInstance,
(int *)&sc_status);
OK. A request is passed to the file generated using MACH Interface Generator. We have
the description in MIG in the file located nearby.
routine configget ( server : mach_port_t;
key : xmlData;
out data : xmlDataOut, dealloc;
out newInstance : int;
out status : int);
Now you have two options — the way of a common person and the way of the Jedi. You can run mig on the file
config.defs and get the codes to be entered into the project.
But unfortunately we did not discover the file during the research so we have to do some reverse engineering :) However, Dmitry Sklyarov did show his
Jedi skills and managed to restore the process of sending the request to the MACH port, configd. So the method was completely restored.
#define kMachPortConfigd "com.apple.SystemConfiguration.configd"
-(NSDictionary *)getSCdata:(NSString *)key
{
if(SYSTEM_VERSION_LESS_THAN(@"6.0"))
{
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;
CFDataRef extRepr;
extRepr = CFStringCreateExternalRepresentation(NULL,
(__bridge CFStringRef)(key), kCFStringEncodingUTF8, 0);
task_get_bootstrap_port(mach_task_self(), &bootstrapport);
bootstrap_look_up2(bootstrapport, kMachPortConfigd, &configport, 0, 8LL);
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;
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;
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 data we needed was located in the key @«Setup:/Network/Interface/en0/AirPort»
.
So we have implemented the part SystemConfiguration.framework
on our own and got the data without jail-breaking and
the illegal use of libraries.
The interesting thing is that there are more than 100 open MACH ports with various names in iOS 6. I guess it sets the stage for researches.
Unfortunately, for the time being I cannot say whether such code can be used in
the App Store, but it is worth trying anyway.
Thanks for your attention.