首页
社区
课程
招聘
[原创]逆向分析(二)--首次针对苹果M1芯片的恶意软件GoSearch22
2024-1-5 12:08 4803

[原创]逆向分析(二)--首次针对苹果M1芯片的恶意软件GoSearch22

2024-1-5 12:08
4803

anti-debugging Logic (via sysctl)

​ 即使绕过了反调试的检测,当恶意软件在调试器中继续执行时,它仍然会终止运行。

1
2
3
(lldb) continue
Process 667 resuming
Process 667 exited with status = 0 (0x00000000)

​ 事实证明,恶意的GoSearch22二进制文件包含更多的反调试逻辑。这个额外的反调试逻辑是通过sysctl API实现的,换句话说,通过这个API,恶意软件可以查询它自己是否正在被调试。

​ 在恶意软件的核心逻辑中,我们发现了对sysctl API的调用:

1
2
3
4
...
0x0000000100054fe8 movz x4, #0x0
0x0000000100054fec movz x5, #0x0
0x0000000100054ff0 bl sysctl

​ 由于恶意软件广泛使用混淆,通过静态分析,不容易看出此API调用将导致恶意软件过早终止。然而,在调试器中,如果我们允许调用sysctl,恶意软件很快就会退出。这时候就要想,该怎么样才能让恶意软件愉快地继续运行下去呢?

​ 我们先来看看sysctl函数的声明:

1
2
int sysctl(int *name, u_int namelen, void *oldp,
 size_t *oldlenp, void *newp, size_t newlen);

​ 可以调用该函数来检索各种信息,包括有关当前流程状态的详细信息。这些细节包括在程序调试时将设置的一些东西。下面的C代码说明了这一点:

1
2
3
4
5
6
7
8
9
10
11
12
struct kinfo_proc processInfo = {0};
size_t size = sizeof(struct kinfo_proc);
int name[4] = {0}
name[0] = CTL_KERN;
name[1] = KERN_PROC;
name[2] = KERN_PROC_PID;
name[3] = getpid();
sysctl(name, 4, &processInfo, &size, NULL, 0);
if(0 != (processInfo.kp_proc.p_flag & P_TRACED))
{
//debugger detected
}

​ 这段C代码首先声明了一个kinfo_proc结构体,并为这个结构体的大小设置了一个变量。然后,它声明并初始化一个带有值(CTL_KERN等)的数组,这些值将指示sysctl函数检索有关正在运行的进程的信息。

​ 然后调用sysctl函数并填充传入的kinfo_proc结构。这包括设置一个p_flag成员,该成员可以针对p_tracked常量进行测试,以确定正在运行的进程是否正在调试(跟踪)。

​ 如下所示,检查恶意软件的反汇编显示,恶意软件也试图以同样的方式检测它是否正在被调试。在反汇编中,我们发现前面提到的sysctl API的调用位于0x0000000100054ff0。这个调用是通过BL指令进行的,它简化了函数调用。

0x0000000100054fcc ldur x8, [x29, var_B8]
0x0000000100054fd0 movz w9, #0x288
0x0000000100054fd4 str x9, [x8]
0x0000000100054fd8 ldur x0, [x29, var_C8]
0x0000000100054fdc ldur x3, [x29, var_B8]
0x0000000100054fe0 ldur x2, [x29, var_A8]
0x0000000100054fe4 orr w1, wzr, #0x4
0x0000000100054fe8 movz x4, #0x0
0x0000000100054fec movz x5, #0x0
0x0000000100054ff0 bl sysctl

​ 调用前的两个指令通过MOVZ指令将第五个和第六个参数(寄存器x4和x5)初始化为零。

1
2
0x0000000100054fe8 movz x4, #0x0
0x0000000100054fec movz x5, #0x0

​ 继续往回看,在地址0x0000000100054fe4,第二个参数设置为4。

1
0x0000000100054fe4 orr w1, wzr, #0x4

​ 由于该参数是一个32位整数,因此使用w1寄存器(x1寄存器的32位部分)。将32位零寄存器(WZR)与4按位或运算将寄存器也设置为4。从函数声明中,我们知道第二个参数是name数组的大小,即4。

​ 第一个、第三个和第四个参数(寄存器x0、x2、x3)都是通过LDUR指令初始化的。

1
2
3
0x0000000100054fd8 ldur x0, [x29, var_C8]
0x0000000100054fdc ldur x3, [x29, var_B8]
0x0000000100054fe0 ldur x2, [x29, var_A8]

​ 第一个参数(X0)用指向数组的指针初始化。在调试器中,我们可以输出它的值(通过x/4wx命令)。

1
2
(lldb) x/4wx $x0
0x16fe86de0: 0x00000001 0x0000000e 0x00000001 0x00000475

​ 对应于CTL_KERN (0x1)、KERN_PROC (0xe)、KERN_PROC_PID (0x1)和当前恶意软件的进程标识符(pid),这些值将指示sysctl函数检索有关恶意软件运行进程的信息。

​ 最后,第四个参数(x3)是用kinfo_proc结构的大小(0x288)初始化的。这个初始化需要四条指令:

1
2
3
4
5
6
0x0000000100054fcc ldur x8, [x29, var_B8]
0x0000000100054fd0 movz w9, #0x288
0x0000000100054fd4 str x9, [x8]
0x0000000100054fd8 ldur x0, [x29, var_C8]
...
0x0000000100054fdc ldur x3, [x29, var_B8]

​ 首先,LDUR指令将变量(var_B8)的地址加载到X8寄存器中。然后通过MOVZ指令将kinfo_proc结构(0x288)的值移到W9寄存器中。STR (store)指令然后将这个值(用X9)存储在X8寄存器的地址中。最后,通过LDUR指令将该值加载到X3寄存器中,以完成参数初始化。

​ 发出sysctl调用后,恶意软件检查现在填充的kinfo_proc结构。具体来说,它检查p_flag标志是否设置了p_tracked位。如果设置了这个位,恶意软件就知道它正在被调试,并将提前终止运行。

​ 下面的指令从已填充的kinfo_proc结构体中提取p_flag成员(其地址存储在堆栈中的专用位置,反汇编程序标记为var_90):

1
2
3
0x000000010005478c ldur x8, [x29, var_90]
0x0000000100054790 ldr w8, [x8, #0x20]
0x0000000100054794 stur w8, [x29, var_88]

​ 首先,(通过LDUR指令)将kinfo_proc结构的地址加载到X8寄存器中。然后(通过LDR指令)32位p_flag成员(位于结构中的偏移量0x20处)被加载到W8寄存器中。然后通过STUR命令将该值存储在var_88变量中。

​ 稍后,恶意软件检查p_flags标志是否设置了p_tracked位(p_tracked是常量0x00000800,这意味着它的第11位设置为0x1)。在调试会话中,我们可以确认,正如预期的那样,p_flags标志确实设置了p_tracked位。

1
(lldb) p/t $w8 0b00000000000000000101100000000110

​ 以下是arm64指令,从恶意软件的反汇编中提取,用于提取p_tracked位:

1
2
3
0x0000000100055428 ldur w8, [x29, var_88]
0x000000010005542c ubfx w8, w8, #0xb, #0x1
0x0000000100055430 sturb w8, [x29, var_81]

​ 在前面的指令中,恶意软件首先通过LDUR指令将保存的p_flag值(var_88)加载到W8寄存器中。然后执行UBFX指令来提取p_tracked位。UBFX指令接受一个目标寄存器(W8)、一个源寄存器(W8)、位域索引(0xb或11d)和宽度(1,表示单个位)。换句话说,它从p_flag中获取偏移量为11的位,这是p_tracked位。然后通过STURB指令保存提取的p_tracked位。稍后,它检查(比较)以确保P_TRACE位没有设置。

1
2
0x00000001000550ac ldurb w8, [x29, var_81]
0x00000001000550b0 cmp w8, #0x0

​ 如果设置了p_tracked位,则恶意软件将提前退出,因为这表明恶意软件正在调试中。

​ 为了绕过第二个反调试检查,以便我们的调试会话可以不受阻碍地继续,我们可以(再一次)跳过有问题的调用。具体来说,一旦恶意软件要执行分支指令来调用sysctl,我们就可以将程序计数器更改为下一条指令。由于没有进行sysctl调用,kinfo_proc结构保持未初始化(带有零),这意味着对p_tracked标志的任何检查都将返回0 (false)。

​ 在这一点上,我们已经确定并挫败了恶意软件的anti-debugging logic。这意味着我们的调试会话可以不受限制地继续进行,这一点很重要,因为其他anti-analysis logic仍然潜伏着。


[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

最后于 2024-3-9 01:08 被boilsnow编辑 ,原因:
收藏
点赞5
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回