之前有介绍过HOOK的方式注入,这次介绍以其它方式注入,而无须HOOK,要知道在Windows这个浩荡的海洋里,API就是宝藏,找到足够多的宝藏那么你就是海贼王~!
实现思路如下:
首先打开一个进程的地址空间,然后开辟一块空间将动态库copy进去,然后找到动态库要调用的函数首地址,在调用此函数!
首先给大家介绍几个dll注入的方法:
1.通过调用Kernel32动态库里的LoadLibraryW函数来加载dll到指定进程下,然后在创建远线程!
这是最常用的一个方法,但是很遗憾这种方法可用性并不大,因为在Win8和Win10以上你会发现可以创建远线程的进程没有几个,Windows内核里已经放弃这个API了,因为这个API本身就不是ring0级别的代码,Windows代代更新内核之后就已经不在支持这个API了!
那么来介绍一下实现这个方法的API:
有些API的原型我在其他文章里已经介绍过,这里就不重复介绍,有兴趣的可以去翻一下:
1.首先第一步使用FindWindow得到窗口句柄,
2.在使用OpenProcess打开窗口进程,但是需要获取窗口的PID,
3.所以要先使用GetWindowThreadProcessId获取PID,
4.在使用VirtualAllocEx开辟内存,开辟内存大小是要注入dll绝对路径的长度,
5.然后在使用WriteProcessMemory函数写入路径字符,
6.在使用GetProcAddress获取Kernel32里的LoadLibraryW函数地址!
7.在使用CreateRemoteThread函数将LoadLibraryW加载到指定进程下,然后调用传递参数
在此之前需要给大家介绍一下CreateRemoteThread,GetProcAddress以及GetModuleHandle这三个API:
1.CreateRemoteThread
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 结构的指针, 该结构指定了线程的安全属性.
dwStackSize [in]
线程初始大小,以字节为单位,如果该值设为0,那么使用系统默认大小.
lpStartAddress [in]
在远程进程的地址空间中,该线程的线程函数的起始地址.
lpParameter [in]
传给线程函数的参数.
dwCreationFlags [in]
线程的创建标志.
值 含义
0 线程创建后立即运行
CREATE_SUSPENDED 线程创建后先将线程挂起,直到 ResumeThread 被调用.
0x00000004
STACK_SIZE_PARAM_IS_A_RESERVATION dwStackSize 参数指定为线程栈预订大小,如果STACK_SIZE_PARAM_IS_A_RESERVATION没有被指定,dwStackSize 参数指定为线程栈分配大小.
0x00010000
lpThreadId [out]
用来存放创建线程后的线程ID,如果线程创建成功,那么线程ID会被存放到这个变量里,一般不需要给NULL,如果创建失败则给NULL!
返回值:
成功返回非0否则可以使用GetLastError获取错误码
2.GetProcAddress
FARPROC GetProcAddress(
HMODULE hModule, // DLL模块句柄
LPCSTR lpProcName // 函数名
);
返回值:
如果函数调用成功,返回值是DLL中的输出函数地址。
如果函数调用失败,返回值是NULL。得到进一步的错误信息,调用函数GetLastError。
3.GetModuleHandle
HMODULE WINAPI GetModuleHandle(
_In_opt_LPCTSTR lpModuleName
);
lpModuleName String,指定模块名,这通常是与模块的文件名相同的一个名字。例如,NOTEPAD.EXE程序的模块文件名就叫作NOTEPAD。
返回值:
HMODULE,如执行成功成功,则返回模块句柄。零表示失败。获取错误信息,请调用
GetLastError。
其次可以使用
来映射指定模块
实现代码如下:
#include "windows.h"
int main()
{
//程序的窗口句柄
HWND hWnd = FindWindow(NULL, "123");
if (hWnd == NULL) {
MessageBox(NULL, "无法打开指定进程", "Error", 0);
}
//根据窗口句柄获取进程PID
DWORD Process_ID = (DWORD)0;
GetWindowThreadProcessId(hWnd, &Process_ID); //PID这里传址
if (Process_ID == (DWORD)0) { //判断是否与原值相同
MessageBox(NULL, "无法获取进程PID", "Error", 0);
}
//打开进程
HANDLE hPro = OpenProcess(PROCESS_CREATE_THREAD | //允许远程创建线程
PROCESS_VM_OPERATION | //允许远程VM操作
PROCESS_VM_WRITE,//允许远程VM写
FALSE, Process_ID);
if (hPro == NULL) {
MessageBox(NULL, "无法打开进程空间", "Error", 0);
}
// 在远程进程中为路径名称分配空间
char str[256] = { 0 };
strcpy_s(str,"d:\\3.dll"); //你的dll路径
int dwSize = (lstrlenA(str)+1); //路径大小+1是因为\0
//开辟空间
LPVOID pszLibFileRemote = (PWSTR)VirtualAllocEx(hPro, NULL, dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (pszLibFileRemote == NULL) {
MessageBox(NULL, "无法开辟内存!", "Error", 0);
return -1;
}
//写入数据
DWORD dwWritten; //写入字节数
DWORD n = WriteProcessMemory(hPro, (LPTHREAD_START_ROUTINE)pszLibFileRemote, "d:\\4.dll", dwSize, &dwWritten);
if (n == NULL) {
MessageBox(NULL, "无法写入内存!", "Error", 0);
return -1;
}
//Kernel32模块已经被包含在依赖库里自动加载进来了,所以我们可以直接获取
PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryA");
if (pfnThreadRtn == NULL) {
MessageBox(NULL, "无法获取dll库函数", "Error", 0);
return -1;
}
//创建远线程
DWORD dwID = NULL;
HANDLE hThread = CreateRemoteThread(hPro, 0, 0, pfnThreadRtn, pszLibFileRemote, 0, &dwID);
if (hThread == NULL) {
MessageBox(NULL, "无法创建远线程!", "Error", 0);
printf("%d", GetLastError());
}
//等待线程结束
WaitForSingleObject(hThread, INFINITE);
return 0;
}
这里有个程序博主已经写好的了:
dll代码:
#include <windows.h>
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
MessageBox(NULL, "wed", "wed", 0);
return TRUE;
}
运行结果:
可以看到已经成功注入到指定窗口下了,而且信息框也是在指定窗口下弹出的!
这里有几个加载问题给大家解释一下:
GetProcAddress是获取当前模块下指定函数的地址,注意是当前进程下的,那么为什么CreateRemoteThread
是创建远程函数应该给远程函数的地址才对,为什么是本地下的?
答:
这是Windows的一套机制,在Windows上运行的话会附加一系列的依赖库,这些依赖库在加载到内存中会被存放在
进程的空间下,但不是乱放的,是有序加载的,而且是通过模块表来记录每个模块的内存位置,而这些顺序在不同的操作系统下可能不同(xp以下是在注册表里的,可以通过修改注册表的方式来加载自己的dll,但在xp以上就不可行了
xp注册表装载dll方式:
打开
HKEY_LOCAL_MACHINE\SoftWare\MicroSoft\Windows NT\CurrentVersion\Windows\
找到:
AppInit_Dlls健值,在里面添加上你的dll绝对路径,用“,”号分开
)
但是GetProcAddress可以很好的帮我们从模块表里找到我们需要的地址,然后返回给我们,然后我们在给它偏移量,最后CreateRemoteThread
在创建的时候会根据句柄找到对应的段地址,在GDT表-LRT表里找到对应的物理地址在偏移量相加即成为每个程序的模块函数地址!
这样的方法仅在win7以下有效,win7以上需要提权!但Win10以后的系统程序无法注入dll!
并且调用LoadLibraryW函数会注册你的dll模块到程序中的模块表里,所以可以被第三方查询出来是否有可疑dll注入!
当然注入方法不止这一种你可以调用其它的创建线程函数来创建远线程!