逆向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

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