事情的起因是本人需要使用到“拍照取自”這款軟件,但是這款軟件識(shí)別后導(dǎo)出文檔需要付費(fèi),想著也就使用幾次付費(fèi)就有點(diǎn)浪費(fèi)了,于是乎就找到了“Pro助手”這款插件,這款插件支持破解的App挺多,還挺方便的; 但是,不清楚啥原因這個(gè)插件會(huì)檢測(cè)某兩個(gè)源,只要你使用了這兩個(gè)源,他就會(huì)彈窗提示你“珍愛(ài)手機(jī),遠(yuǎn)離垃圾源”,如果僅僅是提示一下并且破解功能不生效,那么也就不會(huì)有接下來(lái)的事了,但是這個(gè)插件不僅提示還調(diào)用exit讓App直接退出,這就很過(guò)分了,所以接下我就逆向了這個(gè)插件,分析了下他檢測(cè)的原理并編寫(xiě)了繞過(guò)檢測(cè)的插件,下面為整個(gè)分析過(guò)程:
- 首先我們知道的是,他會(huì)調(diào)用exit函數(shù),所以我們lldb掛上,然后斷點(diǎn)exit函數(shù)
br s -n exit
br s -n _exit
c
- 進(jìn)程繼續(xù)后我們等待插件檢測(cè)到我們安裝了某兩個(gè)源后就會(huì)彈出“珍愛(ài)手機(jī),遠(yuǎn)離垃圾源”的提示,并且5秒后就會(huì)調(diào)用exit函數(shù)并且跳轉(zhuǎn)到Safari,由于我們斷住了exit,所以App還沒(méi)有退出,接下來(lái)我們使用bt命令來(lái)看看調(diào)用棧
(lldb) sbt
==========================================xia0LLDB===========================================
BlockSymbolFile Not Set The Block Symbol Json File, Try 'sbt -f'
=============================================================================================
frame #0: [file:0x1800e1484 mem:0x1a66c1484] libsystem_c.dylib`exit + 0
frame #1: [file:0x3dd2c mem:0x106261d2c] prozs.dylib`___lldb_unnamed_symbol5961$$prozs.dylib + 1172
frame #2: [file:0xa404 mem:0x10622e404] prozs.dylib`___lldb_unnamed_symbol625$$prozs.dylib + 424
frame #3: [file:0x5974 mem:0x106229974] prozs.dylib`___lldb_unnamed_symbol369$$prozs.dylib + 72
frame #4: [file:0x5a1c mem:0x106229a1c] prozs.dylib`___lldb_unnamed_symbol370$$prozs.dylib + 156
frame #5: [file:0x180865c48 mem:0x1a6e45c48] Foundation`__NSFireTimer + 68
frame #6: [file:0x1803e8190 mem:0x1a69c8190] CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 32
frame #7: [file:0x1803e7ea8 mem:0x1a69c7ea8] CoreFoundation`__CFRunLoopDoTimer + 936
frame #8: [file:0x1803e751c mem:0x1a69c751c] CoreFoundation`__CFRunLoopDoTimers + 280
frame #9: [file:0x1803e2274 mem:0x1a69c2274] CoreFoundation`__CFRunLoopRun + 1624
frame #10: [file:0x1803e18f4 mem:0x1a69c18f4] CoreFoundation`CFRunLoopRunSpecific + 480
frame #11: [file:0x18a7f8604 mem:0x1b0dd8604] GraphicsServices`GSEventRunModal + 164
frame #12: [file:0x1845b5358 mem:0x1aab95358] UIKitCore`UIApplicationMain + 1944
frame #13: [file:0x100365bb4 mem:0x100dd1bb4] OssIOSDemo`main + 88
frame #14: [file:0x18025d2dc mem:0x1a683d2dc] libdyld.dylib`start + 4
我們把prozs.dylib拖到Hopper里,并且對(duì)file后面的地址一個(gè)個(gè)跳入進(jìn)去看看這個(gè)調(diào)用方是誰(shuí),于是我找到了一個(gè)比較可疑的調(diào)用
frame #2: [file:0xa404 mem:0x10622e404] prozs.dylib`___lldb_unnamed_symbol625$$prozs.dylib + 424
在Hopper里顯示他的偽代碼為
/* @class SsSsSsSsSs */
-(void)buttonTapped:(void *)arg2;
根據(jù)彈窗頁(yè)面猜測(cè),可能這就是最底下的“朕知道啦!”按鈕的事件,那么我們就Hook這個(gè)函數(shù)來(lái)看看他是不是,于是編寫(xiě)代碼如下
%hook SsSsSsSsSs
- (void)buttonTapped:(id)arg1 {
NSLog(@"iOSRE: BUTTON TAP");
}
%end
然后我們把插件編譯安裝到手機(jī)上(需注意的是要保證我們的插件比他先加載否則Hook不生效),依然是打開(kāi)App、等待檢測(cè)、彈窗,等待5秒過(guò)后,發(fā)現(xiàn)控制臺(tái)輸出了“iOSRE: BUTTON TAP”,然后App也沒(méi)有退出,證實(shí)了這就是這個(gè)按鈕的點(diǎn)擊事件,此時(shí)我在想,我如果把這個(gè)彈窗關(guān)閉了是不是就能正常使用了呢,由于這個(gè)彈窗本人使用過(guò),可以十分確定他是“SCLAlertView”,而這個(gè)名為“SsSsSsSsSs”的類很可能就是混淆后的“SCLAlertView”,于是到GitHub上查看該庫(kù)的源碼,發(fā)現(xiàn)一個(gè)"- [SCLAlertView hideView]"的方法,于是代碼變?yōu)槿缦?/p>
%hook SsSsSsSsSs
- (void)buttonTapped:(id)arg1 {
NSLog(@"iOSRE: BUTTON TAP");
[self hideView];
}
%end
然后再次編譯安裝、打開(kāi)App、等待檢測(cè)、彈窗,等待5秒后,彈窗自動(dòng)消失,目的達(dá)成!
但是,彈窗是消失了,但是功能并沒(méi)有生效,也就是說(shuō)他在檢測(cè)到你安裝了某兩個(gè)源之后就不啟用功能了,所以沒(méi)辦法,繼續(xù)往下分析。
- 由于我們?cè)诘诙看_認(rèn)了該庫(kù)就是“SCLAlertView”,那么接下來(lái)的事情就簡(jiǎn)單了,我們只要找到他的“showView”方法的調(diào)用方即可,由于dylib文件混淆了代碼,直接快捷鍵x并不會(huì)顯示調(diào)用方,于是我們還是只能通過(guò)lldb進(jìn)行分析,首先我們對(duì)“拍照識(shí)圖”這個(gè)App進(jìn)行砸殼,然后拖入Hopper,lldb掛上
然后這時(shí)我們?cè)谒拗鰽pp的“-[MainNoteVC viewDidLoad]”方法處下斷
lldb掛上后我們拿到宿主App的ASLR:0x0000000000e8c000
在Hopper中找到“-[MainNoteVC viewDidLoad]”方法的地址:0000000100264a78
于是我們?cè)?x0000000000e8c000 + 0000000100264a78的地方下斷
xbr -a 0x1010F0A78
c
繼續(xù)運(yùn)行后到達(dá)“-[MainNoteVC viewDidLoad]”斷點(diǎn),此時(shí)我們?cè)俅蜗聰?/p>
image list -o -f
搜索“prozs”找到
[505] 0x0000000104dc4000 /Library/MobileSubstrate/DynamicLibraries/prozs.dylib(0x0000000104dc4000)
然后在通過(guò)分析“SCLAlertView”的源碼我們分析到他可能調(diào)用的方法為
-[SsSsSsSsSs showNotice:title:subTitle:closeButtonTitle:duration:]
于是我們?cè)贖opper中找到該方法的地址: 000000000000ba48
于是我們?cè)?x0000000104dc4000 + 000000000000ba48處下斷
xbr -a 0x104DCFA48
c
繼續(xù)運(yùn)行后我們程序再次到達(dá)我們的斷點(diǎn)處,使用bt命令查看調(diào)用棧
(lldb) sbt
error: libarclite_iphoneos.a(arclite.o) failed to load objfile for /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/arc/libarclite_iphoneos.a
==========================================xia0LLDB===========================================
BlockSymbolFile Not Set The Block Symbol Json File, Try 'sbt -f'
=============================================================================================
frame #0: [file:0xba48 mem:0x104dcfa48] prozs.dylib`___lldb_unnamed_symbol633$$prozs.dylib + 0
frame #1: [file:0x3d51c mem:0x104e0151c] prozs.dylib`___lldb_unnamed_symbol5960$$prozs.dylib + 1444
frame #2: [file:0x18010c33c mem:0x1a66ec33c] libdispatch.dylib`_dispatch_client_callout + 20
frame #3: [file:0x18010eaf8 mem:0x1a66eeaf8] libdispatch.dylib`_dispatch_continuation_pop + 408
frame #4: [file:0x18011f624 mem:0x1a66ff624] libdispatch.dylib`_dispatch_source_invoke + 1224
frame #5: [file:0x1801184f0 mem:0x1a66f84f0] libdispatch.dylib`_dispatch_main_queue_callback_4CF + 560
frame #6: [file:0x1803e76b0 mem:0x1a69c76b0] CoreFoundation`__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 16
frame #7: [file:0x1803e22c8 mem:0x1a69c22c8] CoreFoundation`__CFRunLoopRun + 1708
frame #8: [file:0x1803e18f4 mem:0x1a69c18f4] CoreFoundation`CFRunLoopRunSpecific + 480
frame #9: [file:0x18a7f8604 mem:0x1b0dd8604] GraphicsServices`GSEventRunModal + 164
frame #10: [file:0x1845b5358 mem:0x1aab95358] UIKitCore`UIApplicationMain + 1944
frame #11: [file:0x100365bb4 mem:0x1011f1bb4] OssIOSDemo`main + 88
frame #12: [file:0x18025d2dc mem:0x1a683d2dc] libdyld.dylib`start + 4
由于調(diào)用棧是后入的在上,于是我們著重分析 #1 這個(gè)調(diào)用方
frame #1: [file:0x3d51c mem:0x104e0151c] prozs.dylib`___lldb_unnamed_symbol5960$$prozs.dylib + 1444
我們?cè)贖opper里按G輸入地址0x3d51c跳進(jìn)去, 然后查看一下偽代碼
int sub_3cf78(int arg0) {
var_20 = r22;
stack[-40] = r21;
r31 = r31 + 0xffffffffffffffd0;
var_10 = r20;
stack[-24] = r19;
saved_fp = r29;
stack[-8] = r30;
r29 = &saved_fp;
r19 = arg0;
asm { ldapr w9, [x8] };
if (r9 == 0x0) {
*(int128_t *)0x8a320 = *0x44210;
*(int128_t *)0x8a330 = *0x44220;
*0x8a340 = *0x8a340;
*(int128_t *)0x8a350 = *0x44230;
*(int128_t *)0x8a360 = *0x44240;
*(int128_t *)0x8a370 = *0x44250;
*(int128_t *)0x8a380 = *0x44260;
*(int128_t *)0x8a390 = *0x44270;
*(int128_t *)0x8a3a0 = *0x44280;
*(int128_t *)0x8a3b0 = *0x44290;
*(int128_t *)0x8a3c0 = *0x442a0;
*(int128_t *)0x8a3d0 = *0x442b0;
*(int128_t *)0x8a3e0 = *0x442c0;
*(int128_t *)0x8a3f0 = *(int128_t *)0x8a3f0;
*(int128_t *)0x8a270 = *0x442e0;
*(int128_t *)0x8a280 = *0x442f0;
*(int128_t *)0x8a290 = *0x44300;
*(int128_t *)0x8a2a0 = *0x44310;
*(int128_t *)0x8a2b0 = *0x44320;
*(int128_t *)0x8a2c0 = *0x44330;
*(int128_t *)0x8a2d0 = *0x44340;
*(int128_t *)0x8a2e0 = *0x44350;
*(int128_t *)0x8a2f0 = *0x44360;
*(int128_t *)0x8a300 = *0x44370;
*0x8a310 = *0x8a310;
*0x8a250 = *0x8a250;
*0x8a260 = *0x8a260;
*(int128_t *)0x8a200 = *0x44380;
*(int128_t *)0x8a210 = *0x44390;
*(int128_t *)0x8a220 = *0x443a0;
*(int128_t *)0x8a230 = *0x443b0;
*0x8a243 = *0x8a243;
*(int128_t *)0x8a1b0 = *0x443c0;
*(int128_t *)0x8a1c0 = *0x443d0;
*(int128_t *)0x8a1d0 = *0x443e0;
*(int128_t *)0x8a1e0 = *0x443f0;
*0x8a1f0 = *0x8a1f0;
*(int8_t *)0x8a348 = *(int8_t *)0x8a348 ^ 0x94;
*(int8_t *)0x8a349 = *(int8_t *)0x8a349 ^ 0xa1;
*(int8_t *)0x8a34a = *(int8_t *)0x8a34a ^ 0xfffffffffffffff7;
*(int8_t *)0x8a34b = *(int8_t *)0x8a34b ^ 0x1d;
*(int16_t *)0x8a400 = *(int16_t *)0x8a400 ^ 0x2196;
*(int16_t *)0x8a318 = *(int16_t *)0x8a318 ^ 0x25b3;
*(int16_t *)0x8a31a = *(int16_t *)0x8a31a ^ 0x3af1;
*(int16_t *)0x8a31c = *(int16_t *)0x8a31c ^ 0xb9b9;
*(int16_t *)0x8a258 = *(int16_t *)0x8a258 ^ 0xd38;
*(int16_t *)0x8a25a = *(int16_t *)0x8a25a ^ 0x1546;
*(int16_t *)0x8a268 = *(int16_t *)0x8a268 ^ 0x9f3e;
*(int8_t *)0x8a240 = *(int8_t *)0x8a240 ^ 0xae;
*(int8_t *)0x8a241 = *(int8_t *)0x8a241 ^ 0x8c;
*(int8_t *)0x8a24b = *(int8_t *)0x8a24b ^ 0x1;
*(int8_t *)0x8a24c = *(int8_t *)0x8a24c ^ 0xa6;
*(int8_t *)0x8a1f8 = *(int8_t *)0x8a1f8 ^ 0x16;
*(int8_t *)0x8a1f9 = *(int8_t *)0x8a1f9 ^ 0xfffffffffffffff1;
*(int8_t *)0x8a1fa = *(int8_t *)0x8a1fa ^ 0x3b;
*(int128_t *)0x8a410 = *0x44400;
*(int128_t *)0x8a420 = *0x44410;
*0x8a430 = *0x8a430;
*(int8_t *)0x8a438 = *(int8_t *)0x8a438 ^ 0xc4;
*(int8_t *)0x8a439 = *(int8_t *)0x8a439 ^ 0xffffffffffffffe3;
*(int8_t *)0x8a43a = *(int8_t *)0x8a43a ^ 0x1a;
*(int8_t *)0x8a43b = *(int8_t *)0x8a43b ^ 0xffffffffffffff87;
*(int8_t *)0x8a43c = *(int8_t *)0x8a43c ^ 0xffffffff88888888;
*(int8_t *)0x8a43d = *(int8_t *)0x8a43d ^ 0x6;
}
asm { stlr w9, [x8] };
r20 = r31 - 0x90;
if (stat(0x8a1b0, r20) == 0x0) {
r0 = [SsSsSsSsSs alloc];
r0 = [r0 initWithNewWindow];
r21 = r0;
r0 = [r0 addButton:0x8a440 actionBlock:0x79850];
r29 = r29;
[[r0 retain] release];
[r21 setCustomViewColor:[UIColor colorWithRed:0x8a440 green:0x79850 blue:r4 alpha:r5]];
[r21 addTimerToButtonIndex:0x0 reverse:0x1];
r4 = 0x8a480;
r5 = 0x0;
[r21 showNotice:*(r19 + 0x20) title:0x8a460 subTitle:r4 closeButtonTitle:r5 duration:r6];
[r21 release];
}
if (stat(0x8a320, r20) == 0x0) {
r0 = [SsSsSsSsSs alloc];
r0 = [r0 initWithNewWindow];
r21 = r0;
[[[r0 addButton:0x8a440 actionBlock:0x79890] retain] release];
r0 = [UIColor colorWithRed:0x8a440 green:0x79890 blue:r4 alpha:r5];
r29 = r29;
r22 = [r0 retain];
[r21 setCustomViewColor:r22];
[r22 release];
[r21 addTimerToButtonIndex:0x0 reverse:0x1];
r4 = 0x8a480;
r5 = 0x0;
[r21 showNotice:*(r19 + 0x20) title:0x8a460 subTitle:r4 closeButtonTitle:r5 duration:r6];
[r21 release];
}
if (stat(0x8a200, r20) == 0x0) {
r0 = [SsSsSsSsSs alloc];
r0 = [r0 initWithNewWindow];
r21 = r0;
[[[r0 addButton:0x8a440 actionBlock:0x798d0] retain] release];
r0 = [UIColor colorWithRed:0x8a440 green:0x798d0 blue:r4 alpha:r5];
r29 = r29;
r22 = [r0 retain];
[r21 setCustomViewColor:r22];
[r22 release];
[r21 addTimerToButtonIndex:0x0 reverse:0x1];
r4 = 0x8a4a0;
r5 = 0x0;
[r21 showNotice:*(r19 + 0x20) title:0x8a460 subTitle:r4 closeButtonTitle:r5 duration:r6];
[r21 release];
}
r0 = stat(0x8a410, r20);
if (r0 == 0x0) {
r0 = [SsSsSsSsSs alloc];
r0 = [r0 initWithNewWindow];
r20 = r0;
[[[r0 addButton:0x8a440 actionBlock:0x79910] retain] release];
r0 = [UIColor colorWithRed:0x8a440 green:0x79910 blue:r4 alpha:r5];
r29 = r29;
r21 = [r0 retain];
[r20 setCustomViewColor:r21];
[r21 release];
[r20 addTimerToButtonIndex:0x0 reverse:0x1];
[r20 showNotice:*(r19 + 0x20) title:0x8a460 subTitle:0x8a4a0 closeButtonTitle:0x0 duration:r6];
r0 = [r20 release];
}
return r0;
}
于是發(fā)現(xiàn)了4個(gè)比較可疑的“if”判斷,“stat”函數(shù)也經(jīng)常作用于越獄檢測(cè),難道這就是我們要找的判斷方法嗎?于是我們?cè)谠鹊腍ook代碼上對(duì)“stat”函數(shù)進(jìn)行Hook
static int (*orig_stat)(char *c, struct stat *s);
static int new_stat(char *c, struct stat *s){
NSLog(@"iOSRE: STAT: %s", c);
return orig_stat(c,s);
}
%ctor {
MSHookFunction((void *)stat,(void *)new_stat,(void **)&orig_stat);
}
編寫(xiě)如上代碼后我們編譯插件安裝到手機(jī),然后查看控制臺(tái)的輸出內(nèi)容,在控制臺(tái)輸出的一堆內(nèi)容中我們找到了4個(gè)比較可疑的路徑
/var/mobile/Library/Caches/com.saurik.Cydia/lists/某某源1._Release
/var/lib/apt/lists/某某源1._Release
/var/lib/apt/lists/某某源2._Release
/var/mobile/Library/Caches/com.saurik.Cydia/lists/某某源2._Release
剛好4條路徑,對(duì)應(yīng)那4個(gè)“if”判斷,于是我們改變一下Hook代碼來(lái)進(jìn)行驗(yàn)證
static int new_stat(char *c, struct stat *s) {
if( strcmp(c, "/var/lib/apt/lists/某某源1._Release") == 0
|| strcmp(c, "/var/mobile/Library/Caches/com.saurik.Cydia/lists/某某源1._Release") == 0
|| strcmp(c, "/var/lib/apt/lists/某某源2._Release") == 0
|| strcmp(c, "/var/mobile/Library/Caches/com.saurik.Cydia/lists/某某源2._Release") == 0 ) {
NSLog(@"iOSRE: STAT: R -1");
return -1;
}
return orig_stat(c,s);
}
再次編譯安裝到手機(jī),然后啟動(dòng)App,插件正常載入,并且控制臺(tái)輸出了4個(gè)“iOSRE: STAT: R -1”,并且也不彈窗了,再看看“Pro助手”的功能完全正常使用,到此完成該插件的破解。
需要注意的是:我們編譯的插件需要比逆向分析的插件優(yōu)先注入,然后在他加載后進(jìn)行init才能Hook到。
最終成品插件下載地址:https://wwa.lanzous.com/iVgg4fosbyh