DLL远线程注入

远程线程创建和内存自由分配

 

远线程注入技术:

强制创建一个目标进程的线程:将我们的外挂木马DLL加载进去

 

加载

LoadLibrary api函数来处理

强制创建一个目标进程的线程

CreateRemoteThread

HANDLE CreateRemoteThread( HANDLE hProcess, LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId );

作用:

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

 

参数

HANDLE hProcess

要在进程中创建线程的进程句柄

采用OpenProcess函数来获取进程句柄打开现有的本地进程对象

HANDLE OpenProcess( DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId );

获取句柄函数参数:

第一个dwDesiredAccess表示进程的访问权限,一般采用第一个宏PROCESS_ALL_ACCESS表示获得所有的访问权限

第二个 BOOL binheritHandle

如果该值为TRUE,则此进程创建的进程将继承该句柄。否则,进程将不会继承此句柄。

这里我们只是采用注入所以不用继承

第三个 dwProcessId

要打开的本地进程的标识符。也就是进程ID

 

 

LPSECURITY_ATTRIBUTES lpThreadAttributes
lpThreadAttributes

指向SECURITY_ATTRIBUTES结构的指针,该 结构为新线程指定安全描述符,并确定子进程是否可以继承返回的句柄。如果lpThreadAttributesNULL,则线程获取默认的安全描述符,并且该句柄无法继承。线程的默认安全描述符中的访问控制列表(ACL)来自创建者的主要令牌。

这里我们不用深究,通常直接采用NULL就好

SIZE_T dwStackSize

堆栈的初始大小,如果参数为0就使用默认值,我们直接采用默认值好了

 

LPTHREAD_START_ROUTINE lpStartAddress,

指向由线程执行的,类型为LPTHREAD_START_ROUTINE的应用程序定义的函数的指针,该指针表示远程进程中线程的起始地址。

也就是说该变量必须为LPTHREAD_START_ROUTINE类型的回调函数,表示远程进程中线程的起始位置,所以这里需要赋值为远程进程的地址。

其实就是需要调用的函数的函数指针,只是类型必须用指定的LPTHREAD_START_ROUTINE类型来处理也就是你要插入线程的函数的进程函数名。

这个参数的名称就相当于是一个回调函数的名字,

 

LPVOID lpParameter,

指向要传递线程函数的变量(可以理解为参数)的指针。

也就是线程要执行的函数的参数的变量

这里需要用到VirtualAllocEx()函数

VirtualAllocEx():在指定进程的虚拟地址空间内保留,提交或更改内存区域的状态。该函数将其分配的内存初始化为零。因为CreateRemoteThread是创建一个在另一个进程的虚拟地址空间中运行的线程

LPVOID VirtualAllocEx( HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect );

分配虚拟内存函数

参数

第一个hProcess表示进程的句柄

lpAddress表示需要分配的页面起始地址,一般直接采用NULL用默认值就好。

dwSize:分配内存区域的大小分配给要执行的函数的变量,也就是函数参数dll所在路径的位置。也就是传的一个文件路径的大小

flAllocationType:也就是内存的类型,一般情况下采用第一个宏定义就好了MEM_COMMIT

flProtect:需要指定一个内存保护常量来处理, PAGE_READWRITE这个宏表示即可以读也可以写

返回值:

如果申请成功会返回一个虚拟内存的基质

申请完一个虚拟内存后还要写入

相当于申明一个变量后给变量赋值

使用WriteProcessMemory函数来处理

BOOL WriteProcessMemory( HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesWritten );

hProcess表示进程句柄

lpBaseAddress表示基质的位置也就是用VirtualAllocEX申请的虚拟内存地址的基质

lpBuffer表示指向缓冲区的指针,也就是需要写入的字符串的指针

nSize:表示写入的字节数

lpNumberOfBytesWritten:

指向变量的指针,该变量接收传输到指定进程中的字节数。此参数是可选的。如果lpNumberOfBytesWrittenNULL,则忽略该参数。一般填NULL,这个没啥用

 

DWORD dwCreationFlags,

控制线程创建的标志。

含义
0 线程在创建后立即运行。
CREATE_SUSPENDED0x00000004 该线程以挂起状态创建,并且直到调用ResumeThread函数后才运行 。
STACK_SIZE_PARAM_IS_A_RESERVATION0x00010000 所述dwStackSize参数指定堆栈的初始保留大小。如果未指定此标志,则dwStackSize指定提交大小。
LPDWORD lpThreadId

指向接收线程标识符的变量的指针。

如果此参数为NULL,则不返回线程标识符。

代码实现:

https://github.com/skrandy/dll_input

 

代码实现中没有优化

DLL注入优化

在多线程的情况下:希望等待某一线程完成后再继续做其他事情。

可以使用Windows API :WaitForSingleObject或者 WaitForMultipleObject来等待线程

 

防止线程阻塞

WaitForSingleObject:

DWORD WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds );

第一个参数是对象 的句柄,第二个参数是超时间隔(毫秒为单位)如果指定了非零值,则函数将等待,直到发出信号通知对象或间隔过去为止。如果dwMilliseconds为零,则如果未用信号通知对象,则函数不会进入等待状态;否则,函数将进入等待状态。它总是立即返回。如果dwMillisecondsINFINITE,则该函数仅在信号通知对象时返回。

在创建进程和线程等句柄后应该关闭

CloseHandle()