По следам своего выступления на Positive Hack Days я хотел бы поделиться с вами результатами исследования демона configd на MACH-уровне в iOS 6. Как вы знаете, в iOS доступно не так много информации о состоянии подключения Wi-Fi. В общем-то, Public API не дает возможности узнать ничего, кроме SSID, BSSID и сетевых настроек адаптера. А режим шифрования? мощность сигнала? Под катом я расскажу, как узнать все это без применения Private API и Jailbreak.
Заранее прошу прощения, но в этой статье я буду выкладывать много исходников. Для начала давайте вспомним, как это делалось раньше, на прошивке iOS 5.*. Использовался Apple System Log facility: можно было получить системные сообщения, которые ОС выводит в момент подключения к сети. В них фигурировали режим шифрования и мощность сигнала. А получали мы их вот так:
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);
Но, как это водится у Apple, узнав об этом, они закрыли доступ к системным сообщениям в ASL. Пришлось искать новый путь для получения этих данных. Тогда вопрос был поставлен по-другому: как вообще можно получить эти данные на Mac OS и iOS?
Прежде всего, при помощи утилиты scutil, которая позволяет получить данные о конфигурации системы, в том числе и необходимые нам. Тестовый iPhone с iOS 6 и Jailbreak показал, что утилита исправно отрабатывает и на нем. Для мня это стало намеком, путеводный нитью, и я начал искать, как еще можно «дотянуться» до SystemConfiguration на iOS.
Путь оказался прост и тривиален до безумия — библиотека SystemConfiguration.framework. С ее помощью на Mac OS можно программно подключаться к хранилищу значений и получать property list с данными о беспроводных сетях.
Но если посмотреть заголовочный файлы этой библиотеки на iOS, то становится грустно: использование требуемого метода запрещено.
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);
Все отлично, результат возвращается. Значит, у нас нет никаких программных блокировок, кроме формального запрета Apple, который не даст возможности пройти валидацию в AppStore. Впрочем, почему бы нам не написать кусочек этой библиотеки самостоятельно?
Исходники найти оказалось очень просто: это часть демона 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 *)&sc_status);
Окей. Идет обращение к сгенерированному при помощи 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 и получить коды для вставки в проект. Но, к сожалению, на момент исследования мы этот файл не обнаружили и пришлось заняться реверс-инжинирингом 🙂 Джедайствовал в итоге Дима Скляров, который смог восстановить процесс обращения к 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 самостоятельно и получили необходимые данные не прибегая к Jailbreak или незаконному использованию библиотек. Что любопытно, в iOS 6 имеются больше 100 открытых для подключения MACH-портов с самыми разнообразными именами. Мне кажется, это дает довольно богатую почву для различных исследований. К сожалению, пока я не могу сказать, проходит ли подобный код в AppStore, но попытаться определенно стоит.
Спасибо за внимание.
Ссылки:
ссылка на оригинал статьи http://habrahabr.ru/company/pt/blog/181936/
Добавить комментарий