- 远程线程注入是指一个进程在另一个进程中创建线程的技术。是一种病毒木马所青睐的注入技术。
CreateRemoteThread 远程线程注入
OpenProcess 函数
打开现有的本地进程对象
1 | HANDLE |
OpenProcess
dwDesiredAccess
: 访问进程对象。此访问权限为针对进程的安全描述符进行检查,此参数可以是一个或多个进程访问权限。bInheritHandle
: 此进程创建的进程是否可以继承该句柄dwProcessId
: 要打开的本地进程PID
- 如果函数成功,则返回值是打开指定进程的句柄
- 如果函数失败,则返回值为NULL
VirtualAllocEx 函数
在指定进程的虚拟地址空间内保留、提交或更改内存状态
1 | LPVOID |
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 | BOOL |
WriteProcessMemory
hProcess
: 要修改的进程内存句柄。句柄必须具有PROCESS_VM_WRITE 和 PROCESS_VM_OPERATION访问权限lpBaseAddress
: 指向指定进程中写入数据的基地址指针。在数据传输之前,系统会验证指定大小的基地址和内存中的所有数据是否可以进行写入访问,如果不可以访问,则该函数将失败lpBuffer
: 指向缓冲区指针。其中包含要写入指定进程的地址空间中的数据nSize
: 要写入指定进程的字节数lpNumberOfBytesWritten
: 指向变量指针,该变量接收传输到指定进程的字节数。如果lpNumberOfBytesWritten
为NULL,则忽略该参数
如果函数成功,则返回值不为零
如果函数失败,则返回值为零
CreateRemoteThread 函数
在另一个进程的虚拟地址空间中创建运行的线程
1 | HANDLE |
CreateRemoteThread
hProcess
: 要创建线程的进程句柄。句柄必须具有PROCESS_CREATE_THREAD
、PROCESS_QUERY_INFORMATION
、PROCESS_VM_OPERATION
、PROCESS_VM_WRITE
和PEOCESS_VM_READ
访问权限lpThreadAttributes
: 指向SECURITY_ATTRIBUTES
结构的指针,该结构指定新线程的安全描述符,并确定子进程是否可以继承返回的句柄。如果lpThreadAttributes
为NULL,则线程将获得默认的安全描述符,并且不能继承该句柄dwStackSize
: 堆栈的初始化大小,以字节为单位。如果此参数为0,则新线程使用可执行文件的默认大小lpStartAddress
: 指向由线程执行类型为LPTHREAD_START_ROUTINE
的应用程序定义的函数指针,并表示远程进程中线程的起始地址,该函数必须存在于远程进程中lpParameter
: 指向要传递给线程函数的变量指针dwCreationFlags
: 控制线程创建的标志,若是0,则表示线程在创建后立即执行lpThreadId
: 指向接收线程标识符的变量指针。如果此参数为NULL,则不返回线程标识符
如果函数成功,则返回值是新线程的句柄
如果函数失败,则返回值为NULL
远程线程注入代码实现
1 | BOOL Inject(DWORD dwPid, const WCHAR* szPath) |
ZwCreateThreadEx 突破 SESSION 0 隔离的远程线程注入
与传统的
CreateRemoteThread
函数实现的远程线程注入DLL的唯一区别在于,突破SESSION 0
远程线程注入是使用比CreateRemoteThred
函数更为底层的ZwCreateThreadEx
函数来创建远程线程。ZwCreateThreadEx
函数可以突破SESSION 0
隔离,将DLL成功注入到SESSION 0
隔离的系统服务进程中。其中ZwCreateThreadEx
在ntdll.dll
中并没有声明,所以需要使用GetProcAddress
从ntdll.dll
中获取该函数的导出地址
函数声明
64位系统下函数声明
1 | DWORD WINAPI ZwCreateThreadEx( |
32位系统下函数声明
1 | DWORD WINAPI ZwCreateThreadEx( |
ZwCreateThreadEx 注入代码实现
1 | BOOL ZwCreateThreadExInjectDll(DWORD dwPid, char* szDllPath) |
卸载被注入的DLL文件
FreeLibrary 函数
1 | BOOL |
FreeLibrary
FreeLibrary
函数使用的模块句柄可以通过前面介绍的Module32First
和Module32Next
两个函数获取,需要用到MODULEENTRY32
结构体
1 | typedef struct tagMODULEENTRY32 |
MODULEENTRY32
hModule
: 模块句柄szModule
: 模块名称szExePath
: 完整的模块的路径(包括路径和模块名称)
卸载DLL文件代码实现
1 | VOID UnInjectDll(DWORD dwPid, char* szDllName) |
无DLL的代码注入
1 |
|