Пользователь
0,0
рейтинг
15 августа 2014 в 02:33

Разработка → Как позвонить на iOS7 [jailbreak] из приложения? из песочницы



У меня появилась задача позвонить с iPhone на iOS7. На предыдущих версиях iOS (6 и ранее) было достаточно воспользоваться private API и все работало, но на iOS7 этот подход перестал работать. В этом посте я хочу рассказать как сделать звонок и получить собственный номер телефона из приложения.

Код проекта на github.

Jailbreak


Первым делом, что бы все работало необходимо сделать Jailbreak. Для последней версии iOS 7.1.2 он отлично делается с помощью en.pangu.io — jailbreak tool Инструкцию по jalibreak можно посмотреть, например, здесь. Я проделал это с iPhone4, iOS 7.1.2 на Windows 8.1 После того, как сделан jailbreak устанавливаем OpenSSH через Cydia.



Заходим на iPhone по ssh и меняем пароль для root [default password: alpine]
$ssh root@10.231.65.56
$passwd


Телефон готов. Подготовим приложение, которое будет звонить. В приложении мы будем использовать следующие private APIs:

void        CTCallListDisconnectAll();      // for ending call  header: CTCall.h
CTCallRef   CTCallDial(CFStringRef number); // for calling header: CTCall.h
CFStringRef CTSettingCopyMyPhoneNumber();   // for getting own number header: CTSetting.h

Заголовочные файлыCTCall.h, CTSetting.h можно найти в репозитории на GitHub

Теперь рассмотрим код, который без проблем работает на iOS 6.1.4 и без jailbreak.

Метод старта звонка

- (IBAction)onPlaceCall:(id)sender {

    NSString *numberToCall = [self.tfNumberToCall text];

    NSLog(@"Open CoreTelephony");
    void *ptrCoreTelephone = dlopen("/System/Library/Framework/CoreTelephony.framework/CoreTelephony", RTLD_LAZY);

    if (ptrCoreTelephone == nil){
        NSLog(@"ptrCoreTelephone is nil");
        return;
    }

    NSLog(@"Get CTCallDial from CoreTelephony");
    int (*pCTCallDial)(NSString*) = dlsym(ptrCoreTelephone, "CTCallDial");

    if (pCTCallDial != nil) {
        int error = pCTCallDial(numberToCall);
        NSLog(@"pCTCallDial error: %d", error);
    }
    NSLog(@"Close CoreTelephony");
    dlclose(ptrCoreTelephone);
}

Метод прерывания завонка

- (IBAction)onStopCall:(id)sender {
    NSLog(@"onStopCall");
    void *ptrCoreTelephone = dlopen("/System/Library/Framework/CoreTelephony.framework/CoreTelephony", RTLD_LAZY);

    if (ptrCoreTelephone == nil){
        NSLog(@"ptrCoreTelephone is nil");
        return;
    }

    NSLog(@"Get CTCallListDisconnectAll from CoreTelephony");
    int (*pCTCallListDisconnectAll)() = dlsym(ptrCoreTelephone,
            "CTCallListDisconnectAll");
    if (pCTCallListDisconnectAll != nil) {
        int error = pCTCallListDisconnectAll();
        NSLog(@"pCTCallListDisconnectAll error: %d", error);
    }
    dlclose(ptrCoreTelephone);
}

Метод получения номера

-(NSString*) getMyNumber {
    NSLog(@"Open CoreTelephony");
    void *lib = dlopen("/Symbols/System/Library/Framework/CoreTelephony.framework/CoreTelephony",RTLD_LAZY);
    NSLog(@"Get CTSettingCopyMyPhoneNumber from CoreTelephony");
    NSString* (*pCTSettingCopyMyPhoneNumber)() = dlsym(lib, "CTSettingCopyMyPhoneNumber");
    NSLog(@"Get CTSettingCopyMyPhoneNumber from CoreTelephony");

    if (pCTSettingCopyMyPhoneNumber == nil) {
        NSLog(@"pCTSettingCopyMyPhoneNumber is nil");
        return nil;
    }
    NSString* ownPhoneNumber = pCTSettingCopyMyPhoneNumber();
    dlclose(lib);
    return ownPhoneNumber;
}


Теперь самое интересное. Этот же код будет работать на iOS7 если добавить entitlements приложению и переподписать его с этими entitlements. Для этого нужно проделать следующее.

  • Собрать приложение без подписи
  • Подготовить и подложить приложение xml файл с entitlements
  • Подписать приложение и положить на телефон


Сборка приложения без подписи

Для этого нужно в XCode в Build Settings выставить следующее:

P

Затем запуситить сборку.

Подготовка xml файла с entitlements

Нужно создать файл со следующим содержимым:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <dict>
        <key>com.apple.coretelephony.Calls.allow</key>
        <true/>
        <key>com.apple.coretelephony.Identity.get</key>
        <true/>
    </dict>
</plist>


com.apple.coretelephony.Calls.allow — для звонков
com.apple.coretelephony.Identity.get — для получения номера
Положить его нужно в тоже место, откуда будет запущена команда подписи.

Подпись и установка

Для подписи нам понадобиться папка с бинарниками. Понять, куда их кладет XCode, можно запустив команду:
$ls -la ~/Library/Developer/Xcode/DerivedData/ | grep JBCall


Путь будет примерно таким:
/Users/username/Library/Developer/Xcode/DerivedData/JBCall-cktasembftvbmqaaiiunvljdwocs/Build/Products/Debug-iphoneos/JBCall.app


Копируем JBCall.app в отдельную папку, кладем рядом сней entitlements.xml:
JBCall.app
entitlements.xml


Теперь нужно подписать бинарники следующей командой:
$codesign --sign='iPhone Developer: FirstName  SecondName (XXXXXXXX)’ --entitlements entitlements.xml JBCall.app


iPhone Developer: FirstName SecondName (XXXXXXXX) — название сертификата, котоырй можно посмотреть в keychain.


После проделанных действий можно установить приложение и пользоваться. В данном случае установка заключается в копировании папки *.app на iPhone и перезапуск SpringBoard. Я сделал это через scp в скрпте:

DST_DIR='/Applications'
APP_NAME='JBCall.app'
USER='root'
PASSWD='mypass'
IP='10.231.65.56'
APP_ON_MAC="/Users/username/Library/Developer/Xcode/DerivedData/JBCall-cktasembftvbmqaaiiunvljdwocs/Build/Products/Debug-iphoneos/JBCall.app"
sshpass -p $PASSWD scp -r $APP_NAME $USER@$IP:$DST_DIR


В скрипте нужно поменять только значения переременных под себя и можно запускать. Путь тоже нужно обновить:

/Users/username/Library/Developer/Xcode/DerivedData/JBCall-cktasembftvbmqaaiiunvljdwocs/Build/Products/Debug-iphoneos/JBCall.app

Перезапуск SpringBoard:
$sshpass -p $PASSWD ssh $USER@$IP su mobile -c uicache 1>/dev/null 2>&1


Отдельная команда перезапуска с девайса:
$su mobile -c uicache 1>/dev/null 2>&1

Конечно должны быть установлены sshpass и scp.

Все описанные действия по установки оформлены в виде скрипта
Все, теперь приложение может звонить, класть трубку, показывать номер телефона.

Скриншоты с iOS 6.1.4


Скриншоты с iOS 7.1.2


Итог


В итоге мы получили приложение, которое звонит на последней версии iOS с jailbreak. Надеюсь кому-нибудь пригодиться данный опыт.

Как я узнал об entitlements?


Спасибо ползователю creker на stackoverflow за подсказку о entitlements, которая была им обнаружена в файле:
/System/Library/CoreServices/SpringBoard.app/SpringBoard

Действительно, взглянув на бинарник можно найти текстовые строки:
com.apple.coretelephony.Calls.allow
com.apple.coretelephony.Identity.get




Ссылки


@AMDev
карма
8,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (8)

  • –1
    А с ipad как дела?
    • +2
      На iPad не пробовал, нет под рукой. Думаю jailbreak на нем так же можно сделать.
      • 0
        я к тому, что с ipad ни звонить ни писать смс нельзя в принципе.
        • +6
          Хорошо. А вопрос то о чем был?
          • 0
            Думаю вопрос был в том, появится ли возможность звонить после взлома описанным в статье способом?
            • 0
              Нет. iPad это не телефон, в нем нет аппарартной телфеонной части. Но есть разные спосбобы звонить с iPad через интернет (Skype, GoogleVoice, ect.)
  • 0
    Приложение для iOS умеет определять есть ли на телефоне jailbreak?
    Если умеет, то пропускает ли Apple в AppStore приложения которые при наличии jailbreak содержат дополнительные функции? Или Apple об этом может просто не узнать и приложение пропустит?
  • 0
    Данное приложение не умеет определять есть ли jailbreak. Но это легко исправить, например так:

    FILE *f = fopen("/bin/bash", "r");
    BOOL isbash = NO;
    if (f != NULL)
    {
        //Device is jailbroken
        isbash = YES;
    }
    fclose(f);
    


    Apple не пропускает приложения с private API внутри, они проверяют каждое приложение на это дело, т.е. они узнают и, скорее всего, не пропустят.

    Тут другой момент, как подписать приложение обычным сертификатом для разработки так, что бы можно было включить кастомные entitlements и ставить как обычное приложение, а не копированием на файловую систему. Это как манифест для анройдного приложения, описывает возможности/доступ к функциональности. Если в манифесте это не было прописано, то приложение не может это использовать.
    В случае iOS нужно, чтобы конкретный provisioning profile включал эти самые строки, а для этого нужно договориться с Apple. Для стандартных возможностей, таких как доступ к iCloud это делается по инструкции, а вот для нестандартных com.apple.coretelephony.Calls.allow это вообще не предусмотрено.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.