Moom 分析记录之一(爆破篇 )- tree_fly
本帖最后由 tree_fly 于 2016-10-22 20:47 编辑Moom 分析记录之一 爆破篇
by tree_fly/P.Y.G
Moom 小工具挺实用,测试地址:https://www.chinapyg.com/thread-87211-1-1.html
今天来简单的分析这款软件,首先看下未注册时状态:
这里有三点需要关注:
[*]主程序的右上角有100次的试用标记:Demo: 100 Mooms remainning
[*]注册窗口内主体文本是:this license could be yours
[*]菜单栏有Buy字样
基于这些信息,我们启动Hopper调试工具,载入app后常规查询字符串。
点击 Demo: %lu Mooms remaining此行,查看字符串调用信息,注意一些细节,比如此处有2处调用:
双击右侧边栏中第一个地址:0x100016b67,并于此处下断点:
接下来,启动hopper的调试器,开始动态分析app。
运行程序,尝试使用功能,直到中断在(RIP指向)刚才设置的断点处。
中间可能出现这样的画面,忽略,继续运行即可。
调试器的左下角已显示目前的调用栈内容,除此之外,也可以用lldb的bt命令来显示:
bt
* thread #1: tid = 0x527df, 0x0000000100016b67 Moom`- + 1042, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
* frame #0: 0x0000000100016b67 Moom`- + 1042
frame #1: 0x00007fffb3d8303d libsystem_trace.dylib`_os_activity_initiate + 61
frame #2: 0x00007fff9cebb4e7 AppKit`- + 456
frame #3: 0x00007fff9ca0b245 AppKit`- + 86
frame #4: 0x00007fff9ca0b16d AppKit`__26-_block_invoke + 136
frame #5: 0x00007fffb3d8303d libsystem_trace.dylib`_os_activity_initiate + 61
frame #6: 0x00007fff9ca0b0c5 AppKit`- + 128
frame #7: 0x00007fff9ca4d92a AppKit`- + 98
frame #8: 0x00007fffb3d8303d libsystem_trace.dylib`_os_activity_initiate + 61
frame #9: 0x00007fff9ca09a58 AppKit`- + 2481
frame #10: 0x00007fff9ca4d667 AppKit`- + 785
frame #11: 0x00007fff9ca084c8 AppKit`- + 832
frame #12: 0x00007fff9d01b73d AppKit`- + 6341
frame #13: 0x00007fff9d017f8c AppKit`- + 1942
frame #14: 0x00007fff9d01742a AppKit`- + 541
frame #15: 0x00007fff9ceb7bf5 AppKit`- + 1145
frame #16: 0x0000000100022412 Moom`- + 630
frame #17: 0x00007fff9c79e009 AppKit`- + 1002
frame #18: 0x00007fff9c768a8a AppKit`NSApplicationMain + 1237
frame #19: 0x00000001000012b8 Moom`start + 52
根据堆栈信息, frame #0 以下的都没有什么好分析的,重点分析PMMainController updateReminder...,
粗略分析发现:
char -
if (_PMHasValidCertificate(var_29, var_38, sign_extend_64(r14), var_40, r13) != 0x0) {
r15->numberOfTrialRunsLeft = 0xffffffffffffffff;
;
rbx = r15->licenseBottomStatusField;
var_60 = @selector(mainBundle);
var_68 = @selector(localizedStringForKey:value:table:);
rcx = @"";
r8 = 0x0;
rdx = [ localizedStringForKey:@"Thank You" value:rcx table:r8];
;
if (var_50 != 0x0) {
r8 = 0x0
;
var_54 = r12;
r12 = ;
rbx = [[[[@"~/Library/Preferences" stringByExpandingTildeInPath] stringByAppendingString:@"/.byhost.spo", rcx, r8] stringByAppendingString:@"tli", rcx, r8] stringByAppendingString:@"ght.mdquery", rcx, r8];
rdx = rbx;
if ( != 0x0) {
rcx = 0x0;
rdx = rbx;
;
}
rbx = r15->licenseMainStatusField;
rax = var_40;
if ( != 0x0) {
r13 = rax;
}
;
rbx = r15->licenseTopStatusField;
rax = ;
rax = ;
;
rax = ;
;
}
}
else {
if (r13 != 0x0) {
r14 = r13;
r13 = r15->licenseMainStatusField;
rax = _objc_msgSend(@class(NSBundle), var_60, rdx);
rax = _objc_msgSend(rax, var_68, @"This license could be yours", @"", 0x0);
rdi = r13;
r13 = r14;
_objc_msgSend(rdi, r12, rax);
_objc_msgSend(r15->licenseTopStatusField, r12, @"Moom");
rax = ;
;
}
}
发现一个关键跳转,取决于函数_PMHasValidCertificate返回值。
继续分析:
首先,第一个要注意的函数:
int _PMSimulateExpiredDemo() {
rax = [ boolForKey:@"PMDebugSimulateExpiredManyTricksDemo"];
return rax;
}
检测试用是否过期,那么简单修改返回值为0(False)是否就可以了呢?想想要操作100次,还是算了。
第二个关键的函数:_PMValidPersistentCertificateDictionary
字面意是检测本地证书是否有效,如果有效,读取用户名和邮箱地址等信息,否则就是试用情况了。
那么先来看看是如何处理试用100次的问题。
loc_10000257d:
r14 = [[[[@"~/Library/Preferences" stringByExpandingTildeInPath]
stringByAppendingString:@"/.byhost.spo"] stringByAppendingString:@"tli"]
stringByAppendingString:@"ght.mdquery"];
rax = ;
rax = ;
rax = ;
r12 = 0x0;
if (rax >= 0x0) { r12 = rax;}
if (var_34 != 0x0) {
r12 = r12 + 0x1;
rax = ;
rax = ;
;
}
goto loc_10000265f;
很显然是读和写本地配置文件,简单提取一下文件的路径:
~/Library/Preferences/.byhost.spotlight.mdquery
动态调试验证,在地址00000001000025cc处下断点,中断后,输入lldb命令:po $rax 可读出路径信息:
文件内容是什么?Last login: Fri Oct 21 21:57:44 on ttys000
➜~ cat ~/Library/Preferences/.byhost.spotlight.mdquery
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>c</key>
<integer>2</integer>
</dict>
</plist>
➜~ 这里的c就是count的意思喽,2是已使用的次数。
就目前已获取的信息,可以采用“清零递增值”的方法来固定使用次数,达到无次数限制。
如何patch呢?这里有一种方法:
inc r12
//改成
mov r12,r12
如果手动修改了配置文件,使得c>=100,会如何?
app弹窗提醒了!
综上,所以修改计步器初始值0,递增值0,并补一刀设置检测过期函数返回值为0,即可爆破成功!
将修改后的主程序,替换原主程序,重新打开App。
弹窗提醒出错,看来有程序自校验,根据字符串搜索定位到关键位置:
继续搜索applicationDidFinishLaunching方法,
分析这几段代码,可以发现这个关键函数:_PMCheckBundleSignature
他的伪代码是这样的:
这里调用了这些相关函数:
SecStaticCodeCreateWithPath()
SecStaticCodeCheckValidity();
SecCodeCopySelf()
SecRequirementCreateWithString()
SecCodeCheckValidity()
可以不用处理这些子函数,直接修改_PMCheckBundleSignature的返回值为 1 即可。
再次测试弹窗消失。
以上是Moom的爆破方案,先写到这里。
下一篇将从分析Moom的注册证书采用的RSA、SHA1加密等细节以及伪造数据和如何HOOK关键函数优雅破解等讲起,欢迎阅读哦~
不懂啊,这是啥? 不错!精华了! 赞。
如果inc变成Dec,应该也可以吧。这样与100就成南辕北辙了。
不过用mov感觉更简单,更安全! 版务督察 发表于 2016-10-22 08:38
赞。
如果inc变成Dec,应该也可以吧。这样与100就成南辕北辙了。
不过用mov感觉更简单,更安全!
dec会变成负数。取决于作者用了有符号还是无符号来处理返回值! 飘云 发表于 2016-10-22 08:40
dec会变成负数。取决于作者用了有符号还是无符号来处理返回值!
对。
所以还是Mov的用法更安全!
支持飞树大神! 赞,还是伪代码读着舒服 区待下一篇文章、、、 谢谢分享,学习了!!!