iOS逆向工具之Theos(MacOS)介绍

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS逆向工具之Theos(MacOS)介绍相关的知识,希望对你有一定的参考价值。

参考技术A 终端执行这个命令就可以安装

正向ios开发者,Mac上都安装着Xcode.如果你安装多个版本Xcode,指定其中一版本的Xcode就行.
终端执行命令

越狱iPhone下的签名工具(更改授权entitlements),可以为theos开发的程序进程签名 (支持在OS X和iOS上运行)。

我们可以通过终端命令进行安装

-ldid 用于签名
-fakeroot 用于模拟root权限

安装可能要花费一段时间,耐心等待一下.

注意:
关于ldid签名知识点补充
这里要提一下加密算法:
1. 对称加密算法:
RC4,DES,3DES,AES128,AES356等,加解密双方密钥相同.
2. 非对称加密算法:
RSA,Elgamal等,加解密双方使用密钥对.
3. 哈希算法:

注意
苹果签名:
1. 苹果签名 是苹果官方的私钥签名,公钥验证
2. 以数字签名形式进行签名

注意
数字签名
1. 数字签名是非对称密钥加密技术与数字摘要技术的应用.
2. 对指定信息使用哈希算法,得到一个固定长度的信息摘要.
3. 然后再使用 私钥 对该摘要加密,就得到了数字签名.

注意
数字证书:
1. 数字证书是一个文件,由苹果的Apple Worldwide Developer
Relations Certification Authority(WWDR)证书认证中心进行签名
2. 其主要作用是用来标识身份.

注意
证书文件主要包含两部分内容: 证书信息 和 证书签名
1.证书信息
包含用户的公钥,用户个人信息,证书颁发机构信息,证书有效期等信息
2.证书签名
WWDR将上述证书本身内容,通过哈希算法得到一个固定长度的信息摘要,然后使用自己的私钥对该信息摘要加密生成数字签名.
3.证书验证

注意

到这里,ldid介绍完成

dpkg:用于管理deb包.
deb是越狱开发安装包的标准格式,dpkg-deb是一个用于操作deb文件的工具,有了这个工具,Theos才能正确地把工程打包成为deb文件.

有的朋友会使用这个命令

报错 Error: invalid option: --from-bottle
大概意思是:无效的参数,这个有可能和安装的版本原因.

我多次安装后,把参数去掉了

到这里就完成了dpkg的安装.

我们在终端使用命令
sudo git clone --recursive https://github.com/theos/theos.git /opt/theos

我们通过git clone 到/opt/theos目录下.

克隆需要一部分时间,我们需要耐心等待一会.

我们在终端添加/opt/thoes目录的所有者

sudo chown -R $(id -u):$(id -g) /opt/theos

source ~/.bash_profile

保存环境变量并生效

thoes到这里安装完成.

本篇文章主要介绍了Theos安装前准备.

我们下篇文章会介绍如何是theos,我们会创建tweak demo为大家介绍.
有问题请留言,持续关注.

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. 给微信发现界面增加两行功能

在这里插入图片描述

我们可以根据Revealcycript相结合来分析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中函数代码
  • 所以,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来安装到手机的

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指令操作
    执行makemake 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逆向工具之Theos(MacOS)介绍的主要内容,如果未能解决你的问题,请参考以下文章

IOS逆向学习-加壳脱壳

ios-逆向 手把手安装最新版Theos

IOS逆向学习-Tweak

iOS逆向工程thoes报错处理方案总结

iOS逆向工程之Reveal工具的安装配置与使用

iOS开发逆向之应用重签名(下)