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

丙戌月 癸酉日

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

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

Sketch!一次悸动的小逆向尝试_第1张图片

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

不忍直视的弹窗

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

Sketch!一次悸动的小逆向尝试_第2张图片

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

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

那就开始咯

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

Sketch!一次悸动的小逆向尝试_第3张图片

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

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

Sketch!一次悸动的小逆向尝试_第4张图片

从字面上,我们一眼就能看出每个类每个方法的作用,其中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整体:

Sketch!一次悸动的小逆向尝试_第5张图片

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

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

Sketch!一次悸动的小逆向尝试_第6张图片

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

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

最美的永远是意外

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

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

Sketch!一次悸动的小逆向尝试_第7张图片

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

没办法,看来活还没干完,打开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就可以了。

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

Sketch!一次悸动的小逆向尝试_第8张图片

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

打完收工。

你可能感兴趣的:(Sketch!一次悸动的小逆向尝试)