复杂方案解决结果--->破解版传送门
前提准备
-
Hopper Disassembler
(X,G,Esc,Space...,度娘/谷歌,后面简称Hopper
)- Hopper 下载地址及其攻略
Xcode
(App Store)frida
(前置条件配置好 python环境,下载好pip,随后用pip下载,具体度娘/谷歌找攻略即可)汇编指令基础掌握
(je,jne,jmp,jle,mov,call,ret,nop,xor,lea,rax,rdi,rsi,rcx....)lldb指令基础掌握
(bt, image lookup --address ,p, c...)终端操作
(codesign *,frida *)具备一定的编程思想
基础逆向工程思维基础掌握
(Executable,重签,覆盖entitlements)
小目标:学习下一些简单汇编知识以及市面上的一些付费App的付费模块的代码思路然后顺便逆向破解 PDF Expert
第一步
- 目的---体验原装PDF Expert 修改任意pdf文件后保存查看效果
1.1体验未破解版流程
第二步
在保存操作前是没有提示升级到完整版这个弹窗的,所以我们在Xcode attach成功后可以尝试增加个符号断点windowWillLoad查看调用栈的关系
- 目的---查看调用图层间的调用关系,从而尝试去定位到关键代码。
2.1 Xcode新建个工程
2.2 attach
查看对应图层关系
2.2.1 Attach App
2.2.2 Debug View Hierarchy
查看图层关系 在增加符号断点windowWillLoad
后在修改后的pdf界面触发保存操作出现弹窗发现没有停下断点,说明这条路行不通。
但是我们也获取到了一个关键信息:最上面的控件名称为 DMTrialController
。
第三步
- 目的---使用
frida
通过已知弹出升级框的类DMTrialController
,查找出具体调用的方法堆栈
3.1 frida-trace 查看对应方法调用关系
在终端上输入 frida-trace -m "-[DMActivationController *]" PDF\ Expert
22103 ms -[DMTrialController trialObject]
23074 ms -[DMTrialController trialObject]
23074 ms -[DMActivationController performActivationStepWithStep:0x66]
23074 ms | -[DMActivationController isRunning]
23074 ms | -[DMActivationController setNextPerformStep:0x66]
23074 ms | -[DMActivationController updateStepControllerForCurrentStep]
23074 ms | | -[DMActivationController nextPerformStep]
23074 ms | | -[DMActivationController confirmedNextPerformStep:0x66]
分析log输出在点击保存时触发的方法为-[DMActivationController performActivationStepWithStep:0x66]
记录下来回到第二步操作的2.4查看付费弹窗堆栈信息。
3.2 增加符号断点查看
再次保存操作后触发断点终端查看到嫌疑关键代码如下
frame #1: 0x000000010227846c PDF Expert`___lldb_unnamed_symbol15828$PDF Expert + 380
frame #2: 0x00000001022c5263 PDF Expert`___lldb_unnamed_symbol16722$PDF Expert + 371
frame #3: 0x00000001022a1e37 PDF Expert`___lldb_unnamed_symbol16128$PDF Expert + 39
随后 image寻址找到具体App调用时的地址信息
(lldb) image lookup --address 0x0000000103818a7c
Address: DevMateKit[0x0000000000029a7c] (DevMateKit.__TEXT.__text + 165540)
Summary: DevMateKit`-[DMActivationController performActivationStepWithStep:]
(lldb) image lookup --address 0x00000001022c5263
Address: PDF Expert[0x00000001003ff263] (PDF Expert.__TEXT.__text + 4180867)
Summary: PDF Expert`___lldb_unnamed_symbol16722$PDF Expert + 371
(lldb) image lookup --address 0x00000001022a1e37
Address: PDF Expert[0x00000001003dbe37] (PDF Expert.__TEXT.__text + 4036439)
Summary: PDF Expert`___lldb_unnamed_symbol16128$PDF Expert + 39
得到可疑地址
A:0x0000000000029a7c
B:0x00000001003ff263
C:0x00000001003dbe37
第四步
- 目的---使用
Hooper
查看可疑地址A,B,C。
4.1 科普使用Hooper
4.1.1 直接拽
4.1.2 稍等一会Hopper
加载完成
4.1.3 追查付费弹窗底细
加载完成后在界面左侧输入刚才Xcode Attach控件名 DMTrialController
分析搜索结果发现并没有直接匹配的 DMTrialController
这玩意,猜想这个空间不是直接在主工程实现的,随后看跟 DMTrialController
命名类似的DMTrialWelcomeStepController
看上图可以找到文件路径并且发现关键字DevMateKit
,其实早在Xcode Attach时查看图层就可以发现 这个付费弹窗的命名前缀是DMT
而主工程的命名前缀是PDF_Expert
,很大概率付费弹窗是用的第三方库.
DevMateKit传送门
4.1.4 简单了解DevmateKit
有哪些功能
这个就不在这介绍了,通过传送门下载的demo大概了解到DevMateKit
是做一些付费弹窗,举报弹窗,kevlar
代码混淆。
4.2 逐个排查可疑地址
4.2.1 地址A:0x0000000000029a7c
Hopper 信息分析
根据Hopper
分析结果看出可疑地址A主要是在做一些绘制UI操作,我们主要目的是要改掉App中一个凭证字段信息类似 isActiva , isRegis 之类的字眼。排除掉可疑地址A。
4.2.2 地址B:0x00000001003ff263
Hopper 信息分析
发现定位到的地址操作符是test,这个有着重大嫌疑,大概率是此地址去做是否注册判断。我们继续去排查可疑地址C。
4.2.3 地址C:0x00000001003dbe37
Hopper 信息分析
由分析结果得知可疑地址C主要是做saveDocument:
操作,并且看上图左边红色框可以得知这一顿操作没有做一些test或者是cmp或者是提前ret之类的操作,基本也可以排除掉可疑地址C。
4.3 主要调查重点嫌疑地址
4.3.1 重新查看可疑地址B:0x00000001003ff263
的hopper数据,切换至控制流图(Control Flow Graph
)
备注:快捷键 Space
查看上图分析一处逻辑判断为 al
和 0x1
做test
运算,但是此处逻辑地址为loc_1003ff259
是由loc_1003ff11e
的尾部的je
跳转过来的。
而je
的来源是上一层的al
和0x1
做test
运算,再往上看可以知道al
是通过call sub_1003b21b0
的返回值赋值的。
4.3.2 解析第一层梦境function sub_1003b21b0
双击进入 sub_1003b21b0
的function实现的控制流图。进入目标func的控制流图后发现是个蛮庞大的函数,缩放一下页面看到整个流程图如下
分析上图控制流图结构,猜想如果是已购买用户的判断逻辑应该是一条清晰的流程也就是右侧箭头所指的通道。
接着着重看如何才可以走到阳光大道上
分析上图控制流图重点是通过cmp r13b, 0x3
后 je loc_1003b22cd
,翻译过来也就是判断r13b
是不是0x3
如果是0x3
就去阳光大道,那么我们现在就想办法把r13b
变成0x3
。
继续往上看得知r13b
是eax
怼过来的,而eax
是通过function sub_100382d70
返回的。
那么现在关键就是这个sub_100382d70
4.3.3 解析第二层梦境function sub_100382d70
双击进入sub_100382d70
的function。
分析下第二层梦境的大概实现,首先第一眼嫌疑最大的是_O7RH3WAr7wAQMdz5Xv
这个是被call的function是被混淆过的,其次通过这个_O7RH3WAr7wAQMdz5Xv
返回的al
最后是跟0x1
做test
。
那么现在冷静下思考有以下两个解决思路
简单方案-直接改
sub_100382d70
提前返回0x3
然后去走阳光大道(如果忘记了为何要返回0x3
,可以复习下4.3.2章节)复杂方案-修改
_O7RH3WAr7wAQMdz5Xv
内部实现,继续深究,改最根部判断。
第五步
- 目的---使用
Hooper
修改关键汇编运算逻辑。
5.1 简单方案解法尝试
5.1.1 修改sub_100382d70
实现
根据上述操作我们得知sub_100382d70
做了一串操作,先push,后mov后push…,我们现在其实只要返回个0x3
即可,那直接选中sub_100382d70
第一行修改,输入mov rax,0x3
,点Assemble and Go Next
,再继续输入ret
,继续Assemble and Go Next
。
修改后的结果如下,随后保存新的Executable文件
5.1.2 保存Executable文件
保存Executable
文件时点Cancel
接下来就是验证简单方案是否可行了,把刚生成的新Executable文件替换我们目标App内的旧Executable文件,具体操作如下
5.1.3 覆盖entitlements文件
原版entitlements文件数据如下,随后吧identifier对应的那几行干掉
com.apple.application-identifier
3L68KQB4HG.com.readdle.PDFExpert-Mac
com.apple.developer.team-identifier
3L68KQB4HG
com.apple.security.get-task-allow
修改后
com.apple.security.get-task-allow
保存后覆盖目标App的entitlements文件
codesign -f -s - --entitlements entitlementsFilePathPrefix/Entitlements.plist appPathPrefix/PDF\ Expert.app
执行完终端输出提示/Applications/PDF Expert 3.app: replacing existing signature
即表示完成替换
5.1.4 检验简单方案处理结果
随后重新打开目标App,首先检查当前App的是否注册状态如下图
随后尝试修改任意pdf文件保存,保存成功!
5.2 复杂方案解决尝试
5.2.1 _O7RH3WAr7wAQMdz5Xv
的前世今生
早在上文4.1.4
简单了解目标App所用的到第三方库kevlar
能支持代码混淆,我们先简单看看_O7RH3WAr7wAQMdz5Xv
的函数结构大概如下
看不是很懂,我们切换至伪代码视角看看
这里我们发现个关键字符串 kevlar
,那基本可以认定此处的混淆代码的出处是DevMateKit
了。
DevMateKit提供的demo工程我们搜索kevlar
继续怼进DMKevlarApplication.h
看看代码
//! Function help with running timer for advanced check
#define DMKRunNewIntegrityCheckTimer DzVpwUg0VXKMIfCPA
FOUNDATION_EXTERN void DMKRunNewIntegrityCheckTimer(NSUInteger num, NSTimeInterval checkFrequency);
//! Checks if applicaion activated
#define DMKIsApplicationActivated PfCuPgJSp5KVlvc8W1
FOUNDATION_EXTERN BOOL DMKIsApplicationActivated(DMKevlarError *outKevlarError);
//! Returns user license info
#define DMKCopyLicenseUserInfo dReea3NiUFGwgD52YPa
FOUNDATION_EXTERN CFDictionaryRef DMKCopyLicenseUserInfo(void) CF_RETURNS_RETAINED;
//! Forces license validation request on DevMate server
#define DMKValidateLicense i2rRAQi8BfdE2G9geRSu
FOUNDATION_EXTERN void DMKValidateLicense(void (^completionHandler)(NSError *errorOrNil));
//! Deactivates application and invalidates license info
#define DMKInvalidateLicense kLLTbFMUP234v8xDp6Uck
FOUNDATION_EXTERN BOOL DMKInvalidateLicense(void);
/**
This category will extend functionality of NSApplication to be complies with Kevlar concept of protection.
Rigth now, some helper inteface have been declare there, because it is kind of complicated to load category.
*/
#define com_devmate_Kevlar YC2eXYjMnR
@interface NSApplication (com_devmate_Kevlar)
发现好多混淆函数,简单看注释//! Checks if applicaion activated
理论上这个函数应该是我们要找的最关键函数。
- 那么如何核实
FOUNDATION_EXTERN BOOL DMKIsApplicationActivated(DMKevlarError *outKevlarError);
就是我们要找的function呢?
现在手头上有下载好的DevMateKit
的demo工程,那么直接run一个.app
文件出来丢到Hopper
分析对比就可以了
上图是run的CustomTrialExample这个target后的截图,此时记录下可疑函数的混淆标记为PfCuPgJSp5KVlvc8W1
随后把这个CustomTrialExample.app
丢到Hopper
一顿分析后直接搜索混淆标记PfCuPgJSp5KVlvc8W1
这下就基本上真相大白了,根据下图对比得知
也就是我们基本核实
FOUNDATION_EXTERN BOOL DMKIsApplicationActivated(DMKevlarError *outKevlarError)
就是函数_O7RH3WAr7wAQMdz5Xv
上辈子的初始形态了。
5.2.2 _O7RH3WAr7wAQMdz5Xv
重写
-
DMKIsApplicationActivated
主要做了两个事完整函数返回了BOOL
传入指针
DMKevlarError *outKevlarError
内部可能做修改
-
我们要重写成什么样子?
鉴于此function是要返回个是否已激活/付费,那尝试下完整函数返回固定为
0x1
常规来说已激活/付费的用户调此function理论上不应该有error存在,也就是我们需要把传入指针对应的地址的内容变成
0x0
那么改成下图的样子也就基本上没啥毛病了
转成伪代码一看就懂了
int _O7RH3WAr7wAQMdz5Xv(int arg0) {
rdi = arg0;
if (rdi != 0x0) {
*rdi = 0x0;
}
return 0x1;
}
那么我们就把我们重写的目的都实现了,接下来就是见证奇迹的时刻了
5.2.3 检验复杂方案处理结果
重写 _O7RH3WAr7wAQMdz5Xv
后和上文5.1.2
,5.1.3
,5.1.4
做法一致,最后验证出来结果也是一样的可以保存成功!
总结
快捷键补充(windows/Mac)
hopper修改 alt+a / option+a
hopper保存 win+shift+e / cmd+shift+e
hopper寻址 g
hopper查引用 x
声明:此文仅用于学习用途,请勿用于非法用途。
转载请注明出处。谢谢!
逆向技术积累相关链接
破解
YY Flutter技术积累相关链接
flutter多实例实战 by共田君
一行代码教你解决FlutterPlatformViews内存泄露 by
AShawn
手把手教你在Flutter项目优雅的使用ORM数据库 by
williamwen1986
flutter通用基础库flutter_luakit_plugin by
williamwen1986
github - flutter_luakit_plugin使用例子 by
williamwen1986
手把手教你编译Flutter engine by 共田君
手把手教你解决 Flutter engine 内存泄漏 by 共田君
github - 编译产物下载 修复内存泄漏后的flutter engine(可直接使用)by 共田君
github demo - 修复内存泄漏后的flutter engine by 共田君
持续更新中...