远程线程注入DLL
远程线程注入
0x00 前言
远程线程注入是一种经典的DLL注入技术。其实就是指一个新进程中另一个进程中创建线程的技术。
0x01 介绍
1.远程线程注入原理
画了一个图大致理解了下远程线程注入dll的原理。
如果是实现注入dll的话,流程大致就是:
通过OpenProcess获取目标进程句柄。
通过VirtualAllocEx在目标进程空间中申请内存,通过WriteProcessMemory放入需要载入的dll的路径。
通过GetModuleHandleA获取诸如kernel32.dll这类系统dll的模块句柄,进而获取LoadLibraryA这类载入动态链接库的函数地址(固定)
通过CreateRemoteThread的参数传入目标进程对象句柄、写入到目标进程空间的dll路径、LoadLibraryA函数地址,实现中目标中创建多线程加载dll。
2.函数介绍
OpenProcess 函数
打开现有的本地进程对象。
函数声明
HANDLE WINAPI OpenProcess( _In_ DWORD dwDesiredAccess, _In_ BOOL bInheritHandle, _In_ DWORD dwProcessId);
参数
- dwDesiredAccess [in]
访问进程对象。此访问权限针对进程的安全描述符进行检查。此参数可以是一个或多个进程访问权限。如果调用该函数的进程启用了SeDebugPrivilege权限,则无论安全描述符的内容如何,都会授予所请求的访问权限。- bInheritHandle [in]
若此值为TRUE,则此进程创建的进程将继承该句柄。否则,进程不会继承此句柄。- dwProcessId [in]
要打开的本地进程的标识符。
如果指定的进程是系统进程(0x00000000),则该函数失败,最后一个错误代码为ERROR_INVALID_PARAMETER。如果指定的进程是空闲进程或CSRSS进程之一,则此功能将失败,并且最后一个错误代码为ERROR_ACCESS_DENIED,因为它们的访问限制会阻止用户级代码打开它们。
如果您使用GetCurrentProcessId作为此函数的参数,请考虑使用GetCurrentProcess而不是OpenProcess,以提高性能。返回值
- 如果函数成功,则返回值是指定进程的打开句柄。
- 如果函数失败,返回值为NULL。 要获取扩展错误信息,请调用GetLastError。
VirtualAllocEx 函数
在指定进程的虚拟地址空间内保留,提交或更改内存区域的状态。 该函数初始化其分配给零的内存。
函数声明
LPVOID WINAPI VirtualAllocEx( _In_ HANDLE hProcess, _In_opt_ LPVOID lpAddress, _In_ SIZE_T dwSize, _In_ DWORD flAllocationType, _In_ DWORD flProtect);
参数
- hProcess [in]
过程的句柄。该函数在该进程的虚拟地址空间内分配内存。
句柄必须具有PROCESS_VM_OPERATION权限。有关更多信息,请参阅流程安全和访问权限。- lpAddress [in]
指定要分配的页面的所需起始地址的指针。
如果您正在保留内存,则该函数会将该地址舍入到分配粒度的最接近的倍数。
如果您提交已经保留的内存,该功能会将该地址舍入到最接近的页面边界。要SESSION 0 隔离页面的大小和主机上的分配粒度,请使用GetSystemInfo函数。
如果lpAddress为NULL,则该函数确定在哪里分配该区域。- dwSize [in]
要分配的内存大小,以字节为单位。
如果lpAddress为NULL,则函数将dwSize循环到下一个页面边界。
如果lpAddress不为NULL,则该函数将从lpAddress到lpAddress + dwSize的范围内分配包含一个或多个字节的所有页面。这意味着,例如,跨越页面边界的2字节范围会导致功能分配两个页面。- flAllocationType [in]
内存分配类型。此参数必须包含以下值之一:
VALUE MEANING MEM_COMMIT 为指定的预留内存页分配内存费用(从磁盘上的内存和分页文件的总体大小)。 该函数还保证当调用者稍后初次访问存储器时,内容将为零。 除非/直到虚拟地址被实际访问,实际的物理页面才被分配 MEM_RESERVE 保留进程的虚拟地址空间的范围,而不会在内存或磁盘上的分页文件中分配任何实际物理存储 MEM_RESET 表示由lpAddress和dwSize指定的内存范围内的数据不再受关注。 页面不应从页面文件中读取或写入页面文件。 然而,内存块将在以后再次被使用,所以不应该被分解。 该值不能与任何其他值一起使用 MEM_RESET_UNDO 只能在早期成功应用了MEM_RESET的地址范围上调用MEM_RESET_UNDO。 它指示由lpAddress和dwSize指定的指定内存范围内的数据对呼叫者感兴趣,并尝试反转MEM_RESET的影响。 如果功能成功,则表示指定地址范围内的所有数据都是完整的。 如果功能失败,地址范围中的至少一些数据已被替换为零
- flProtect [in]
要分配的页面区域的内存保护。 如果页面被提交,您可以指定任何一个内存保护常量。
如果lpAddress指定了一个地址,flProtect不能是以下值之一:
PAGE_NOACCESS
PAGE_GUARD
PAGE_NOCACHE
PAGE_WRITECOMBINE返回值
- 如果函数成功,则返回值是分配的页面区域的基址。
- 如果函数失败,返回值为NULL。 要获取扩展错误信息,请调用GetLastError。
WriteProcessMemory 函数
在指定的进程中将数据写入内存区域。 要写入的整个区域必须可访问或操作失败。
函数声明
BOOL WINAPI WriteProcessMemory( _In_ HANDLE hProcess, _In_ LPVOID lpBaseAddress, _In_ LPCVOID lpBuffer, _In_ SIZE_T nSize, _Out_ SIZE_T *lpNumberOfBytesWritten);
参数
- hProcess [in]
要修改的进程内存的句柄。 句柄必须具有PROCESS_VM_WRITE和PROCESS_VM_OPERATION访问进程。- lpBaseAddress [in]
指向写入数据的指定进程中的基地址的指针。 在数据传输发生之前,系统会验证指定大小的基地址和内存中的所有数据是否可以进行写入访问,如果不可访问,则该函数将失败。- lpBuffer [in]
指向缓冲区的指针,其中包含要写入指定进程的地址空间的数据。- nSize [in]
要写入指定进程的字节数。- lpNumberOfBytesWritten [out]
指向变量的指针,该变量接收传输到指定进程的字节数。 此参数是可选的。 如果lpNumberOfBytesWritten为NULL,则忽略该参数。返回值
- 如果函数成功,则返回值不为零。
- 如果函数失败,返回值为0(零)。 要获取扩展错误信息,请调用GetLastError。
CreateRemoteThread 函数
创建在另一个进程的虚拟地址空间中运行的线程。
使用CreateRemoteThreadEx函数创建在另一个进程的虚拟地址空间中运行的线程,并可选地指定扩展属性。函数声明
HANDLE WINAPI CreateRemoteThread( _In_ HANDLE hProcess, _In_ LPSECURITY_ATTRIBUTES lpThreadAttributes, _In_ SIZE_T dwStackSize, _In_ LPTHREAD_START_ROUTINE lpStartAddress, _In_ LPVOID lpParameter, _In_ DWORD dwCreationFlags, _Out_ LPDWORD lpThreadId);
参数
- hProcess [in]
要创建线程的进程的句柄。 句柄必须具有PROCESS_CREATE_THREAD,PROCESS_QUERY_INFORMATION,PROCESS_VM_OPERATION,PROCESS_VM_WRITE和PROCESS_VM_READ访问权限,如果某些平台上没有这些权限,可能会失败。 有关更多信息,请参阅流程安全和访问权限。- lpThreadAttributes [in]
指向SECURITY_ATTRIBUTES结构的指针,该结构指定新线程的安全描述符,并确定子进程是否可以继承返回的句柄。 如果lpThreadAttributes为NULL,则线程将获得默认安全描述符,并且该句柄不能被继承。 线程的默认安全描述符中的访问控制列表(ACL)来自创建者的主令牌。dwStackSize [in]
堆栈的初始大小,以字节为单位。 系统将此值循环到最近的页面。 如果此参数为0(零),则新线程使用可执行文件的默认大小。 有关更多信息,请参阅线程堆栈大小。- lpStartAddress [in]
指向由线程执行的类型为LPTHREAD_START_ROUTINE的应用程序定义函数的指针,并表示远程进程中线程的起始地址。 该功能必须存在于远程进程中。 有关更多信息,请参阅ThreadProc。- lpParameter [in]
指向要传递给线程函数的变量的指针。- dwCreationFlags [in]
控制线程创建的标志。若是 0,则表示线程在创建后立即运行。- lpThreadId [out]
指向接收线程标识符的变量的指针。
如果此参数为NULL,则不返回线程标识符。返回值
- 如果函数成功,则返回值是新线程的句柄。
- 如果函数失败,返回值为NULL。 要获取扩展错误信息,请调用GetLastError。
0x02 编码实现
测试demo,指定进程pid和dll位置。
TestCreatePId.cpp
// TestCreatePId.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <iostream>
void InjectDLL(DWORD PID, char* Path)
{
DWORD dwSize;
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, PID);
dwSize = strlen(Path) + 1;
LPVOID lpParamAddress = VirtualAllocEx(hProcess, 0, dwSize, PARITY_SPACE, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(hProcess, lpParamAddress, (PVOID)Path, dwSize, NULL);
HMODULE hModule = GetModuleHandleA("kernel32.dll");
LPTHREAD_START_ROUTINE lpStartAddress = (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "LoadLibraryA");
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, lpStartAddress, lpParamAddress, 0, NULL);
WaitForSingleObject(hThread, 1000);
CloseHandle(hThread);
}
int main(int argc,char *argv[])
{
char* a = argv[2];
//std::cout << atoi(argv[1]) << a <<std::endl;
InjectDLL(int(argv[1]),a);
//InjectDLL(atoi(argv[1]), "c:\\test\\TestDll.dll");
return 0;
}
0x03 实测
经测试对应位数的dll可以加载到对应位数的进程中。
不过遇到个问题,这里也记录下,当想在同一进程载入两个dll,如果先载入的dll有dllmain中,里面的函数没有走完逻辑,那么第二个dll会在线程挂起结束之后载入到进程。比如我载入到第一个dll里面DLL_PROCESS_ATTACH有个MessageBox,我没给他结束窗口,第二个dll没有加载到进程中,而结束了MessageBox窗口才注入到了进程。
0x04 总结
部分进程需要以管理员权限才可注入到进程,具体原因是OpenProcess打开高权限进程会因权限不足无法打开,其实就是权限继承到原因,这里如果以powershell默认启动就会获得SE_DEBUG权限,而进程令牌权限提升准备再挑一篇文章单独总结学习。
另一个问题就是,不能成功注入到一些系统服务的进程,因为系统存在 SESSION 0 隔离。如果想向系统服务进程中注入,需要突破SESSION 0 隔离,这个也准备另启一篇文章做记录。
0x05 参考
https://www.write-bug.com/article/2011.html
https://www.cnblogs.com/LyShark/p/11066063.html
远程线程注入DLL的更多相关文章
- 远程线程注入DLL突破session 0 隔离
远程线程注入DLL突破session 0 隔离 0x00 前言 补充上篇的远程线程注入,突破系统SESSION 0 隔离,向系统服务进程中注入DLL. 0x01 介绍 通过CreateRemoteTh ...
- 详细解读:远程线程注入DLL到PC版微信
一.远程线程注入的原理 1.其基础是在 Windows 系统中,每个 .exe 文件在双击打开时都会加载 kernel32.dll 这个系统模块,该模块中有一个 LoadLibrary() 函数,可以 ...
- 远程线程注入dll,突破session 0
前言 之前已经提到过,远线程注入和内存写入隐藏模块,今天介绍突破session 0的dll注入 其实今天写这个的主要原因就是看到倾旋大佬有篇文章提到:有些反病毒引擎限制从lsass中dump出缓存,可 ...
- CreateRemoteThread远程线程注入Dll与Hook
CreateRemoteThread虽然很容易被检测到,但是在有些场合还是挺有用的.每次想用的时候总想着去找以前的代码,现在在这里记录一下. CreateRemoteThread远程注入 DWORD ...
- 【windows核心编程】使用远程线程注入DLL
前言 该技术是指通过在[目标进程]中创建一个[远程线程]来达到注入的目的. 创建的[远程线程]函数为LoadLibrary, 线程函数的参数为DLL名字, 想要做的工作在DLL中编写. 示意图如下: ...
- 微信 电脑版 HOOK(WeChat PC Hook)- 远程线程注入dll原理
Windows加载dll的特性 1.Windows系统中,每个exe软件运行的时候,会加载系统模块kernel32.dll 2.所有加载进exe软件的系统模块kernel32.dll,内存地址都是一样 ...
- 安全之路 —— 借助DLL进行远程线程注入实现穿墙与隐藏进程
简介 大多数后门或病毒要想初步实现隐藏进程,即不被像任务管理器这样典型的RING3级进程管理器找到过于明显的不明进程,其中比较著名的方法就是通过远程线程注入的方法注入将恶意进程的DLL文 ...
- 安全之路 —— 无DLL文件实现远程线程注入
简介 在之前的章节中,笔者曾介绍过有关于远程线程注入的知识,将后门.dll文件注入explorer.exe中实现绕过防火墙反弹后门.但一个.exe文件总要在注入时捎上一个.dll文件着 ...
- Dll注入技术之远程线程注入
DLL注入技术之远线程注入 DLL注入技术指的是将一个DLL文件强行加载到EXE文件中,并成为EXE文件中的一部分,这样做的目的在于方便我们通过这个DLL读写EXE文件内存数据,(例如 HOOK EX ...
随机推荐
- MSTP
目录 一.生成树存在的问题 二.MSTP 三.MSTP的网络层次 四.MSTP的端口状态 五.MSTP的保护功能 一.生成树存在的问题 STP和RSTP的问题 PVST的问题 二.MSTP 多生成树 ...
- Promise/A+规范-翻译
Promise 表示一个异步操作的最终结果,与之进行交互的方式主要是 then 方法,该方法注册了两个回调函数,用于接收 promise 的终值或本 promise 不能执行的原因. 本规范详细列出了 ...
- 针对Cloud-init的可行性报告
by hyc 针对Cloud-init的可行性报告 一.Cloud-init研究进展: (1)ubuntu镜像: 已在版本为ubuntu-server-14.04-amd64上实现了修改主机名和用户密 ...
- 害...原来阿里面试Redis最常问的是它呀
一提到Redis缓存,我们不得不了解的三个问题就是:缓存雪崩.缓存击穿和缓存穿透.这三个问题一旦发生,会导致大量的请求直接请求到数据库层.如果并发压力大,就会导致数据库崩溃.那p0级的故障是没跑了. ...
- MySQL-03-基础管理
用户和权限管理 用户管理 作用:登录,管理数据库逻辑对象 定义:用户名@'白名单' 白名单支持的方式 wordpress@'10.0.0.%' wordpress@'%' wordpress@'10. ...
- NOIP 模拟 $24\; \rm graph$
题解 \(by\;zj\varphi\) 首先一个点能否选择的条件是 \(dis_{1,x}+dis_{x,n}=dis_{1,n}\) 正解是计算一条道路上的所有为 \(-1\) 边的选择范围,是个 ...
- net start mongodb 提示:发生系统错误 5,拒绝访问。
问题: net start mongodb 提示:发生系统错误 5,拒绝访问. 无法启动mongodb 服务. 解决办法: 右键cmd,选择以管理员身份运行即可
- Spring-Boot-Bean的使用,@Repository,@Service,@Controller,@Component
前言 在Spring MVC的时候,我们使用xml来配置bean,如今的Spring boot推荐我们使用元注解的发生,那就听Spring Boot的推荐,下面我就为大家来介绍下Spring Boot ...
- mfc HackerTools全局钩子
钩子英文名叫Hook,是一种截获windows系统中某应用程序或者所有进程的消息的一种技术. 如在键盘中按下一键,操作系统将收到键按下消息,把消息放入消息队列,然后消息队列对消息进行派发,发给相应的应 ...
- 大数据Shell编程 之 常用正则表达式
使用man grep查看grep的帮助文档,有如下内容: 可以看出,正则表达式由三类,分别是 基本的正则表达式(Basic Regular Expression 又叫 Basic RegEx 简称 B ...