点击上方蓝字关注我们
前言
- 原文:
https://kunnan.blog.csdn.net/article/details/114634689
- 项目描述
1、下载流程:清理进程和数据(包括keychain及修改设备信息)、
切换IP
、登录appID、打开App Store、在App Store搜索应用、下载并安装app(打码)、注销app ID、关闭App Store、卸载app 2、 评论流程:在下载流程的基础上进行评论 ———————————————— 版权声明:本文为CSDN博主「#公众号:iOS逆向」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/z929118967/article/details/114634689
本文介绍IP切换的实现方案:三种切换IP的方式,涉及两种实现方案
方案一:使用performSelectorOnMainThread 结合递归来执行切换IP的任务
应用场景:使用代理app进行切换 切换IP方式一: 直接调用注入SB的方法,而非请求 切换IP方式二:直接请求接口进行IP切换
方案二: 使用基于CFRunLoopDoSources0自定义Operation执行切换IP任务
1、每个Operation 暂时定为尝试切换5次,5次失败之后(尝试切换Wi-Fi,);直到OperationSucceed ,才执行下一步
登录appID
2、应用场景:在设置->通用->V_P_N 配置代理IP账号 切换IP方式三:通过代码来开启和关闭设置->通用->V_P_N
开关进行切换
I、 方案一:采用递归执行切换IP任务
- SwitchIPOperation的用法
// start 使用xxxx切换IP
while (YES) {
@autoreleasepool {
SwitchIPOperation *knop = [[[SwitchIPOperation alloc] initWithTimeOut:3 Operation:YES tryTimes:1] autorelease]; //tryTimes表示尝试次数,0为无限次,直到成功为止
//时间initWithTimeOut设置长些,避免,OperationTimeOut = 4,
[self runSubOperation:knop];
CheckCancel(self);
[LogWindow info: FMSTR(@"[sb] op.status:%ld",(long)knop.status)];
if (knop.knstatus == 1) {//Succeed
break;
}
// [LogWindow warn: FMSTR(@"[sb] 发现切换IP状态异常, 尝试切换Wi-Fi修正")];//
// op = [[[KillProcessOperation alloc] initWithProcessName:@"pppd" containsMode:YES] autorelease]; // 并没有什么卵用, 改到daemon里去做
// RunSubOperation(self, op);
// [[objc_getClass("SBWiFiManager") sharedInstance] setWiFiEnabled:offOrOn];
// DPost(@"kill", @"pppd");//尝试切换Wi-Fi pppd修正()--
// [[objc_getClass("SBWiFiManager") sharedInstance] setPowered:NO];
// [[objc_getClass("SBWiFiManager") sharedInstance] setWiFiEnabled:NO];
//
// [[objc_getClass("SBWiFiManager") sharedInstance] setPowered:YES];
// [[objc_getClass("SBWiFiManager") sharedInstance] setWiFiEnabled:YES];
// [WindowTool setupCloseOpenWifi];
[LogWindow info: FMSTR(@"[sb] 稍后重新尝试切换IP!")];
Sleep(self, 3);
}
}
// end 使用xxxx切换IP
1.1 SwitchIPOperation
- SwitchIPOperation.h
@interface SwitchIPOperation : Operation
// (void) handleVPNChanged:(NSNotification*) notification;//处理VPN鉴权的事件
-(id) initWithTimeOut:(NSTimeInterval) expire Operation:(BOOL) operation tryTimes:(int) tryTimes;
@property (atomic, assign) NSInteger knstatus;
@end
- private : SwitchIPOperation.m
@implementation SwitchIPOperation {
int _wannaTryTimes;
int _tryTimes;
BOOL operation;
}
-(id) initWithTimeOut:(NSTimeInterval) expire Operation:(BOOL) op tryTimes:(int) tryTimes{
//tryTimes 重试操作的次数,每次超时expire
//tryTimes 为0时,无限尝试,直到成功,或被取消为止
self = [super initWithTimeOut:expire];
if (self) {
_tryTimes = 0;
_wannaTryTimes = tryTimes;
operation = op;
[LogWindow debug:@"[switch ip] 任务创建!"];
}
return self;
}
-(void) dealloc {
[super dealloc];
}
-(void) willLeaveRunloop {
}
-(NSString*) name {
return @"switch ip操作";
}
-(void) actionVpn:(id) op {
// 使用递归,在block中触发退出条件
__block typeof(self) weakSelf = self;
[WindowTool setupSwitchIp:^(NSString *errorMsg, id result) {
if (errorMsg == nil && result != nil) {
[LogWindow info:FMSTR(@"[switch ip] result %@!", result)];
[LogWindow debug:FMSTR(@"[switch ip] -%@- 成功!", [op boolValue] ? @"开启" : @"关闭")];
[weakSelf wakeUpForReason:OperationSucceed];// 切换成功调用的方法
weakSelf.knstatus = 1;
}else{
[LogWindow info:FMSTR(@"[switch ip] errorMsg %@!", errorMsg)];
if (_wannaTryTimes > 0 && _tryTimes >= _wannaTryTimes){//递归结束
[weakSelf wakeUpForReason:OperationFail];
[LogWindow warn:FMSTR(@"[switch IP] 尝试次数:%d 已达到尝试上限次数:%d 仍未成功!", _tryTimes, _wannaTryTimes)];
return;
}else{
[weakSelf runOnce];
}
}
}
-(void) runOnce{//多次执行runOnce,采用递归的方式
_tryTimes ;
if (_tryTimes >= 1) {
[LogWindow info:MGSTR(@"[switch IP] 尝试%@切换IP 第%d次!", operation?@"开启":@"关闭", _tryTimes)];
}
[self performSelectorOnMainThread:@selector(actionVpn:) withObject:@(operation) waitUntilDone:YES];
}
-(void) run {
[self runOnce];// 要求这个方法必须是同步的,否则会存在多次执行此方法的情况,可以采用dispatch信号量(dispatch semaphore)dispatch_semaphore_wait 实现同步,目前采用递归
[super run];
}
@end
1.2 工具类:WindowTool
WindowTool功能:
1、 网络异常的时候自动关闭VPN(防止VPN有效性过期),并且自动断开Wi-Fi,再次重新连接(防止IP有效期超时)。
- WindowTool.h
///errorMsg 等于 nil 就表示切换成功了, errorMsg不等于nil里面就是失败的原因
typedef void(^ABYCallback)( NSString *errorMsg, id result);
@interface WindowTool : NSObject
(void) setupCloseOpenWifi;
(void) setupSwitchIp:(ABYCallback)callback;// 切换IP,可通过直接调用方法切换,也可以通过请求API进行切换IP
@end
- WindowTool.m
代码语言:javascript复制切换IP方式一: 直接调用注入SB的方法,而非请求 切换IP方式二:直接请求接口进行IP切换
@implementation ASWindowTool
static int launchProcess(const char* path, char** args) {
posix_spawnattr_t attr;
posix_spawn_file_actions_t fact;
posix_spawnattr_init(&attr);
posix_spawn_file_actions_init(&fact);
char* const environ[2] = {"", NULL};
pid_t pid = 0;
posix_spawnp(&pid, path, &fact, &attr, args, environ);
return pid;
}
(BOOL) noZombieRun:(NSString*) path withParams:(NSArray*)args {
const char* _path = [path UTF8String];
int n = args ? args.count : 0;
char** _args = alloca(sizeof(char*) * (n 2));
_args[0] = "";
_args[n 1] = NULL;
for (int i = 0; i < n; i ) {
NSString* arg = args[i];
_args[i 1] = [arg UTF8String];
}
pid_t pid = fork();
if (pid < 0) {
return NO;
} else if (pid == 0) {
launchProcess(_path, _args);
_exit(0);
}
waitpid(pid, NULL, 0);
return YES;
}
/**
网络异常的时候自动关闭VPN(防止VPN有效性过期),并且自动断开Wi-Fi,再次重新连接(防止IP有效期超时)。
文章: https://blog.csdn.net/z929118967/article/details/112829168
*/
(void)setupWifi{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
VPNOperation *op = [[[VPNOperation alloc] initWithTimeOut:10 Operation:NO tryTimes:1] autorelease];
[op actionVpn:NO];
// //打开Wi-Fi开关
// popen(@"wifiutil enable-wifi", @"r");
// popen(@"wifiutil disassociate", @"r");//断开连接
// //重新连接
// popen(@"wifiutil associate @PHICOMM_08 -p kunnan2018", @"r");
NSLog(@"wifiutil enable-wifi");
//打开Wi-Fi开关
// run(@"/usr/bin/wifiutil enable-wifi");
// run(@"/usr/bin/wifiutil disassociate");//断开连接
//重新连接
// run(@"/usr/bin/wifiutil associate @PHICOMM_08 -p kunnan2018");
// NSArray* args = @[[@(self.level) stringValue]];//构建一个数组
NSArray* args = @[@"enable-wifi"];
[self noZombieRun:@"/usr/bin/wifiutil" withParams:args];
NSArray* args1 = @[@"disassociate"];
[self noZombieRun:@"/usr/bin/wifiutil" withParams:args1];
NSArray* args2 = @[@"associate",@"@PHICOMM_08",@"-p",@"kunnan2018"];
[self noZombieRun:@"/usr/bin/wifiutil" withParams:args2];
});
}
/**
目前用于处理网络异常情况:
1、自动连接Wi-Fi: xxx/ASO/wifiutil.git
2、自动设置永不锁屏
3、自动处理进程通信的问题
4、网络异常的时候自动关闭VPN(防止VPN有效性过期),并且自动断开Wi-Fi,再次重新连接(防止IP有效期超时)。
*/
(void) setupCloseOpenWifi{
if (!isUsingAbunSwithIp) {// 处理场景1: 【通过代码来开启和关闭`设置->通用->V_P_N`开关进行切换】
[self setupWifi];
return;
[[objc_getClass("SBWiFiManager") sharedInstance] setPowered:NO];//-fno-objc-arc
[[objc_getClass("SBWiFiManager") sharedInstance] setWiFiEnabled:NO];
__block typeof(self) weakSelf = self;
//
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[[objc_getClass("SBWiFiManager") sharedInstance] setPowered:YES];
[[objc_getClass("SBWiFiManager") sharedInstance] setWiFiEnabled:YES];
//如果发现网络异常,就立马关闭VPN 一定要放到主进程运行
VPNOperation *op = [[[VPNOperation alloc] initWithTimeOut:10 Operation:NO tryTimes:1] autorelease];
[op actionVpn:NO];
});
}else{//处理场景2:【直接调用注入SB的方法,而非请求 or 直接请求接口进行IP切换】
// 考虑异步通知xxxxx切换IP,此时可能IP 失效了,目前的IP失效时间是900‘
[self setupSwitchIp:nil];// 此方法是异步的
}
}
/**
ABYCallback callback = ^(NSString *errorMsg, id result)
{
if (errorMsg)
{
NSLog(@"lg error:%@",errorMsg);
} else
{
NSLog(@"lg switch ip success");
}
};
必须异步处理,否则sb 会进入安全模式
@param callback <#callback description#>
*/
(void) setupSwitchIp:(ABYCallback)callback{
if(isUsingAbunSwithIpType1){
//直接调用方法
[self setupSwitchIpABUYUN1:callback];
}else{
//最新的方案,直接请求接口进行IP切换
[self actionSwitchIP:callback];
}
return;
}
/**
///errorMsg 等于 nil 就表示切换成功了, errorMsg不等于nil里面就是失败的原因
typedef void(^ABYCallback)( NSString *errorMsg, id result);
{ code : 10000, status : 407, error : 'proxy authentication required' }
{ code : 10000, status : 402, error : 'payment required' }
@param callback <#callback description#>
*/
(void) actionSwitchIP:(ABYCallback)callback{
NSString *urlstring = @"http://proxy.xxxyun.com/switch-ip";
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlstring]];
request.timeoutInterval = 10.0;//最多10‘
[[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"requestFinished");
NSHTTPURLResponse *hTTPURLResponse =(NSHTTPURLResponse*)response;
NSLog(@"statusCode: %li", (long)[hTTPURLResponse statusCode]);
NSString * errorMsg = nil;
if (error == nil) {//请求成功
if (hTTPURLResponse.statusCode == 500)
{
NSLog(@"%s %@",__func__,@"系统内部错误");
errorMsg =@"系统内部错误";
if (callback) {
callback(errorMsg,nil);
}
return;
}
if (hTTPURLResponse.statusCode != 200){
NSLog(@"%s %ld",__func__,(long)hTTPURLResponse.statusCode);
errorMsg =[NSString stringWithFormat:@"%s %ld",__func__,(long)hTTPURLResponse.statusCode];
//非200
if (callback) {
callback(errorMsg,nil);
}
return;
}
NSError *JSONObjectWithDataError = nil;
NSDictionary *response = [NSJSONSerialization JSONObjectWithData:data options:0 error:&JSONObjectWithDataError];
if (JSONObjectWithDataError) {
NSLog(@"%s %@",__func__,JSONObjectWithDataError);
// invalid json data
errorMsg = [JSONObjectWithDataError description];
if (callback) {
callback(errorMsg,nil);
}
return;
}
//处理返回数据
NSNumber *code = response[@"code"];
if (code.intValue == 0){
if (callback) {
//切换成功
callback(nil,response[@"proxy"]);//返回代理信息
}
}else{
if (callback) {//业务逻辑失败
errorMsg = [NSString stringWithFormat:@"switch-ip failed, error:%@ %@",response[@"status"],response[@"error"]];
callback(errorMsg,nil);
}else{//默认的业务逻辑失败处理
//可选
}
}
}else{ //请求失败
NSString *errorMsg = @"请求失败,请检查网络是否可用,或者阿布云app未打开";
if (callback) {
callback(errorMsg,nil);
}else{//默认的请求失败处理
NSLog(@"%s %@",__func__,@"请求失败,请检查网络是否可用");
}
}
}]resume];//所有类型的task都要调用resume方法才会开始进行请求.
return;
// NSString *urlstring = @"http://proxy.xxxyun.com/switch-ip";
// NSString *urlstring2 = @"http://proxy.xxxyun.com/current-ip";
NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString: urlstring]];
NSDictionary * root = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
if (root) {
NSNumber *code = root[@"code"];
if ( code.intValue == 0) {
NSDictionary *proxy = root[@"proxy"];
NSLog(@"switch-ip success,当前IP: %@,剩余时间: %@",proxy[@"ip"],proxy[@"left"]);
callback(nil,proxy);
}
else{
NSString *errorMsg = [NSString stringWithFormat:@"switch-ip failed, error:%@ %@",root[@"status"],root[@"error"]];
NSLog(@"switch-ip failed, error:%@ %@",root[@"status"],root[@"error"]);
callback(errorMsg,nil);
}
}
else{
NSLog(@"switch-ip error,invalid json data");
NSString *errorMsg = @"switch-ip error,invalid json data";
callback(errorMsg,nil);
}
}
/**
切换IP方式一:直接调用方法,而非请求
@param callback <#callback description#>
*/
(void)setupSwitchIpABUYUN1:(ABYCallback)callback{
//sb 已经包含这个ABuyunClient2
if (NSClassFromString(@"xxxyunClient2") == nil) {
if(callback){
callback(@"尚未安xxxdeb 包",nil);
}
return;
}
[NSClassFromString(@"xxxyunClient2") performSelector:NSSelectorFromString(@"actionSwitchIP:") withObject:callback];
}
II、 方案二: 使用基于CFRunLoopDoSources0自定义Operation执行切换IP任务
- VPNOperation的用法