由于想看一下在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呢
一点微小的工作
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]);
}
感谢,看了下你的代码获益良多,你是通过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呢?
苟利国家生死以
嗯权限问题,具体没研究。
你的demo也是在启动Preferences的时候才有效吗请问
膜拜~可惜我都没达到这个层次看懂你的代码,好可惜
我是注入SpringBoard的
原来可以监听"VPNConnectionStatusChanged"这个消息啊,我都是启动一个定时器,每隔一阵子检查下VPN状态…跟你这方法比起来low爆了…囧
你好,我按照您这边的思路将设置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
PS:我是希望将整个创建vpn,连接vpn的代码放到一个可以在后台一直执行的进程里,如果放在Preferences进程,只要Preferences被切换到后台,我用socket就没法通知切换vpn了,哪位达人可以帮忙指点一下,十分感谢
呃, 创建要在Preferences, 切换可以在SB
请教这里面可以通过修改app权限的配置文件来实现一个进程里完成这些事情吗?
我原来做安卓,安卓是有一个上层的权限体系,app可以通过申请的方式向系统注册权限(有些权限是只有系统级应用才可以申请的),其实就是保存在一个类似windows注册表的xml文件里。有了这个权限就可以控制比如创建vpn类似的动作。ios这里面我看也有ent.xml类似的文件,会不会也是采用注册申请的方式呢?
之前玩过这个,如果还没解决,可以通过q号交流,名字是q号。
大神,在iOSOpenDev 中建的tweak 的capabilites 权限开关选项在哪?我想实现vpn的切换,怎么弄
tweak capabilities权限开关在哪?我想实现vpn切换 ,,该怎么实现?大神请赐教。
如果你按上面写的代码,应该没问题。可能是没加权限吧。
大神,我现在需要在tweak 里创建并连接VPN 用NEVPNManager ???而不是打开或关闭一个已经配置过的VPN。这个该怎么弄??NEVPNProtocolIKEv2 该怎么配置??
先学会好好说话, 看不懂