赞
踩
2020年 IOS 逆向 反编译 注入修改游戏或APP的调用参数新手系列教程——用bfinject脱壳、注入自己的动态framework、cycript的使用
本篇文章是继上一篇文章:2020年 IOS 逆向 反编译 注入修改游戏或APP的调用参数新手系列教程——按键精灵脚本来模拟合成灯笼后本继续分享的教程,一天最多写一篇了,有时候太懒了沉迷打游戏就没写了~
网上很多教程讲了一大堆话,最终翻来翻去不知道是想实现什么功能和效果,我觉得一开始把需求&最终效果展示能让读者了解个大概和引起兴趣,不会不知所以然。后面我会按这种文章思路来分享,先把结果呈现,再详叙过程,我想是个不错的分享思路?。

动态注入后的效果

cycript的使用
之前我的MacOSX版本是10.10,只能安装xcode7以下的版本,xcode7以下的版本没有真机调试功能,于是升级了版本到MacOSX Catalina 10.15.4也安装了最新版xcode,但是这个版本太卡而且有些问题,很多软件兼容不了了,想以后降级。
| 环境 | 版本 |
|---|---|
| 操作系统 | MacOSX Catalina 10.15.4 版本太新了不太好用很多工具用不了,我后面打算降级 |
| 手机系统 | Iphone7 IOS11 需要越狱 |
| bfinject | 最新版 |
| 手机助手传输工具或SSH连接操作 | - |
| xcode | 11.5 |
| 电脑上的cycript | 最新版 |
| 手机终端工具比如terminal | - |
| 电脑工具class-dump用来导出头文件查看 | 最新 |
bfinject是一款注入工具,安装后坑挺多。可以注入xcode开发的framework,也可以注入ios10以前人们用的cycript工具,因为ios11已经不支持cycript的使用了,只能通过这个工具来执行cycript的全部命令,然后用电脑的cycript连接手机cycript提供出来的端口来操作。
电脑上的cycript安装教程参考这篇文章:https://www.jianshu.com/p/d93e9fccef4b,这玩意安装后坑很多,一一填坑吧,而且官网打开好慢~。
cycript是一款动态注入工具,可以动态执行cy代码,常用来打印ui界面和调试。
ios11的ssh本人用不了,从cydia安装了openssh,但是用命令行执行ssh报无法打开二进制文件的英文错误,不知道为何,谁能在ios11运行ssh并且电脑连接手机ssh的麻烦告知我一下谢谢。
首先电脑下载bfinject,然后用手机助手等工具把二进制文件bfinject拷贝到iphone手机的随意位置下,我是放在/User/Media/目录下。bfinject下载和安装教程参考github。
这个bfinject的坑还是很多的安装后执行会出现很多报错~比如electra和bootstrap目录问题的坑;还有md5: command not found的报错,我的做法是把md5sum这个命令复制一个改成md5执行就不报错,网上有填坑例子,遇到的可以看看。另外说明,这个bfinject的执行需要关闭Tweaks才能运行成功:打开越狱工具Electra,把Tweaks选项禁用,然后重新启动。
这个bfinject的执行需要关闭Tweaks才能运行成功:打开越狱工具Electra,把Tweaks选项禁用,然后重新启动。
这个bfinject的执行需要关闭Tweaks才能运行成功:打开越狱工具Electra,把Tweaks选项禁用,然后重新启动。
以下及下文所有手机命令都是用root用户来操作。
打开terminal到/User/Media/目录下执行:
bash bfinject -P test1.app -L test
上图界面的app是我自己随便写的一个demo app ,安装在了iphone里,我调试用的,这个app叫test1.app,这里拿来演示,-L test 是指调用bfinject内置的framework来注入,用来确定bfinject是否安装成功和生效,成功界面如下:

运行

打开到app的结果
接下来用这个命令来导出ipa
bash bfinject -P test1.app -L decrypt
回到app界面

脱壳打包成.ipa文件
打包完毕!我们选择No它会把包存储到App的文档目录。
我们把包找出来:
find /var/mobile/Containers/Data/Application/ -name decrypted-app.ipa

结果
其中某个decrypted-app.ipa就是我们打包出来的路径了,把这个包/User/Media/目录下,然后用电脑手机助手工具就能拿到啦。接下来就是提取头文件了。
class-dump安装比较简单我就不说了,是用来提取match-o格式文件的工具,可以把ios开发的app的头文件导出来,就能知道app里面的类和方法、变量名。以此来注入指定方法。
电脑命令:
class-dump -H test1 -o test1Headers
test1是我把decrypted-app.ipa解压后里面的match-o文件,test1Headers是指输出所有头文件到这个文件夹。
好了,我打开其中一个头文件

头文件
因为这个app是我自己写的,我知道down是其中一个Button按钮是点击后输出一个弹窗的功能,我就拿这个方法来演示注入把,就是实现点击Button按钮后再注入一个弹窗。当然其他app就要靠经验分析啦,可以用hooper等反编译工具分析。
打开xcode,新建工程选择IOS中的Framework & Library 中的Framework。
Product Name 等信息你们自己填,我的命名是snakeGameHacker
创建好工程目录后,snakeGameHacker.h就是头文件,我的这样写:
#import >UIKit/UIKit.h<//! Project version number for snakeGameHacker.FOUNDATION_EXPORT double snakeGameHackerVersionNumber;//! Project version string for snakeGameHacker.FOUNDATION_EXPORT const unsigned char snakeGameHackerVersionString[];// In this header, you should import all the public headers of your framework using statements like #import #import >HackerLoader.h<#import >NSObject+Hacker.h<
然后再创建Cocoa Touch class 文件,HackerLoader,会自动生成HackerLoader.h和HackerLoader.m文件。
HackerLoader.h
#import >Foundation/Foundation.h<#import >UIKit/UIKit.h<@interface HackerLoader : NSObject@end
HackerLoader.m
#import "HackerLoader.h"#import "NSObject+Hacker.h"#import >objc/runtime.h<@implementation HackerLoaderstatic void __attribute__((constructor)) entry(void) { NSLog(@"<<<<>>>>"); NSObject *obj = [[NSObject alloc] init]; [obj hack];}@end
再创建objective-c File文件,类型选择Category,名字Hacker
最终产生:NSObject+Hacker.h和NSObject+Hacker.m
NSObject+Hacker.h
#import >Foundation/Foundation.h<#import >UIKit/UIKit.h<@interface NSObject (Hacker)- (void)hack;@end
NSObject+Hacker.m
这就是我们的核心文件了
#import "NSObject+Hacker.h"#import >objc/runtime.h<@implementation NSObject (Hacker)- (void)hack { NSLog(@"<<<<>>>>"); NSString *className = @"ViewController"; [self hookMethod:@"down" ofClass:className hookMethodName:@"down2"];}// 封装方法挂载函数- (void)hookMethod:(NSString *)oriMethodName ofClass:(NSString *)ClassName hookMethodName:(NSString *)hookMethodName { NSLog(@"挂载方法。。。。"); static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Class oriMethodClass = NSClassFromString(ClassName); Class class = [self class]; SEL originalSelector = NSSelectorFromString([oriMethodName stringByAppendingString:@":"]); SEL swizzledSelector = NSSelectorFromString([hookMethodName stringByAppendingString:@":"]); Method originalMethod = class_getInstanceMethod(oriMethodClass, originalSelector); Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); BOOL didAddMethod = class_addMethod(oriMethodClass,originalSelector,method_getImplementation(swizzledMethod),method_getTypeEncoding(swizzledMethod)); if (didAddMethod) {// 判断是否已经有这个方法了 class_replaceMethod(oriMethodClass,swizzledSelector,method_getImplementation(originalMethod),method_getTypeEncoding(originalMethod)); } else { method_exchangeImplementations(originalMethod, swizzledMethod); } });}// 按钮按下- (void)down2:(id)sender{ NSLog(@"----down2----weirui3----"); [self showError:@"我在app的方法里注入自己的代码啦!"]; return [self down2:sender];}- (UIViewController *)_topViewController:(UIViewController *)vc { if ([vc isKindOfClass:[UINavigationController class]]) { return [self _topViewController:[(UINavigationController *)vc topViewController]]; } else if ([vc isKindOfClass:[UITabBarController class]]) { return [self _topViewController:[(UITabBarController *)vc selectedViewController]]; } else { return vc; } return nil;}- (UIViewController *)topViewController { UIViewController *resultVC; resultVC = [self _topViewController:[[UIApplication sharedApplication].keyWindow rootViewController]]; while (resultVC.presentedViewController) { resultVC = [self _topViewController:resultVC.presentedViewController]; } return resultVC;}- (void)showError:(NSString *)errorMsg { UIViewController *uvc = [self topViewController]; NSLog(@"----weirui3----当前vc%@", NSStringFromClass([uvc class])); // 1.弹框提醒 // 初始化对话框 UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提示" message:errorMsg preferredStyle:UIAlertControllerStyleAlert]; [alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil]]; // 弹出对话框 [uvc presentViewController:alert animated:true completion:nil]; NSLog(@"uvc show");}@end
static void attribute((constructor)) entry(void) {}
这个方法会被进程注入的时候执行到这里。我们实例化一个NSObject执行NSObject+Hacker.m.hack()方法,hack方法里面有一段
NSString *className = @"ViewController"; [self hookMethod:@"down" ofClass:className hookMethodName:@"down2"];
hookMethod是我封装的函数调用hook方法,是利用OC语言中swizzledMethod实现方法替换和注入,theos 中tweak的%orig原理就是用了swizzledMethod实现hook。后面我会分享tweak注入的教程,我觉得比bfinject稳定靠谱。
意思是ViewController类下面的down()方法挂载一个我们指定的方法down2()
接下来我们编写down2方法。
// 按钮按下- (void)down2:(id)sender{ NSLog(@"----down2----weirui3----"); [self showError:@"我在app的方法里注入自己的代码啦!"]; return [self down2:sender];}
拦截了down的执行,插入了一段代码 [self showError:@"我在app的方法里注入自己的代码啦!"];
就是弹窗代码,在本页面弹窗的实现,具体你们自己看弹窗实现代码吧,这个网上很多类似代码。然后再 return [self down2:sender];
就是真正的%orig那段代码了,看似是递归调用,实际不会。
我们先测试一下报错没有吧。如果要用单例测试要让编译目标 改为 iOS Simulators,我选ios8,然后按command+U,运行,证实不报错跑通到
hack()方法里面:

单例测试
当然down2方法是不会被执行的,因为down2是动态执行,需要利用bfinject注入后启动对应app触发down那个方法才会被执行。
我们编译代码。
注意,编译目标 切换为Generic iOS Device。
注意,编译目标 切换为Generic iOS Device
注意,编译目标 切换为Generic iOS Device
注意,编译目标 切换为Generic iOS Device
其他证书相关配置改为None,目标版本改为你手机能执行的版本,等一些手续。
然后按下Command+B编译。
Products/目录下产生snakeGameHacker.framework目录,用finder打开进入找到里面的snakeGameHacker的 Unix可执行文件,就是我们注入的对象了。
把snakeGameHacker上传到手机目录。
手机里执行
bash bfinject -P test1.app -l snakeGameHacker
注意这里的 -l 和上面不同,这里的是小写的。
执行后回到app按一下Button按钮(down方法的执行触发)
结果注入成功:

动态注入后的效果
至此,framework注入讲解完毕。
手机执行:
bash bfinject -P test1.app -L cycript

cycript的使用
然后电脑打开终端,输入对应地址:
cycript -r 192.168.0.101:1337
不过我经常连接好久或失败,感觉不好用。多试几次才能成功。
本来想写几个命令的,现在连接不上,算了~
此教程仅做学习交流和知识记录方便以后查看使用,如果涉及到利益相关的请告知本人进行删帖处理。
今天就先写到这,后面我再把我学习的其他相关知识分享给大家。下一篇就介绍theos 中编写tweak实现修改游戏数据的文章吧。
我会把这系列的文章更新到这个入口里面,分享我的心得,大家互相学习。
2020年 IOS 逆向 反编译 注入修改游戏或APP的调用参数新手系列教程主目录入口
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。