本帖最后由 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命令来显示:
[Plain Text] 纯文本查看 复制代码 bt
* thread #1: tid = 0x527df, 0x0000000100016b67 Moom`-[PMMainController updateReminderButtonWithIncrementedUseCounter:applyRefractoryPeriod:showLicense:showAlert:] + 1042, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
* frame #0: 0x0000000100016b67 Moom`[b][color=#0000ff]-[PMMainController updateReminderButtonWithIncrementedUseCounter:applyRefractoryPeriod:showLicense:showAlert:][/color][/b] + 1042
frame #1: 0x00007fffb3d8303d libsystem_trace.dylib`_os_activity_initiate + 61
frame #2: 0x00007fff9cebb4e7 AppKit`-[NSApplication(NSResponder) sendAction:to:from:] + 456
frame #3: 0x00007fff9ca0b245 AppKit`-[NSControl sendAction:to:] + 86
frame #4: 0x00007fff9ca0b16d AppKit`__26-[NSCell _sendActionFrom:]_block_invoke + 136
frame #5: 0x00007fffb3d8303d libsystem_trace.dylib`_os_activity_initiate + 61
frame #6: 0x00007fff9ca0b0c5 AppKit`-[NSCell _sendActionFrom:] + 128
frame #7: 0x00007fff9ca4d92a AppKit`-[NSButtonCell _sendActionFrom:] + 98
frame #8: 0x00007fffb3d8303d libsystem_trace.dylib`_os_activity_initiate + 61
frame #9: 0x00007fff9ca09a58 AppKit`-[NSCell trackMouse:inRect:ofView:untilMouseUp:] + 2481
frame #10: 0x00007fff9ca4d667 AppKit`-[NSButtonCell trackMouse:inRect:ofView:untilMouseUp:] + 785
frame #11: 0x00007fff9ca084c8 AppKit`-[NSControl mouseDown:] + 832
frame #12: 0x00007fff9d01b73d AppKit`-[NSWindow(NSEventRouting) _handleMouseDownEvent:isDelayedEvent:] + 6341
frame #13: 0x00007fff9d017f8c AppKit`-[NSWindow(NSEventRouting) _reallySendEvent:isDelayedEvent:] + 1942
frame #14: 0x00007fff9d01742a AppKit`-[NSWindow(NSEventRouting) sendEvent:] + 541
frame #15: 0x00007fff9ceb7bf5 AppKit`-[NSApplication(NSEvent) sendEvent:] + 1145
frame #16: 0x0000000100022412 Moom`-[PMApplication sendEvent:] + 630
frame #17: 0x00007fff9c79e009 AppKit`-[NSApplication run] + 1002
frame #18: 0x00007fff9c768a8a AppKit`NSApplicationMain + 1237
frame #19: 0x00000001000012b8 Moom`start + 52
根据堆栈信息, frame #0 以下的都没有什么好分析的,重点分析PMMainController updateReminder...,
粗略分析发现:
[Objective-C] 纯文本查看 复制代码 char -[PMMainController updateReminderButtonWithIncrementedUseCounter:applyRefractoryPeriod:showLicense:showAlert:]
if ([color=#ff0000][b]_PMHasValidCertificate(var_29, var_38, sign_extend_64(r14), var_40, r13) != 0x0[/b][/color]) {
r15->numberOfTrialRunsLeft = 0xffffffffffffffff;
[r15->licenseReminderButton setHidden:0x1];
rbx = r15->licenseBottomStatusField;
var_60 = @selector(mainBundle);
var_68 = @selector(localizedStringForKey:value:table:);
rcx = @"";
r8 = 0x0;
rdx = [[NSBundle mainBundle] localizedStringForKey:@"Thank You" value:rcx table:r8];
[rbx setStringValue:rdx];
if (var_50 != 0x0) {
r8 = 0x0
;
var_54 = r12;
r12 = [NSFileManager defaultManager];
rbx = [[[[@"~/Library/Preferences" stringByExpandingTildeInPath] stringByAppendingString:@"/.byhost.spo", rcx, r8] stringByAppendingString:@"tli", rcx, r8] stringByAppendingString:@"ght.mdquery", rcx, r8];
rdx = rbx;
if ([r12 fileExistsAtPath:rdx, rcx, r8] != 0x0) {
rcx = 0x0;
rdx = rbx;
[r12 removeItemAtPath:rdx error:rcx];
}
rbx = r15->licenseMainStatusField;
rax = var_40;
if ([var_40 length] != 0x0) {
r13 = rax;
}
[rbx setStringValue:*r13];
rbx = r15->licenseTopStatusField;
rax = [NSBundle mainBundle];
rax = [rax localizedStringForKey:@"Moom - Licensed to" value:@"" table:0x0];
[rbx setStringValue:rax];
rax = [r15->licenseBuyButton superview];
[rax setHidden:0x1];
}
}
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 = [r15->licenseBuyButton superview];
[rax setHidden:0x0];
}
}
发现一个关键跳转,取决于函数_PMHasValidCertificate返回值。
继续分析:
首先,第一个要注意的函数:
[Objective-C] 纯文本查看 复制代码 int [b][color=#0000ff]_PMSimulateExpiredDemo[/color][/b]() {
rax = [[NSUserDefaults standardUserDefaults] boolForKey:@"PMDebugSimulateExpiredManyTricksDemo"];
return rax;
}
检测试用是否过期,那么简单修改返回值为0(False)是否就可以了呢?想想要操作100次,还是算了。
第二个关键的函数:_PMValidPersistentCertificateDictionary
字面意是检测本地证书是否有效,如果有效,读取用户名和邮箱地址等信息,否则就是试用情况了。
那么先来看看是如何处理试用100次的问题。
[Objective-C] 纯文本查看 复制代码 loc_10000257d:
r14 = [[[[@"~/Library/Preferences" stringByExpandingTildeInPath]
stringByAppendingString:@"/.byhost.spo"] stringByAppendingString:@"tli"]
stringByAppendingString:@"ght.mdquery"];
rax = [NSDictionary dictionaryWithContentsOfFile:r14];
rax = [rax objectForKey:@"c"];
rax = [rax integerValue];
r12 = 0x0;
if (rax >= 0x0) { r12 = rax;}
if (var_34 != 0x0) {
r12 = r12 + 0x1;
rax = [NSNumber numberWithInteger:r12];
rax = [NSDictionary dictionaryWithObject:rax forKey:@"c"];
[rax writeToFile:r14 atomically:0x1];
}
goto loc_10000265f;
很显然是读和写本地配置文件,简单提取一下文件的路径:
~/Library/Preferences/.byhost.spotlight.mdquery
动态调试验证,在地址00000001000025cc处下断点,中断后,输入lldb命令: po $rax 可读出路径信息:
文件内容是什么? [XML] 纯文本查看 复制代码 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呢?这里有一种方法:
[Asm] 纯文本查看 复制代码 inc r12
//改成
mov r12,r12
如果手动修改了配置文件,使得c>=100,会如何?
app弹窗提醒了!
综上,所以修改计步器初始值0,递增值0,并补一刀设置检测过期函数返回值为0,即可爆破成功!
将修改后的主程序,替换原主程序,重新打开App。
弹窗提醒出错,看来有程序自校验,根据字符串搜索定位到关键位置:
继续搜索applicationDidFinishLaunching方法,
分析这几段代码,可以发现这个关键函数:_PMCheckBundleSignature
他的伪代码是这样的:
这里调用了这些相关函数:
[C] 纯文本查看 复制代码 SecStaticCodeCreateWithPath()
SecStaticCodeCheckValidity();
SecCodeCopySelf()
SecRequirementCreateWithString()
SecCodeCheckValidity()
可以不用处理这些子函数,直接修改_PMCheckBundleSignature的返回值为 1 即可。
再次测试弹窗消失。
以上是Moom的爆破方案,先写到这里。
下一篇将从分析Moom的注册证书采用的RSA、SHA1加密等细节以及伪造数据和如何HOOK关键函数优雅破解等讲起,欢迎阅读哦~
|