逆向Preferences中关于VPN部分的问题


#1

由于想看一下在Preferences中,是怎么去打开关闭VPN的,现在通过分析,得出在Preferences中的时候是通过调用VPNPreferences.bundle里面的VPNBundleController的setVPNActive:forSpecifier:方法实现的,并且可以用Cycript注入Preferences,生成一个VPNBundleController对象直接调用_setVPNActive:达到在Preferences中的时候同样实现打开已配置过的VPN,同样也知道通过VPNBundleController的initWithParentListController:方法可以拿到一个VPNBundleController实例去调用_setVPNActive:方法。那么现在如果我想直接写一个app,提供在我的app里面去打开关闭VPN,该怎么实现呢?由于VPNBundleController存在于VPNPreferences.bundle,我可以通过代码去加载VPNPreferences.bundle,但是加载了之后我该怎么去生成VPNBundleController实例呢,因为缺乏头文件,使用VPNBundleController的时候会提示未定义该类型,把class-dump出来的头文件引入到项目,还是会缺乏某些头文件,尝试过使用runtime去获取该类型实例再调方法可以通过编译,可是运行时却崩溃提示该对象没有_setVPNActive:方法,VPNPreferences.bundle也是已经加载成功了的,请问是哪里出错了吗,还是缺乏什么步骤了,该怎么在自己的项目上去关闭打开VPN呢


#2

现成代码 https://GitHub.com/Naville/ManualVPN


#3

一点微小的工作

void createVPN(NSString* server, NSString* username, NSString* password, NSString* type)
{
    NSBundle* vpn = [NSBundle bundleWithPath:@"/System/Library/PreferenceBundles/VPNPreferences.bundle"];

    if ([vpn load] == NO)
    {
        DbgLog(@"load vpn failed");
        return;
    }

    VPNConnectionStore* vpnStore;
    VPNSetupListController* vpnSetup;

    vpnStore = [objc_getClass("VPNConnectionStore") sharedInstance];

    for (NEConfiguration* cfg in [vpnStore configurations])
    {
        NSString* uuid = [[cfg identifier] UUIDString];

        if ([vpnStore respondsToSelector:@selector(deleteVPNWithServiceID:)])
        {
            [vpnStore deleteVPNWithServiceID:uuid];
        }
        else if ([vpnStore respondsToSelector:@selector(deleteVPNWithServiceID:withGrade:)])
        {
            [vpnStore deleteVPNWithServiceID:uuid withGrade:nil];
        }
        else
        {
            DbgLog(@"cant delete vpn");
            return;
        }
    }

    sleep(2);

    vpnSetup = [[objc_getClass("VPNSetupListController") alloc] init];
    [vpnSetup setDisplayName:@"displayName" forSpecifier:nil];
    [vpnSetup setVPNType:(__bridge CFStringRef)type forSpecifier:nil];
    [vpnSetup setServer:server forSpecifier:nil];
    [vpnSetup setUsername:username forSpecifier:nil];
    [vpnSetup setPassword:password forSpecifier:nil];
    [vpnSetup setSendAllTraffic:[NSNumber numberWithBool:YES] forSpecifier:nil];
    [vpnSetup setPPTPEncryptionLevel:@1 forSpecifier:nil];

    if ([vpnSetup respondsToSelector:@selector(saveConfigurationSettings)])
    {
        [vpnSetup saveConfigurationSettings];
    }
    else if ([vpnSetup respondsToSelector:@selector(_saveConfigurationSettings)])
    {
        [vpnSetup _saveConfigurationSettings];
    }
    else
    {
        DbgLog(@"cant saveConfigurationSettings");
        return;
    }

    sleep(2);
}
VPNStatus::Status connectVPN(BOOL connect)
{
    VPNConnectionStore*     vpnStore;
    VPNConnection*          vpn;
    dispatch_semaphore_t    semaphore;
    NSNotificationCenter*   center;
    id                      changeObserver;

    if (loadVPNBundle() == NO)
        return VPNStatus::NotConnected;

    vpnStore = [objc_getClass("VPNConnectionStore") sharedInstance];
    vpn = [vpnStore currentConnectionWithGrade:[vpnStore currentOnlyConnectionGrade]];
    if (vpn == nil)
        return VPNStatus::NotConnected;

    DbgLog(@"vpn = %@", vpn);
    DbgLog(@"session_status = %d, status = %ld, statusText = %@", [vpn session_status], [vpn status], [vpn statusText]);

    if (connect == NO && VPNStatus::fromStatus([vpn status]) == VPNStatus::NotConnected)
        return VPNStatus::NotConnected;

    semaphore = dispatch_semaphore_create(0);
    center    = [NSNotificationCenter defaultCenter];

    changeObserver = [center
                        addObserverForName:@"VPNConnectionStatusChanged"
                        object:nil
                        queue:[NSOperationQueue mainQueue]
                        usingBlock:^(NSNotification* note) {
                            NSString*       name;
                            VPNConnection*  vpn;
                            // NSDictionary*   userInfo;

                            name     = [note name];
                            vpn      = [note object];
                            // userInfo = [note userInfo];

                            DbgLog(@"name     = %@", name);
                            DbgLog(@"object   = %@", vpn);
                            // DbgLog(@"userInfo = %@", userInfo);
                            DbgLog(@"session_status = %d, status = %ld, statusText = %@", [vpn session_status], [vpn status], [vpn statusText]);

                            switch (VPNStatus::fromStatus([vpn status]))
                            {
                                case VPNStatus::Connecting:
                                case VPNStatus::Disconnecting:
                                    break;

                                default:
                                    dispatch_semaphore_signal(semaphore);
                            }
                        }
                    ];

    connect ? [vpn connect] : [vpn disconnect];

    const int64_t tenSeconds = 15ll * 1000 * 1000 * 1000;

    if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, tenSeconds)) != 0)
    {
        DbgLog(@"connectVPN timeout");
        [vpn disconnect];
        dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, tenSeconds));
    }

    [center removeObserver:changeObserver];

    DbgLog(@"connectVPN done");

    return VPNStatus::fromStatus([vpn status]);
}

#4

感谢,看了下你的代码获益良多,你是通过hook springboard在启动app时,app在自己的plist列表里面,去调用VPNBundleController 的setVPNActive:方法去控制VPN的吧,我想问下你在测试的时候启动的是哪个APP让它去加载的呢,我试了下hook com.apple.Preferences,在Preferences启动的时候去调setVPNActive:也可以实现效果,可是我在常规app里,比如在app里有一个按钮,点击就打开VPN,我在app上写了同样的代码,就没效果了,log打印出Failed to lock SCPreferences Permission denied,不知道是什么原因,并且我的app是以root权限运行的,是不是还要什么ent呢?


#5

苟利国家生死以


#6

嗯权限问题,具体没研究。


#7

你的demo也是在启动Preferences的时候才有效吗请问


#8

膜拜~可惜我都没达到这个层次看懂你的代码:disappointed_relieved:,好可惜


#9

我是注入SpringBoard的


#10

原来可以监听"VPNConnectionStatusChanged"这个消息啊,我都是启动一个定时器,每隔一阵子检查下VPN状态…跟你这方法比起来low爆了…囧


#11

你好,我按照您这边的思路将设置vpn的代码放到SpringBoard里执行有两个小问题想请教:
1、saveConfigurationSettings没有报错,但是实际并没有保存下来;
2、代码放到SpringBoard里执行,connnect的时候会有如下错误:
SpringBoard[29999]: Save error: Error Domain=NEConfigurationErrorDomain Code=10 “permission denied” UserInfo={NSLocalizedDescription=permission denied}
其中,第一个问题我尝试将代码放到Preferences进程执行的时候可以保存成功,但是链接没有成功。
请问您有遇到这样的问题吗?我的环境是iphone4s iOS9.0.2


#12

PS:我是希望将整个创建vpn,连接vpn的代码放到一个可以在后台一直执行的进程里,如果放在Preferences进程,只要Preferences被切换到后台,我用socket就没法通知切换vpn了,哪位达人可以帮忙指点一下,十分感谢


#13

呃, 创建要在Preferences, 切换可以在SB


#14

请教这里面可以通过修改app权限的配置文件来实现一个进程里完成这些事情吗?
我原来做安卓,安卓是有一个上层的权限体系,app可以通过申请的方式向系统注册权限(有些权限是只有系统级应用才可以申请的),其实就是保存在一个类似windows注册表的xml文件里。有了这个权限就可以控制比如创建vpn类似的动作。ios这里面我看也有ent.xml类似的文件,会不会也是采用注册申请的方式呢?


#15

之前玩过这个,如果还没解决,可以通过q号交流,名字是q号。


#16

大神,在iOSOpenDev 中建的tweak 的capabilites 权限开关选项在哪?我想实现vpn的切换,怎么弄


#17

tweak capabilities权限开关在哪?我想实现vpn切换 ,,该怎么实现?大神请赐教。


#18

如果你按上面写的代码,应该没问题。可能是没加权限吧。


#21

大神,我现在需要在tweak 里创建并连接VPN 用NEVPNManager ???而不是打开或关闭一个已经配置过的VPN。这个该怎么弄??NEVPNProtocolIKEv2 该怎么配置??


#22

先学会好好说话, 看不懂