前言
- 工具:
- Reveal查看App视图层级
- class-dump导出App所有头文件
- theos插件开发工具
- IDA反汇编,可以友好地生成伪代码
- restore-symbol恢复App符号信息
- 注意:此文仅限于技术交流,如果损害了App方利益,请发邮件
zhulongfei28@gmail.com
,谢谢。 - 目的:去除开屏广告和允许内部打开网页链接
新建插件
新建内容如下:
1
2
3
4
5
6
7
8
9
10
11nic.pl
NIC 2.0 - New Instance Creator
------------------------------
...
[10.] iphone/tweak
[11.] iphone/xpc_service
Choose a Template (required): 10
...
[iphone/tweak] MobileSubstrate Bundle filter [com.apple.springboard]: com.sina.weibo
...
Done.更改Makefile,增加内容如下
1
2ARCHS = arm64 arm64e
TARGET = iPhone:latest:10.0目前支持的架构有armv7 armv7s arm64 arm64e等
- 添加
ARCHS = armv7 armv7s arm64
表示支持 armv7 armv7s arm64 三种处理器架构 - 对于最新的A12处理器(iPhoneX以后)的设备,需要添加arm64e,否则生成的dylib文件不能正常加载
- 添加
指定目标规范:
TARGET = iPhone:BaseSDK:DeploymentTarget
BaseSDK
代表编译用的SDK版本Deployment Target
是最低兼容的系统版本,
开屏广告
定位到微博开屏广告界面,使用Reveal查看界面层级,发现
WBAdFlashAdView
是广告视图。使用class-dump导出所有头文件。打开
WBAdFlashAdView.h
文件,很容易发现closeAd
和showAd
。我们首先这样做:在执行弹出广告的时候去关闭广告,代码如下:1
2
3
4
5
6
7
8
9@interface WBAdFlashAdView
- (void)closeAd;
@end
%hook WBAdFlashAdView
- (void)showAd {
[self closeAd];
}
%end执行安装,安装成功后,重新打开微博发现还是有开屏广告,why?第一反应是在执行
closeAd
方法时肯定是有其它判断的,判断通过了才会真正地执行跳过广告的代码。把微博Mach-O文件拖进去IDA,使用IDA查看
closeAd
伪代码,发现如下伪代码1
2
3if ( (unsigned int)-[WBAdFlashAdView isShowing](self, "isShowing") ) {
}查看以上说明,要想真正地执行
closeAd
,[self isShowing]
必须成立,所以最终代码为1
2
3
4
5
6
7
8
9
10
11@interface WBAdFlashAdView
- (void)closeAd;
- (void)setIsShowing:(BOOL)isShowing;
@end
%hook WBAdFlashAdView
- (void)showAd {
[self setIsShowing: YES];
[self closeAd];
}
%end安装后发现确实跳过了开屏广告。
内部打开网页链接
寻找内部浏览器
- 如上图所示,当点击网页链接时,并不会执行打开,而是显示链接地址,提示用户手动复制到到浏览器中进行打开。这样的操作太麻烦了,所以有了下面的文章
- 点击网页链接会进入一个控制器,使用Reveal查看界面层级,发现
WBTopNavigationWebViewController
就是展示网页的控制器。 - 那么接下来要干什么呢,其实是一脸懵逼的。那我们不妨先这么干:恢复符号,然后断点
WBTopNavigationWebViewController
所有方法,判断初始化时会调用哪些方法。
恢复符号
macOS步骤如下:
restore-symbol Weibo -o WeiboWithSymbol
ldid -e WeiboWithSymbol > WeiboWithSymbol.entitlements
使用VSCode软件打开WeiboWithSymbol.entitlements,添加如下,然后保存。
1
2<key>platform-application</key>
<true/>ldid -SWeiboWithSymbol.entitlements WeiboWithSymbol
把WeiboWithSymbol上传到iPhone里面的Weibo.app目录
iPhone上步骤如下:
- cd /var/containers/Bundle/Application/62CA4E3D-37BE-4CF9-8564-3863BD3928CF/Weibo.app/
- mv Weibo WeiboNoSymbol // 备份原有的
- mv WeiboWithSymbol Weibo // 改成新的
重新启动微博
寻找原始URL
执行如下命令,断点
WBTopNavigationWebViewController
所有方法,找到了73个断点位置1
2(lldb) br set -r '\[WBTopNavigationWebViewController .*\]'
Breakpoint 1: 73 locations.点击
网页链接
首先命中了-[WBTopNavigationWebViewController generateWithOpenUrlLink:inAppParas:]
方法,下文把这个方法叫做生成
;然后命中了-[WBTopNavigationWebViewController initWithURL:parameters:]
方法,下文把这个方法叫做初始化
。我们试想一下:为什么有的链接可以直接打开,而有的链接只能在外部浏览器打开?答案是在发送微博时,微博对链接进行过包装,在点击链接时,会传入包装过的URL,然后通过判断包装好的参数来区分是内部还是外部打开。那么我们要做的就是拿到最原始的URL替换掉微博封装的URL,这样就可以内部打开链接。
生成
和初始化
都传入了URL,我们在这两个方法里面都可以拿到原始URL,我们这里在初始化
里面拿最原始的URL,Why?当LLDB里面命中初始化
时,执行bt
查看一下调用堆栈,发现生成
里面调用了初始化
,初始化
层级更深,更核心。命中
初始化
断点时,打印URL参数。确实如我们上面所说,原始的URL被封装了,我们需要提取出来。1
2
3
4
5
6
7
8
9(lldb) c
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000103ae2bd4 Weibo` -[WBTopNavigationWebViewController initWithURL:parameters:]
Weibo`-[WBTopNavigationWebViewController initWithURL:parameters:]:
(lldb) po $x2
http://weibo.cn/sinaurl?toasturl=https%3A%2F%2Fiosre.com%2Ft%2Fswift-runtime-frida-alamofire%2F17602
(lldb) po [$x2 class]
NSURL从打印值发现
初始化
的第一个参数是NSURL类型并且进行了转义操作,所以我们进行反转义,得到能被肉眼识别的字符串1
2
3(lldb) e NSString *$query = (NSString *)[((NSURL *)$x2) query]
(lldb) po [$query stringByRemovingPercentEncoding]
toasturl=https://iosre.com/t/swift-runtime-frida-alamofire/17602在这里就很容易提取原始URL,然后在进行转义,最后封装成NSURL类型,代码如下
1
2NSString *lastStr = [[query componentsSeparatedByString:@"="] lastObject];
NSURL *lastURL = [NSURL URLWithString:lastStr];Tweak.x里面填写如下代码,进行安装,安装成功后,发现和原来效果一样,还是不能内部打开链接,怎么回事呢?
1
2
3
4
5
6
7
8
9
10
11
12
13%hook WBTopNavigationWebViewController
- (id)initWithURL:(NSURL *)url parameters:(NSDictionary *)arg2 {
NSString *query = [[url query] stringByRemovingPercentEncoding];
if (![query containsString:@"toasturl="]) {
return %orig;
}
NSString *lastStr = [[query componentsSeparatedByString:@"="] lastObject];
NSURL *lastURL = [NSURL URLWithString:lastStr];
return %orig(lastURL,arg2);
}
%end
禁用包装的URL
初始化
传入的URL为包装过的URL,而我们传入的URL为原始的URL,可能需要禁用掉包装过URL的其它逻辑,让它可以加载原始URL。往上面几步找线索,包装过URL里面有个sinaurl
,这个代表包装URL的特征。我们去WBTopNavigationWebViewController
查找关键字sinaurl
,发现- (_Bool)disablesSinaURL;
方法,禁用这个方法也许可以加载原始URL。最终代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15%hook WBTopNavigationWebViewController
- (BOOL)disablesSinaURL {
return YES;
}
- (id)initWithURL:(NSURL *)url parameters:(NSDictionary *)arg2 {
NSString *query = [[url query] stringByRemovingPercentEncoding];
if (![query containsString:@"toasturl="]) return %orig;
NSString *lastStr = [[query componentsSeparatedByString:@"="] lastObject];
NSURL *lastURL = [NSURL URLWithString:lastStr];
return %orig(lastURL,arg2);
}
%end安装后测试成功,网页链接都可以在微博内部打开