Sketch!一次悸动的小逆向尝试

丙戌月 癸酉日

故事从一开始应该是这样的:最近一直在使用Sketch,然而呢,直到某一天弹出这个着实让我惊叹不已:

想着今天是1024专属日,于是乎上官网看看然而发现并没有限免,万年不变的价格也是出奇的合理,99美元能买到这样一款优秀到超出想象,好用到违反广告法的软件真的是赚到了。

看看还有没有其它类似的App,要不找找看有木有破解版?故事的结尾刚要如此,然而笔锋一转,情节立马一波三折,没想到用盗版软件的也会有自己破解软件的今天-_-||。因为最近刚看完了一些iOS逆向开发的书,奈何没有越狱的设备,只好在OS X上练练手。正所谓道法自然,虽平台,工具不同,但内功都是一样的。

不忍直视的弹窗

现在先让我们为这个万恶的弹窗默哀一分钟,接着就开始仔细关注分析一下有哪些入手点。仔细一看就会发现,那个welcome界面依然还在的,也就是说主程序仍然是可以使用的,只是在其上多出了一个如下图的提示注册的弹窗,获取了当前的焦点,以至于我们无法进行其他的操作。

所以我们的任务很简单啦,要么在register上做文章,弄清楚注册流程和算法(第一次我就不要不要试这个了,顿时压力好大);要么在quit按钮上做文章,使其只关闭弹窗但不退出整个程序;或者在判断是否Trial Expired的过程中做手脚;最后,还有最简单的,但是却不优雅的,就是直接去掉弹窗就好了。

工具嘛,IDA或Hopper Disassembler按自己的喜欢就可以了,这里我就用Hopper Disassembler就可以了。开始前也可以class dump一下头文件看看,会有助于后面的分析,这里我就不用了。

那就开始咯

进入Sketch.app目录,找到Sketch二进制文件把它丢进Hopper里然后坐等分析,分析完后如下图:

Hopper这个软件很强大,不过是收费的,但提供免费试用的,试用版的每30分钟会退出一次(咳咳,用自己来破解自己,想想就可怕)。

我们在左边搜索一下弹窗出现的相关关键字Trial

从字面上,我们一眼就能看出每个类每个方法的作用,其中BCTrialCountdown这个类我猜就是负责计算剩余试用天数的,里面的trialPopUpWithNumberOfDays方法应该就是那个不厌其烦每次弹出提示我们还有多少天过期。

BCTrialExpiredWindowController应该就是我们要找的弹窗的controller,有showquitvisitStoreregisterLicense等方法,嗯,没错,就是它了。这样我们就很快定位到相关函数了。

下面我们来仔细看看show方法的汇编代码:

                     +[BCTrialExpiredWindowController show]:
00000001002aa6e0         push       rbp                                         ; Objective C Implementation defined at 0x100368688 (class)
00000001002aa6e1         mov        rbp, rsp
00000001002aa6e4         push       r15
00000001002aa6e6         push       r14
00000001002aa6e8         push       r13
00000001002aa6ea         push       r12
00000001002aa6ec         push       rbx
00000001002aa6ed         push       rax
00000001002aa6ee         mov        rbx, rdi
00000001002aa6f1         mov        rdi, qword [ds:objc_cls_ref_BCTrialExpiredWindowController] ; objc_cls_ref_BCTrialExpiredWindowController, argument "instance" for method _objc_msgSend
00000001002aa6f8         mov        rsi, qword [ds:0x1003d2178]                 ; @selector(alloc)
00000001002aa6ff         mov        r12, qword [ds:imp___got__objc_msgSend]     ; imp___got__objc_msgSend
00000001002aa706         call       r12                                         ; _objc_msgSend
00000001002aa709         mov        r14, rax
00000001002aa70c         mov        rsi, qword [ds:0x1003d2230]                 ; @selector(class), argument "selector" for method _objc_msgSend
00000001002aa713         mov        rdi, rbx                                    ; argument "instance" for method _objc_msgSend
00000001002aa716         call       r12                                         ; _objc_msgSend
00000001002aa719         mov        rdi, rax                                    ; argument "aClass" for method imp___stubs__NSStringFromClass
00000001002aa71c         call       imp___stubs__NSStringFromClass
00000001002aa721         mov        rdi, rax                                    ; argument "instance" for method imp___stubs__objc_retainAutoreleasedReturnValue
00000001002aa724         call       imp___stubs__objc_retainAutoreleasedReturnValue
00000001002aa729         mov        rbx, rax
00000001002aa72c         mov        rsi, qword [ds:0x1003d4100]                 ; @selector(initWithWindowNibName:), argument "selector" for method _objc_msgSend
00000001002aa733         mov        rdi, r14                                    ; argument "instance" for method _objc_msgSend
00000001002aa736         mov        rdx, rbx
00000001002aa739         call       r12                                         ; _objc_msgSend
00000001002aa73c         mov        r14, rax
00000001002aa73f         mov        r13, qword [ds:imp___got__objc_release]     ; imp___got__objc_release
00000001002aa746         mov        rdi, rbx                                    ; argument "instance" for method _objc_release
00000001002aa749         call       r13                                         ; _objc_release
00000001002aa74c         mov        rsi, qword [ds:0x1003d4108]                 ; @selector(window), argument "selector" for method _objc_msgSend
00000001002aa753         mov        rdi, r14                                    ; argument "instance" for method _objc_msgSend
00000001002aa756         call       r12                                         ; _objc_msgSend
00000001002aa759         mov        rdi, rax                                    ; argument "instance" for method imp___stubs__objc_retainAutoreleasedReturnValue
00000001002aa75c         call       imp___stubs__objc_retainAutoreleasedReturnValue
00000001002aa761         mov        rbx, rax
00000001002aa764         mov        rsi, qword [ds:0x1003d4110]                 ; @selector(center), argument "selector" for method _objc_msgSend
00000001002aa76b         mov        rdi, rbx                                    ; argument "instance" for method _objc_msgSend
00000001002aa76e         call       r12                                         ; _objc_msgSend
00000001002aa771         mov        rdi, rbx                                    ; argument "instance" for method _objc_release
00000001002aa774         call       r13                                         ; _objc_release
00000001002aa777         mov        rax, qword [ds:imp___got__NSApp]            ; imp___got__NSApp
00000001002aa77e         mov        r15, qword [ds:rax]
00000001002aa781         mov        rsi, qword [ds:0x1003d4108]                 ; @selector(window), argument "selector" for method _objc_msgSend
00000001002aa788         mov        rdi, r14                                    ; argument "instance" for method _objc_msgSend
00000001002aa78b         call       r12                                         ; _objc_msgSend
00000001002aa78e         mov        rdi, rax                                    ; argument "instance" for method imp___stubs__objc_retainAutoreleasedReturnValue
00000001002aa791         call       imp___stubs__objc_retainAutoreleasedReturnValue
00000001002aa796         mov        rbx, rax
00000001002aa799         mov        rsi, qword [ds:0x1003d4118]                 ; @selector(runModalForWindow:), argument "selector" for method _objc_msgSend
00000001002aa7a0         mov        rdi, r15                                    ; argument "instance" for method _objc_msgSend
00000001002aa7a3         mov        rdx, rbx
00000001002aa7a6         call       r12                                         ; _objc_msgSend
00000001002aa7a9         mov        rdi, rbx                                    ; argument "instance" for method _objc_release
00000001002aa7ac         call       r13                                         ; _objc_release
00000001002aa7af         mov        rdi, r14                                    ; argument "instance" for method _objc_release
00000001002aa7b2         mov        rax, r13
00000001002aa7b5         add        rsp, 0x8
00000001002aa7b9         pop        rbx
00000001002aa7ba         pop        r12
00000001002aa7bc         pop        r13
00000001002aa7be         pop        r14
00000001002aa7c0         pop        r15
00000001002aa7c2         pop        rbp
00000001002aa7c3         jmp        rax                                         ; _objc_release
                        ; endp
00000001002aa7c5         nop        qword [cs:rax+rax+0x0]

其实也没什么可看的,看最后的一个call,传给_objc_msgSend的两个参数,一个是自身的实例,另一个是runModalForWindow的selector,也就是说这里才弹出窗口:

你可以下个断点确认下,我已经确认了,也就是说,在0x1002aa7a6处将这个call r12置成nop不让它调用就可以了。

这样弹窗就不会出现了。到这里,尽管目的达到了,可是感觉不怎么优雅,明明是考验开锁技巧的,却偏偏用锤子直接把锁砸开了。尽管我现在做不到写注册机这种地步,可是还是让我们把修改还原,还是再多看看吧。

代码辣么长,我想再看看

上面我们是粗鲁的破坏掉了BCTrialExpiredWindowControllershow方法,使其失去了弹窗的作用。与其破坏,还不如不让其调用。我们找找看看是在哪调用的。

可以看到,从0x1002a8834处开始调用BCTrialExpiredWindowControllershow方法,往上看看这个sub procedure整体:

整体上这样看就很明白了,0x1002a87f20x1002a8832这段是判断是否有license,如果有,就跳转到0x1002a884c处,然后返回,如果不正确,就往下到0x1002a8834调用BCTrialExpiredWindowControllershow方法弹窗。这样也表明了0x1002a87e7的test是用来判断是否过期,过期的话就跳转去判断是否有license。

看懂了这里,我们就只用修改cmp r14, 0x1这句为cmp r14, 0x0,然后就能无限使用了,这比之前的是不是更好一些呢。

然后Shift+Cmd+E将其保存为二进制文件,将原来的二进制文件备份,然后相应的替换即可。

至此,弹窗已去,万佛朝宗,大功告成,天下我有。

最美的永远是意外

悲剧往往发生在你双击的那一刻,因为你永远都不会知道打开的是什么。当我们双击运行看看,咦,怎么打不开,而且是闪退有木有,难道是我们打开的方式不对?

没错,真的是打开方式不对。这次我们不点击应用图标,而是进入其目录直接运行二进制文件看看:

这下就明白了。程序自己退出了,并打印了一些信息,说明程序内部有对自身进行检测,如果发现代码,签名等被修改了就强制退出。嗯,我猜应该是这样。

没办法,看来活还没干完,打开Hopper接着干。这次,我们查找Invalid Signing这个字符串,看是哪里输出的。

00000001001bffbf         lea        rdi, qword [ds:cfstring_Invalid_Signing]    ; @"Invalid Signing", argument "format" for method imp___stubs__NSLog, XREF=sub_1001bfe40+357
00000001001bffc6         xor        eax, eax
00000001001bffc8         call       imp___stubs__NSLog
00000001001bffcd         mov        edi, 0xae                                   ; argument "status" for method imp___stubs__exit
00000001001bffd2         call       imp___stubs__exit

嗯,没错,就是这里。调用NSLog输出@"Invalid Signing",然后就退出程序。

再来看看全部代码,下图会更直观一些:

可以看到,不管上面进行了什么操作,都要经过0x1001bff7a这里,然后通过test r13d, r13d判断是直接返回呢,还是跳到0x1001bffbf这里退出呢。既然这样,我们将下面的jne 0x1001bffbf置为nop就可以了。

修改后保存成二进制文件,再运行就没有问题了。

最后放出链接,http://pan.baidu.com/s/1qWmXcTm

打完收工。

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

推荐阅读更多精彩内容

  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,357评论 0 17
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,443评论 25 707
  • 说起旅行,每个人内心都对它的幻想和期待,于是才有了一场又一场说走就走的旅行,才有了口口声声的身体和灵魂,总有一个在...
    毒与幻阅读 336评论 0 0
  • AFN (AFNetworking) 网络请求中, 使用最多的就是AFNetworking框架, AFNetwor...
    邻家菇凉阅读 2,168评论 0 6
  • 十年游学意 书剑两无成 笔低云烟骤 红尘事无更 对月诗当赋 临风酒自凭 闲来何所寄 一曲到天明 鹿十五-诗文精选
    鹿十五阅读 156评论 0 0