逆向(五)Theos工具

一、工程准备

1.1 安装签名工具

1
$ brew install ldid

1.2 修改环境变量

  • 编辑用户的配置文件

    1
    $ vim ~/.bash_profile
  • .bash_profile文件后加入变量,配置变量可参考macOS环境变量配置

    1
    2
    3
    # THEOS
    export THEOS=~/theos
    export PATH=$PATH:$THEOS/bin
  • .bash_profile配置的环境变量立即生效,或者重启终端

    1
    $ source ~/.bash_profile

##1.3 下载Theos

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

1
$ git clone --recursive https://github.com/theos/theos.git $THEOS

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

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

  • 确认开发需求:比如去广告、加会员,破解加锁功能等等。
  • 根据需求,确认需要修改的方案。
    • 比如去广告需要确定视图关系,对视图关系进行分析。
    • 加会员、破解功能则需要分析函数调用、逻辑关系,猜测实现,最后尝试hook逻辑。
  • 项目开发
      1. Clutch、dumpdecrypted破壳;
      1. class dump导出头文件;
      1. Reveal、Cycript分析界面;
      1. 分析类关系、函数调用逻辑,尝试进行hook;
      1. 调试、编译、打包、安装;
      1. 重签名、发布;

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

2.1 新建Tweak项目

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

需求是:去广告

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

1
$ cd ~/Desktop/crackApp/ting/

tweak_project

  • 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 项目文件结构

tweak_project_structure

2.2.1 MakeFile

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

我们看MakeFile文件:

1
2
3
4
5
6
7
8
9
10
11
12
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和端口访问手机:

1
2
3
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生效:

1
2
3
4
5
# 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中插入
1
2
//其他项目,请修改tingtweak为项目名
tingtweak_CFLAGS = -fobjc-arc

2.2.2 control

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

1
2
3
4
5
6
7
8
9
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:

1
{ 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找到对应的破壳文件,拷贝到电脑

      屏幕快照 2018-10-30 23.08.05

  • class dump导出头文件,执行命令:

    class-dump -H ting -o Headers

2.4 分析界面

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



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

屏幕快照 2018-10-31 00.12.39

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//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类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#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文件中:

1
2
3
4
5
6
7
8
%hook XMSoundPatchImageView

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

%end

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

在电脑Tweak项目目录下:

2.6.1 编译

1
make

2.6.2 打包

打包成deb:

1
make package

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

1
make package debug=0
  • 版本号,可以再control文件中指定;

  • debug包会比release版本大,主要包含了一些调试信息;

  • make package 包含了make指令的动作。

2.6.3 安装

默认会重启SpringBoard

1
make install

流程如下:

屏幕快照 2018-10-31 00.43.59

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

2.6.4 卸载

  • 直接去删除对应的动态库及plist文件。

    /Library/MobileSubstrate/DynamicLibraries

  • Cydia直接卸载

2.7 错误

2.7.1 - make package

1
2
3
4
5
6
7
8
9
10
11
12
 $ 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文件,用#号注释下面两句

    1
    2
    3
    $ vim $THEOS/vendor/dm.pl/dm.pl
    #use IO::Compress::Lzma;
    #use IO::Compress::Xz;
  • 修改deb.mk文件第6行的压缩方式gzip

    1
    2
    $ vim $THEOS/makefiles/package/deb.mk
    _THEOS_PLATFORM_DPKG_DEB_COMPRESSION ?= gzip

2.7.2 - make的错误

1)

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

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

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

2)

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

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

1
2
$ make clean
$ make

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

1
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代码:

屏幕快照 2018-10-31 11.12.43

那么在MakeFile中指定该文件:

1
tweakwechat_FILES = Tweak.xm Cell/MMTableViewCell.xm

需要注意的是:

  • 每个文件以空格隔开

  • 假如以通配符配置如下:

    1
    tweakwechat_FILES = Tweak.xm Cell/*.xm

    会报错:

    1
    2
    3
    4
    5
    6
    7
    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系统中。

屏幕快照 2018-10-31 11.12.57

在iOS中的映射:

屏幕快照 2018-10-31 11.21.38

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

3.2.2 读取

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

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

3.3 宏

宏定义语法和之前一致:

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

3.4 安装脚本

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

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

安装成功之后:



五、Theos

目录结构:https://github.com/theos/theos/wiki/Structure

环境变量:http://iphonedevwiki.net/index.php/Theos

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

    屏幕快照 2018-10-31 01.07.31

  • 当打开APP时

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

5.2 Logos语法

http://iphonedevwiki.net/index.php/Logos

  • %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文件

    1
    logify.pl xx.h > xx.xm

5.3 logify.pl

1
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#编写的,一般也会进行代码混淆;