iPhone получит SSID без приватной библиотеки


154

У меня есть коммерческое приложение, которое имеет вполне законную причину для просмотра SSID сети, к которой он подключен: если оно подключено к сети Adhoc для стороннего аппаратного устройства, оно должно функционировать не так, как если бы оно было подключен к интернету.

Все, что я видел о получении SSID, говорит мне, что я должен использовать Apple80211, который, как я понимаю, является частной библиотекой. Я также читал, что если я использую личную библиотеку, Apple не одобрит приложение.

Я застрял между Apple и наковальней, или мне чего-то не хватает здесь?


Ответы:


186

Начиная с iOS 7 или 8, вы можете сделать это (необходимо получить право на iOS 12+, как показано ниже):

@import SystemConfiguration.CaptiveNetwork;

/** Returns first non-empty SSID network info dictionary.
 *  @see CNCopyCurrentNetworkInfo */
- (NSDictionary *)fetchSSIDInfo {
    NSArray *interfaceNames = CFBridgingRelease(CNCopySupportedInterfaces());
    NSLog(@"%s: Supported interfaces: %@", __func__, interfaceNames);

    NSDictionary *SSIDInfo;
    for (NSString *interfaceName in interfaceNames) {
        SSIDInfo = CFBridgingRelease(
            CNCopyCurrentNetworkInfo((__bridge CFStringRef)interfaceName));
        NSLog(@"%s: %@ => %@", __func__, interfaceName, SSIDInfo);

        BOOL isNotEmpty = (SSIDInfo.count > 0);
        if (isNotEmpty) {
            break;
        }
    }
    return SSIDInfo;
}

Пример вывода:

2011-03-04 15:32:00.669 ShowSSID[4857:307] -[ShowSSIDAppDelegate fetchSSIDInfo]: Supported interfaces: (
    en0
)
2011-03-04 15:32:00.693 ShowSSID[4857:307] -[ShowSSIDAppDelegate fetchSSIDInfo]: en0 => {
    BSSID = "ca:fe:ca:fe:ca:fe";
    SSID = XXXX;
    SSIDDATA = <01234567 01234567 01234567>;
}

Обратите внимание, что в симуляторе не поддерживаются if. Протестируйте на своем устройстве.

iOS 12

Вы должны включить доступ к информации Wi-Fi из возможностей.

Важно! Чтобы использовать эту функцию в iOS 12 и более поздних версиях, включите возможность доступа к информации Wi-Fi для вашего приложения в XCode. Когда вы включаете эту возможность, XCode автоматически добавляет право доступа к WiFi информации в ваш файл разрешений и идентификатор приложения. Ссылка на документацию

Swift 4.2

func getConnectedWifiInfo() -> [AnyHashable: Any]? {

    if let ifs = CFBridgingRetain( CNCopySupportedInterfaces()) as? [String],
        let ifName = ifs.first as CFString?,
        let info = CFBridgingRetain( CNCopyCurrentNetworkInfo((ifName))) as? [AnyHashable: Any] {

        return info
    }
    return nil

}

8
Спасибо! Если вы используете ARC, вот как это должно выглядеть: - (id) fetchSSIDInfo {NSArray * ifs = ( bridge_transfer id) CNCopySupportedInterfaces (); NSLog (@ "% s: поддерживаемые интерфейсы:% @", _func , ifs); id info = ноль; for (NSString * ifnam in ifs) {info = ( идентификатор_бриджа_трансфера) CNCopyCurrentNetworkInfo (( _bridge CFStringRef) ifnam); NSLog (@ "% s:% @ =>% @", __func , ifnam, info); if (info && [info count]) {break; }} вернуть информацию; }
elsurudo

1
+1 Отлично работает! Не забудьте добавить / связать фреймворк [+] с вашим проектом. Если вы видите странные ошибки компиляции при использовании этого метода, это, вероятно, ваша проблема. Например, чтобы получить SSID из возвращенного словаря, используйте // Получение объекта словаря, содержащего информацию о сети, к которой подключен iPhone. NSDictionary * networkDict = [self fetchSSIDInfo]; // Выбираем SSID из информации о сети NSString * iPhoneNetworkSSID = [networkDict objectForKey: @ "SSID"];
Groot

Кто-нибудь знает, что такое BSSID? это выглядит как MAC-адрес маршрутизатора, но на самом деле это не так. это не MAC-адрес устройства.
peetonn

1
@Filip Обновленный ARC-дружественный код решает эту проблему с помощью модульных включений ( @importа не #import). Clang автоматически связывается в нужной среде, когда импортируется модуль, а не просто заголовок.
Джереми У. Шерман

1
Бета-версия iOS 13 теперь требует разрешения для определения местоположения в дополнение к возможности CNCopyCurrentNetworkInfoвозврата полезной информации; в противном случае это возвращается nil. Смотрите: stackoverflow.com/questions/56583650/…
RedMatt

61

Вот очищенная версия ARC, основанная на коде @ elsurudo:

- (id)fetchSSIDInfo {
     NSArray *ifs = (__bridge_transfer NSArray *)CNCopySupportedInterfaces();
     NSLog(@"Supported interfaces: %@", ifs);
     NSDictionary *info;
     for (NSString *ifnam in ifs) {
         info = (__bridge_transfer NSDictionary *)CNCopyCurrentNetworkInfo((__bridge CFStringRef)ifnam);
         NSLog(@"%@ => %@", ifnam, info);
         if (info && [info count]) { break; }
     }
     return info;
}

1
Это действительно должен быть предпочтительный ответ, поскольку он использует ARC. Я пропустил это с самого начала только потому, что это не был предложенный ответ.
Йохан Карлссон

@mindbomb прав, вот вопрос об этой проблеме: stackoverflow.com/questions/31555640/…
newenglander

да, Apple снова включила API-интерфейс CaptiveNetwork после того, как устарела в бета-версиях iOS9 ..
mindbomb

@mindbomb это все еще так? У меня есть проблемы с реализацией этого.
SamYoungNY

@SamYoungNY Обратите внимание, что это работает только на устройстве, в симуляторе возвращается пустое значение
esad

60

ОБНОВЛЕНИЕ для iOS 10 и выше

CNCopySupportedInterfaces больше не поддерживается в iOS 10. ( Справочник по API )

Вам необходимо импортировать SystemConfiguration / CaptiveNetwork.h и добавить SystemConfiguration.framework в связанные библиотеки вашей цели (на этапах сборки).

Вот фрагмент кода в быстром (ответ RikiRiocma) :

import Foundation
import SystemConfiguration.CaptiveNetwork

public class SSID {
    class func fetchSSIDInfo() -> String {
        var currentSSID = ""
        if let interfaces = CNCopySupportedInterfaces() {
            for i in 0..<CFArrayGetCount(interfaces) {
                let interfaceName: UnsafePointer<Void> = CFArrayGetValueAtIndex(interfaces, i)
                let rec = unsafeBitCast(interfaceName, AnyObject.self)
                let unsafeInterfaceData = CNCopyCurrentNetworkInfo("\(rec)")
                if unsafeInterfaceData != nil {
                    let interfaceData = unsafeInterfaceData! as Dictionary!
                    currentSSID = interfaceData["SSID"] as! String
                }
            }
        }
        return currentSSID
    }
}

( Важно: CNCopySupportedInterfaces возвращает ноль на симуляторе.)

Для Objective-c см . Ответ Эсада здесь и ниже.

+ (NSString *)GetCurrentWifiHotSpotName {    
    NSString *wifiName = nil;
    NSArray *ifs = (__bridge_transfer id)CNCopySupportedInterfaces();
    for (NSString *ifnam in ifs) {
        NSDictionary *info = (__bridge_transfer id)CNCopyCurrentNetworkInfo((__bridge CFStringRef)ifnam);
        if (info[@"SSID"]) {
            wifiName = info[@"SSID"];
        }
    }
    return wifiName;
}

ОБНОВЛЕНИЕ ДЛЯ iOS 9

Начиная с iOS 9 Captive Network устарела *. ( источник )

* Больше не рекомендуется в iOS 10, см. Выше.

Рекомендуется использовать NEHotspotHelper ( источник )

Вам нужно будет отправить электронное письмо по адресу networkextension@apple.com и запросить разрешения. ( источник )

Пример кода ( не мой код. См. Ответ Пабло А ):

for(NEHotspotNetwork *hotspotNetwork in [NEHotspotHelper supportedNetworkInterfaces]) {
    NSString *ssid = hotspotNetwork.SSID;
    NSString *bssid = hotspotNetwork.BSSID;
    BOOL secure = hotspotNetwork.secure;
    BOOL autoJoined = hotspotNetwork.autoJoined;
    double signalStrength = hotspotNetwork.signalStrength;
}

Примечание: Да, они отказались от CNCopySupportedInterfaces в iOS 9 и полностью изменили свою позицию в iOS 10. Я разговаривал с сетевым инженером Apple, и это изменение произошло после того, как очень много людей подали заявки на Radars и высказались о проблеме на форумах Apple Developer.


Я получил одобрение Apple на использование расширения сети, но я все еще получил пустой массив из [NEHotspotHelper supportNetworkInterfaces]. Вы знаете возможную причину?
JZAU

28

Это работает для меня на устройстве (не симулятор). Убедитесь, что вы добавили каркас конфигурации системы.

#import <SystemConfiguration/CaptiveNetwork.h>

+ (NSString *)currentWifiSSID {
    // Does not work on the simulator.
    NSString *ssid = nil;
    NSArray *ifs = (__bridge_transfer id)CNCopySupportedInterfaces();
    for (NSString *ifnam in ifs) {
        NSDictionary *info = (__bridge_transfer id)CNCopyCurrentNetworkInfo((__bridge CFStringRef)ifnam);
        if (info[@"SSID"]) {
            ssid = info[@"SSID"];
        }
    }
    return ssid;
}

10

Этот код хорошо работает, чтобы получить SSID.

#import <SystemConfiguration/CaptiveNetwork.h>

@implementation IODAppDelegate

@synthesize window = _window;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{


CFArrayRef myArray = CNCopySupportedInterfaces();
CFDictionaryRef myDict = CNCopyCurrentNetworkInfo(CFArrayGetValueAtIndex(myArray, 0));
NSLog(@"Connected at:%@",myDict);
NSDictionary *myDictionary = (__bridge_transfer NSDictionary*)myDict;
NSString * BSSID = [myDictionary objectForKey:@"BSSID"];
NSLog(@"bssid is %@",BSSID);
// Override point for customization after application launch.
return YES;
}

И вот результаты:

Connected at:{
BSSID = 0;
SSID = "Eqra'aOrange";
SSIDDATA = <45717261 27614f72 616e6765>;

}


1
Работал отлично.
Yomo

9

Если вы используете iOS 12, вам нужно будет сделать дополнительный шаг. Я изо всех сил пытался заставить этот код работать и, наконец, нашел его на сайте Apple: «Важно! Чтобы использовать эту функцию в iOS 12 и более поздних версиях, включите возможность доступа к информации Wi-Fi для вашего приложения в XCode. Когда вы включите эту возможность, XCode автоматически добавляет право доступа к информации Wi-Fi в ваш файл разрешений и идентификатор приложения. " https://developer.apple.com/documentation/systemconfiguration/1614126-cncopycurrentnetworkinfo


Спасибо, это исправило мою проблему!
Давид72


5

Вот короткая и сладкая версия Swift.

Не забудьте связать и импортировать Framework:

import UIKit
import SystemConfiguration.CaptiveNetwork

Определите метод:

func fetchSSIDInfo() -> CFDictionary? {
    if let
        ifs = CNCopySupportedInterfaces().takeUnretainedValue() as? [String],
        ifName = ifs.first,
        info = CNCopyCurrentNetworkInfo((ifName as CFStringRef))
    {
        return info.takeUnretainedValue()
    }
    return nil
}

Вызовите метод, когда вам это нужно:

if let
    ssidInfo = fetchSSIDInfo() as? [String:AnyObject],
    ssID = ssidInfo["SSID"] as? String
{
    println("SSID: \(ssID)")
} else {
    println("SSID not found")
}

Как уже упоминалось, это работает только на вашем iDevice. Когда не по WiFi, метод вернет ноль - следовательно, необязательный.


2

Для iOS 13

Начиная с iOS 13, вашему приложению также необходим доступ к Core Location, чтобы использовать эту CNCopyCurrentNetworkInfo функцию, если только он не настроил текущую сеть или не имеет конфигурации VPN:
выдержка из https://developer.apple.com/documentation/systemconfiguration/1614126-cncopycurrentnetworkinfo?language=objc

Так что это то, что вам нужно (см. Документацию Apple ):
- Связать CoreLocation.frameworkбиблиотеку
- Добавить location-servicesв качестве UIRequiredDeviceCapabilitiesключа / значения в Info.plist
- Добавить NSLocationWhenInUseUsageDescriptionключ / значение в Info.plist, описывающий, почему вашему приложению требуется расположение ядра
- Добавить «Access WiFi» Информация "Право на ваше приложение

Теперь в качестве примера Objective-C, сначала проверьте, был ли принят доступ к местоположению, прежде чем читать информацию о сети, используя CNCopyCurrentNetworkInfo:

- (void)fetchSSIDInfo {
    NSString *ssid = NSLocalizedString(@"not_found", nil);

    if (@available(iOS 13.0, *)) {
        if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied) {
            NSLog(@"User has explicitly denied authorization for this application, or location services are disabled in Settings.");
        } else {
            CLLocationManager* cllocation = [[CLLocationManager alloc] init];
            if(![CLLocationManager locationServicesEnabled] || [CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined){
                [cllocation requestWhenInUseAuthorization];
                usleep(500);
                return [self fetchSSIDInfo];
            }
        }
    }

    NSArray *ifs = (__bridge_transfer id)CNCopySupportedInterfaces();
    id info = nil;
    for (NSString *ifnam in ifs) {
        info = (__bridge_transfer id)CNCopyCurrentNetworkInfo(
            (__bridge CFStringRef)ifnam);

        NSDictionary *infoDict = (NSDictionary *)info;
        for (NSString *key in infoDict.allKeys) {
            if ([key isEqualToString:@"SSID"]) {
                ssid = [infoDict objectForKey:key];
            }
        }
    }        
        ...
    ...
}

Это требуется на iOS13, иначе вы получите ноль для CNCopyCurrentNetworkInfo ()
Франц Ван

@Sugar да, действительно, iOS13 - смотрите первую строку моего ответа
Франсуа Б
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.