iOS逆向之移除Oplayer lite广告
一、环境要求:
1、iPod touch6: iOS10.3.1(已经越狱)
2、Xcode安装MonkeyDev
3、OPlayer Lite.ipa(Window PP助手获取)【非必须】
二、lldb调试定位
1、直接在app store上下载Oplayer lite播放神器,为了方便调试,最好将设备设置成语言英文,后面会用到!
2、通过ssh连接越狱设备
ssh [email protected]
3、关闭设备上的其他进程,最好只保留Oplayer lite,通过以下命令获取设备上所有运行的进程,只查看目标进程:
iPod:~ root# ps aux | grep OPlayer
mobile 6593 3.6 6.5 1384832 66024 ?? Ss 5:27PM 0:20.84 /var/containers/Bundle/Application/AAAB1B0F-A9A6-455C-BE5B-8E0230A75252/OPlayer Lite.app/OPlayer Lite
root 6607 0.0 0.0 624224 8 s000 R+ 5:29PM 0:00.00 grep OPlayer
4、根据教程iOS10.3.1 砸壳之路使用两种方式进行应用砸壳,这里笔者就不详述了。
笔者通过第一种静态方式结果如下:
iPod:~ root# Clutch -i
Installed apps:
1: 快拍 - Snapchat
2: 优酷视频-世界杯赛事全程高清直播
3: 央视影音
4: A4 Player
5: 可可英语-英语听力口语训练神器
6: VPN Plus Privacy Protector
7: 搜狐视频-法医秦明1、2两季独家连播
8: 微博
9: 腾讯视频
10: Shazam 音乐神搜
11: OPlayer Lite - media player
12: VPN - Super Unlimited Proxy
13: 天天快报 - 腾讯兴趣阅读平台
iPod:~ root# Clutch -d 11
Zipping OPlayer Lite.app
Error: Could not obtain mach port, either the process is dead (codesign error?) or entitlements were not properly signed!
Error: Failed to dump with arch arm64
2018-08-06 17:36:20.796 Clutch[6610:278690] failed operation :(
2018-08-06 17:36:20.796 Clutch[6610:278690] application {name = 'NSOperationQueue 0x1004be080'}
Error: Failed to dump
2018-08-06 17:36:20.797 Clutch[6610:278690] failed operation :(
2018-08-06 17:36:20.797 Clutch[6610:278690] application {name = 'NSOperationQueue 0x1004be080'}
ASLR slide: 0x100020000
Dumping (arm64)
Patched cryptid (64bit segment)
Writing new checksum
Zipping OPlayer WatchKit Extension.appex
FAILED:
Finished dumping com.olimsoft.oplayer.lite in 20.9 seconds
很遗憾失败了,其原因自行百度。。。
所以最好通过动态砸壳来解决,若是大佬能通过Clutch方式解决,请私信me!!!
5、查看广告存在的位置
iPod:~ root# cycript -p 6593
cy# [[UIApp keyWindow] recursiveDescription].toString()
`; layer = >
| >
| | >
| | | >
| | | | >
| | | | >
| | | | | (layer)
| | | | | | (layer)
| | | | | | | (layer)
| | | | | | (layer)
| | | | | | (layer)
| | | >
| | | >
| | | >
| | | >
| | | | >
| | | | | >
| | | | >
| | | >
| | | | >
| | | | | >
| | | | | ; value: 3.000000>
| | | | | | >
| | | | | | | >
| | | | | | >
| | | | | | >
| | | | | >
| | | | >
| | | | >
| | | | >
| | | | | >
| | | | >
| | | | | >
| | | >
| | | | >
| | | | | >
| | | | >
| | | | | >
| | | | >
| | | | | >
| | | | >
| | | | | >
| | | | >
| | | | | >
| | | | >
| | | | | >
| | | | | | >
| | | | | | >
| | | | | ; value: 0.000000>
| | | | | | >
| | | | | | | >
| | | | | | >
| | | | | | >
| | | | >
| | | | | >
| | | | >
| | | >
| | | | >
| | | | | >
| | | | >
| | | | | >
| | | | >
| | | | | >
| | | | >
| | | | | >
| | | | >
| | | | | >
| | | | >
| | | | | >
| | | | >
| | | | | >
| | | >
| | | | >
| | | | | >
| | | | >
| | | | | >
| | | | >
| | | | | >
| | | | >
| | | | | >
| | | >
| | | | >
| | | | | >
| | | | >
| | | | | >
| | | | >
| | | | | >
| | | | >
| | | | | >
| | | >
| | | >
| | | | <_UILabelContentLayer: 0x1706337c0> (layer)
| | | >
| | | | >
| | | | >
| | | | | >
| | | | >
| | | | | >
| | | | >
| | | | | >
| | | | >
| | | | | >
| | | | >
| | | | | >
| | | | >
| | | | | >
| | | >
| | | | >
| | | | >
| | | | | >
| | | | >
| | | | | >
| | | | >
| | | | | >
| | | | >
| | | | | >
| | | | >
| | | | | >
| | | | >
| | | | | >
| | | >
| | | | >
| | | >
| | | | >`
这里需要说明的是,[[UIApp keyWindow] recursiveDescription].toString()
是查看当前页面的所有view。稍微仔细点的同学就会主要到,每次进入播放界面时,界面上方都有一个banner,内容是Buy the full version to remove ads?
.所以简单的方式是,通过搜索关键字查找控件。可得到如下结果:
我们可以在后面进行lldb调试的时候,根据这个为依据进行查找调试。
5、利用debugserver结合LLDB调试app
debugserver *:1234 -a "OPlayer Lite"
6、新建终端LLDB连接App
lldb
process connect connect://172.20.128.176:1234
7、接下来查看偏移地址
image list -o -f
[ 0] 0x000000000005c000 /var/containers/Bundle/Application/AAAB1B0F-A9A6-455C-BE5B-8E0230A75252/OPlayer Lite.app/OPlayer Lite(0x000000010005c000)
......
[ 7] 0x0000000000350000 /Users/weihua/Library/Developer/Xcode/iOS DeviceSupport/10.3.1 (14E304)/Symbols/System/Library/Frameworks/UIKit.framework/UIKit
通过hopper v4分析addSubview
在UIKit框架的偏移地址:
addSubview:0x0000000187775d24
通过image list -o -f
分析UIKit框架在模块中加载的的起始偏移地址:
UIKit: 0x0000000000350000
设置断点:
br s -a 0x0000000000350000+0x0000000187775d24
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000187ac5d24 UIKit`-[UIView(Hierarchy) addSubview:]
UIKit`-[UIView(Hierarchy) addSubview:]:
-> 0x187ac5d24 <+0>: stp x24, x23, [sp, #-0x40]!
0x187ac5d28 <+4>: stp x22, x21, [sp, #0x10]
0x187ac5d2c <+8>: stp x20, x19, [sp, #0x20]
0x187ac5d30 <+12>: stp x29, x30, [sp, #0x30]
Target 0: (OPlayer Lite) stopped.
(lldb) po $x2
>
(lldb) c
Process 432 resuming
Process 432 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000187ac5d24 UIKit`-[UIView(Hierarchy) addSubview:]
UIKit`-[UIView(Hierarchy) addSubview:]:
-> 0x187ac5d24 <+0>: stp x24, x23, [sp, #-0x40]!
0x187ac5d28 <+4>: stp x22, x21, [sp, #0x10]
0x187ac5d2c <+8>: stp x20, x19, [sp, #0x20]
0x187ac5d30 <+12>: stp x29, x30, [sp, #0x30]
Target 0: (OPlayer Lite) stopped.
(lldb) po $x2
>
......
......
......
(lldb) po $x2
>
(lldb) c
Process 432 resuming
Process 432 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000187ac5d24 UIKit`-[UIView(Hierarchy) addSubview:]
UIKit`-[UIView(Hierarchy) addSubview:]:
-> 0x187ac5d24 <+0>: stp x24, x23, [sp, #-0x40]!
0x187ac5d28 <+4>: stp x22, x21, [sp, #0x10]
0x187ac5d2c <+8>: stp x20, x19, [sp, #0x20]
0x187ac5d30 <+12>: stp x29, x30, [sp, #0x30]
Target 0: (OPlayer Lite) stopped.
(lldb) po $x2
>
(lldb) c
Process 432 resuming
Process 432 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000187ac5d24 UIKit`-[UIView(Hierarchy) addSubview:]
UIKit`-[UIView(Hierarchy) addSubview:]:
-> 0x187ac5d24 <+0>: stp x24, x23, [sp, #-0x40]!
0x187ac5d28 <+4>: stp x22, x21, [sp, #0x10]
0x187ac5d2c <+8>: stp x20, x19, [sp, #0x20]
0x187ac5d30 <+12>: stp x29, x30, [sp, #0x30]
Target 0: (OPlayer Lite) stopped.
(lldb) po $x2
>
到此为止找到了相关控件,然后通过ni
命令往回追溯目标模块调用时的起始地址。
(lldb) ni
Process 432 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step over
frame #0: 0x0000000187ac5d28 UIKit`-[UIView(Hierarchy) addSubview:] + 4
UIKit`-[UIView(Hierarchy) addSubview:]:
-> 0x187ac5d28 <+4>: stp x22, x21, [sp, #0x10]
0x187ac5d2c <+8>: stp x20, x19, [sp, #0x20]
0x187ac5d30 <+12>: stp x29, x30, [sp, #0x30]
0x187ac5d34 <+16>: add x29, sp, #0x30 ; =0x30
Target 0: (OPlayer Lite) stopped.
(lldb)
Process 432 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step over
frame #0: 0x0000000187ac5d2c UIKit`-[UIView(Hierarchy) addSubview:] + 8
UIKit`-[UIView(Hierarchy) addSubview:]:
-> 0x187ac5d2c <+8>: stp x20, x19, [sp, #0x20]
0x187ac5d30 <+12>: stp x29, x30, [sp, #0x30]
0x187ac5d34 <+16>: add x29, sp, #0x30 ; =0x30
0x187ac5d38 <+20>: mov x20, x0
Target 0: (OPlayer Lite) stopped.
(lldb)
(lldb)
error: invalid thread
Process 432 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step over
frame #0: 0x0000000187ac5d30 UIKit`-[UIView(Hierarchy) addSubview:] + 12
UIKit`-[UIView(Hierarchy) addSubview:]:
-> 0x187ac5d30 <+12>: stp x29, x30, [sp, #0x30]
0x187ac5d34 <+16>: add x29, sp, #0x30 ; =0x30
0x187ac5d38 <+20>: mov x20, x0
0x187ac5d3c <+24>: mov x0, x2
Target 0: (OPlayer Lite) stopped.
(lldb)
........
Process 432 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step over
frame #0: 0x0000000187ac6074 UIKit`-[UIView(Hierarchy) addSubview:] + 848
UIKit`-[UIView(Hierarchy) addSubview:]:
-> 0x187ac6074 <+848>: b 0x180414250 ; objc_release
UIKit`-[UIView(Internal) _addSubview:positioned:relativeTo:]:
0x187ac6078 <+0>: stp x28, x27, [sp, #-0x60]!
0x187ac607c <+4>: stp x26, x25, [sp, #0x10]
0x187ac6080 <+8>: stp x24, x23, [sp, #0x20]
Target 0: (OPlayer Lite) stopped.
(lldb)
Process 432 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step over
frame #0: 0x00000001003d01f8 OPlayer Lite`_mh_execute_header + 3621368
OPlayer Lite`_mh_execute_header:
-> 0x1003d01f8 <+3621368>: adrp x8, 5089
0x1003d01fc <+3621372>: ldr x20, [x8, #0x630]
0x1003d0200 <+3621376>: mov x0, x19
0x1003d0204 <+3621380>: mov x1, x20
Target 0: (OPlayer Lite) stopped.
(lldb)
Process 432 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step over
frame #0: 0x00000001003d01fc OPlayer Lite`_mh_execute_header + 3621372
OPlayer Lite`_mh_execute_header:
-> 0x1003d01fc <+3621372>: ldr x20, [x8, #0x630]
0x1003d0200 <+3621376>: mov x0, x19
0x1003d0204 <+3621380>: mov x1, x20
0x1003d0208 <+3621384>: bl 0x10106f28c ; symbol stub for: objc_msgSend
Target 0: (OPlayer Lite) stopped.
由结果看来,OPlayer Lite
模块起始基地址为0x1003d01f8
,
然后通过减去OPlayer Lite
偏移地址0x000000000005c000
,
(lldb) p/x 0x1003d01f8-0x000000000005c000
(long) $74 = 0x00000001003741f8
然后把0x00000001003741f8
放入已经打开的Hopper Disassembler v4中,用快捷键G
进行查找,结果如下:
这里我们进行更加结果进行猜测,addAds_OnLocalAds
很有可能是我们需要查找的结果。接下来,我们对其进行进行断点调试,先找到addAds_OnLocalAds
的地址:
地址为:0x000000010037c518
(lldb) p/x 0x000000010037c518+0x000000000005c000
(long) $76 = 0x00000001003d8518
可能此时设备卡顿不动,继续运行,并且移除所有命令:
(lldb) c
Process 432 resuming
(lldb) br del
About to delete all breakpoints, do you want to do that?: [Y/n] y
All breakpoints removed. (1 breakpoint)
重新设置断点,即addAds_OnLocalAds
处设置断点:
br s -a 0x00000001003d8518
然后返回上一界面,重新播放视频,此时命令行输出:
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
frame #0: 0x00000001003d8518 OPlayer Lite`_mh_execute_header + 3654936
OPlayer Lite`_mh_execute_header:
-> 0x1003d8518 <+3654936>: stp d9, d8, [sp, #-0x50]!
0x1003d851c <+3654940>: stp x24, x23, [sp, #0x10]
0x1003d8520 <+3654944>: stp x22, x21, [sp, #0x20]
0x1003d8524 <+3654948>: stp x20, x19, [sp, #0x30]
Target 0: (OPlayer Lite) stopped.
(lldb)
接下来获取名称及地址:
(lldb) p (char*)$x1
(char *) $78 = 0x0000000101454c97 "addAds_OnLocalAds"
方法执行完以后应该返回的地址
(lldb) p/x $lr
(unsigned long) $83 = 0x00000001003d01f8
(lldb) p/x 0x00000001003d01f8-0x000000000005c000
(long) $84 = 0x00000001003741f8
0x00000001003741f8
是我们需要的地址,根据这个地址可以在Hopper V4中进行跳转:
在上图中,找了方法addAds_OnLocalAds
方法,说明位置正确,然后根据汇编指令cbnz
,可知这一处是一个判断语句。
最关键的信息是,我们还看到了在一个"PlayViewController"控制器中存在一个'localAdView'的成员变量。
同时继续往上继续查找,可以找到该方法是在[PlayViewController viewWillAppear:]
中调用的,如下图:
接下来,我们进行确认。
通过class-dump
的方式获取头文件。这里不说具体原因了,命令如下:
class-dump OPlayer_Lite.decrypted -H -o header
然后在header文件夹中可以进行确认。
OK,到此为止,我们已经找到了 广告加载的界面的了。我们需要通过工程来进行最后的实现。
三、MonkeyDev调试定位
这里为了简单,我采用了MonkeyDev来实现,当然也可以通过 Theos 的方式,笔者亲测成功!!!
关于如何安装MonkeyDev,请移步MonkeyDev安装教程及简介。
需要说明的是,MonkeyDev的好处就是能通过界面调试app,定位控件,当然如果安装了Reveal那就更加简单,iOS上面的界面调试神器。但是
MonkeyDev需要已经破解了ipa,这里可以通过Window上的PP助手
获取。
首先新建工程,命名Oplayerlite
.其它的也不多了,直接贴关键代码。
然后运行工程,发现之前的 'Buy the full version to remove ads?' 相关的UIView已经没了,但是又出现了新的广告,以下截图来自Reveal,Xcode也可以。
然后在PlayViewController
中找到了相关的调用GADBannerView *gAdView;
,然后通过头文件查找GADBannerView
,结果找到了如下调用函数:
此时抱着怀疑的态度试了修改工程中OplayerliteDylib.xm
内容如下:
// See http://iphonedevwiki.net/index.php/Logos
#import
@interface PlayViewController
@property(strong, nonatomic) UIView *localAdView;
@end
%hook PlayViewController
- (void)viewWillAppear:(BOOL)arg1
{
self.localAdView = [[UIView alloc]initWithFrame:CGRectZero];
%orig;
}
%end
%hook GADBannerView
- (void)setFrame:(struct CGRect)arg1
{
NSLog(@"__%s__",__func__);
}
%end
然后run一下,结果居然成功了。
OK,恭喜,到此为止真的实现了Oplayer lite播放时移除广告的功能。
四、打包安装App至非越狱
后面,我想这如何将此app安装的到非越狱的设备上。
将此app进行到处,放入Payload文件中压缩,重命名为.ipa的文件。此时可能还无法进行安装,需要最后一步操作,进行ipa重签名。
具体请参考iOS重签名操作