IOS逆向学习-Tweak
Posted GY-93
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了IOS逆向学习-Tweak相关的知识,希望对你有一定的参考价值。
ios逆向学习-Tweak
1. theos指令及可能遇到问题
-
编译-打包-安装
make
:编译make package
:打包make install
: 安装(默认会自动重启SpringBoard)make clean
:清除编译缓存
-
可能遇到问题:
make package
的错误:- 是因为打包压缩方式有问题,改成
gzip
压缩就行:
-
make package
报如下错误 :
解决办法:把/Users/xxx/theos/makefiles/package/deb.mk
用文本编辑器打开将_THEOS_PLATFORM_DPKG_DEB_COMPRESSION ?= lzma 改成 _THEOS_PLATFORM_DPKG_DEB_COMPRESSION ?= gzip
⚠️ 修改完成后,将文件夹中的 .theos 和 packages 文件夹删掉,再进行make和后续的操作 -
make
的错误:
2. thes的实战练习
2.1 将桌面的更新数字去掉
-
首先我们需要知道桌面实际也是一个app, 我们可以使用指令
ps -A
来查找名为SpringBoard
的app: -
我们需要根据上述路径,使用
iFunBox
工具,找到app的Mach-o
文件: -
使用otool工具查看是否加壳, 没有任何打印,一般来说也是没有加壳的
-
在使用
class-dump
指令吧SpringBoard.app
的头文件导出来: -
在使用
cycript
查看app的id: -
当我打印出
SpringBoard
的所有子视图的时候,发现并没有找到对应消息角标的视图, 但是我通过网络查找,表示消息图标的类SBIconParallaxBadgeView
,在SpringBoard
头文件中是存在的, 所以 这里 我直接hook
这个类 -
创建一个tweak项目:
-
配置环境并编写hook代码:
#import "SBIconBadgeView.h"
#import "_UISettingsKeyObserver.h"
@class NSString, SBFParallaxSettings;
@interface SBIconParallaxBadgeView : SBIconBadgeView <_UISettingsKeyObserver>
{
SBFParallaxSettings *_parallaxSettings;
}
- (void).cxx_destruct;
- (void)_applyParallaxSettings;
- (void)settings:(id)arg1 changedValueForKey:(id)arg2;
- (void)dealloc;
- (id)init;
// Remaining properties
@property(readonly, copy) NSString *debugDescription;
@property(readonly, copy) NSString *description;
@property(readonly) unsigned long long hash;
@property(readonly) Class superclass;
@end
-
然后执行
make && make package && make install
,就会把我们编写好的插件安装到手机上,然后重启SpringBoard,重启手机之后, 所有的消息角标都不见了 -
其实我们为了避免每次都在makefile文件中配置ip和端口的环境变量 , 我们可以把这两个环境变量配置到当前电脑用户的配置文件中, 也就是
.bash_profile
文件中 -
另外我们为了每次都方便执行命令, 我们可以把
make
等指令编写一个脚本文件,这样以后直接执行脚本文件就可以了,我们可以在用户路径下创建一个tweak.sh
的脚本文件,内容如图:
2.2. 给微信发现界面增加两行功能
我们可以根据Reveal
和cycript
相结合来分析app的页面
2.2.1 hook代码语法知识
%orig
: 表示调用原来的方法,如果原来的方法有返回值,就返回对应的数值,示例如下图:
如果make
编译出现如下错误:
解决办法:在头文中申明这个在hook类中被调用的方法:
exit(0)
:当我们使用此方法退出微信的时候,会有一点卡顿之后才退出, 后面使用abort()
方法,表示终止进程 ,就不会有卡顿- 在hook的类中添加新的方法时候,需要在方法面前添加关键字
%new
代表是我们新增的方法, 不然我们在hook类中写的方法默认都是重写该类中的方法
2.2.2 如何在hook代码中加载自己的资源文件
hook文件中我们可以编写宏,编写的宏是可以直接使用的:
我们插件中需要用到的图片,可以使用如下方法:[UIImage imageWithContentsOfFile:@"图片全路径"];
,这样插件就可以使用图片了
我们应该把图片资源和代码一起打包放到手机上面,而不是直接把图片拖拽到手机里面,theos
是有规定的,如果你希望打包一些资源,你需要再项目下新建一个layout
文件夹,布局文件夹,在把图片资源放在layout
文件加下,这样theos
会将layout
文件夹中的一些资源顺便安装到手机上 。layout
相当于手机的根路径,layout
文件夹里面的图片资源是什么结构,那么安装到手机里面就是什么结构
但是 最好是把图片资源放到手机的Library/PreferenceLoader/Preference
路径下面
,如果想要把图片资源放到该路径下, 那么我们需要再layout
文件夹下面新建同样名字的文件夹,然后把图片资源放到该路径下。但是如果我们不是开发设置偏好的功能,我们可以把图片资源放到layout/Library/Caches/xxx
路径下
安装之后可以在手机路径上看到该图片资源:
但是我们的图片资源有可能跟别人的资源冲突,我们可以在建一层文件夹存放资源
//在OC中是字符串是支持这样的写法的
NSString *path = @"/Library/PreferenceLoader/"
"Preferences/GYWeChat/"
"skull.png";
NSString *path1 = @"/Library/PreferenceLoader/Preferences/GYWeChat/skull.png";
NSLog(@"path======%@\\n path1=====%@",path,path1);
打印结果:
然后根据宏定义的语法在参数面前加上一个#
,就表示给这个参数加上一个双引号
我们可以定义如下宏定义:
#define GYPath(path) @"/Library/PreferenceLoader/Preferences/GYWeChat/" #path
2.2.3 微信实战最终代码
@import UIKit;
@interface FindFriendEntryViewController : UIViewController
//声明方法
- (long long)numberOfSectionsInTableView:(id)tableView;
@end
#define GYDefaults [NSUserDefaults standardUserDefaults]
#define GYAutoKey @"gy_auto_key"
#define GYFile(path) @"/Library/PreferenceLoader/Preferences/GYWeChat/" #path
//微信发现界面所在挑个控制器 , 也是表格数据源所在控制器
%hook FindFriendEntryViewController
//有多少个分组
- (long long)numberOfSectionsInTableView:(id)tableView {
//首先我们不能破坏原来的组, 然后我们相加新的组, 那么怎么获取原来的组了
// %orig : 表示调用原来的方法,并返回对应的组
return %orig + 1;
}
// 每个分组有多少行
- (long long)tableView:(id)tableView numberOfRowsInSection:(long long)section {
if (section == [self numberOfSectionsInTableView:tableView] - 1)
{
//表示是最后一个分组, 也就是我们自己添加的分组
return 2;
} else {
return %orig;
}
}
//监听自动抢红包的方法
%new
- (void)gy_autoChange:(UISwitch *)switchView {
[GYDefaults setBool:switchView.isOn forKey:GYAutoKey];
[GYDefaults synchronize];
}
// 返回每一个cell的样式
- (id)tableView:(id)tableView cellForRowAtIndexPath:(id)indexPath {
if ([indexPath section] != [self numberOfSectionsInTableView: tableView] - 1)
{
return %orig;
}
// 最后一组cell的公共代码s
NSString *cellId = ([indexPath row] == 1) ? @"exitCellId" : @"autoCellId";
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:cellId];
if (cell == nil) {
cell = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:cellId];
cell.backgroundColor = [UIColor whiteColor];
// 图片
cell.imageView.image = [UIImage imageWithContentsOfFile:GYFile(skull.png)];
}
// 最后一组cell的具体代码
if ([indexPath row] == 0) {
cell.textLabel.text = @"自动抢红包";
// 开关
UISwitch *switchView = [[UISwitch alloc] init];
switchView.on = [GYDefaults boolForKey:GYAutoKey];
[switchView addTarget:self action:@selector(gy_autoChange:) forControlEvents:UIControlEventValueChanged];
cell.accessoryView = switchView;
} else if ([indexPath row] == 1) {
cell.textLabel.text = @"退出微信";
}
return cell;
}
//每一行cell的高度
- (double)tableView:(id)tableView heightForRowAtIndexPath:(id)indexPath {
if ([indexPath section] == [self numberOfSectionsInTableView:tableView] - 1)
{
return 44;
} else {
return %orig;
}
}
// 点击的监听
- (void)tableView:(id)tableView didSelectRowAtIndexPath:(id)indexPath
{
if ([indexPath section] !=
[self numberOfSectionsInTableView:tableView] - 1) {
%orig;
return;
}
[tableView deselectRowAtIndexPath:indexPath animated:YES];
if ([indexPath row] == 1) {
// exit(0);
// 终止进程
abort();
}
}
%end
3. theos的实现过程
3.1 theos的实现原理过程
- 编写
Tweak
代码 make
:编译Tweak代码为动态库(*.dylib)make package
:将dylib打包为deb
文件make install
:将deb文件传送到手机上,通过Cydia
安装deb- 插件将会安装在
/Library/MoblieSubstrate/Dynamiclibraries
文件夹中- *.dylib:编译后的Tweak代码
- *.plist:存放着需要hook的APP ID
- 当打开APP时
- Cydia Substrate(Cydia已自动安装的插件)会让app去加载对应的
dylib
- 修改APP内存中的代码逻辑,去执行
dylib
中函数代码
- Cydia Substrate(Cydia已自动安装的插件)会让app去加载对应的
- 所以,
theos
的tweak并不会对APP原来的可执行文件进行修改,仅仅是修改了内存中的逻辑代码- 如果希望卸载插件,只需要将
/Library/MoblieSubstrate/Dynamiclibraries
中对应的dylib、plist删除即可,但是这样可能卸载的不是很干净,我们可以通多Cydia去卸载,打开Cydia,选择最近安装,就可以看到安装的插件名称,然后点击卸载即可
- 如果希望卸载插件,只需要将
- 疑问:
- 未脱壳的APP是否支持tweak?
- 支持未脱壳的app,因为tweak是在内存中实现的,并没有需改app的可执行文件
- tweak的效果是否永久性的?
- 取决于tweak中用到的app的代码是否被修改过
- 如果一旦跟新APP,tweak会不会失效?
- 取决于tweak中用到的app的代码是否被修改过
- 未越狱的手机是否支持tweak?
- 不支持, 因为tweak是通过cydia来安装到手机的
- 未脱壳的APP是否支持tweak?
3.2 从汇编角度分析theos调用原理
首先我们创建一个app项目,编写如下代码,然后运行在手机上,并打两个断点看下运行过程中的汇编代码:
当我们运行程序过掉第一个断点的时候,我们可以看到汇编代码最后调用的是ViewController中的click():
接下来我们需要编写hook代码,替换click
方法中的内容,看看最终是不是使用我们编写的方法
我们断点分析,当我们把编译的插件安装到手机上之后,在运行项目,最终调用的click方法是我们编译好动态库中的click方法
3.3 编写hook代码中的问题
- (void)click {
UIView *view = [[UIView alloc] init];
view.frame = CGRectMake(100,100,100,100);
view.backgroundColor = [UIColor redColor];
[[self view] addSubview:view];
}
编写如上面代码在hook类中,make
编译报错
Tweak.xm:10:2: error: unknown type name 'UIView
:解决方式,在文件顶部导入@import UIKit;
解决方法:
@import UIKit;
//如果想hookAPP中的某个类,需要在文件前面声明的hook的类,我这里需要hook APP中的ViewController这个类,需要这样申明,才可以使用[self view]属性
@interface ViewController : UIViewController
@end
%hook ViewController
- (void)click {
UIView *testView = [[UIView alloc] init];
testView.frame = CGRectMake(100,100,100,100);
testView.backgroundColor = [UIColor redColor];
[[self view] addSubview:testView];
}
%end
不能识别当前self,目前还没有找到解决办法
4. theos相关资料
- 目录结构:https://github.com/theos/theos/wiki/Structure
- 环境变量:https://iphonedevwiki.net/index.php/Theos
- Logos语法:https://iphonedevwiki.net/index.php/Theos
%hook、%end
:hook一个类的开始和结束%log
:打印方法调用详情%new
:添加一个新的方法HBDebugLog
:跟NSLog
类似%c(className)
:生成一个Class对象,比如%c(NSObject)
%orig
:函数原来的代码逻辑%ctor
:在加载动态库时调用%dtor
:在程序退出时调用%logify.pl
:可以将一个头文件快速转换成已经包含打印信息的xm
文件:logify.pl xx.h > xx.xm
- 如果有额外的资源文件(比如图片),放在项目的layout文件夹中,对应着手机的根路径
/
,一般会建议放到layout/Library/PreferenceLoader/Preference
文件夹中。
5. theos的多文件开发
但是如果有很多个文件需要编译,我们可以使用通配符来配置编译文件的路径:
如果文件所在的路径层次较深, 那么必须要把路径写清楚,这里是不支持通配符**
的,类似xxx/**/*.m
这样的路径写法是不支持的
- 如果需要打包一个release版本的包,只需要在打包的时候,在后面加上
debug=0
打出来的deb包就是release版本的,例如:make package debug=0
6 theos命令补充
6.1 make package
make package
指令实质已近包含make
指令操作
执行make
和make package
指令时:
由上图可知, 当我们执行make package
时,该指令中也包含编译操作,也就是说包含make
指令的功能, 所以我们实际可以不执行make
指令,直接执行make package
指令即可:
6.2 %new
%new
:如果直接调用新增的方法,需要在@inetrface
中声明一下
6.3 %log
当我们hook
方法时,我们可以给方法添加打印信息,根据打印信息来确定,我们使用NSLog
时非常的不方便,这时候我们可以使用%log
,来打印方法的调用信息:
%log
: 可以打印出方法调用者、方法名、方法参数
6.4 logify.pl
当我们hook
某个类时,我们想知道调用了那个方法,然后通过给每个方法添加打印来确定调用,但是当该类特别多方法时,那么我们一个一个方法hook时,是非常费时间点 ,这时我们可以使用logify.pl
指令来把.h
文件直接转换成xxx.x
文件:
然后执行该指令之后,回生成对应的xxx.x
文件,可以返现文件中所有的方法都自动帮我们hook
好了,并且添加了对应的打印:
%logify.pl
生成的xm文件,有很多时候是编译不成功的,需要进行一些处理- 删除
__weak
- 删除
inout
- 删除协议,比如
<XXTestDelegate>
- 或者声明一下协议信息
@protocol XXTestDelegate
- 或者声明一下协议信息
- 删除
-(void).cxx_desture{%log ;%orig;}
- 替换
HBLogDebug(@" = 0x%x",(unsigned int)r);
为HBLogDebug(@" = 0x%@",r);
- 替换类名为
void
,比如讲XXPerson *
替换为void *
- 或则声名一下类信息
@class XXPerson
;
- 或则声名一下类信息
- 删除
6.5 多个.x的开发文件
当我们有多个.x
的开发文件时,我们可以把这些个文件放到一个文件夹下,比如src,那么我们可以在makefile
文件中配置编译文件路径:
$(wildcard) src/*.xm
: 固定配置,wildcard:通配符的意思
以上是关于IOS逆向学习-Tweak的主要内容,如果未能解决你的问题,请参考以下文章