逆向Pro助手弹窗的过程

事情的起因是本人需要使用到“拍照取自”这款软件,但是这款软件识别后导出文档需要付费,想着也就使用几次付费就有点浪费了,于是乎就找到了“Pro助手”这款插件,这款插件支持破解的App挺多,还挺方便的; 但是,不清楚啥原因这个插件会检测某两个源,只要你使用了这两个源,他就会弹窗提示你“珍爱手机,远离垃圾源”,如果仅仅是提示一下并且破解功能不生效,那么也就不会有接下来的事了,但是这个插件不仅提示还调用exit让App直接退出,这就很过分了,所以接下我就逆向了这个插件,分析了下他检测的原理并编写了绕过检测的插件,下面为整个分析过程:

  1. 首先我们知道的是,他会调用exit函数,所以我们lldb挂上,然后断点exit函数
br s -n exit
br s -n _exit

c
  1. 进程继续后我们等待插件检测到我们安装了某两个源后就会弹出“珍爱手机,远离垃圾源”的提示,并且5秒后就会调用exit函数并且跳转到Safari,由于我们断住了exit,所以App还没有退出,接下来我们使用bt命令来看看调用栈
(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里,并且对file后面的地址一个个跳入进去看看这个调用方是谁,于是我找到了一个比较可疑的调用

frame #2: [file:0xa404 mem:0x10622e404] prozs.dylib`___lldb_unnamed_symbol625$$prozs.dylib + 424

在Hopper里显示他的伪代码为

/* @class SsSsSsSsSs */
-(void)buttonTapped:(void *)arg2;

根据弹窗页面猜测,可能这就是最底下的“朕知道啦!”按钮的事件,那么我们就Hook这个函数来看看他是不是,于是编写代码如下

%hook SsSsSsSsSs
- (void)buttonTapped:(id)arg1 {
    NSLog(@"iOSRE: BUTTON TAP");
}
%end

然后我们把插件编译安装到手机上(需注意的是要保证我们的插件比他先加载否则Hook不生效),依然是打开App、等待检测、弹窗,等待5秒过后,发现控制台输出了“iOSRE: BUTTON TAP”,然后App也没有退出,证实了这就是这个按钮的点击事件,此时我在想,我如果把这个弹窗关闭了是不是就能正常使用了呢,由于这个弹窗本人使用过,可以十分确定他是“SCLAlertView”,而这个名为“SsSsSsSsSs”的类很可能就是混淆后的“SCLAlertView”,于是到GitHub上查看该库的源码,发现一个"- [SCLAlertView hideView]"的方法,于是代码变为如下

%hook SsSsSsSsSs
- (void)buttonTapped:(id)arg1 {
    NSLog(@"iOSRE: BUTTON TAP");
    [self hideView];
}
%end

然后再次编译安装、打开App、等待检测、弹窗,等待5秒后,弹窗自动消失,目的达成!
但是,弹窗是消失了,但是功能并没有生效,也就是说他在检测到你安装了某两个源之后就不启用功能了,所以没办法,继续往下分析。

  1. 由于我们在第二部确认了该库就是“SCLAlertView”,那么接下来的事情就简单了,我们只要找到他的“showView”方法的调用方即可,由于dylib文件混淆了代码,直接快捷键x并不会显示调用方,于是我们还是只能通过lldb进行分析,首先我们对“拍照识图”这个App进行砸壳,然后拖入Hopper,lldb挂上

然后这时我们在宿主App的“-[MainNoteVC viewDidLoad]”方法处下断

lldb挂上后我们拿到宿主App的ASLR:0x0000000000e8c000
在Hopper中找到“-[MainNoteVC viewDidLoad]”方法的地址:0000000100264a78
于是我们在0x0000000000e8c000 + 0000000100264a78的地方下断

xbr -a 0x1010F0A78

c

继续运行后到达“-[MainNoteVC viewDidLoad]”断点,此时我们再次下断

image list -o -f
搜索“prozs”找到
[505] 0x0000000104dc4000 /Library/MobileSubstrate/DynamicLibraries/prozs.dylib(0x0000000104dc4000)
然后在通过分析“SCLAlertView”的源码我们分析到他可能调用的方法为
-[SsSsSsSsSs showNotice:title:subTitle:closeButtonTitle:duration:]
于是我们在Hopper中找到该方法的地址: 000000000000ba48
于是我们在0x0000000104dc4000 + 000000000000ba48处下断

xbr -a 0x104DCFA48

c

继续运行后我们程序再次到达我们的断点处,使用bt命令查看调用栈

(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

由于调用栈是后入的在上,于是我们着重分析 #1 这个调用方

frame #1: [file:0x3d51c mem:0x104e0151c] prozs.dylib`___lldb_unnamed_symbol5960$$prozs.dylib + 1444

我们在Hopper里按G输入地址0x3d51c跳进去, 然后查看一下伪代码

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;
}

于是发现了4个比较可疑的“if”判断,“stat”函数也经常作用于越狱检测,难道这就是我们要找的判断方法吗?于是我们在原先的Hook代码上对“stat”函数进行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);
}

编写如上代码后我们编译插件安装到手机,然后查看控制台的输出内容,在控制台输出的一堆内容中我们找到了4个比较可疑的路径

/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条路径,对应那4个“if”判断,于是我们改变一下Hook代码来进行验证

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);
}

再次编译安装到手机,然后启动App,插件正常载入,并且控制台输出了4个“iOSRE: STAT: R -1”,并且也不弹窗了,再看看“Pro助手”的功能完全正常使用,到此完成该插件的破解。

需要注意的是:我们编译的插件需要比逆向分析的插件优先注入,然后在他加载后进行init才能Hook到。

最终成品插件下载地址:https://wwa.lanzous.com/iVgg4fosbyh

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。