iOS 逆向(五)Theos工具

1,579 阅读5分钟

这个系列,是很早听 MJ 课程时的整理,现在分享出来。 其中一些参考资料有些有引用,有些可能忘记添加了,如果有引用部分资料,可以联系我。

iOS 逆向(一)环境搭建
iOS 逆向(二)Cycript
iOS 逆向(三)逆向工具
iOS 逆向(四)脱壳
审核中 iOS 逆向(五)Theos工具 iOS 逆向(六)动态调试
iOS 逆向(七)重签名

一、工程准备

1.1 安装签名工具

$ brew install ldid

1.2 修改环境变量

  • 编辑用户的配置文件```bash $ vim ~/.bash_profile

- 在`.bash_profile`文件后加入变量,配置变量可参考[macOS环境变量配置](https://wenghengcong.com/posts/349bdc7c/)```bash
# THEOS
export THEOS=~/theos
export PATH=$PATH:$THEOS/bin
  • .bash_profile配置的环境变量立即生效,或者重启终端```bash $ source ~/.bash_profile




##1.3 下载Theos


建议在上述配置的`$THEOS`目录下载代码:


​```bash
$ git clone --recursive https://github.com/theos/theos.git $THEOS

二、项目开发-喜马拉雅去广告

开发一个tweak项目的流程大致如下:

  • 确认开发需求:比如去广告、加会员,破解加锁功能等等。
  • 根据需求,确认需要修改的方案。
    • 比如去广告需要确定视图关系,对视图关系进行分析。
    • 加会员、破解功能则需要分析函数调用、逻辑关系,猜测实现,最后尝试hook逻辑。
  • 项目开发

    1. Clutch、dumpdecrypted破壳;
    

    - 2. class dump导出头文件;

    3. Reveal、Cycript分析界面;
    

    - 4. 分析类关系、函数调用逻辑,尝试进行hook;

    5. 调试、编译、打包、安装;
    
    - 6. 重签名、发布;

我们在这里,将会实施前5步,重签名会在后面讲述。

2.1 新建Tweak项目

我们今天破解的是喜马拉雅FM  APP。

需求是:去广告

cd到存放项目代码的目录,此处:

$ cd ~/Desktop/crackApp/ting/

  • Project Name:
    • 必选项
    • 此处我们工程的名字是tingtweak;
  • Package Name
    • 包名,一般规则即可,也可以随便
    • 此处,我们com.luci.tingtweak
  • Author/Maintainer Name
    • 作者,当然是Wenghengcong
    • 直接敲回车默认Mac用户名
  • [iphone/tweak] MobileSubstrate Bundle filter [com.apple.springboard]
    • 我们要tweak App的bundle id,针对喜马拉雅tweak:com.gemd.iting
    • 可以使用Cycript或者MJAppTool来查看对应的App的Bundle Identifier;
  • [iphone/tweak] List of applications to terminate upon installation (space-separated, '-' for none) [SpringBoard]
    • 直接回车

2.2 项目文件结构

2.2.1 MakeFile

MakeFile,指定工程用到的文件、框架、库等信息,将整个过程自动化。

我们看MakeFile文件:

include $(THEOS)/makefiles/common.mk
# tweak的名字,即用Theos创建工程时指定的“Project Name”,跟control文件中的“Name”字段对应,不要更改。
TWEAK_NAME = tingtweak

# tweak包含的源文件(不包括头文件),多个文件间以空格分隔
tingtweak_FILES = Tweak.xm

include $(THEOS_MAKE_PATH)/tweak.mk

#在tweak安装之后杀掉SpringBoard进程,好让CydiaSubstrate在进程启动时加载对应的dylib
after-install::
	install.exec "killall -9 SpringBoard"

在前面加入环境变量,写清楚通过哪个IP和端口访问手机:

export THEOS_DEVICE_IP=127.0.0.1
export THEOS_DEVICE_IP THEOS_DEVICE_PORT=10010
...include $(THEOS)/makefiles/common.mk

此处通过本机地址,及10010端口访问手机,参考逆向(一)环境搭建通过USB连接手机一节。

如果不希望为每个项目的MakeFile都export端口,可以添加到用户配置文件中,同上面添加$THEOS变量类似,source生效:

# THEOS
export THEOS_DEVICE_IP=127.0.0.1
export THEOS_DEVICE_IP THEOS_DEVICE_PORT=10010
export THEOS=~/theos
export PATH=$PATH:$THEOS/bin
  • Tweak默认编码方式是MRC 如果需要ARC的话 在MakeFile中插入
//其他项目,请修改tingtweak为项目名
tingtweak_CFLAGS = -fobjc-arc

2.2.2 control

主要是项目有关的信息,比如项目的名称、版本、开发者等信息。

Package: com.luci.tingtweak
Name: tingtweak
Depends: mobilesubstrate
Version: 0.0.1
Architecture: iphoneos-arm
Description: An awesome MobileSubstrate tweak!
Maintainer: Wenghengcong
Author: Wenghengcong
Section: Tweaks

2.2.3 tingtweak.plist

主要是设置需要被逆向的app的bundle Id,如果需要逆向多个APP,就在Bundles数组中添加其bundle Id:

{ Filter = { Bundles = ( "com.gemd.iting" ); }; }

2.2.4 xm文件

xm就是hook代码文件。

2.3 脱壳、导出头文件

  • Clutch -d 破壳,始终无法破壳,则换采用dumpdecrypted工具

  • 先使用MJAppTool列出应用列表,获取喜马拉雅的app路径,得到:> /private/var/mobile/Containers/Bundle/Application/77CC1D65-FAD8-4E87-AA39-88756270F899/ting.app/

  • dumpdecrypted破壳:

    • 破壳,执行命令:> 5s:~ root# DYLD_INSERT_LIBRARIES=dumpdecrypted.dylib  /private/var/mobile/Containers/Bundle/Application/77CC1D65-FAD8-4E87-AA39-88756270F899/ting.app/ting

    • /var/root找到对应的破壳文件,拷贝到电脑

  • class dump导出头文件,执行命令:> class-dump -H ting -o Headers

2.4 分析界面

收听界面,展示了如下广告:

通过Reveal分析,基本可以判定广告视图类为:XMSoundPatchImageView

可以通过Cycript验证上面的猜想:

//ps -A查找到喜马拉雅进程id 893
//进入cy环境调试
5s:~ root#cycript -p 893
cy# @import mjcript
cy# MJFrontVc()
#"<XMPlayingViewController: 0x12eca0a00>"

//1. 从Reveal获取到猜测的广告视图的地址,打印其子视图
cy# #0x130524080.recursiveDescription().toString()
`<XMSoundPatchImageView: 0x130524080; frame = (0 105; 320 223); layer = <CALayer: 0x12ff8c8b0>>
   | <UIView: 0x130524a60; frame = (0 0; 320 223); layer = <CALayer: 0x1302a6fe0>>
   |    | <UIImageView: 0x130524220; frame = (56 15; 208 208); clipsToBounds = YES; opaque = NO; gestureRecognizers = <NSArray: 0x1301c7950>; layer = <CALayer: 0x130524050>>
   |    |    | <XMAdMarkView: 0x130524880; baseClass = UIImageView; frame = (0 196; 21.6 12); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x13026faf0>>
   | <UIButton: 0x1305245f0; frame = (249 0; 30 30); opaque = NO; layer = <CALayer: 0x13027e6e0>>
   |    | <UIImageView: 0x1300e0eb0; frame = (0 0; 30 30); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x1303f17c0>>`

//2. 根据猜测,真正的广告视图是UIImageView,从父视图移除
cy# #0x130524220.removeFromSuperview()

通过上面移除,我们从手机看到,广告没有了。

猜想是对的。

2.5 编写代码

下面,我们就要开始针对XMSoundPatchImageView来做一点事情了。

先从之前导出的头文件里看看XMSoundPatchImageView类:

#import <UIKit/UIView.h>

#import "CAAnimationDelegate-Protocol.h"
#import "XMSoundPatchImageViewProtocol-Protocol.h"

@class NSString, UIButton, UIImageView, XMADAudioItem, XMAdMarkView;

@interface XMSoundPatchImageView : UIView <CAAnimationDelegate, XMSoundPatchImageViewProtocol>
{
    _Bool _hideToTop;
    _Bool _onShow;
    unsigned long long _animationType;
    CDUnknownBlockType _soundPatchImageViewWillClose;
    UIButton *_adHidButton;
    UIImageView *_adImageView;
    UIView *_shadow;
    XMAdMarkView *_adMark;
}

@property(retain, nonatomic) XMAdMarkView *adMark; // @synthesize adMark=_adMark;
@property(nonatomic) _Bool onShow; // @synthesize onShow=_onShow;
@property(retain, nonatomic) UIView *shadow; // @synthesize shadow=_shadow;
@property(retain, nonatomic) UIImageView *adImageView; // @synthesize adImageView=_adImageView;
@property(retain, nonatomic) UIButton *adHidButton; // @synthesize adHidButton=_adHidButton;
@property(copy, nonatomic) CDUnknownBlockType soundPatchImageViewWillClose; // @synthesize soundPatchImageViewWillClose=_soundPatchImageViewWillClose;
@property(nonatomic) _Bool hideToTop; // @synthesize hideToTop=_hideToTop;
@property(nonatomic) unsigned long long animationType; // @synthesize animationType=_animationType;
- (void).cxx_destruct;
- (void)onHidButtonClicked:(id)arg1;
- (void)onAdImageViewTapped:(id)arg1;
@property(readonly, nonatomic) XMADAudioItem *audioItem;
......
- (void)initUI;
- (void)cleanWithAnimation:(_Bool)arg1;
- (void)clean;
- (id)initWithFrame:(struct CGRect)arg1;

// Remaining properties
@property(readonly, copy) NSString *debugDescription;
@property(readonly, copy) NSString *description;
@property(readonly) unsigned long long hash;
@property(readonly) Class superclass;

@end

从上面的头文件,我们发现的信息不多,但是我们可以直接hook掉视图,将该视图始终置为nil。

在Tweak.xm文件中:

%hook XMSoundPatchImageView

- (id)initWithFrame:(struct CGRect)arg1
{
	return nil;
}

%end

2.6 编译、打包、安装、卸载

在电脑Tweak项目目录下:

2.6.1 编译

make

2.6.2 打包

打包成deb:

make package

默认make package打包debug版本,如果要打包release版本:

make package debug=0
  • 版本号,可以再control文件中指定;
  • debug包会比release版本大,主要包含了一些调试信息;
  • make package 包含了make指令的动作。

2.6.3 安装

默认会重启SpringBoard

make install

流程如下:

安装完之后,会重启Springboard,再次打开喜马拉雅,惊喜的是,广告没了!

2.6.4 卸载

  • 直接去删除对应的动态库及plist文件。 /Library/MobileSubstrate/DynamicLibraries
  • Cydia直接卸载

2.7 错误

2.7.1 - make package

 $ make package
Can't locate IO/Compress/Lzma.pm in @INC (you may need to install the
IO::Compress::Lzma module) (@INC contains: /Library/Perl/5.18/darwin-
thread-multi-2level /Library/Perl/5.18 /Network/Library/Perl/5.18/darwin-
thread-multi-2level /Network/Library/Perl/5.18 /Library/Perl/Updates/5.18.2
/System/Library/Perl/5.18/darwin-thread-multi-2level
/System/Library/Perl/5.18 /System/Library/Perl/Extras/5.18/darwin-thread-
multi-2level /System/Library/Perl/Extras/5.18 .) at
/Users/mj/theos/bin/dm.pl line 12.
BEGIN failed--compilation aborted at /Users/mj/theos/bin/dm.pl line 12.

make: *** [internal-package] Error 2

是因为打包压缩方式有问题,改成gzip压缩就行。

  • 修改dm.pl文件,用#号注释下面两句```bash vimvimTHEOS/vendor/dm.pl/dm.pl #use IO::Compress::Lzma; #use IO::Compress::Xz;

- 修改deb.mk文件第6行的压缩方式gzip```bash
$ vim $THEOS/makefiles/package/deb.mk
_THEOS_PLATFORM_DPKG_DEB_COMPRESSION ?= gzip

2.7.2 - make的错误

1)

$ make
Error: You do not have an SDK in
/Library/Developer/CommandLineTools/Platforms/iPhoneOS.platform/Developer/S
DKs

是因为多个Xcode(安装多个Xcode),需要指定Xcode。

$ sudo xcode-select -s /Applications/Xcode.app/Contents/Developer/

2)

$ make
> Making all for tweak xxx...
make[2]: Nothing to be done for `internal-library-compile'.

之前编译过有缓存,需要clean。

$ make clean
$ make

三、更近一步-微信加功能

5s:~ root# DYLD_INSERT_LIBRARIES=dumpdecrypted.dylib  /private/var/mobile/Containers/Bundle/Application/4E7CFE17-8B9E-4B55-84B1-14E70653EFC3/WeChat.app/WeChat

3.1 多个tweak文件

假如新建Cell文件夹存放对应的hook代码:

那么在MakeFile中指定该文件:

tweakwechat_FILES = Tweak.xm Cell/MMTableViewCell.xm

需要注意的是:

  • 每个文件以空格隔开
  • 假如以通配符配置如下:```makefile tweakwechat_FILES = Tweak.xm Cell/*.xm

会报错:```bash
clang: error: no such file or directory: '/Users/wenghengcong/Desktop/crackApp/wechat/tweakwechat/.theos/obj/debug/armv7/Cell/MMTableViewCell.xm.mm'
clang: error: no input files
make[3]: *** [/Users/wenghengcong/Desktop/crackApp/wechat/tweakwechat/.theos/obj/debug/armv7/Cell/*.xm.298cc4f9.o] Error 1
rm /Users/wenghengcong/Desktop/crackApp/wechat/tweakwechat/.theos/obj/debug/armv7/Tweak.xm.mm /Users/wenghengcong/Desktop/crackApp/wechat/tweakwechat/.theos/obj/debug/armv7/Cell/*.xm.mm
make[2]: *** [/Users/wenghengcong/Desktop/crackApp/wechat/tweakwechat/.theos/obj/debug/armv7/tweakwechat.dylib] Error 2
make[1]: *** [internal-library-all_] Error 2
make: *** [tweakwechat.all.tweak.variables] Error 2

3.2 资源文件

资源文件存放,需要在tweak项目中新进layout文件夹用于存放。

3.2.1 layout

layout相当于iOS 系统根目录,在layout路劲中创建的文件路径,在打包之后,都会映射到iOS系统中。

在iOS中的映射:

所以,假如需要存放资源,规划好存放路径

3.2.2 读取

//路径,在文件头部定义个宏
#define BFFile(path) @"/Library/PreferenceLoader/Preferences/BFWeChat/" #path

cell.imageView.image = [UIImage imageWithContentsOfFile:BFFile(exit.png)];

3.3 宏

宏定义语法和之前一致:

#define BFUserDefaults [NSUserDefaults standardUserDefaults]
#define BFAutoKey @"bf_auto_get_key"

3.4 安装脚本

将以上打包等步骤编写为脚本:

# bftweak-make.sh
#!/bin/bash
#不包含make命令,因为make package包含了make指令
make clean && make package && make install

安装成功之后:

五、Theos

目录结构:github.com/theos/theos…

环境变量:iphonedevwiki.net/index.php/T…

5.1 theos-tweak实现过程

  • 编写Tweak代码
  • make:编写Tweak代码为动态库(*.dylib)
  • make package:将dylib打包为deb文件
  • make install:将deb文件传送到手机上,通过Cydia安装deb
  • 插件将会安装在/Library/MobileSubstrate/DynamicLibraries文件夹中
    • *.dylib:编译后的Tweak代码
    • *.plist:存放着需要hook的App Id

  • 当打开APP时
    • Cydia Substrate(Cydia已自动安装的插件)会让APP去加载对应的dylib
    • 修改APP内存中的代码逻辑,去执行dylib中的函数代码

5.2 Logos语法

iphonedevwiki.net/index.php/L…

  • %hook􏱮 %end:hook 一个类的开始与结束
  • %log:打印方法调用详情
    • 通过Xcode -> Window -> Devices and Simulators 查看日志
  • HBDebugLog:和NSLog类似
  • %new:添加一个新方法
  • %c(className):生成一个Class对象,比如%c(NSObjct) ,类似NSStringFromClass()、􏱮objc_getClass()
  • %orig:函数原来的代码逻辑
  • %ctor:在加载动态库时调用
  • %dtor:在程序退出时调用
  • logify.pl xm􏰅:将一个头文件快速转换成一家包含打印信息的xm文件```bash logify.pl xx.h > xx.xm




## 5.3 logify.pl```ba
logify.pl xx.h > xx.xm

logify.pl生成的xm文件,很多时候编译不通过,需要进行一些处理

  • 删掉__weak
  • 删掉inout
  • 删掉协议
    • 或者声明一下协议信息@protocol XXDelegate
  • 删除- (void).cxx_destruct { %log; %orig; }
  • 删除 HBLogDebug(@" = 0x%x", (unsigned int)r);
  • 将所有不认识的类,均替换成id
    • 比如instancetype 直接替换id
  • 替换类名为void,比如**XXPerson **_ 替换为 **void **_
    • 或者声明一下类信息 @class XXPerson

六、其他一些小tip

  • SpringBoard的目录:/System/Library/CoreService/SpringBoard.app
  • 未脱壳的APP也支持tweak,因为tweak是在内存中以动态库的形式实现的,并没有修改.app包中的可执行文件。
  • tweak效果是否一直有效,只要tweak用到的代码没有修改过,就一直有效。假如更新到新版本,用到的代码有修改,就会失效;
  • 未越狱手机不支持tweak;
  • 当前已经逐步对Swift项目进行支持;
  • 对游戏项目进行tweak难度大,主要因为游戏大多由C/C++/C#编写的,一般也会进行代码混淆;