Ещё один способ получить нестандартные данные в iOS

в 9:46, , рубрики: разработка под iOS

Привет!

Я хочу показать, как, не используя приватных API ( = не используя приватных фреймворков/классов/функций) можно собирать разнообразные данные о использовании устройства. Ещё один способ получить нестандартные данные в iOS

Вот описание информации, которую можно получить:

  • Мощность сигнала сотовой сети: RSSI в дБм и относительную мощность в «палках»
  • Качество сигнала WiFi (0 — плохо, 4 — хорошо)
  • Состояние регистрации в сотовой сети: наличие SIM, поиск сети
  • Тип сети передачи данных: 2G, 3G, WiFi
  • Заряд аккумулятора с точностью до процента (стандартные средства дают точность в 5%)
  • Включен ли «режим самолёта» («airplane mode»)
  • Включены ли различные сервисы: будильник, Airplay, VPN, перенаправление звонков, Nike+

Собственно, суть метода:

	typedef struct {
		BOOL itemIsEnabled[24];
		char timeString[64];				
		int gsmSignalStrengthRaw;			
		int gsmSignalStrengthBars;			
		char serviceString[100];			
		char serviceCrossfadeString[100];
		char serviceImages[2][100];
		char operatorDirectory[1024];		
		unsigned serviceContentType;
		int wifiSignalStrengthRaw;
		int wifiSignalStrengthBars;			
		unsigned dataNetworkType;			
		int batteryCapacity;				
		unsigned batteryState;				
		char batteryDetailString[150];		
		int bluetoothBatteryCapacity;
		int thermalColor;
		unsigned thermalSunlightMode : 1;
		unsigned slowActivity : 1;			
		unsigned syncActivity : 1;			
		char activityDisplayId[256];		
		unsigned bluetoothConnected : 1;
		unsigned displayRawGSMSignal : 1;	
		unsigned displayRawWifiSignal : 1;
		unsigned locationIconType : 1;
	} iOS6Data;

	// retrieve data
	void *app = (__bridge void *)([UIApplication sharedApplication]);
	ptrdiff_t providerOffset = 52;
	void *provider = *(void**)(app + providerOffset);
	ptrdiff_t iOS6DataOffset = 116;
	iOS6Data *data = (iOS6Data*)(provider + iOS6DataOffset);
Минимальная программа, демонстрирующая возможности подхода

enum {
	kTimeItem = 0,
	kLockItem,
	kAirplaneItem,
	kSignalStrengthItem,
	kServiceItem,
	kDataNetworkItem,
	kBatteryItem,
	kBatteryPercentItem,
	kNotChargingItem,
	kBluetoothBatteryItem,
	kBluetoothItem,
	kTTYItem,
	kAlarmItem,
	kPlusItem,
	kPlayItem,
	kLocationItem,
	kRotationLockItem,
	kDoubleHeightItem,
	kAirPlayItem,
	kVPNItem,
	kCallForwardItem,
	kActivityItem,
	kThermalColorItem

};

typedef struct {
	BOOL itemIsEnabled[24];
	char timeString[64];				
	int gsmSignalStrengthRaw;			
	int gsmSignalStrengthBars;			
	char serviceString[100];			
	char serviceCrossfadeString[100];
	char serviceImages[2][100];
	char operatorDirectory[1024];		
	unsigned serviceContentType;
	int wifiSignalStrengthRaw;
	int wifiSignalStrengthBars;			
	unsigned dataNetworkType;			
	int batteryCapacity;				
	unsigned batteryState;				
	char batteryDetailString[150];		
	int bluetoothBatteryCapacity;
	int thermalColor;
	unsigned thermalSunlightMode : 1;
	unsigned slowActivity : 1;			
	unsigned syncActivity : 1;			
	char activityDisplayId[256];		
	unsigned bluetoothConnected : 1;
	unsigned displayRawGSMSignal : 1;	
	unsigned displayRawWifiSignal : 1;
	unsigned locationIconType : 1;
} iOS6Data;

void proof_of_concept()
{
	// we need to check runtime before start
	NSString *systemVersion = [[UIDevice currentDevice] systemVersion] ;
	NSScanner *scanner = [NSScanner scannerWithString:systemVersion];
	int runtime;
	[scanner scanInt:&runtime];
	
	if (runtime != 6) {
		NSLog(@"К сожалению, программа работает только на iOS 6");
		
		UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Не удалось получить данные"
															message:@"Для работы программы необходима iOS 6."
														   delegate:nil
												  cancelButtonTitle:@"Закрыть"
												  otherButtonTitles:nil];
		[alertView show];
		
		return;
	}
	
	// retrieve data
	void *app = (__bridge void *)([UIApplication sharedApplication]);
	ptrdiff_t providerOffset = 52;
	void *provider = *(void**)(app + providerOffset);
	ptrdiff_t iOS6DataOffset = 116;
	iOS6Data *data = (iOS6Data*)(provider + iOS6DataOffset);
	
	// usage example
	NSMutableString *example = [NSMutableString stringWithCapacity:1000];
	
	[example appendFormat: @"Сигнал сотовой сети: %d дБмn", data->gsmSignalStrengthRaw ];
	[example appendFormat: @"Заряд батареи: %@n", @(data->batteryDetailString)];

	switch (data->dataNetworkType) {
		case 2:
			[example appendString: @"Тип сети передачи данных: 2Gn"];
			break;
		case 3:
			[example appendString: @"Тип сети передачи данных: 3Gn"];
			break;
		case 5:
			[example appendString: @"Тип сети передачи данных: WiFin"];

		default:
			break;
	}
	
	if (data->itemIsEnabled[kAlarmItem]) {
		[example appendString:@"Будильник включен"];
	}

	if (data->itemIsEnabled[kCallForwardItem]) {
		[example appendString:@"Влючено перенаправление звонков"];
	}
	
	if (data->itemIsEnabled[kAirplaneItem]) {
		[example appendString:@"Влючен "Режим самолёта""];
	}

	
	NSLog(@"%@", example);
	
	UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Info"
													   message:example
													  delegate:nil
											 cancelButtonTitle:@"Закрыть"
											 otherButtonTitles: nil];
	[alertView show];
}

Плюсы:

  • Данные постоянно остаются актуальными, их обновлением занимается само приложение (UIApplication)
  • Решение не использует приватные API

Минусы:

  • Решение построено на рантайме iOS, поэтому структуры и константы отличаются для iOS 5, 6 и 7
  • Полученная информация довольна поверхностна, нельзя например получить другие статистики сотовой сети

Если интересно, как это работает, откуда берутся константы и описания структур данных, пишите в комментариях, дополню статью.

Автор: Trahman

Источник

Поделиться

* - обязательные к заполнению поля