电脑上建设银行网站打不开,体育网站模版,做ppt选小图案的网站,百度网站是怎么做的远线程注入
远线程(RemoteThread)注入是指一个进程在另一个进程中创建线程的技术#xff0c;这是一种很经典的DLL注入技术。
虽然比较古老#xff0c;但是很实用。通过远线程注入#xff0c;再配合api函数的hook技术#xff0c;可以实现很多有意思的功能。 实现远线程注入…远线程注入
远线程(RemoteThread)注入是指一个进程在另一个进程中创建线程的技术这是一种很经典的DLL注入技术。
虽然比较古老但是很实用。通过远线程注入再配合api函数的hook技术可以实现很多有意思的功能。 实现远线程注入的关键函数
OpenProcess
打开现有的本地进程函数声明如下 1 WINAPI
2 OpenProcess(
3 _In_ DWORD dwDesiredAccess,
4 _In_ BOOL bInheritHandle,
5 _In_ DWORD dwProcessId
6 ); 参数
dwDesiredAccess
访问进程对象。此访问权限为针对进程的安全描述符进行检查此参数可以是一个或多个进程访问权限。如果调用该函数的进程启用了SeDebugPrivilege权限则无论安全描述符的内容是什么它都会授予所请求的访问权限。
bInheritHandle
若此值为TRUE则此进程创建的进程将继承该句柄。否则进程不会继承此句柄。
dwProcessId
要打开的本地进程的PID
返回值
成功返回进程的句柄
失败返回NULL要获取错误信息调用GetLastError VirtualAllocEx
在指定进程的虚拟地址空间内保留、提交或更改内存的状态。声明如下 1 WINAPI
2 VirtualAllocEx(
3 _In_ HANDLE hProcess,
4 _In_opt_ LPVOID lpAddress,
5 _In_ SIZE_T dwSize,
6 _In_ DWORD flAllocationType,
7 _In_ DWORD flProtect
8 ); 参数
hProcess
进程的句柄。VirtualAllocEx函数在该进程的虚拟地址空间内分配内存句柄必有具有PROCESS_VM_OPERATION权限。 lpAddress
指定要分配页面所需起始地址的指针。如果lpAddress为NULL则该函数自动分配内存。 dwSize
要分配的内存大小以字节为单位。 flAllocationType
内存分配类型。此参数必须为以下值之一
值含义 MEM_COMMIT 0x00001000 从指定保留内存页的磁盘) 的总内存大小和分页文件 (分配内存费用。 函数还保证当调用方稍后最初访问内存时内容将为零。 除非实际访问虚拟地址否则不会分配实际物理页面。 若要在一个步骤中保留和提交页面请使用 MEM_COMMIT | MEM_RESERVE调用 VirtualAllocEx。 尝试通过指定 MEM_COMMIT 而不指定 MEM_RESERVE 和非 NULLlpAddress 来提交特定地址范围除非已保留整个范围。 生成的错误代码ERROR_INVALID_ADDRESS。 尝试提交已提交的页面不会导致函数失败。 这意味着无需先确定每个页面的当前承诺状态即可提交页面。 如果 lpAddress 指定 enclave 中的地址则必须MEM_COMMITflAllocationType。 MEM_RESERVE 0x00002000 保留进程的虚拟地址空间范围而无需在内存或磁盘上的分页文件中分配任何实际物理存储。 使用 MEM_COMMIT 再次调用 VirtualAllocEx 来提交保留页。 若要在一个步骤中保留和提交页面请使用 MEM_COMMIT | MEM_RESERVE调用 VirtualAllocEx。 其他内存分配函数如 malloc 和 LocalAlloc在释放内存之前不能使用预留内存。 MEM_RESET 0x00080000 指示 lpAddress 和 dwSize 指定的内存范围中的数据不再感兴趣。 不应从分页文件读取或写入页面。 但是内存块稍后将再次使用因此不应取消提交。 此值不能与任何其他值一起使用。 使用此值并不能保证使用 MEM_RESET 操作的范围将包含零。 如果希望范围包含零请取消提交内存然后重新提交。 使用 MEM_RESET 时 VirtualAllocEx 函数会忽略 fProtect 的值。 但是仍必须将 fProtect 设置为有效的保护值例如 PAGE_NOACCESS。 如果使用 MEM_RESET并且内存范围映射到文件VirtualAllocEx 将返回错误。 仅当共享视图映射到分页文件时才可接受该视图。 MEM_RESET_UNDO 0x1000000 应 仅对之前成功应用MEM_RESET的地址范围调用 MEM_RESET_UNDO 。 它指示调用方对 lpAddress 和 dwSize 指定的指定内存范围中的数据感兴趣并尝试反转 MEM_RESET的影响。 如果该函数成功则表示指定地址范围中的所有数据都保持不变。 如果函数失败则至少将地址范围中的某些数据替换为零。 此值不能与任何其他值一起使用。 如果在之前未MEM_RESET的地址范围上调用MEM_RESET_UNDO则行为未定义。 指定 MEM_RESET时 VirtualAllocEx 函数将忽略 flProtect 的值。 但是仍必须将 flProtect 设置为有效的保护值例如 PAGE_NOACCESS。 Windows Server 2008 R2、Windows 7、Windows Server 2008、Windows Vista、Windows Server 2003 和 Windows XP 在 Windows 8 和 Windows Server 2012 之前不支持 MEM_RESET_UNDO 标志。
此参数还可以按指示指定以下值。 MEM_LARGE_PAGES 0x20000000 使用 大页支持分配内存。 大小和对齐方式必须是大页最小值的倍数。 若要获取此值请使用 GetLargePageMinimum 函数。 如果指定此值还必须指定 MEM_RESERVE 和 MEM_COMMIT。 MEM_PHYSICAL 0x00400000 保留可用于将 地址窗口扩展 (AWE) 页映射的地址范围。 此值必须与 MEM_RESERVE 一起使用不能与其他值一起使用。 MEM_TOP_DOWN 0x00100000 在可能的最高地址分配内存。 这可能比常规分配慢尤其是在有许多分配时。
flProtect
要分配的页面区域的内存保护。如果页面已提交则可以指定任何一个如下内存保护常量。
常量/值说明 PAGE_EXECUTE 0x10 启用对页面已提交区域的执行访问。 尝试写入已提交区域会导致访问冲突。CreateFileMapping 函数不支持此标志。 PAGE_EXECUTE_READ 0x20 启用对页面已提交区域的执行或只读访问。 尝试写入已提交区域会导致访问冲突。 Windows Server 2003 和 Windows XP 在 Windows XP SP2 和 Windows Server 2003 SP1 之前 CreateFileMapping 函数不支持此属性。 PAGE_EXECUTE_READWRITE 0x40 启用对已提交页面区域的执行、只读或读/写访问权限。 Windows Server 2003 和 Windows XP 在 Windows XP SP2 和 Windows Server 2003 SP1 之前 CreateFileMapping 函数不支持此属性。 PAGE_EXECUTE_WRITECOPY 0x80 启用对文件映射对象的映射视图的执行、只读或写入时复制访问权限。 尝试写入已提交的写入时复制页会导致为进程创建页面的专用副本。 专用页面标记为 PAGE_EXECUTE_READWRITE更改将写入新页面。VirtualAlloc 或 VirtualAllocEx 函数不支持此标志。 Windows Vista、Windows Server 2003 和 Windows XP 在具有 SP1 和 Windows Server 2008 的 Windows Vista 之前 CreateFileMapping 函数不支持此属性。 PAGE_NOACCESS 0x01 禁用对已提交页面区域的所有访问。 尝试从提交区域读取、写入或执行区域会导致访问冲突。CreateFileMapping 函数不支持此标志。 PAGE_READONLY 0x02 启用对已提交页面区域的只读访问。 尝试写入已提交区域会导致访问冲突。 如果启用了 数据执行防护 则尝试在已提交的区域中执行代码会导致访问冲突。 PAGE_READWRITE 0x04 启用对已提交页面区域的只读或读/写访问。 如果启用了 数据执行保护 则尝试在提交的区域中执行代码会导致访问冲突。 PAGE_WRITECOPY 0x08 启用对文件映射对象的映射视图的只读或写入时复制访问权限。 尝试写入已提交的写入时复制页会导致为进程创建页面的专用副本。 专用页面标记为 PAGE_READWRITE更改将写入新页面。 如果启用了 数据执行保护 则尝试在提交的区域中执行代码会导致访问冲突。VirtualAlloc 或 VirtualAllocEx 函数不支持此标志。 PAGE_TARGETS_INVALID 0x40000000 将页面中的所有位置设置为 CFG 的无效目标。 与任何执行页保护如 PAGE_EXECUTE、 PAGE_EXECUTE_READ、 PAGE_EXECUTE_READWRITE 和 PAGE_EXECUTE_WRITECOPY一起使用。 对这些页面中的位置的任何间接调用都将失败 CFG 检查并且进程将终止。 分配的可执行页面的默认行为是标记为 CFG 的有效调用目标。VirtualProtect 或 CreateFileMapping 函数不支持此标志。 PAGE_TARGETS_NO_UPDATE 0x40000000 当 VirtualProtect 的保护发生更改时区域中的页面将不会更新其 CFG 信息。 例如如果区域中的页面是使用 PAGE_TARGETS_INVALID 分配的则在页面保护更改时将保留无效信息。 仅当保护更改为可执行类型如 PAGE_EXECUTE、PAGE_EXECUTE_READ、PAGE_EXECUTE_READWRITE 和 PAGE_EXECUTE_WRITECOPY时此标志才有效。 VirtualProtect 保护更改为可执行文件的默认行为是将所有位置标记为 CFG 的有效调用目标。 如果lpAddress指定了一个地址则flProtect不能是以下值之一
PAGE_NOACCESS PAGE_GUARD PAGE_NOCACHE PAGE_WRITECOMBINE 返回值
成功返回分配页面的基址
失败返回NULL要获得更多的错误信息请调用 GetLastError。 WriteProcessMemory
在指定的进程中将数据写入内存区域要写入的整个区域必须可访问否则操作失败。函数声明如下 1 BOOL
2 WINAPI
3 WriteProcessMemory(
4 _In_ HANDLE hProcess,
5 _In_ LPVOID lpBaseAddress,
6 _In_reads_bytes_(nSize) LPCVOID lpBuffer,
7 _In_ SIZE_T nSize,
8 _Out_opt_ SIZE_T* lpNumberOfBytesWritten
9 ); 参数
hProcess
要修改的进程内存的句柄句柄必有具有 PROCESS_VM_WRITE和PROCESS_VM_OPERATION访问权限 lpBaseAddress:
指向指定进程中写入数据的基地址指针。在数据传输发生之前系统会验证指定大小的基地址和内存中的所有数据是否可以进行写入访问如果不可以访问则该函数将失败。 lpBuffer
指向缓冲区的指针其中包含要写入指定进程的地址空间中的数据。 nSize:
要写入指定进程的字节数。 lpNumberOfBytesWritten
指向变量的指针该变量接收传输到指定进程的字节数。如果lpNumberOfBytes Written为NULL则忽略该参数。 返回值
成功返回值不为0
失败返回值为0 CreateRemoteThread
在另一个进程的虚拟地址空间中创建运行的线程。声明如下 1 HANDLE2 WINAPI3 CreateRemoteThread(4 _In_ HANDLE hProcess,5 _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,6 _In_ SIZE_T dwStackSize,7 _In_ LPTHREAD_START_ROUTINE lpStartAddress,8 _In_opt_ LPVOID lpParameter,9 _In_ DWORD dwCreationFlags,
10 _Out_opt_ LPDWORD lpThreadId
11 ); 参数
hProcess
要创建线程的进程的句柄。句柄必须具有
PROCESS_CREATE_THREAD、
PROCESS_QUERY_INFORMATION、
PROCESS_VM_OPERATION、
PROCESS_VM_WRITE
PROCESS_VM_READ
访问权限 lpThreadAttributes:
指向SECURITY_ATTRIBUTES结构的指针该结构指定新线程的安全描述符并确定子进程是否可以继承返回的句柄。如果lpThreadAttributes为NULL则线程将获得默认的安全描述符并且不能继承该句柄。 dwStackSize
堆栈的初始大小以字节为单位。如果此参数为o则新线程使用可执行文件的默认大小。 lpStartAddress
指向由线程执﹐行类型为LPTHREAD_START_ROUTINE的应用程序定义的函数指针并表示远程进程中线程的起始地址该函数必须存在于远程进程中。 lpParameter:
指向要传递给线程函数的变量的指针。 dwCreationFlags
控制线程创建的标志。若是0则表示线程在创建后立即运行。 lpThreadId:
指向接收线程标识符的变量的指针。如果此参数为NULL则不返回线程标识符。 返回值
成功返回新线程的句柄
失败返回NULL如果要获取错误信息请调用GetLastError 远线程注入实现原理
程序在加载DLL时通常调用LoadLibrary函数来实现DLL的动态加载。LoadLibrary函数的声明如下
1 HMODULE WINAPI LoadLibrary(LPCTSTR lpFileName);
LoadLibrary函数只有一个参数传递的是要加载的DLL路径字符串。
而CreateRemoteThread需要传递的是目标进程空间中的多线程函数地址以及多线程参数。
如果程序能够获取目标进程LoadLibrary函数的地址而且还能够获取目标进程空间中某个DLL路径字符串的地址
那么可将LoadLibrary函数的地址作为多线程函数的地址某个DLL路径字符串作为多线程函数的参数并传递给CreateRemoteThread函数在目标进程空间中创建一个多线程这样能不能成功呢
答案是可以的。这样就可以在目标进程空间中创建一个多线程这个多线程就是LoadLibrary函数加载DLL。 远线程注入的原理就大致清晰了。那么要想实现远线程注入DLL还需要解决以下两个问题
1、是目标进程空间中LoadLibrary函数的地址是多少
2、是如何向目标进程空间中写入DLL路径字符串函数 对于第一个问题由于Windows引入了基址随机化ASLR ( Address Space Layout Randomization )安全机制所以导致每次开机时系统DLL的加载基址都不一样从而导致了DLL导出函数的地址也都不一样。 有些系统DLL(例如kernel32.dll、ntdll.dll )的加载基地址要求系统启动之后必须固定如果系统重新启动则其地址可以不同。 也就是说虽然进程不同但是开机后kernel32.dll的加载基址在各个进程中都是相同的因此导出函数的地址也相同。所以自己程序空间的LoadLibrary函数地址和其它进程空间的LoadLibrary函数地址相同。 对于第二个问题我们可以调用VirtualAllocEx函数在目标进程空间中申请一块内存然后再调用WriteProcessMemory函数将指定的DLL路径写入到目标进程空间中。 总结起来就是
在目标进程中创建一个线程线程会在创建后执行。
因为线程里的函数地址是LoadLibrary所以等于调用了LoadLibrary函数。
需要申请内存的原因是传递DLL的路径。 远线程注入实现步骤
1、调用OpenProcess函数打开进程获取进程句柄
2、调用VirtualAllocEx函数在需要注入的进程中申请内存空间
3、在申请的内存空间中写入DLL路径
4、获取LoadLibrary函数地址
5、调用CreateRemoteThread函数创建远线程函数基地址传LoadLibrary函数地址参数传申请的内存空间 完整实现代码
我们先创建一个dll工程 1 // dllmain.cpp : 定义 DLL 应用程序的入口点。2 #include framework.h3 #include Windows.h4 #include TlHelp32.h5 6 BOOL APIENTRY DllMain(HMODULE hModule,7 DWORD ul_reason_for_call,8 LPVOID lpReserved9 )
10 {
11 switch (ul_reason_for_call)
12 {
13 case DLL_PROCESS_ATTACH:
14 {
15 //TCHAR szBuffer[MAX_PATH]{};
16 //GetModuleFileName(hModule, szBuffer, 260);
17 MessageBox(NULL, LHello World, LInject Success, MB_OK);
18 }
19 break;
20 case DLL_THREAD_ATTACH:
21 case DLL_THREAD_DETACH:
22 case DLL_PROCESS_DETACH:
23 break;
24 }
25 return TRUE;
26 } 创建远线程注入代码如下 1 BOOL CreateRemoteThreadInject(DWORD dwPid, LPCTSTR pszDllFileName)2 {3 HANDLE hProcess OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);4 5 if (NULL hProcess)6 return FALSE;7 8 SIZE_T dwSize (1 lstrlen(pszDllFileName)) * sizeof(TCHAR);9 LPVOID lpDllAddr VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
10
11 if (NULL lpDllAddr)
12 {
13 CloseHandle(hProcess);
14 return FALSE;
15 }
16
17 BOOL bRet WriteProcessMemory(hProcess, lpDllAddr, pszDllFileName, dwSize, NULL);
18
19 if (!bRet)
20 {
21 CloseHandle(hProcess);
22 return FALSE;
23 }
24
25 LPVOID lpLoadLibraryFunc LoadLibraryW;
26
27 HANDLE hRemoteThread CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)lpLoadLibraryFunc, (LPVOID)lpDllAddr, 0, NULL);
28
29 if (NULL hRemoteThread)
30 {
31 CloseHandle(hProcess);
32 return FALSE;
33 }
34
35 //WaitForSingleObject(hRemoteThread, INFINITE);
36
37 CloseHandle(hProcess);
38 CloseHandle(hRemoteThread);
39 return TRUE;
40 } 我们打开记事本用任务管理器找到进程ID增加测试代码如下
将xxxx.dll路径改成前面dll工程输出dll的路径
1 int main()
2 {
3 CreateRemoteThreadInject(13764, LR(xxxxxx.dll));
4 } 运行效果 通过ProcessHacker工具可以看到notepad加载的模块里有RemoteThread_lib.dll 示例代码 参考资料
c - CreateRemoteThread() succeeds yet doesnt do anything - Stack Overflow