0%
Theme NexT works best with JavaScript enabled
说在前面
注意事项:此文仅限于技术交流,请不要做违法的事情。对于那些居心叵测的人根据此文造成违法的事情与本人无关。此文章不得转载!!!如果APP方需要删除,请发邮件xxoo@hotmail.com
,谢谢。
推荐先阅读macOS逆向之跳过XtraFinder试用界面 ,这篇文章各种工具的使用介绍的更详细。
开发环境:macOS11.2.3、Xcode11.7、IDA7.0、class-dump、超级右键2.1.9(写这篇文章时的最新版)
具备技能:X64汇编基础、OC基础知识、iOS逆向基础知识
为什么使用:
macOS系统本身并不支持右键新建文件,本人每次通过iTerm2
,使用touch
命令新建文件,这样就很麻烦,效率很低。1 2 3 cd 目标目录 touch test.txt touch test.md
如果要用VSCode
打开一个文件夹,需要首先打开VSCode
,然后再从里面选取对应的文件夹。一两次操作还行,次数多了,真的是浪费时间。
超级右键是macOS上最强大的右键菜单管理工具,拥有丰富的功能,可以提升使用体验。针对上面两个问题,超级右键一键解决,看下图是不是很爽。
目标结果:使用了一段时间,正爽的时候,突然给你来个弹框,提示你试用到期了,让你付费使用。作为一个技术,难道还能被技术难住了,于是走向了逆向之路。
分析界面
新建一个macOS App项目,用来调试超级右键界面。如下图所示,发现购买弹框是一个NSWindow
类对象,名字叫BuyWindows
从/Applications/iRightMouse.app/Contents/MacOS
拷贝出二进制文件iRightMouse
,拖到IDA里面进行分析。看来已经适配了M1
机型,本人还是老式处理器,所以直接选择x86_64
架构进行分析。
分析完毕后,打开Strings window
窗口,搜索buy
时,已经出现了如下结果
双击buyProWindow
进去后,使用x
查看引用关系,发现购买窗口其实是AppDelegate
的一个成员变量1 __objc2_ivar <offset _OBJC_IVAR_$_AppDelegate_buyProWindow, \ ; NSWindow *buyProWindow;
在IDA里面搜索buyProWindow
并没有搜索到,用class-dump
导出iRightMouse
头文件,找到AppDelegate.h
发现有这个成员变量,没有深究原因。分析到这里后,可以确定弹框操作就是在AppDelegate
里面执行的。整个弹框过程应该是这样的:当新建某个文件或要把某个目录在VSCode
中打开时,会首先判断试用到期了没有,如果到期了,就[self->buyProWindow showMethod]
显示弹框。
分析弹框
为了不让购买弹框显示出来,我们需要判断哪里进行了弹框操作。1 2 3 4 5 if (!isOutOfDate) { // 如果试用没有到期 // 正常使用,不弹框 } else { // 弹框 }
显示弹框就是显示一个window,在iOS中是需要调用makeKeyAndVisible
方法的,而本人并没有做过macOS开发,不知道在macOS显示window,需要探究一下。1 [self .window makeKeyAndVisible]
macOS中不是用UIWindow
,而是用NSWindow
。使用刚才新建的项目,进入NSWindow.h
文件,发现三个与显示window有关的方法。1 2 3 makeKeyAndOrderFront: makeKeyWindow makeMainWindow
使用LLDB连接上iRightMouse,对NSWindow
上面的三个方法进行断点,猜想会调用某个方法显示弹窗。1 2 3 4 5 6 7 8 9 $ lldb (lldb) attach -n iRightMouse ... (lldb) br set -n makeKeyWindow Breakpoint 2: 5 locations. (lldb) br set -n makeMainWindow Breakpoint 4: 3 locations. (lldb) br set -n makeKeyAndOrderFront: Breakpoint 5: 6 locations.
随意进入一个目录,然后在目录里面右键
->进入 VSCode
或新建文件
,这个时候会命中断点。按c
后,购买弹窗就会显示出来,说明makeKeyAndOrderFront:
就是显示弹框的方法。1 2 3 4 5 6 7 8 9 10 11 12 13 14 Process 11199 stopped * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 frame #0: 0x00007fff22f7c275 AppKit` -[NSWindow makeKeyAndOrderFront:] AppKit`-[NSWindow makeKeyAndOrderFront:]: -> 0x7fff22f7c275 <+0>: push rbp 0x7fff22f7c276 <+1>: mov rbp, rsp 0x7fff22f7c279 <+4>: push r14 0x7fff22f7c27b <+6>: push rbx 0x7fff22f7c27c <+7>: mov rbx, rdi 0x7fff22f7c27f <+10>: mov rsi, qword ptr [rip + 0x65cddfca] ; "_resolveAutomaticEnterFullScreenFlags" 0x7fff22f7c286 <+17>: mov r14, qword ptr [rip + 0x5d7b1d53] ; (void *)0x00007fff203c5d00: objc_msgSend 0x7fff22f7c28d <+24>: call r14 Target 0: (iRightMouse) stopped. (lldb)
重复上面的步骤,当命中断点后,执行bt
打印调用堆栈。1 2 3 4 5 6 (lldb) bt * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 * frame #0: 0x00007fff22f7c275 AppKit` -[NSWindow makeKeyAndOrderFront:] frame #1: 0x00000001026ff9c0 iRightMouse` ___lldb_unnamed_symbol423$$iRightMouse + 135 ... (lldb)
很明显在0x00000001026ff9c0
调用了显示弹框的方法,这个地址是在内存中的地址,我们需要的是在Mach-O中的地址,使用如下命令打印出我们需要的地址0x000000010001f9c0
。1 2 3 4 (lldb) image lookup -a 0x00000001026ff9c0 Address: iRightMouse[0x000000010001f9c0] (iRightMouse.__TEXT.__text + 124128) Summary: iRightMouse`___lldb_unnamed_symbol423$$iRightMouse + 135 (lldb)
进入IDA,输入G
,在弹框内输入0x000000010001f9c0
,点击确定后,会到以下代码出,这不正是我们需要的。1 2 3 4 5 6 7 8 9 10 11 12 LABEL_11: v14 = v8; v15 = 0LL; goto LABEL_12; } objc_msgSend((void *)self->buyProWindow, "makeKeyAndOrderFront:", self); objc_msgSend(NSApp, "activateIgnoringOtherApps:", 1LL); v9 = 0; LABEL_14: objc_release(v8); return v9; }
上面显示的是伪代码界面,需要按空格键切换到流程图界面,方便定位哪里进行了判断是否过期
的操作。沿着上一步弹窗代码
一直往上找,直到遇到某个判断跳转方法的分支就停下来,最终会来到如下图所示的代码处。如果执行左边红线的代码,就会到上一步显示弹框的代码处;如果执行右边绿线的代码,就会直接执行相应的操作。
可以很明显发现弹窗操作是在-[AppDelegate application:openFile:]
方法中进行的,这不正印证了上面所说的弹窗操作是在AppDelegate
里面执行的
汇编代码分析
上图汇编指令解析
指令cmp
的意思是对两个数执行减法。当运算结果为0时,把ZF
(零标志位)置1,否则置0。
指令jz
的意思是根据cmp
运算结果的值,判断执行的逻辑。如果结果为0就执行short loc_10001F9E0
,否则执行弹框操作。
指令lea
是地址赋值的意思,就是把byte_100038940
这个地址值赋值给rax
.
经过上面几步的分析,我们已经知道了目前是走左边红色线条的逻辑(也就是说会进行购买弹框操作),可以得出结论:
此时的cmp byte ptr [rax], 0
运算结果不为0
此时的byte ptr [rax]
不为0,也就是说[rax]
不为0。
我们可以使用LLDB在地址000000010001F9A9
打断点验证一下上面的猜想
因为并没有恢复Mach-O的符号,所以只能使用地址断点,1 2 3 4 5 6 7 8 9 10 $ lldb (lldb) attach -n iRightMouse Executable module set to "/Applications/iRightMouse.app/Contents/MacOS/iRightMouse". ...省略很多... Architecture set to: x86_64h-apple-macosx-. (lldb) image list -o -f | grep iRightMouse [ 0] 0x0000000004560000 /Applications/iRightMouse.app/Contents/MacOS/iRightMouse (lldb) br set -a 0x0000000004560000+0x000000010001F9A9 Breakpoint 1: where = iRightMouse`___lldb_unnamed_symbol423$$iRightMouse + 112, address = 0x000000010457f9a9 (lldb) c
当执行相应的操作后,会命中断点,然后打印[rax]
的值,发现此时的值为1,这不正验证了上一步我们的猜想1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Process 50188 stopped * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 frame #0: 0x000000010457f9a9 iRightMouse` ___lldb_unnamed_symbol423$$iRightMouse + 112 iRightMouse`___lldb_unnamed_symbol423$$iRightMouse: -> 0x10457f9a9 <+112>: cmp byte ptr [rax], 0x0 0x10457f9ac <+115>: je 0x10457f9e0 ; <+167> 0x10457f9ae <+117>: mov rdi, qword ptr [r12 + 0x8] 0x10457f9b3 <+122>: mov rsi, qword ptr [rip + 0x1b15e] ; "makeKeyAndOrderFront:" 0x10457f9ba <+129>: mov rdx, r12 0x10457f9bd <+132>: call r13 0x10457f9c0 <+135>: mov rax, qword ptr [rip + 0x10651] ; (void *)0x00007fff88569260: NSApp 0x10457f9c7 <+142>: mov rdi, qword ptr [rax] Target 0: (iRightMouse) stopped. (lldb) memory read $rax 0x10459b940: 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x10459b950: 65 df 58 04 01 00 00 00 88 4c 59 04 01 00 00 00 e�X......LY..... (lldb)
怎样让代码走右边绿色线条的逻辑(也就是说不会出现购买弹框,直接进行相应的操作)呢?上面已经分析了很多,到这里已经很简单了,让cmp byte ptr [rax], 0
运算结果为0就可以了。从上面可以知道此时byte ptr [rax]
的值为1,所以把cmp byte ptr [rax], 0
中0更改为1,就能都达到让整个运算结尾为0的效果。
修改汇编代码