老文重发。
使用工具: class-dump, Hopper Disassembler。
CMM试用版的限制是:只能清理最多500MB的垃圾。这里要做的是在试用版下也能突破这个限制。
首先用class-dump导出所有头文件备参考,用Hopper打开可执行文件,应该就是CMM:
当超过500MB以后,每次点清理按钮会弹出下面的窗口:
在Hopper中搜索窗口中字符串"I already have a license":
得到变量名cfstring_I_already_have_a_licens,然后继续搜索该变量:
可以看到[CMPurchaseViewController loadView]中引用到了它,从名字判断这个controller应该对应了弹出来的窗口。应该是点了按钮后什么东西初始化了这个controller,之前的探索方向可能有点问题,应该去找button的处理函数。于是在Hopper搜索框里猜测buttonHandler,buttonTapped,buttonClick等等之类,最后发现有个叫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的欢迎补充。