我正在对 Mac OS X 上的特定 dylib 进行逆向工程。 dylib 被高度混淆,但我怀疑它来自一些我不知道的知名技术。
我想描述一下,希望这里有人可以识别它:
到处都插入了垃圾指令,每个基本块都被很多无条件跳转分割(但没有使用不透明谓词)。
从本质上讲,它看起来像一个混淆的虚拟机。它的行为是这样的:
在入口处,它在堆栈上推送一个起始值,然后call是一个入口点:
000000010013E070 68 5C 98 42 11 push 1142985Ch
000000010013E075 E8 B5 B4 0E 00 call sub_10022952F
条目(例如sub_10022952F)不是通常的功能。它将保存所有寄存器并指向rsi由push(即push 1142985Ch上述)确定的嵌入数据位置,然后开始读取rsi数组并相应地跳转:
000000010013EED7 8B 06 mov eax, [rsi]
000000010013EED9 F5 ;; cmc
000000010013EEDA 45 84 FD ;; test r13b, r15b
000000010013EEDD 48 81 C6 04 00 00 00 add rsi, 4
000000010013EEE4 66 41 81 FA 1C 3B ;; cmp r10w, 3B1Ch
000000010013EEEA 33 C3 xor eax, ebx
000000010013EEEC D1 C0 rol eax, 1
000000010013EEEE E9 FD 8E 15 00 jmp loc_100297DF0
0000000100297DF0 FF C0 inc eax
0000000100297DF2 0F C8 bswap eax
0000000100297DF4 F8 ;; clc
0000000100297DF5 E9 F2 D6 E4 FF jmp loc_1000E54EC
00000001000E54EC C1 C0 03 rol eax, 3
00000001000E54EF 0F C8 bswap eax
00000001000E54F1 53 push rbx
00000001000E54F2 31 04 24 xor [rsp], eax
00000001000E54F5 0F B7 D8 ;; movzx ebx, ax
00000001000E54F8 0F BA F3 82 ;; btr ebx, 82h
00000001000E54FC 5B pop rbx
00000001000E54FD F5 ;; cmc
00000001000E54FE 49 F7 C7 E5 0F 9B 74 ;; test r15, 749B0FE5h
00000001000E5505 F9 ;; stc
00000001000E5506 48 63 C0 movsxd rax, eax
00000001000E5509 48 03 F8 add rdi, rax
00000001000E550C E9 D0 49 08 00 jmp loc_100169EE1
0000000100169EE1 FF E7 jmp rdi
我已经注释掉了垃圾代码。代码将从[rsi]into 中获取值rax,并使用rbx;进行一些位级操作。然后前进rdi <- rdi + rax; 然后跳转到rdi。
这个结构在dylib中无处不在,在一个由 连接的链中jmp rdi,位级操作也是一样的。但是来自不同入口点的链可能对rax和有不同的位操作rbx。
VM 没有任何中央结构(例如调度程序或其他东西),并且跳转无处不在,不限于某个位置。
调用外部函数,如pthread_mutex_lock,会先跳出VM循环;然后调用外部函数;然后去另一个入口点。有许多入口点 ( push xxxx / call xxxx) 可以进入 VM 循环。
我相信这是一些众所周知的技术,因为当dylib被修改时,它会提示信息:
文件损坏!该程序已被操纵,可能被病毒感染或被破解。该文件将不再起作用。
这不是标准的 Mac OS X 消息,谷歌搜索这条确切消息会给出很多结果,但没有一个解释技术本身。
一些附加信息:
它有名为UPX0/UPX1的部分,但我认为它们是伪装的,技术与UPX无关。因为代码只做自检,没有自修改。结果,它在完全加载后仍然像原始文件一样被混淆。
我现在不知道它是如何工作的。但我认为它是某种VM,因为我已经跟踪了dylib的一个函数的调用,我只找到了以下正常的函数调用:
pthread_mutex_lock--> 3 new operator--> pthread_mutex_unlock.
其他一切都由上述jump rdi结构在内部完成,并在许多入口点之间切换。因此,其余的代码逻辑驻留在 VM 循环中。