iOS打包教程
2023-04-18 本文已影响0人
一直在路上66
iOS逆向知识掌握
- 通过脱壳、运行时分析、静态分析、动态调试、Hook、注入等一系列技术手段,推导出目标文件的结构、流程、算法、代码等,称为“iOS应用逆向工程”(后续简称为“iOS逆向工程”)
- 了解Objective-C、Swift的基本特性以及Xcode的使用方法,等熟练之后,还需要了解一些底层知识,例如Mach-O文件结构、Runtime机制等。
- 推荐阅读 《iOS应用逆向工程》 《iOS应用逆向与安全之道》
越狱手机
- 需要一台越狱手机 安装爱思助手
应用脱壳
- iOS端App在上线之前会由苹果商店进行FairPlayDRM数字版权加密保护(称为“加壳”)otool可以看到二进制文件的信息里有一个cryptid字段,cryptid=1表示已加壳,cryptid=0表示未加壳。具体如下。
- 使用 Frida-ios-dump工具脱壳 frida-ios-dump
工具之IDA
- 下载安装ida ida地址
- 加载文件并分析完成之后,用户便可以根据需求进行逆向分析。在进行逆向分析前有必要了解IDA界面的各种组件,IDA分析完成后的默认界面如下所示。 image.png
- 主要分析每个类里面主要有哪些方法 例如PFSDManger集成brsdk的一些方法在hook时 hook这些方法就可以了 image.png
越狱开发工具之Theos
- Theos是一个跨平台的越狱开发工具包,可以在Windows、Linux、macOS甚至iOS下工作。Theos无论是下载安装还是编译发布都比较简单,它为开发者准备好了一些代码模板、预置了一些基本的Makefile脚本,这样开发一个插件就会变得方便很多,其Logos语法也是相当优雅,让开发者可以更专注于功能开发。Theos相当于对Cydia Substrate的封装,因而能实现对Objective-C运行时的Hook,也能实现对C语言函数的Hook
Theos安装
- 在Theos开发中,iOS文件的签名由ldid工具来完成,ldid取代了Xcode自带的codesign。使用brew安装即可
brew install ldid
- 2.安装xz、lzma
xz、lzma是两种压缩模块,为了后面“make package”能够顺利通过,需要安装它们。安装方法如下:
$ brew install xz
$ sudo cpan IO::Compress::Lzm
- 3.添加$THEOS环境变量
读者可以使用任何编辑工具打开.bash_profile文件,添加下面的环境变量:
export THEOS=/opt/theos
export PATH=$PATH:$THEOS/bin
- 4.安装Theos
sudo git clone --recursive https://github.com/theos/theos.git $THEOS
- 5.设置$THEOS的用户组权限
sudo chown -R $(id -u):$(id -g) $THEOS
- 更多关于theos知识可以查询资料 掌握如何创建工程 如何书写Tweak.xm文件
- %hook
指定需要Hook的类名,接着就是需要Hook的函数,最后必须以%end结尾 - (2)%log
该命令在%hook内部使用,将类名、函数名、参数信息打印出来,非常实用。需要注意的是只在DEBUG模式下才会输出信息。 - (3)%orig
- (4)%new
该命令在%hook内部使用,为现有类增加新的方法,功能与class_addMethod相同。示例如下:
工具之monkeydev
- Theos使用Makefile基于命令行方式编译生成deb文件,习惯使用Xcode的开发者可能不太适应这种方式,于是有了后来的iOSOpenDev。它基于Xcode模板开发,在很长一段时间内都是越狱开发者的福音,但是该项目不再维护,因此在新的Xcode版本中不能很好地工作。国内安全研究员在此基础上进行了修改,推出了一个全新的集成框架,名为“MonkeyDev”,它不仅能完美支持新版Xcode和新版Theos,还增加了一些非常实用的功能
- MonkeyDev安装
brew install https://raw.githubusercontent.com/kadwanev/bigboybrew/
master/Library/Formula/sshpass.r
- 选择xcode 版本
sudo xcode-select -s /Applications/Xcode9.
*新建项目选择Monkeyapp
image.png
- 选择Logos Tweak模板之后,会自动生成基础代码,由于Xcode无法识别*.xm文件,导致代码没有高亮效果,这时需要在Xcode界面右侧设置“Type”为“Objective-C++Source”,然后重新打开工程。完成后如图8-13所示。
-
把IPA放到这里
image.png -
在monkeyDemoDylib.xm 编写hook代码
image.png
开始集成SDK
将sdk拖入到工程中
image.png image.png添加依赖库
image.png支付宝添加回调
#pragma mark - 支付回调,必接
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
[WCmsSDK application:application handleOpenURL:url];
return %orig;
}
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<NSString*, id> *)options {
[WCmsSDK application:application openURL:url options:options];
return %orig;
}
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
[WCmsSDK application:application openURL:url sourceApplication:sourceApplication annotation:annotation];
return %orig;
}
wansdk初始化代码
%hook AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// sdk初始化
[WCmsSDK didFinishLaunching];
return %orig;
}
接入登录
%hook BRSDKApi
- (void)login{
%log;
// %orig;
[self wanlogin];
}
%new
- (void)wanlogin{
[WCmsSDK loadLoginViewResultSuccess:^(NSString *logintime, NSString *userName, NSString *sign, NSString *rzStatus) {
NSLog(@"WCmsSDK____%@%@%@%@",logintime,userName,sign,rzStatus);
NSString* openId = [NSString stringWithFormat:@"%@_%@", @"brsyp", userName];
dispatch_async(dispatch_get_main_queue(), ^{
[BoRanSDK thirdLoginWithOpenId:openId];
});
} failed:^(NSInteger code, NSString *message) {
}];
}
%end
接入支付
-(void)pay_param:(NSDictionary *)param{
%log;
%orig;
[BoRanSDK tradeOrderNum:param];
// [self wanPay:param];
}
+(void)tradeOrderNum:(NSDictionary*)param{
BRSDKOrderInfo* orderInfo = [[BRSDKOrderInfo alloc] init];
orderInfo.productId = param[@"product_id"];
orderInfo.productName = param[@"product_name"];
orderInfo.productDesc = param[@"product_name"];
NSString *amount = param[@"amount"];
orderInfo.productPrice = [NSString stringWithFormat:@"%ld",amount.integerValue*100];
orderInfo.roleId = param[@"role_id"];
orderInfo.roleName = param[@"role_name"];
orderInfo.serverId = param[@"server_id"];
orderInfo.serverName = param[@"server_name"];
orderInfo.extInfo = param[@"extend"];
NSMutableDictionary *resDict = [[NSMutableDictionary alloc]init];
resDict[@"productId"] = orderInfo.productId;
resDict[@"product"] = orderInfo.productName;
resDict[@"orderMoneyFen"] = orderInfo.productPrice;
resDict[@"roleId"] = orderInfo.roleId;
resDict[@"roleName"] = orderInfo.roleName;
resDict[@"serverId"] = orderInfo.serverId;
resDict[@"serverName"] = orderInfo.serverName;
resDict[@"extInfo"] = orderInfo.extInfo;
[YouRequestManager requtForPouWayWithParams:resDict Block:^(NSDictionary *dic) {
NSLog(@"requtForPouWayWithParams === %@",dic);
} withFail:^(NSError *error) {
}];
}
上传角色
-(void)upLoadRoleType:(NSString *)type Uid:(NSString *)user_id serverID:(NSString *)server_id serverName:(NSString *)server_name role_ID:(NSString *)role_id roleName:(NSString *)role_name level:(NSString *)level balanceID:(NSString *)balanceid balanceName:(NSString *)balancename balanceNum:(NSString *)balancenum vip:(NSString *)vip power:(NSString *)power gender:(NSString *)gender professionid:(NSString *)professionid profession:(NSString *)profession turn_level:(NSString *)turn_level partyId:(NSString *)partyid partyName:(NSString *)partyname{
%orig;
[WCmsSDK SetUserInfoDataWithRoleId:role_id
roleName:role_name
roleLevel:level
zoneId:server_id
zoneName:server_name
attach:@"无"
block:^(NSInteger code) {
if (code == 1) {
// [YGMessage showMessage:@"上传成功"];
NSLog(@"角色上传成功");
};
}];
GameRoleInfo *roleInfo = [[GameRoleInfo alloc]init];
roleInfo.uploadType = UploadRoleInfoTypeCreateRole;
roleInfo.serverId = server_id;
roleInfo.serverName = server_name;
roleInfo.roleId = role_id;
roleInfo.roleName = role_name;
roleInfo.roleLevel = level;
roleInfo.partyName = @"无";
roleInfo.roleCTime = [self gs_getCurrentTimeBySecond];
roleInfo.balance = 20.0;
roleInfo.fightingCapacity = @"10";
roleInfo.reincarnation = @"0";
roleInfo.roleVip = @"0";
[BoRanSDK sendGameRoleInfo:roleInfo];
}
#pragma mark - 角色上报
+ (void)sendGameRoleInfo:(GameRoleInfo *)roleInfo
{
NSMutableDictionary* infoDict = [NSMutableDictionary dictionary];
[infoDict setValue:@(roleInfo.uploadType) forKey:@"uploadType"];
[infoDict setValue:roleInfo.serverId forKey:@"serverId"];
[infoDict setValue:roleInfo.serverName forKey:@"serverName"];
[infoDict setValue:roleInfo.roleId forKey:@"roleId"];
[infoDict setValue:roleInfo.roleName forKey:@"roleName"];
[infoDict setValue:roleInfo.roleLevel forKey:@"roleLevel"];
[infoDict setValue:roleInfo.roleVip forKey:@"roleVip"];
[infoDict setValue:@(roleInfo.balance) forKey:@"balance"];
[infoDict setValue:roleInfo.partyName forKey:@"partyName"];
[infoDict setValue:roleInfo.fightingCapacity forKey:@"fightingCapacity"];
[infoDict setValue:roleInfo.reincarnation forKey:@"reincarnation"];
[infoDict setValue:@(roleInfo.roleCTime) forKey:@"roleCTime"];
//1.上报渠道角色信息
#if HAS_INCLUDE_CHANNEL
if ([[sdkTools shareTool].loginMode intValue] != (int)LOGIN_MODE_BORAN) {
int nType = 1;
switch (roleInfo.uploadType) {
case UploadRoleInfoTypeEnterGame:
nType = 1;
break;
case UploadRoleInfoTypeCreateRole:
nType = 2;
break;
case UploadRoleInfoTypeRoleLevelUp:
nType = 3;
break;
case UploadRoleInfoTypeExitGame:
nType = 4;
break;
default:
break;
}
[infoDict setValue:@(nType) forKey:@"dataType"];
[infoDict setValue:roleInfo.roleVip forKey:@"roleVipLevel"];
[infoDict setValue:@(roleInfo.balance) forKey:@"roleBalence"];
[infoDict setValue:@(roleInfo.roleCTime) forKey:@"rolelevelCtime"];
[[BoranChannel sharedInstance] SendRoleInfo:infoDict];
}
#endif
//2.上报官方角色信息
[YouRequestManager sendFforRole:infoDict WithBlock:^(NSDictionary *dic) {
NSLog(@"send roleInfoDict = %@", infoDict);
NSString *stuats = [[dic objectForKey:@"status"]stringValue];
if ([stuats isEqualToString:@"1"]) {
NSDictionary *succ = [[NSDictionary alloc]initWithObjectsAndKeys:@"401",@"code", nil];
[[NSNotificationCenter defaultCenter]postNotificationName:kBRSDKSendRoleNotification object:nil userInfo:succ];
}else{
NSDictionary *succ = [[NSDictionary alloc]initWithObjectsAndKeys:@"402",@"code", nil];
[[NSNotificationCenter defaultCenter]postNotificationName:kBRSDKSendRoleNotification object:nil userInfo:succ];
}
} withFail:^(NSError *error) {
NSDictionary *succ = [[NSDictionary alloc]initWithObjectsAndKeys:@"403",@"code", nil];
[[NSNotificationCenter defaultCenter]postNotificationName:kBRSDKSendRoleNotification object:nil userInfo:succ];
}];
}