iOS逆向学习之六(Theos实战演练)

Theos

如果要去掉某个界面上的某个UIView,我们可以通过Cycript获取到UIView对应的内存地址,通过执行<font color=red>[#内存地址 removeFromSuperview]</font>命令就可以去掉UIView,但是这种方式仅仅是去掉了内存中的UIView,下一次再次进入此页面的时候,又会重新加载UIView。显然,这不是我们想要的结果,那么怎么永久的去掉UIView呢?这就要使用到了hook,通过hook UIView的指定方法改变成我们自己写的方法,然后将我们自己的代码注入到App中,就能实现永久去除UIView的效果。

如何进入hook操作呢?这就要使用到Theos,通过Thoes就可以创建tweak项目,添加需要hook的类以及方法,然后将tweak项目进行编译、打包成deb插件,最终安装到iPhone上。

签名工具ldid安装

  • ldid需要通过brew安装,如果mac上没有安装brew,可以通过以下命令安装HomeBrew
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
  • 安装完brew之后,通过brew安装ldid
brew install ldid

修改环境变量

Theos官方建议我们将Theos克隆到<font color=red>~/theos</font>目录下,而且<font color=red>~/theos</font>路径之后需要多次使用,所以将<font color=red>~/theos</font>配置为环境变量

  • 首先,进入<font color=red>cd ~</font>进入根目录,在根目录下有访问.bash_profile文件
vim ~/.bash_profile
  • 在.bash_profile文件最后加入下面两行
export THEOS=~/theos
export PATH=$THEOS/bin:$PATH

此处将~/theos/bin配置进PTAH中,PATH通过:来配置多个路径,配置好Path之后,在终端输入某一个指令时,会到PATH里的所有目录下去寻找可执行文件。注意:此处的:$PATH一定不能遗漏,否则PATH会被THEOS的目录覆盖。

  • 配置完成之后,在终端执行source命令,使.bash_profile文件的配置立即生效
source ~/bash_profile
  • 此时我们通过$PATH来打印出PATH信息,就可以看到THEOS目录已经添加到了PATH中
image

下载Theos

通过以下命令将Theos下载到上面配置的$THEOS目录中

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

--recursive表示递归下载,因为Theos会依赖其它库,使用此命令可以递归的将所有依赖的库都下载下来

tweak项目

创建tweak项目

  • cd到存放项目的目录,执行nic.pl指令,选择序号10,对应iphone/tweak
cd ~/Desktop/tweak
nic.pl
image
  • 填写项目所需要的信息
#项目名称
Project Name
#项目ID,按规则填写即可
Package Name 
#作者名,直接按回车使用默认名
Author/Maintainer Name 
#需要hook的App的Bundle Identifier,可以使用Cycript查看
[iphone/tweak] MobileSubstrate Bundle filter
#直接回车,使用默认配置就可以
[iphone/tweak] List of applications to terminate upon installation

至此,tweak项目创建完毕

image

tweak项目配置

  • 编辑Makefile文件,需要在Makefile文件中增加以下环境变量,指明通过哪个IP和端口号访问手机,由于之前使用的是USB的方式访问,所以IP写本机地址即可
export THEOS_DEVICE_IP=127.0.0.1 #本机IP地址
export THEOS_DEVICE_PORT=10088   #本机端口号

INSTALL_TARGET_PROCESSES = SpringBoard

include $(THEOS)/makefiles/common.mk

TWEAK_NAME = test_tweak

test_tweak_FILES = Tweak.x
test_tweak_CFLAGS = -fobjc-arc

include $(THEOS_MAKE_PATH)/tweak.mk

  • 上述方式配置的环境变量只能在当前的tweak项目中生效,以后如果有新的tweak项目,就需要重新配置。如果不希望每个项目都要重新编写IP和端口号的环境变量,可以将这两个变量配置到.bash_profile中
export THEOS=~/theos
export PATH=$THEOS/bin:$PATH
export THEOS_DEVICE_IP=127.0.0.1
export THEOS_DEVICE_PORT=10088

配置完成后,使用source让配置生效

source ~/.bash_profile

tweak项目编写代码

代码编写

tweak项目的拦截代码编写在Tweak.x文件中,代码如下

%hook ClassName

// Hooking a class method
+ (id)sharedInstance {
    return %orig;
}

// Hooking an instance method with an argument.
- (void)messageName:(int)argument {
    %log; // Write a message about this call, including its class, name and arguments, to the system log.

    %orig; // Call through to the original function with its original arguments.
    %orig(nil); // Call through to the original function with a custom argument.

    // If you use %orig(), you MUST supply all arguments (except for self and _cmd, the automatically generated ones.)
}

// Hooking an instance method with no arguments.
- (id)noArguments {
    %log;
    id awesome = %orig;
    [awesome doSomethingElse];

    return awesome;
}

%end

语法简介

  • tweak使用的Logos语法简介,可以点击Logos官网查看完整的语法介绍。
%hook、%end :#表示hook一个类的开始和结束

%log :#打印方法调用详情,可以通过Xcode -> Window -> Devices and Simulators

HBDebugLog : #和NSLog类似

%new :#添加一个新的方法

%c(className) : #生成一个Class对象,比如%c(NSObject),类似于NSStringFromClass()、objc_getClass()

%orig :#调用原先的方法逻辑

%ctor :#加载动态库的时候调用

%dtor :#在程序退出时调用
  • logify.pl

此工具可以将一个头文件快速转换成已经包含打印信息的x文件,这可以为我们节省很多事,比如我们要监听一个页面所有方法的调用,使用此工具就能自动将所有的OC方法转换成Logos方法,并且在方法中为我们加上对应的log方法和返回值。具体转换指令如下:

logify.pl xx.h > xx.xm

但是使用这种自动转换的方法转换之后的代码在编译时会遇到很多问题。解决方法如下

  • 删除__weak
  • 删除inout
  • 删除掉协议,或者使用@protocol XXTestDelegate声明一下协议
  • 删除掉- (void).cxx_destruct { %log; %orig; }方法
  • 删除HBLogDebug(@" = 0x%x", (unsigned int)r)
  • 替换类名为void,例如将XLPerson * 替换成void *,获取在头部声明一下类名@class Person
  • 资源文件

如果有额外的资源文件,例如图片等等,放在项目的layout文件夹中,对应手机的根目录 <font color=red>/</font>,通常开发中可以将图片存放在以下目录中<font color=red>/Library/PreferenceLoader/preferences/</font>和<font color=red>/Library/Caches</font>下,具体的做法是在tweak项目的layout文件夹中依次创建<font color=red>Library</font>和<font color=red>Caches</font>目录,将图片存放到此目录下。加载图片的时候使用全路径即可

编译->打包->安装

#对项目进行编译
make

#打包成deb文件
make package

#安装,安装过程中会默认重启SpringBoard
make install

#以上命令也可写成如下格式
make && make package && make install

如果之前已经编译过,需要再次重新编译的话,执行make clean即可

去除hook程序

  • 如果我们不想再hook别人的程序了,可以通过iFunBox,在<font color=red>~/Library/MobileSubstrate/DynamicLibraries/</font>目录下找到我们的deb插件生成的.dylib和.plist文件,一般是以我们的tweak项目名命名的。删除这两个文件,然后重启SpringBoard就可以去除我们的hook插件。
image
  • 第二种方式是通过Cydia来卸载,打开Cydia,在已安装插件中找到我们刚刚安装好的插件,点击卸载,完成之后重启SpringBoard即可

tweak执行原理

  • 首先,当我们创建好tweak项目,并且编写好hook代码后,执行make操作,在tweak项目目录下会生成一个隐藏的文件夹<font color=red>.theos</font>,在<font color=red>.theos/obj/debug/</font>目录下会生成动态库<font color=red>.dylib</font>文件
  • 执行make package时,项目目录下的plist文件和第一步生成的.dylib文件打包成对应的deb文件,存放在packages目录下
  • 执行make install时,首先会根据之前配置的<font color=red>THEOS_DEVICE_IP</font>和<font color=red>THEOS_DEVICE_PORT</font>两个变量去远程连接iPhone,然后通过iPhone上的Cydia来安装对应的deb插件
  • 在App启动之后,会同时将dylib加载到内存中,App中如果访问被我们hook的类中的方法,会直接执行dylib中的方法。

make package默认是debug模式,如果要发布release版本的包,执行make package debug=0即可

tweak多文件开发

在tweak项目开发中,如果我们需要Hook的方法过多,所有的hook方法都写在同一个类里,明显不是一种好的方式。这个时候就需要多文件开发。tweak支持多文件开发

  • 首先在项目目录下增加src文件夹,然后将Tweak.x文件移到src目录下,这个时候执行make指令会提示
==> Error: File Tweak.x does not exist.
make[1]: *** [before-test_wechat-all] Error 1
make: *** [test_wechat.all.tweak.variables] Error 2
  • tweak在编译时,会到Makefile中寻找可编译的文件路径,因为我们改变的Tweak.x的路径,相应的就要修改Makefile文件下的<font color=red>test_wechat_FILES</font>,如下
INSTALL_TARGET_PROCESSES = SpringBoard

include $(THEOS)/makefiles/common.mk

TWEAK_NAME = test_wechat

test_wechat_FILES = src/Tweak.x
test_wechat_CFLAGS = -fobjc-arc

include $(THEOS_MAKE_PATH)/tweak.mk
  • tweak支持OC类,想tweak项目中的src目录下导入Person类,在Tweak.x中#import "Person.h",然后执行make编译,会出现以下错误
ndefined symbols for architecture armv7:
  "_OBJC_CLASS_$_Person", referenced from:
      objc-class-ref in Tweak.x.01c0bdf0.o
ld: symbol(s) not found for architecture armv7
  • 修改Makefile中的<font color=red>test_wechat_FILES</font>,增加Person.m的路径
INSTALL_TARGET_PROCESSES = SpringBoard

include $(THEOS)/makefiles/common.mk

TWEAK_NAME = test_wechat

test_wechat_FILES = src/Tweak.x src/*.m
test_wechat_CFLAGS = -fobjc-arc

include $(THEOS_MAKE_PATH)/tweak.mk

注意两个路径之间需要用空格分隔,Tweak支持通配符,可以使用*.m指定目录下的所有.m文件

实战一、去掉喜马拉雅音频播放界面的广告

  1. 如果iPhone上的喜马拉雅App被加密的话,首先需要通过Clutch或者dumpdecrypted工具进行解密。得到解密后的可执行文件。
DYLD_INSERT_LIBRARIES=dumpdecrypted.dylib /var/mobile/Containers/Bundle/Application/14C4F899-BD7B-41A4-BC1A-61892E7B943B/ting.app/ting
  1. 使用class-dump导出喜马拉雅可执行文件中所有头文件备用
class-dump -H ~/Desktop/ting -o Headers
  1. 使用Cycript获取喜马拉雅App的bundle Identifier,后续创建tweak项目时需要用到
cy# [NSBundle mainBundle].bundleIdentifier
@"com.gemd.iting"
cy#
  1. 使用Reveal定位到广告视图的具体位置
image
  1. 获取到广告页面的名称为XMSoundPatchImageView和XMAdAnimationViewThree,查看头文件,找到XMSoundPatchImageView.h和XMAdAnimationViewThree.h,以下为部分代码
@interface XMSoundPatchImageView : UIView <CAAnimationDelegate, XMSoundPatchImageViewProtocol>
- (void)adImageShadowCreateIfNeeded;
- (void)defaultAnimationShowImage:(_Bool)arg1 withAnimation:(_Bool)arg2 andReoportAdShow:(_Bool)arg3 andReportOnce:(_Bool)arg4;
- (void)showImage:(_Bool)arg1 withAnimation:(_Bool)arg2 andReoportAdShow:(_Bool)arg3 andReportOnce:(_Bool)arg4;
- (void)showImage:(_Bool)arg1 withAnimation:(_Bool)arg2 andReoportAdShow:(_Bool)arg3;
- (void)initUI;
- (void)cleanWithAnimation:(_Bool)arg1;
- (void)clean;
- (void)dealloc;
- (id)initWithFrame:(struct CGRect)arg1;

@end

XMAdAnimationViewThree的初始化方法和XMSoundPatchImageView方法相同,喜马拉雅App存在多种广告View,这里只列举了其中两种。

可以看到整个View是通过initWithFrame方法进行初始化的。

  1. 创建tweak项目,在Tweak.x中编写拦截代码,拦截XMSoundPatchImageView的initWithFrame方法,返回nil
%hook XMSoundPatchImageView

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

%end

%hook XMAdAnimationViewThree

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

%end

  1. 编译,打包以及安装
make && make package && make install
  1. 执行以上命令之后,iPhone桌面会重启,再次打开喜马拉雅App,点击进入播放界面,就会发现不会再弹出广告。至此,去除广告的功能就完成了。
image

实战二、去掉iPhone桌面上所有角标提示

其实iPhone的桌面也是一个应用,叫做SpringBoard。那么怎么验证它是一个应用呢?通过Cycript指令

ps -A | grep SpringBpard
508SC:~ root# ps -A | grep SpringBoard
 1365 ??         0:26.56 /System/Library/CoreServices/SpringBoard.app/SpringBoard
 1425 ttys000    0:00.01 grep SpringBoard
508SC:~ root#

通过以上结果我们可以看出,SpringBoard其实就是一个App,安装路径为<font color=red>/System/Library/CoreServices/</font>,既然知道SpringBoard是本质是一个App,那么,我们就可以为SpringBoard创建一个tweak应用

  1. 首先,通过iFunBox,找到iPhone的<font color=red>/System/Library/CoreServices/</font>目录下的可执行文件SpringBoard,拖拽到Mac上
  2. 通过otool或者MachOView查看Mach-O文件是否被加密,结果发现SpringBoard可执行文件中,Load Commands中并没有LC_ENCRYPTION_INFO,这也就说明SpringBoard没有被加密。
  3. 通过class-dump导出SpringBoard所有头文件备用
  4. 获取微信的BundleIdentifier为<font color=red>com.tencent.xin</font>
  5. 因为Reveal只能调试安装在SpringBoard上的应用,不能调试SpringBoard本身,所以想要查询SpringBoard的UI界面,就要使用Cycript指令。
  6. 首先连接iPhone,通过Cycript监听SpringBoard,找到SpringBoard主界面,通过主界面的内存地址打印出主界面的subViews
508SC:~ root# cycript -p SpringBoard
cy# MJRootVc ()
#"<SBHomeScreenViewController: 0x1308cc8d0>"
cy# MJSubviews (#0x1308cc8d0)
`<SBHomeScreenView: 0x1308c0aa0; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x1308d6ac0>>
   | <SBIconContentView: 0x1309f6150; frame = (0 0; 320 568); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x1309a3940>>
   |    | <SBRootFolderView: 0x130b68910; frame = (0 0; 320 568); layer = <CALayer: 0x130b6b230>>
   |    |    | <SBSearchBlurEffectView: 0x131d885c0; variant: static wallpaper; style: LightTintedBlur; frame = (0 0; 320 568); clipsToBounds = YES; alpha = 0; layer = <CALayer: 0x131b94ac0>>
   |    |    |    | <_SBFakeBlurView: 0x1330823c0; style: LightTintedBlur; frame = (0 0; 320 568); animations = { AlignFakeWallpaperToLayer-0x1308d4dc0=<CAMatchMoveAnimation: 0x1333904e0>; }; layer = <CALayer: 0x131a6d300>>
   
   ......
  1. 通过分析主界面的所有subViews,可以找到SBIconView,通过拿到SBIconView的内存地址,通过设置hidden,最终确定界面上每个App图标都是一个SBIconView。
cy# #0x130ec4ff0.hidden = 1
1
cy# #0x130ec4ff0.hidden = 0
0
cy#
  1. 通过查看SBIconView的subViews可以确定SBIconParallaxBadgeView就是我们要找的角标View
  2. 查看SpringBoard所有头文件,找到SBIconParallaxBadgeView.h文件如下
#import "SBIconBadgeView.h"

#import "_UISettingsKeyObserver.h"

@class NSString, SBFParallaxSettings;

@interface SBIconParallaxBadgeView : SBIconBadgeView <_UISettingsKeyObserver>
{
    SBFParallaxSettings *_parallaxSettings;
}

- (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
  1. 创建tweak项目,在Tweak.x编写拦截代码,拦截SBIconParallaxBadgeView的init方法,返回nil
%hook SBIconParallaxBadgeView

- (id)init{
    return nil;
}

%end
  1. 编译,打包以及安装,等待SpringBoard重启之后,就可以看到整个桌面上的所有App,都没有角标了。
make && make package && make install

实战三、给微信的“发现界面”增加自动抢红包、退出微信两个功能,仅改变UI,不实现具体功能

  1. 首先通过ps -A指令找到微信的可执行文件路径
/var/mobile/Containers/Bundle/Application/048B71C8-42E4-4EE0-8E50-EF262251DE17/WeChat.app/WeChat
  1. 使用Clutch或者dumpdecrypted工具进行解密。得到解密后的可执行文件。如果已解密,则忽略此步骤

优先选择dumpdecrypted工具,解密速度快,不易出错

DYLD_INSERT_LIBRARIES=dumpdecrypted.dylib /var/mobile/Containers/Bundle/Application/048B71C8-42E4-4EE0-8E50-EF262251DE17/WeChat.app/WeChat
  1. 在<font color=red>/var/root</font>目录下找到解密后的可执行文件,使用class-dump导出所有头文件备用
class-dump -H ~/Desktop/WeChat -o Headers
  1. 使用Cycript获取微信App的bundle Identifier,后续创建tweak项目时需要用到
cy# [NSBundle mainBundle].bundleIdentifier
@"com.tencent.xin"
cy#
  1. 通过Reveal分析微信UI界面,可以得出微信的发现页面是一个UITableView,类名为MMMainTableView,我们想要在UITableView上增加cell,必须要知道MMMainTableView的dataSource。通过Cycript可以得到MMMainTableView的dataSource就是FindFriendEntryViewController
cy# #0x13d20ae00.dataSource
#"delegate[0x13dadaa00], class[FindFriendEntryViewController]"
cy#
  1. 查找微信的头文件,在FindFriendEntryViewController中找到我们需要hook的几个方法,如下
- (void)tableView:(id)arg1 didSelectRowAtIndexPath:(id)arg2;
- (id)tableView:(id)arg1 cellForRowAtIndexPath:(id)arg2;
- (double)tableView:(id)arg1 heightForRowAtIndexPath:(id)arg2;
- (long long)tableView:(id)arg1 numberOfRowsInSection:(long long)arg2;
- (long long)numberOfSectionsInTableView:(id)arg1;
  1. 创建tweak项目,在Tweak.x中编写以下hook代码
#define XLUserDefault [NSUserDefaults standardUserDefaults]
#define XLAutoSwitchKey @"xl_auto_switch_key"
#define XLFile(path) @"/Library/PreferenceLoader/Preferences/XLWeChat/" #path

/* 对FindFriendEntryViewController及numberOfSectionsInTableView方法做前向声明。
 * 否则无法调用[self numberOfSectionsInTableView:]方法
 */
@interface FindFriendEntryViewController

- (long long)numberOfSectionsInTableView:(id)tableView;

@end

%hook FindFriendEntryViewController

//当前tableView有几组
- (long long)numberOfSectionsInTableView:(id)tableView
{   
    //调用微信原始的numberOfSectionsInTableView方法,获取原始section,在此基础上+1
    return %orig + 1;
}

//当前tableView每组对应多少row
- (long long)tableView:(id)tableView numberOfRowsInSection:(long long)section
{
    if (section == [self numberOfSectionsInTableView:tableView] - 1){
        return 2;
    }

    return %orig;
}

//返回每个row对应的cell
- (id)tableView:(id)tableView cellForRowAtIndexPath:(id)indexPath
{
    //判断是否是最后一组
    if ([indexPath section] == [self numberOfSectionsInTableView:tableView] - 1){
        NSString *cellId = [indexPath row] == 0 ? @"autoCellId" : @"exitCellId";
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];
        if (!cell){
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault 
                                          reuseIdentifier:cellId];
            cell.backgroundColor = [UIColor whiteColor];
            //设置图片
            cell.imageView.image = [UIImage imageWithContentsOfFile:XLFile(icon.png)];
        }
        if ([indexPath row] == 0) {
            cell.textLabel.text = @"自动抢红包";
            
            UISwitch *switchView = [[UISwitch alloc] init];
            switchView.on = [XLUserDefault boolForKey:XLAutoSwitchKey];
            [switchView addTarget:self 
                           action:@selector(xl_switchChange:) 
                 forControlEvents:UIControlEventValueChanged];
            cell.accessoryView = switchView;

        }else if ([indexPath row] == 1){
            cell.textLabel.text = @"退出微信";
        }
        return cell;
    }

    return %orig;
}

/* 
 * 特别注意:在%hook和%end内部所写的方法,默认是覆盖当前hook的类中的方法,如果在原类中没有此方法,会崩溃
 *         如果想要在原类中增加新的方法,需要在方法前增加 %new 标识
 */
%new
- (void)xl_switchChange:(UISwitch *)switchView{
    [XLUserDefault setBool:switchView.isOn forKey:XLAutoSwitchKey];
    [XLUserDefault synchronize];
}


//cell的高度
- (double)tableView:(id)tableView heightForRowAtIndexPath:(id)indexPath
{
    //此处判断当前是否是我们新加的一组,如果是,返回cell的高度为44
    if ([indexPath section] == [self numberOfSectionsInTableView:tableView] - 1)
    {
        return 44;
    }
    //如果不是最后一组,返回微信自己定义的高度
    return %orig;
}

//cell点击事件
- (void)tableView:(id)tableView didSelectRowAtIndexPath:(id)indexPath
{
    if ([indexPath section] == [self numberOfSectionsInTableView:tableView] - 1){
        if ([indexPath row] == 0){
            [tableView deselectRowAtIndexPath:indexPath animated:YES];
        }else if([indexPath row] == 1){
            //调用此函数,直接退出App
            abort();
        }
        
    }else{
        %orig;
    }
}

%end

注意:

  • 在Theos中,使用<font color=red>%orig</font>来调用原先的方法逻辑。所以我们在编写hook代码时,会在原来方法的基础上进行编写。
  • 在<font color=red>%hook</font>和<font color=red>%end</font>内部所写的方法,默认是覆盖当前hook的类中的方法,如果在原类中没有此方法,会崩溃。如果想要在原类中增加新的方法,需要在方法前增加 <font color=red>%new</font> 标识
  • 在宏定义中,#path代表在path前后拼接上"",得到"path",使用如下调用方式<font color=red>XLFile(icon.png)</font>即可
  • 在make编译时可能会出现以下错误,原因是self在此处会被认为是id类型,而numberOfSectionsInTableView方法属于FindFriendEntryViewController的方法,如果不对FindFriendEntryViewController进行前向声明,是无法调用numberOfSectionsInTableView的。具体前向声明代码如上
Tweak.x:37:30: error: receiver type 'FindFriendEntryViewController' for instance
      message is a forward declaration
        if ([indexPath section] == [self numberOfSectionsInTableView:tab...
                                    ^~~~
Tweak.x:34:8: note: forward declaration of class here
@class FindFriendEntryViewController;
  1. tweak项目中资源文件如何配置?
  • 首先需要在我们的tweak项目下创建一个<font color=red>layout</font>文件夹。<font color=red>layout</font>目录对应iPhone上的根目录,如果我们将图片存放在<font color=red>layout</font>目录下,那么打包tweak项目时,会将图片存放到iPhone的根目录下。
  • 通常我们开发插件的资源文件都存放在iPhone的<font color=red>/Library/PreferenceLoader/Preferences/</font>目录下,所以我们需要在<font color=red>layout</font>目录中依次创建<font color=red>Library、PreferenceLoader、Preferences</font>目录,为防止资源重复,在<font color=red>Preferences</font>目录下再创建一个<font color=red>XLWeChat</font>目录,将我们的资源文件存放在此目录。
  • 在OC中我们一般通过<font color=red>[UIImage imageNamed:]</font>方法加载图片,但是在tweak项目中,需要使用<font color=red>[UIImage imageWithContentsOfFile:]</font>方法加载图片,传入图片的全路径,如下
[UIImage imageWithContentsOfFile:@"/Library/PreferenceLoader/Preferences/XLWeChat/icon.png"]
  1. 最后通过编译、打包、安装,等待SpringBoard重启,打开微信就可以看到在发现页面增加了两个功能,如下:
image

实战四、去除腾讯视频客户端视频播放时的广告

  • 获取腾讯视频App的Bundle Identifier为com.tencent.live4iphone
  • 获取腾讯视频客户端的可执行文件路径,通过dumpdecrypted工具进行脱壳
  • 找到脱壳的可执行文件,使用class-dump导出所有头文件
  • 通过Reveal找到播放视频时的广告View为QNBPlayerVideoAdsView,它所对应的UIViewController为QNBPlayerVideoAdsViewController
  • 查找头文件,找到QNBPlayerVideoAdsViewController.h,在新版本的腾讯视频的可执行文件中,可能找不到QNBPlayerVideoAdsViewController对应的头文件,我这边用的是旧版本的ipa安装包。
  • 找到QNBPlayerVideoAdsViewController.h里的初始化方法
- (id)initWithEventProxy:(id)arg1 withPlayerInfo:(id)arg2 withParentViewController:(id)arg3 withPageViewController:(id)arg4 withAddToParenViewControllerNow:(_Bool)arg5
- (id)initWithEventProxy:(id)arg1 withPlayerInfo:(id)arg2 withParentViewController:(id)arg3 withParentEventViewController:(id)arg4 withAddToParenViewControllerNow:(_Bool)arg5
- (id)initWithEventProxy:(id)arg1 withPlayerInfo:(id)arg2 withParentViewController:(id)arg3 withAddToParenViewControllerNow:(_Bool)arg4
- (id)initWithEventProxy:(id)arg1 withPlayerInfo:(id)arg2 withParentViewController:(id)arg3 withParentEventViewController:(id)arg4
- (id)initWithEventProxy:(id)arg1 withPlayerInfo:(id)arg2 withParentViewController:(id)arg3
  • 创建tweak项目,编写tweak代码,替换QNBPlayerVideoAdsViewController.h的所有初始化方法,返回nil
  • 执行编译安装后发现还是存在广告,继续使用Reveal进行查看,找到QNBPlayerImageAdsViewControllerTADVideoAdController,继续hook这两个类的初始化方法,最后发现广告没有了,完整的hook代码如下

%hook QNBPlayerVideoAdsViewController

- (id)initWithEventProxy:(id)arg1 withPlayerInfo:(id)arg2 withParentViewController:(id)arg3 withPageViewController:(id)arg4 withAddToParenViewControllerNow:(_Bool)arg5{
    return nil;
}
- (id)initWithEventProxy:(id)arg1 withPlayerInfo:(id)arg2 withParentViewController:(id)arg3 withParentEventViewController:(id)arg4 withAddToParenViewControllerNow:(_Bool)arg5{
    return nil;
}
- (id)initWithEventProxy:(id)arg1 withPlayerInfo:(id)arg2 withParentViewController:(id)arg3 withAddToParenViewControllerNow:(_Bool)arg4{
    return nil;
}
- (id)initWithEventProxy:(id)arg1 withPlayerInfo:(id)arg2 withParentViewController:(id)arg3 withParentEventViewController:(id)arg4{
    return nil;
}
- (id)initWithEventProxy:(id)arg1 withPlayerInfo:(id)arg2 withParentViewController:(id)arg3{
    return nil;
}
%end

%hook QNBPlayerImageAdsViewController

- (id)initWithEventProxy:(id)arg1 withPlayerInfo:(id)arg2 withParentViewController:(id)arg3 withPageViewController:(id)arg4 withAddToParenViewControllerNow:(_Bool)arg5{
    return nil;
}
- (id)initWithEventProxy:(id)arg1 withPlayerInfo:(id)arg2 withParentViewController:(id)arg3 withParentEventViewController:(id)arg4 withAddToParenViewControllerNow:(_Bool)arg5{
    return nil;
}
- (id)initWithEventProxy:(id)arg1 withPlayerInfo:(id)arg2 withParentViewController:(id)arg3 withAddToParenViewControllerNow:(_Bool)arg4{
    return nil;
}
- (id)initWithEventProxy:(id)arg1 withPlayerInfo:(id)arg2 withParentViewController:(id)arg3 withParentEventViewController:(id)arg4{
    return nil;
}
- (id)initWithEventProxy:(id)arg1 withPlayerInfo:(id)arg2 withParentViewController:(id)arg3{
    return nil;
}
%end

%hook TADVideoAdController
- (id)init{
    return nil;
}
%end

由于我这里用的是老版的腾讯视频ipa,所以可以成功去除广告,新版的腾讯视频改变了广告的逻辑,所以无法对新版的腾讯视频进行去广告的操作。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容