AirBuddy2 2.6.3 的dylib注入方案 (2)
本帖最后由 QiuChenly 于 2023-1-27 22:18 编辑# AirBuddy2 2.6.3 的dylib注入方案(2)
## 上篇概要
妙控键盘2电源开关坏了,不知道怎么修,就不写了。
有兴趣的直接翻我帖子上一篇看。
就这个小开关,全网找不到卖的。有知道怎么修的朋友给个提示。
现在关机没用,拨回去按两下又通电了,好烦。放包里严重误按。
## 准备工具
1. AppCode(需要预装XCode).
2. 编译好的insert_dylib二进制执行文件.
3. IDA 7.0 Mac.
4. VSCode + Frida.
5. 关闭Mac上的SIP以注入进程内存.
6. 人.
IDA在论坛资源区下载,AppCode不会用可以直接用XCode。
Frida不会用可以百度,TypeScript看不懂可以百度。
insert_dylib源代码下载地址:
(https://github.com/Tyilo/insert_dylib)
注入我用的是rd_route劫持库.
h文件和c文件下载(后面用到):
https://github.com/rodionovd/rd_route
注意:
楼主没写过macOS App,也不会写。以下发表的所有Hook理解也是根据百度搜索到的资料理解的,如果有理解错误的地方请指正。
关于评论区有朋友问别人拿我的成果卖钱/伪装第一作者/不注明来源偷盗转载的看法:
```
偷盗转载卖钱狗nm似了捏。
我希望这是极少部分人,如果所有人都在盗取别人的研究结果,而没有人愿意分享,那么这对整个行业是一个沉重的打击。
众所周知,原创凉的快,火的都是抄袭者。但是没有原创,那么也就没有人能学到知识。即使对我没有任何好处,我也愿意做一个分享的人。
但是很可惜,上面那些屁话糊弄糊弄刚毕业的还行,毕业三年看遍世间百态,皆为追名逐利之徒,把别人自由分享的东西据为己有变现,偷盗转载提升个人名气的宵小之徒比比皆是。毕竟狗改不了吃屎。
以上种种人间丑态,任世事纷扰,我自且听风吟,一笑置之。
```
关于xxxx的看法省流:
```
偷盗转载卖钱狗nm4了。
```
## 直入主题
### 第一步 寻找特征
这次我们不破坏原始文件二进制,使用dylib注入Framework文件的方式劫持内存实施运行时代码注入达到修改目标函数的目的。
其缺点是必须要关闭SIP,否则无法通过App文件的完整性检查。
不破坏App的原始签名可以绕过一些权限问题,如后面释出的CleanMyMac X 4.12.3破解后无限弹窗问题。
打开软件寻找目标。
Your copy of AirBuddy is not activated.
这行字我们去搜一下。
我们直接搜索整个app文件里面:
好 我们已经找到结果,下面就是根据字符串的引用key去ida搜索了。
此时多个文件我们怎么检查呢?
注意看,这些文件是一个framework和一个xpc文件中存在的。xpc就不多说了,就没有意义。我们直接重点关注AirCore.framework文件。
可以看到字符串引用key就是“LICENSE_STATE_NOT_ACTIVATED”。
我们直接在IDA中打开此文件搜索:
我们查到了索引,那么这里很可能就是调用方了。跳过去看下:
继续跳!
来到函数6B4C0.
继续查找:
有两个调用方,那么我们看看第一个:
事情此时变得有趣了起来:这个一看就很不对劲的函数没有任何调用方!
下面那个函数也是如此,那么他是如何被调用的呢?
由于这不是在主进程中,所以我们大胆假设:这是一个导出函数!
我们到Exports里面复制一部分的文字搜索,果然。
那么问题来了,哪里引用了呢?
我们直接搜主程序试试:
(主程序: /Applications/AirBuddy.app/Contents/MacOS/AirBuddy)
果然不出所料。
我们继续跟进去看看他是如何被调用的:
到这里我们继续在函数名上按下X查找引用:
又跳到了一个符号表:
继续跳:
终于逮住了。
我们继续追查:
原来如此。我们F5看下伪代码:
他是来自一个叫100050480的函数调用:
在这个函数的入口中,a1所在的r13寄存器对内存+153的偏移处进行判断,如果不等于1那么执行这里的A1F78函数(101行),否则执行刚刚找到的NotActivated。
而且注意最上面一个return sub_100017DF0(v19,v20),也就是说如果这个内存偏移地址的值不等于1就会返回这个函数的返回值而不执行下面的NotActivated。也就是界面上红色的X。
那么我们可以算是找到爆破点了,但是我们不打算用暴力修改的方式,我们今天试一下内存注入。
打开最爱的VSCode,打开frida,开始测试注入结果!
### 第二步 注入内存修改内存指针所处的值
python文件新建,写入如下:
然后我们要准备一个_index.js,否则就没戏唱了。
那么有玩frida比较多的朋友可能会问,为什么不用frida -f xxx启动这个app进程 还要python这么麻烦?
解答:
frida在macOS上的inlinehook有问题,会造成app挂起无法恢复运行状态导致超时,系统强制终止进程。因为系统认为app卡死了。启动失败嘛。
下面我们看下ts文件:
其中,Utils.ts的代码如下:
```ts
export function log(...message: any): void {
console.log(...message);
}
enum AccessType {
Pointer,
ObjectCFunction,
}
export type TargetFunction = {
target: NativePointerValue,
accessType: AccessType,
msgTag: string//打印的日志标记
}
export type Tools = {
watchMemory: (target: TargetFunction) => void;
}
export function HookApp(module: string, hookStart: (
hook: (
target: TargetFunction,
leave: (
ths: InvocationContext,
retval: InvocationReturnValue
) => void,
enter?: (
ths: InvocationContext,
args: InvocationArguments
) => void,
) => void,
getPointer: (offsetMemmory: string | number | NativePointerValue | UInt64 | Int64) => TargetFunction,
getClassMethod: (clazzName: string, clazzMethodSign: string) => TargetFunction,
appBaseAddr: NativePointer,
tools: Tools
) => void) {
var appBaseAddr = Module.findBaseAddress(module)
log(`App [${module}] 内存基址: ${appBaseAddr}`)
let init = (appBaseAddr: NativePointer) => {
let hook = (
target: TargetFunction,
leave: (ths: InvocationContext, retval: InvocationReturnValue) => void,
enter?: (ths: InvocationContext, args: InvocationArguments) => void
) => {
attachTarget(target, appBaseAddr, enter, leave, module)
}
hookStart(
hook,
(offsetMemmory: string | number | NativePointerValue | UInt64 | Int64) => {
return {
target: appBaseAddr.add(offsetMemmory),
msgTag: "0x" + offsetMemmory.toString(16),
accessType: AccessType.Pointer
}
},
(clazzName: string, clazzMethodSign: string) => {
return {
target: ObjC.classes.implementation,
accessType: AccessType.ObjectCFunction,
msgTag: clazzName + " [" + clazzMethodSign + "]"
}
},
appBaseAddr,
{
watchMemory: (target) => {
MemoryAccessMonitor.enable({
base: new NativePointer(target.target),
size: 16
}, {
onAccess: function (details) {
log("对内存[" + target.msgTag + "]访问来自", (details.from.sub(appBaseAddr)).toString())
}
})
}
}
)
}
if (appBaseAddr != null) init(appBaseAddr)
}
function attachTarget(
target: TargetFunction,
appBaseAddr: NativePointer,
enter?: (
ths: InvocationContext,
args: InvocationArguments
) => void,
leave?: (
ths: InvocationContext,
retval: InvocationReturnValue
) => void,
module?: string) {
var line = "=".repeat(32);
Interceptor.attach(target.target, {
onEnter(this: InvocationContext, args: InvocationArguments) {
log(line + ` \n${module} 进入函数 ` + target.msgTag + "\n")
enter?.(this, args)
log(line, "\n")
},
onLeave(retval) {
log(line + ` \n${module} 退出函数 ` + target.msgTag + "\n")
log("修改前返回值", retval)
leave?.(this, retval)
log("修改后返回值", retval, "\n" + line, "\n")
},
});
}
```
然后编译ts到js文件的指令:
build是编译,-w是监视文件修改自动编译。
我们执行watch的指令即可自动编译了。
```
注意:
关于nodejs前端代码的问题请不要发问,不会解答任何关于前端js的问题,因为我默认你会使用并知晓以上简单的基本js知识。
实在不会可以百度。
```
准备工作完成,那么我们开始hook函数`__int64 __usercall sub_100050480@<rax>(__int64 a1@<r13>)`看看:
我们把可执行文件的名称写在参数1中,这样就会自动锁定内存里这个同名进程进行注入。
然后我们获取这个指针所在的地址,开始进入函数的挂钩:
启动python文件,我们可以看到已经对函数成功挂钩。
那么前面我们知道他是对内存偏移进行判断:
其实到这个时候也不怕你们知道, v28 = sub_1000A1F78(); 函数sub_1000A1F78进去一看:
再回到AirCore文件里一搜:
函数6B860进去看下:
搜一下key发现是已经激活的提示文字。
那么此时回到AirBuddy文件,我们整个函数已经一目了然:
if ( *(a1 + 153) != 1 )
{
已经激活
} else {
需要激活
}
那么我们还犹豫什么?
只要在函数进入之前修改*(a1 + 153)的值即可。
但是怎么修改呢?
别急,我们看下反汇编:
cmp就是这个判断,我们要修改的就是r13+99h内存指针的值为0即可。
回到Frida,我们首先已经挂上钩子了:
所以我们先拿到CPU寄存器此时r13的指针,然后add(153),99H就是十进制的153.
先读一下内存,发现是1,那么按正常逻辑我们会发现确实和if ( *(a1 + 153) != 1 )条件不成立,所以未激活。
那么我们就狠狠的writeInt(0)!
保存,自动编译为_index.js,然后运行python文件让他自动注入app:
绿绿的,很喜欢。
是不是很简单?大家都学会了吧?下面我们用object-c写一遍去持久化注入App。
```
估计没几个人能认真看到这里,我偷偷喵两句:
这个软件真是不懂那个几十美元收的是什么钱,激活了也不知道新增了什么功能,完全不知道收费版和不收费有什么区别。难道是这个绿色和红色的区别?
大家都喜欢绿油油的?呃呃呃蝈楠那可真够下头的捏。
@ 腾讯公司 过来学习先进皮肤经验。(一开始没想到论坛真有这个人 还艾特出来了。。。)
```
### 第三步 dylib持续化注入
毕竟不能靠Frida先启动然后再启动app,这并不酷,也不符合我对高科技的想象。
新建项目
然后随便起个名:
点创建选择一个保存的路径即可。
然后我们打开.m文件开始编写代码。
直接把rd_route复制两个文件到项目里即可。
如图所示。
然后import一下几个基本库:
UI控件库是注入CleanMyMac的,可以不写。
然后我们在load中写自己的ObjectC代码
我们今天主要看AirBuddy,大家不要在意上面的一连串注入代码。
我们在frida里面直接能修改寄存器,OC里很遗憾不能直接操作。
但是我们有ASM!
直接写内联汇编啊铁子!(突然激动
我们首先屏蔽掉activate函数,这没用。
然后我们用_dyld_get_image_vmaddr_slide(0) + 0x100050480;加上函数偏移得到内存中这个函数的偏移地址。
为什么_dyld_get_image_vmaddr_slide参数选0捏?因为这是选择第一个镜像(也就是可执行文件本身的内存空间镜像基址),具体细节懒得说,百度搜一下大家就懂了。
记住写0就是表示App可执行文件本身,也就是你注入的那个进程本身。别乱写哦,因为app在运行时不止一个内存镜像。
然后我们伪造一个函数进行挂钩。
```c
int _0x100050480New() {
NSLog(@"==== _0x100050480New called");
__asm
{ //内联汇编直接修改寄存器的值
//对比原始二进制反汇编 cmp byte ptr , 1
mov byte ptr , 0
}
NSLog(@"==== _0x100050480New call end");
return _0x100050480Ori();//调用原函数恢复执行
}
```
接下来声明一下原始函数保存地址,这里直接转换成函数结构体可以直接调用
int (*_0x100050480Ori)();
定义原函数返回值int 无参数 加上 * 取该变量内存地址里的值以被下面挂钩函数赋新值:
rd_route((void *) _0x100050480, _0x100050480New, (void **) &_0x100050480Ori);
(void **) &_0x100050480Ori这里用(void **)进行类型转换,&取地址符传递地址进函数。
然后 *_0x100050480Ori 保存的就是原函数地址了,注意如果不写“ * ”你会得到_0x100050480Ori的内存地址而不是这个内存地址里面的值。
然后使用经典的mov指令在函数进入时将寄存器的值赋予0,并恢复原函数执行,这样原函数读到的就是新的r13寄存器里的这个0值啦!
```c
__asm
{ //内联汇编直接修改寄存器的值
mov byte ptr , 0
}
```
按下Command + F9 狠狠的编译出dylib文件。
好耶!
现在拿出我们的注入文件去注入app:
insert_dylib文件是编译自项目:insert_dylib,代码在开头自己编译。我不提供的原因是老有阴谋论在那边发癫说这个可执行文件有后门 让我们关SIP一定是你的阴谋 我要控制所有人的MacBook 统治整个世界!我是淫魔是吧?阴谋论歇歇吧别搁那发癫了。
注入完成打开app:
成功。
### 第四步 注意事项
我们想修改内存的是主程序二进制文件,所以我们千万不要注入任何影响到偏移地址有关的文件,如AirCore和AirBuddy二进制文件,为什么呢?因为这样会造成文件偏移错误。我们的偏移地址是根据IDA读出的原始二进制来算的,如果你注入了这些文件地址就会发生移位,这就无法通过原地址进行正确的注入了。所以我挑了个好欺负的小文件。也别去注入几MB的大文件,手动来回复制文件很麻烦的。
## 注入文件和补丁
仅供参考学习使用,有些人不要发癫!
试试注入CleanMyMac 4.12.3 中国特供版,有彩蛋哦。
源码:
insert_dylib
rd_toute
编译好的二进制文件:
仅供参考学习使用。
注入方法:
sudo insert_dylib libInlineInjectPlugin.dylib /Applications/AirBuddy.app/Contents/Frameworks/LetsMove.framework/Versions/A/LetsMove /Applications/AirBuddy.app/Contents/Frameworks/LetsMove.framework/Versions/A/LetsMove
libInlineInjectPlugin.dylib文件建议固定放在某个位置 注入成功后移动或删除文件都会导致注入失败。
sudo insert_dylib <dylib库文件路径> <目标文件路径> <写出的新文件路径>
## 参考资料 Reference&Credit
1. 2023, Search Engine By Google.
2. 2023, Bing Global Search Engine By Microsoft.
3. 2023, MacOS动态注入的三种方式及hook方案, https://blog.csdn.net/tangsilian/article/details/89442802
4. 2023, macOS 逆向從入門到破解 Frida + Hopper + dylib 注入, https://beeeeemo.dev/2021/08/macos-%E9%80%86%E5%90%91%E5%BE%9E%E5%85%A5%E9%96%80%E5%88%B0%E7%A0%B4%E8%A7%A3-frida-hopper-dylib-%E6%B3%A8%E5%85%A5/
5. 2023, 404 WebSite by code-examples.net, https://code-examples.net/zh-CN/q/204273
6. 2023, JianShu by https://www.jianshu.com/p/3c8a9a6cee8d
7. 2023, 404 WebSite by https://github.com/rodionovd/rd_route
8. 2023, 使用Frida优雅调试010 Editor https://www.chinapyg.com/thread-134972-1-1.html
9. 2023,Objective-C的hook方案(一): Method Swizzling by https://blog.csdn.net/yiyaaixuexi/article/details/9374411
10. 2023,运行时注入方式破解最新Mac版010 Editor v9.0.1https://www.52pojie.cn/forum.php?mod=viewthread&tid=861809&extra=page%3D1%26filter%3Dtypeid%26typeid%3D377
11. 还有好多,懒癌犯了不想写了。 不知道这个有没有合适的,可以问一下他【闲鱼】https://m.tb.cn/h.UNUXPMt?tk=nZP2d6oGLhH CZ0001 「快来捡漏【苹果二代蓝牙长键盘黑白妙控苹果二代蓝牙键盘妙控进水拆电池,键】」
点击链接直接打开 本帖最后由 Vvvvvoid 于 2024-1-7 03:07 编辑
QiuChenly 发表于 2024-1-7 01:22
直接注入这个二进制 但是要注意修复文件权限和可能出现的崩溃问题 这都可以通过hook修正。
还有一个问题
为啥 frida hook 是手动获取模块基址 ,然后 + 函数偏移 0x50480
而 dylib 注入是直接写死的函数地址 0x100050480 啊 大佬厉害,应该是精华帖 牛,花了半小时全看完了,很详细{:1_921:}{:1_921:}{:1_921:} 谢谢大佬分享 学习了。 看完了大神所有帖子,受益匪浅,希望继续分享 @QiuChenly 是有人拿附件去卖了吗?在哪里我可以去查一下。 大佬牛逼、就是步骤太多了 太复杂了、诶 我等菜鸡搞不定啊