在iOS系统中,获取无线网络连接状态的信息相对有限。公共API仅允许获取SSID、BSSID和适配器网络设置等信息。那么,加密模式和信号强度等数据如何获取呢?本文将介绍一种在不使用私有API和越狱的情况下获取这些数据的方法。
在iOS 5.x版本中,可以通过Apple系统日志设施获取系统消息,这些消息在连接到网络时显示,其中包含了加密模式和信号强度数据。以下是Objective-C代码示例,展示了如何使用系统日志获取这些信息:
aslmsg asl, message;
aslresponse searchResult;
int i;
const char *key, *val;
NSMutableArray *result_dicts = [NSMutableArray array];
asl = asl_new(ASL_TYPE_QUERY);
if (!asl) {
NSLog(@"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);
然而,苹果公司在意识到这一点后,像往常一样关闭了对系统消息的访问。因此,需要找到一种新的方法来获取这些数据。
首先,可以使用scutil工具,它允许获取系统配置数据,包括所需的信息。在iOS 6上测试越狱的iPhone证明该工具工作得很好。这对来说是一个线索,开始寻找在iOS上访问SystemConfiguration的方法。
SystemConfiguration.framework允许连接到Mac OS值存储并获取属性列表,其中包含了无线网络数据。但是,当查看库的头文件时,会发现使用所需方法受到限制。
CFPropertyListRef SCDynamicStoreCopyValue(
SCDynamicStoreRef store,
CFStringRef key
) __OSX_AVAILABLE_STARTING(__MAC_10_1,__IPHONE_NA);
首先,确保该方法是有效的。
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);
一切都很好。结果返回了。所以没有阻碍,只有苹果公司的形式限制,这将不允许在App Store中通过验证。
不管怎样,为什么不自己写一个库呢?源代码很容易找到:它是守护进程configd的一部分。
当阅读SCDynamicStoreCopyValue的描述时,最有趣的部分开始了。
#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*)≻_status);
OK。请求被传递到使用MACH Interface Generator生成的文件。在附近的文件中找到了MIG的描述。
routine configget ( server : mach_port_t;
key : xmlData;
out data : xmlDataOut, dealloc;
out newInstance : int;
out status : int);
现在有两个选择——普通人的方式和绝地武士的方式。可以运行mig在文件config.defs上,并获得要输入到项目中的代码。
但不幸的是,在研究过程中没有发现该文件,所以不得不做一些逆向工程:)然而,Dmitry Sklyarov展示了他的绝地武士技能,并设法恢复了发送请求到MACH端口configd的过程。因此,该方法被完全恢复了。
#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;
}
所需的数据位于键@«Setup:/Network/Interface/en0/AirPort»。因此,实现了SystemConfiguration.framework的一部分,并在不越狱和非法使用库的情况下获取了数据。
有趣的是,iOS6中有100多个开放的MACH端口,具有各种名称。猜这为研究提供了舞台。