Нестандартный способ получить недоступную информацию на iOS

от автора

По следам своего выступления на 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, но попытаться определенно стоит.

Спасибо за внимание.

Ссылки:

MACH Kernel programming guide

iOS Hackers handbook

Mac OS X internals

ссылка на оригинал статьи http://habrahabr.ru/company/pt/blog/181936/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *