关于CleanMyMac 3 试用版使用限制的研究(非完美方案)

使用工具: class-dump, Hopper Disassembler。

CleanMyMac试用版的限制是:只能清理最多500MB的垃圾。这里要做的是在试用版下也能突破这个限制。
首先用class-dump导出所有头文件备参考,用Hopper打开可执行文件,应该就是CleanMyMac 3:

当超过500MB以后,每次点清理按钮会弹出下面的窗口:

在Hopper中搜索窗口中字符串"I already have a license":

得到变量名cfstring_I_already_have_a_licens,然后继续搜索该变量:

可以看到[CMPurchaseViewController loadView]中引用到了它,从名字判断这个controller应该对应了弹出来的窗口。应该是点了按钮后什么东西初始化了这个controller,之前的探索方向可能有点问题,应该去找button的处理函数。于是在Hopper搜索框里猜测buttonHandlerbuttonTappedbuttonClick等等之类,最后发现有个叫ubiquitousButtonPurchaseClicked的函数很可疑:


在0x1006dec58的位置引用到了它,于是顺藤摸瓜到这个地址:

是被这个函数给引用了:-[CMUbiquitousButtonView performActionOnDelegate]
打开它的头文件,发现里面有个mouseDown: 函数,应该就是按钮处理函数,但是是不是清理按钮现在还不知道,总之先来看看mouseDown:的实现。 用Hopper找到它,查看伪代码:

可以看到最后还是调用了performActionOnDelegate,看来是在这个函数里决定怎么处理接下来的逻辑,那么就继续看看performActionOnDelegate的实现:

哈哈,在这个函数里很惊喜的看到了疑似开始清理过程的ubiquitousButtonStartCleanProcess:函数,迫不及待的打开看看:

有两个类有这个函数,这里可以通过打断点判断得出是CMModuleViewController调用了。看到实现:

于是痛苦开始了,关键部分做了代码混淆, 函数名变成了:JwUMdSW7rENEEIgVEGUtnns7cx3JNc9UTOuabo1ThwTJyDSydBKrFyvIIyTL6IgTvp9KwMqg1pF1VqvBEF8tC8YjbebfGbCBzIiRHQiX1wgasjtB0yneXyLo8vUGJhOmWNxu6FDurz8vOBkOSCpGfyGpMC8S1eJS8VWY9JRKfv7dahJuH0MAth7SwKv48LilHi63doAFcf1WDN2c7aJErpPKXKh3n08CPwiOcQxI888pDSR6K4XcjiWsYV3zHreX
姑且称为加密函数A,是属于CMMainWindowController的某个敏感函数,看看它的实现如下:

忍不住吐血,里面又调了一个加密函数B。不管了,继续看B的实现,由于太长了就不贴出来了,总之里面又调了一堆加密函数。这段时间比较痛苦,一度想放弃,尝试着去理清程序执行的顺序,但又没有get到Hopper其实可以单步调试的技能,一度都是用下面的手段让程序挂掉来得知程序的执行流程:

MOV  AX,  4C00H

INT   21H

注意到伪代码里经常出现一个类似下面的代码片段:

猜测是block调用,于是写了段代码用Hopper反汇编一下验证了果然是。

对于加密函数B,通过修改汇编的判断条件,让程序避过了弹出警告框的逻辑,最后发现这个函数其实没做什么实质的清理工作,只不过是在各种判断用户有没有权限进行清理。最实质性的调用是这一句:

这其实是一句block调用,参数是1,block是外面传进来的。接下来就在函数开始合适的地方加上它的汇编代码,汇编代码如下:

mov qword [ss:rbp + var_D8], r15

mov        esi, 0x1

mov        rdi, qword [ss:rbp + var_D8]
call       qword [ds:rdi+0x10]

jmp        0x100207266

这里有个插曲,直接加上如上的代码会让Hopper挂掉。查了下原因是Hopper这个版本还不支持编辑的时候引用var_开头的变量,尝试换了种办法,D8==十进制的216,所以上面的汇编代码等价于:

mov qword [ss:rbp - 216], r15

mov        esi, 0x1

mov        rdi, qword [ss:rbp - 216]

call      qword [ds:rdi+0x10]

jmp        0x100207266

这里又要看到之前的ubiquitousButtonStartCleanProcess:函数,然后结合加密函数A和B可以知道,ubiquitousButtonStartCleanProcess里有个block,然后把block丢给加密函数A,加密函数A又把block丢给加密函数B,由B执行到最后再调用了这个block。这种执行流程很像是这个block就叫onAuthorizeSuccess,两个加密函数做了点能否执行清理的判断,如果成功的话执行onAuthorizeSuccess block。 那么这里就应该看到ubiquitousButtonStartCleanProcess: 里的block执行体,也就是sub_1000dd914函数:

接下来一路顺藤摸瓜,从-[CMModuleViewController startClean]:-[CMGroupScanner startClean]:到sub_100090e3a到-[CMGroupScanner cleanWithSession] -[CMScanner cleanWithSession]-[CMScanner cleanThreadWithSession] -[CMScanner recursivelyCleanNode:parentNode:session:]都比较顺利,最后在-[CMScanner recursivelyCleanNode:parentNode:session:]里发现有很多-[shouldScanner:pauseCleaningWithNextNodeToClean:]函数,这个函数有好几个类里都有,一一把它们直接返回false。
以为大功告成了,跑一下程序,发现挂了。幸好Hopper的debugger给出了exception的位置,发现是加密函数B中由于改变了程序执行流程,导致最后某个不需要release的变量被release了,于是把这局操作置空就行。

以为接下来肯定大功告成了,结果发现清理系统垃圾的时候需要管理员权限,而被patch过的程序始终无法成功。这里牵涉到了SMJobBless和privileged helper tool等mac上的获取系统权限接口,搞了两天没搞定。 最后只能简单粗暴的让-[CMAgentController install]返回false来跳过所有需要系统权限的垃圾的清理。

有大神知道怎么搞定SMJobBless的欢迎补充。

2 个赞

大神我太菜了,不会看64的指令啊,可以告诉我,你修改了地址多少的内容改了些啥吗!

汗要修改的函数地址是0x10009ADBF可是可是啊,怎么改64指令的false啊啊啊啊,改了那几个地址可以详细点吗,满地打滚求啊

有分析它的激活流程吗?有时间交流交流