DLL注入是一种将自身代码附加到目标进程中运行的一种技术,通过让目标进程主动加载指定的DLL,执行DllMain函数从而执行预期的代码,主要用于隐藏代码,绕过一些防御措施,控制目标程序的代码逻辑。DLL注入有非常多种方式,本篇演示远程线程注入DLL的方式。
远程线程注入是一种很‘古老’,但使用最为频繁使用的一种方式。通过函数CreateRemoteThread的方式在对方进程中开启一个线程执行代码。
首先我们先制作一个DLL,弹出一个窗口代表注入成功,并且通过事件通知注入者注入已经完毕。DllMain函数在进程、线程,加载、卸载DLL的时候被调用。返回FALSE后DLL被卸载,返回TRUE时DLL会被驻留在内存中等待FreeLibrary将其释放。
BOOL WINAPI DllMain(
HINSTANCE hinstDLL,
DWORD fdwReason,
LPVOID lpReserved)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
{
MessageBox(NULL, _T("注入成功"), _T("注入成功"), MB_ICONINFORMATION);
HANDLE hEvt = OpenEvent(EVENT_ALL_ACCESS, FALSE, GLOBAL_EVENT);
if(hEvt)
SetEvent(hEvt);
break;
}
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return FALSE;
}
注入代码流程:
1. 打开进程句柄
2. 获得LoadLibraryA函数地址
3. 分配一块可读写的内存空间
4. 将所需DLL的路径写入内存
5. 通过远程线程执行LoadLibraryA函数,并且指定参数为DLL路径的内存地址
首先打开进程句柄
OpenProcess函数声明:
WINBASEAPI
HANDLE
WINAPI
OpenProcess(
_In_ DWORD dwDesiredAccess,//访问权限
_In_ BOOL bInheritHandle, //是否继承此句柄至子进程
_In_ DWORD dwProcessId //进程ID
);
我们需要分配内存、内存写入、创建远程线程,因此需要
PROCESS_CREATE_THREAD、PROCESS_VM_WRITE、PROCESS_VM_OPERATION 权限打开进程
hProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_WRITE | PROCESS_VM_OPERATION , FALSE, dwProcessId)
随后获得LoadLibraryA的地址
如果与目标进程之间因某种保护导致kernel32.dll的基址不一致时,可使用枚举进程模块地址的方式加上函数的RVA获得对方进程中的LoadLibraryA函数的地址,这里假设kernel32.dll基址一致。
通过LoadLibrary函数加载kernel32.dll库并从导出函数中找到LoadLibraryA的函数地址,这看上去像是套娃,最终是获得LoadLibraryA的地址,那为什么不直接&LoadLibrary的形式直接获取呢。原因是通过后者的方法获得的实际上不是LoadLibrary的地址,而是一个跳转到LoadLibrary函数指令的一个自身的代码地址。当然同样也可以通过运算e9跳转指令来获得跳转的目标地址,也就是LoadLibrary的地址。这里追求简单直接通过GetProcAddress获得:
hKernel = LoadLibrary(_T("kernel32.dll"));
LoadLibraryAddress = GetProcAddress(hKernel, "LoadLibraryA");
在对方进程中分配一块内存
VirtualAllocEx函数声明:
LPVOID //如果成功则是远程内存地址,失败会为NULL
WINAPI
VirtualAllocEx(
_In_ HANDLE hProcess, //进程句柄
_In_opt_ LPVOID lpAddress, //操作的目标地址(使用随机地址分配填NULL)
_In_ SIZE_T dwSize, //分配大小 必须是内存页大小的整倍数(一般为0x1000)
_In_ DWORD flAllocationType,//操作类型,保留或者提交,这里不保留
_In_ DWORD flProtect //内存属性,可读可写
);
在目标进程中分配一块0x1000大小可读可写的内存。
Address = VirtualAllocEx(hProcess, NULL, 0x1000, MEM_COMMIT, PAGE_READWRITE);
随后通过WriteProcessMemory向分配的内存中写入DLL全路径。
WriteProcessMemory函数声明:
BOOL //是否成功
WINAPI
WriteProcessMemory(
_In_ HANDLE hProcess, //进程句柄
_In_ LPVOID lpBaseAddress, //进程内存地址
_In_reads_bytes_(nSize) LPCVOID lpBuffer, //写入的数据基地址
_In_ SIZE_T nSize, // 数据大小
_Out_opt_ SIZE_T* lpNumberOfBytesWritten //成功写入数据量
);
DLL_NAME为欲注入的DLL全路径
WriteProcessMemory(hProcess, Address, DLL_NAME, sizeof(DLL_NAME), &dwWriteBytes);
随后通过CreateRemoteThread函数创建远程线程
CreateRemoteThread函数声明:
HANDLE //线程句柄
WINAPI
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 //返回线程ID
);
创建一个入口点为LoadLibraryA函数并且参数为欲注入DLL的地址。默认线程安全性,默认栈大小。
CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibraryAddress, Address, 0, &dwThreadId)
随后远程线程被创建,执行DllMain后弹出提示窗口
完整代码:
注入代码:
int main()
{
BOOLEAN isSuccess = FALSE;
DWORD dwProcessId;
HANDLE hProcess = NULL;
HMODULE hKernel = NULL;
HANDLE hLoadEvt = NULL;
PVOID Address = NULL;
printf("输入注入对象进程ID:");
scanf_s("%d", &dwProcessId);
do {
hProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, dwProcessId);
if (!hProcess)
{
printf("无法打开进程%x,因为%d错误n", dwProcessId, GetLastError());
break;
}
hKernel = LoadLibrary(_T("kernel32.dll"));
if (!hKernel)
{
printf("打开kernel32.dll失败!n");
break;
}
PVOID LoadLibraryAddress = GetProcAddress(hKernel, "LoadLibraryA");
if (!LoadLibraryAddress)
{
printf("获得函数地址失败!n");
break;
}
Address = VirtualAllocEx(hProcess, NULL, 0x1000, MEM_COMMIT, PAGE_READWRITE);
if (!Address)
{
printf("分配远程内存失败!n");
break;
}
SIZE_T dwWriteBytes;
if (!WriteProcessMemory(hProcess, Address, DLL_NAME, sizeof(DLL_NAME), &dwWriteBytes))
{
printf("写内存失败!n");
break;
}
hLoadEvt = CreateEvent(NULL, FALSE, FALSE, GLOBAL_EVENT);
if (!hLoadEvt)
{
printf("创建事件失败!n");
break;
}
DWORD dwThreadId;
if (!CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibraryAddress, Address, 0, &dwThreadId))
{
printf("创建远程线程失败!n");
break;
}
printf("创建成功!n");
if (!WaitForSingleObject(hLoadEvt, INFINITE));
isSuccess = TRUE;
} while (FALSE);
if (!isSuccess)
printf("错误%dn", GetLastError());
if (hLoadEvt)
CloseHandle(hLoadEvt);
if (hProcess)
{
if (Address)
VirtualFreeEx(hProcess, Address, 0, MEM_RELEASE);
CloseHandle(hProcess);
}
if (hKernel)
FreeLibrary(hKernel);
return 0;
}
本文始发于微信公众号(锋刃科技):DLL注入--远程线程注入
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论