简单编写DLL文件:
#include <Windows.h>
extern "C" __declspec(dllexport) void MsgBox(LPCWSTR szMsg, LPCWSTR Title)
{
MessageBox(NULL, szMsg, Title, MB_OK);
}
BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
// 进程被加载后执行
break;
case DLL_THREAD_ATTACH:
// 线程被创建后加载
break;
case DLL_THREAD_DETACH:
// 正常退出执行的代码
break;
case DLL_PROCESS_DETACH:
// 进程卸载本Dll后执行的代码
break;
}
return TRUE;
}
#include <Windows.h>
#include <iostream>
typedef VOID(*PFUNMSG)(LPCWSTR szMsg, LPCWSTR Title);
int main(int argc, char *argv[])
{
HMODULE hModule = LoadLibrary("./hook.dll");
if (hModule != NULL)
{
PFUNMSG pMsgBox = (PFUNMSG)GetProcAddress(hModule, "MsgBox");
pMsgBox(L"hello lyshark", L"msgbox");
}
system("pause");
return 0;
}
x86 实现远程线程注入: 注入原理是利用了Windows系统中提供的CreateRemoteThread()
这个API函数,该函数第四个参数是准备运行的线程,我们将LoadLibrary()
函数填入其中,这样就可以执行远程进程中的LoadLibrary()
函数,进而将我们自己准备的DLL加载到远程进程空间中执行,DLL在被装载后则会自动执行初始化部分,X86注入代码如下.
#include <windows.h>
#include <stdio.h>
// 使用 CreateRemoteThread 实现远线程注入
BOOL CreateRemoteThreadInjectDll(DWORD Pid, char *DllName)
{
HANDLE hProcess = NULL;
SIZE_T dwSize = 0;
LPVOID pDllAddr = NULL;
FARPROC pFuncProcAddr = NULL;
// 打开注入进程,获取进程句柄
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Pid);
if (NULL == hProcess)
{
return FALSE;
}
// 计算欲注入 DLL 文件完整路径的长度
dwSize = sizeof(char)+lstrlen(DllName);
// 在目标进程申请一块长度为 nDllLen 大小的内存空间
pDllAddr = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
if (NULL == pDllAddr)
{
return FALSE;
}
// 将欲注入 DLL 文件的完整路径写入在目标进程中申请的空间内
if (FALSE == WriteProcessMemory(hProcess, pDllAddr, DllName, dwSize, NULL))
{
return FALSE;
}
// 获得 LoadLibraryA()函数的地址
pFuncProcAddr = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
if (NULL == pFuncProcAddr)
{
return FALSE;
}
// 使用 CreateRemoteThread 创建远线程, 实现 DLL 注入
HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFuncProcAddr, pDllAddr, 0, NULL);
if (NULL == hRemoteThread)
{
return FALSE;
}
// 关闭句柄
CloseHandle(hProcess);
return TRUE;
}
int main(int argc, char *argv[])
{
CreateRemoteThreadInjectDll(4668, "./x86.dll");
system("pause");
return 0;
}
x64 实现远程线程注入: 如果想要注入X64程序,则需要在编译时指定为64位编译模式,并且使用LoadLibraryW()
来加载动态链接库,我们只需要在上面代码的基础上稍加改进就可以实现64位进程的注入了.
#include <windows.h>
#include <stdio.h>
// 使用 CreateRemoteThread 实现远线程注入
BOOL CreateRemoteThreadInjectDll(DWORD Pid, PCWSTR DllName)
{
BOOL ret = FALSE;
HANDLE hProcess, hThread = NULL;
FARPROC pfnThreadRtn = NULL;
PWSTR pwszPara = NULL;
// 打开注入进程,获取进程句柄
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Pid);
if (NULL == hProcess)
{
return FALSE;
}
// 计算欲注入 DLL 文件完整路径的长度
size_t iProxyFileLen = wcslen(DllName) * sizeof(WCHAR);
// 在目标进程申请一块长度为 nDllLen 大小的内存空间
pwszPara = (PWSTR)VirtualAllocEx(hProcess, NULL, iProxyFileLen, MEM_COMMIT, PAGE_READWRITE);
if (NULL == pwszPara)
{
return FALSE;
}
// 将欲注入 DLL 文件的完整路径写入在目标进程中申请的空间内
if (FALSE == WriteProcessMemory(hProcess, pwszPara, (PVOID)DllName, iProxyFileLen, NULL))
{
return FALSE;
}
// 获得 LoadLibraryW()函数的地址
pfnThreadRtn = GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW");
if (NULL == pfnThreadRtn)
{
return FALSE;
}
// 使用 CreateRemoteThread 创建远线程, 实现 DLL 注入
hThread = CreateRemoteThread(hProcess, NULL, 1024, (LPTHREAD_START_ROUTINE)pfnThreadRtn, pwszPara, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
if (NULL != hThread)
{
CloseHandle(hThread);
CloseHandle(hProcess);
VirtualFreeEx(hProcess, pwszPara, 0, MEM_RELEASE);
return TRUE;
}
return FALSE;
}
int main(int argc, char *argv[])
{
CreateRemoteThreadInjectDll(8224, L"./x64.dll");
system("pause");
return 0;
}
实现普通消息钩子注入: Windows提供的钩子类型非常多,其中一种类型的钩子非常实用,那就是WH_GETME SSAGE
钩子,它可以很方便地将DLL文件注入到所有的基于消息机制的目标程序中,代码非常简单,这里直接给出DLL文件的代码,具体如下:
#include <windows.h>
extern "C" __declspec(dllexport) VOID SetHookOn();
extern "C" __declspec(dllexport) VOID SetHookOff();
HHOOK g_HHook = NULL;
HINSTANCE g_hInst = NULL;
VOID DoSomeThing()
{
MessageBoxA(0, "hello lyshark", 0, 0);
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
{
g_hInst = hinstDLL;
DoSomeThing();
break;
}
}
return TRUE;
}
LRESULT CALLBACK GetMsgProc(int code,WPARAM wParam,LPARAM lParam)
{
return CallNextHookEx(g_HHook, code, wParam, lParam);
}
VOID SetHookOn()
{
g_HHook = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, g_hInst, 0);
}
VOID SetHookOff()
{
UnhookWindowsHookEx(g_HHook);
}
实现全局消息钩子注入: 该注入的原理是利用系统中的SetWindowHookEx()
这个API函数,该函数可以拦截目标进程的消息到指定DLL中的导出函数上,利用这个特性,我们可以将DLL注入到全局进程中,但是在使用SetWindowsHookEx()
之前首先需要将HOOK的DLL加载到本身的进程中,以此得到DLL的模块句柄,再使用GetProcAddress()
得到DLL中公开的函数地址,最后遍历出待注入进程的线程ID,这样SetWindowHookEx()
就可以利用这些参数进行HOOK了.
我们先来编写DLL文件,创建Dll工程hook.cpp
然后将SetHook()
函数导出,由于该注入方式是全局注入,所以如果我们想要注入到指定进程中,则需要在DllMain()
也就是动态链接库开头位置进行判断,如果是我们需要Hook的进程,则加载Dll到指定进程中,如果不是则不执行任何操作,这样一来即可实现指定进程注入.
#include <windows.h>
HHOOK Global_Hook;
// 设置全局消息回调函数
LRESULT CALLBACK MyProc(int nCode, WPARAM wParam, LPARAM lParam)
{
return CallNextHookEx(Global_Hook, nCode, wParam, lParam);
}
// 安装全局钩子,此处的hook.dll可以是外部其他的dll
extern "C" __declspec(dllexport) void SetHook()
{
Global_Hook = SetWindowsHookEx(WH_CBT, MyProc, GetModuleHandle(TEXT("hook.dll")), 0);
}
// 卸载全局钩子
extern "C" __declspec(dllexport) void UnHook()
{
if(Global_Hook)
UnhookWindowsHookEx(Global_Hook);
}
bool APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
HWND hwnd = FindWindowW(L"valve001",NULL);
DWORD pid;
GetWindowThreadProcessId(hwnd, &pid);
if (GetCurrentProcessId() == pid)
{
MessageBox(hwnd, TEXT("hello lyshark"), 0, 0);
}
return true;
}
调用代码:注意必须将上方编译好的hook.dll
与下方工程放到同一个目录下,通过LoadLibrary()
函数获取到模块句柄,然后通过GetProcAddress()
获取到导出函数地址,并通过函数指针调用,由于全局注入依赖于父进程,所以下面的代码必须一直运行.
#include <windows.h>
int main(int argc, char *argv[])
{
HMODULE hMod = LoadLibrary(TEXT("hook.dll"));
typedef void(*pSetHook)(void);
pSetHook SetHook = (pSetHook)GetProcAddress(hMod, "SetHook");
SetHook();
while (1)
{
Sleep(1000);
}
return 0;
}
APC应用层异步注入: APC 是异步过程调用,在Windows下每个线程在可被唤醒时在其APC链中的函数将有机会执行被执行,每一个线程都具有一个APC链,那么只要在可以在APC链中添加一个APC,就可以完成我们所需要的DLL注入的功能.
1.将需要加载的DLL的完整路径写入目标进程空间.
2.获得LoadLibraryA()函数的地址,当然也可以是LoadLibraryW()函数的地址.
3.枚举目标进程中的所有线程,为每个线程添加一个APC函数,这样增加了注入成功的机会.
该注入的原理是利用当线程被唤醒时APC中的注册函数会被执行的机制,并以此去执行我们的DLL加载代码,进而完成DLL注入的目的,通过APC注入的流程步骤大致如下
1.当EXE里某个线程执行到
SleepEx()
或者WaitForSingleObjectEx()
时,系统就会产生一个软中断.
2.当线程再次被唤醒时,此线程会首先执行APC队列中的被注册的函数.
3.利用QueueUserAPC()
这个API可以在软中断时向线程的APC队列插入一个函数指针,如果我们插入的是Loadlibrary()
执行函数的话,就能达到注入DLL的目的,不论如何目标程序必须有执行SleepEx()
或者WaitForSingleObjectEx()
否则DLL不会加载.
#include <windows.h>
#include <tlhelp32.h>
#include <stdio.h>
// APC注入
BOOL ApcInjectDll(DWORD dwPid, char * szDllName)
{
// 计算欲注入 DLL 文件完整路径的长度
int nDllLen = lstrlen(szDllName) + sizeof(char);
// 打开目标进程
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE, dwPid);
if (hProcess == NULL)
{
return FALSE;
}
// 在目标进程申请一块长度为 nDllLen 大小的内存空间
PVOID pDllAddr = VirtualAllocEx(hProcess,NULL, nDllLen,MEM_COMMIT,PAGE_READWRITE);
if (pDllAddr == NULL)
{
CloseHandle(hProcess);
return FALSE;
}
DWORD dwWriteNum = 0;
// 将欲注入 DLL 文件的完整路径写入在目标进程中申请的空间内
WriteProcessMemory(hProcess, pDllAddr, szDllName,nDllLen, &dwWriteNum);
CloseHandle(hProcess);
THREADENTRY32 te = { 0 };
te.dwSize = sizeof(THREADENTRY32);
//得到线程快照
HANDLE handleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (INVALID_HANDLE_VALUE == handleSnap)
{
CloseHandle(hProcess);
return FALSE;
}
// 获得 LoadLibraryA()函数的地址
FARPROC pFunAddr = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
DWORD dwRet = 0;
//得到第一个线程
if (Thread32First(handleSnap, &te))
{
do
{
//进行进程 ID 对比
if (te.th32OwnerProcessID == dwPid)
{
//得到线程句柄
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS,FALSE,te.th32ThreadID);
if (hThread)
{
//向线程插入 APC
dwRet = QueueUserAPC((PAPCFUNC)pFunAddr,hThread,(ULONG_PTR)pDllAddr);
//关闭句柄
CloseHandle(hThread);
}
}
//循环下一个线程
} while (Thread32Next(handleSnap, &te));
}
CloseHandle(handleSnap);
return TRUE;
}
int main(int argc, char *argv[])
{
ApcInjectDll(9608, "c:/x86.dll");
system("pause");
return 0;
}
ZwCreateThreadEx强力注入: 在前面的注入方式中,我们使用了CreateRemoteThread()
这个函数来完成线程注入,此方式可以注入普通的进程,但却无法注入到系统进程中,因为系统进程是处在SESSION0高权限级别的会话层.
由于CreateRemoteThread()
底层会调用ZwCreateThreadEx()
这个未公开的内核函数,所以我们必须手动调用ZwCreateThread()
这一内核函数,将第七个参数设置为0即可,ZwCreateThreadEx函数在ntdll.dll中并未声明,所以必须手动使用GetProcAddress函数将其地址导出.
#include <windows.h>
#include <stdio.h>
// 使用 ZwCreateThreadEx 实现远线程注入
BOOL ZwCreateThreadExInjectDll(DWORD dwProcessId, char * pDllName)
{
HANDLE hProcess = NULL;
SIZE_T dwSize = 0;
LPVOID pDllAddr = NULL;
FARPROC pFuncProcAddr = NULL;
HANDLE hRemoteThread = NULL;
DWORD dwStatus = 0;
// 打开注入进程,获取进程句柄
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
if (NULL == hProcess)
{
return FALSE;
}
// 在注入进程中申请内存
dwSize = sizeof(char)+lstrlen(pDllName);
pDllAddr = ::VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
if (NULL == pDllAddr)
{
return FALSE;
}
// 向申请的内存中写入数据
if (FALSE == ::WriteProcessMemory(hProcess, pDllAddr, pDllName, dwSize, NULL))
{
return FALSE;
}
// 加载 ntdll.dll
HMODULE hNtdllDll = ::LoadLibrary("ntdll.dll");
if (NULL == hNtdllDll)
{
return FALSE;
}
// 获取LoadLibraryA函数地址
pFuncProcAddr = ::GetProcAddress(::GetModuleHandle("Kernel32.dll"), "LoadLibraryA");
if (NULL == pFuncProcAddr)
{
return FALSE;
}
// 获取ZwCreateThread函数地址
#ifdef _WIN64
typedef DWORD(WINAPI *typedef_ZwCreateThreadEx)(
PHANDLE ThreadHandle,
ACCESS_MASK DesiredAccess,
LPVOID ObjectAttributes,
HANDLE ProcessHandle,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
ULONG CreateThreadFlags,
SIZE_T ZeroBits,
SIZE_T StackSize,
SIZE_T MaximumStackSize,
LPVOID pUnkown);
#else
typedef DWORD(WINAPI *typedef_ZwCreateThreadEx)(
PHANDLE ThreadHandle,
ACCESS_MASK DesiredAccess,
LPVOID ObjectAttributes,
HANDLE ProcessHandle,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
BOOL CreateSuspended,
DWORD dwStackSize,
DWORD dw1,
DWORD dw2,
LPVOID pUnkown);
#endif
typedef_ZwCreateThreadEx ZwCreateThreadEx = (typedef_ZwCreateThreadEx)::GetProcAddress(hNtdllDll, "ZwCreateThreadEx");
if (NULL == ZwCreateThreadEx)
{
return FALSE;
}
// 使用 ZwCreateThreadEx 创建远线程, 实现 DLL 注入
dwStatus = ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, hProcess,
(LPTHREAD_START_ROUTINE)pFuncProcAddr, pDllAddr, 0, 0, 0, 0, NULL);
if (NULL == hRemoteThread)
{
return FALSE;
}
// 关闭句柄
::CloseHandle(hProcess);
::FreeLibrary(hNtdllDll);
return TRUE;
}
int main(int argc, char *argv[])
{
BOOL bRet = ZwCreateThreadExInjectDll(2940, "hook.dll");
system("pause");
return 0;
}
强制卸载进程中的DLL:
#include <Windows.h>
#include <stdio.h>
#include <TlHelp32.h>
BOOL UnLoad_Module(DWORD dwPID, LPCTSTR szDllName)
{
BOOL bMore = FALSE, bFound = FALSE;
HANDLE hSnapshot, hProcess, hThread;
HMODULE hModule = NULL;
MODULEENTRY32 me = { sizeof(me) };
LPTHREAD_START_ROUTINE pThreadProc;
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID);
bMore = Module32First(hSnapshot, &me);
for (; bMore; bMore = Module32Next(hSnapshot, &me))
{
if (!_tcsicmp((LPCTSTR)me.szModule, szDllName) || !_tcsicmp((LPCTSTR)me.szExePath, szDllName))
{
bFound = TRUE;
break;
}
}
if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)))
return FALSE;
hModule = GetModuleHandle(L"kernel32.dll");
pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "FreeLibrary");
hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, me.modBaseAddr, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
if (hThread != 0)
{
CloseHandle(hThread);
CloseHandle(hProcess);
CloseHandle(hSnapshot);
return TRUE;
}
return FALSE;
}
int main(int argc, char *argv[])
{
UnLoad_Module(2012, "lyshark.dll");
system("pause");
return 0;
}
纯汇编实现远程Dll注入:
.data
szMyDll db "\lyshark.dll",0h ; 要注入的DLL
szDllKernel db "Kernel32.dll",0h
szLoadLibrary db "LoadLibraryA",0h
lpFileName db "Tutorial-i386",0h ; 指定要注入进程
lpDllName dd ?
.data?
szMyDllFull db MAX_PATH dup (?)
lpLoadLibrary dd ?
dwProcessID dd ?
dwThreadID dd ?
hProcess dd ?
.code
main PROC
; 准备工作:获取dll的全路径文件名、获取LoadLibrary函数地址等
invoke GetCurrentDirectory,MAX_PATH,addr szMyDllFull
invoke lstrcat,addr szMyDllFull,addr szMyDll
invoke GetModuleHandle,addr szDllKernel
invoke GetProcAddress,eax,offset szLoadLibrary
mov lpLoadLibrary,eax
; 查找文件管理器窗口并获取进程ID,然后打开进程
invoke FindWindow,NULL,addr lpFileName
invoke GetWindowThreadProcessId,eax,offset dwProcessID
mov dwThreadID,eax
invoke OpenProcess,PROCESS_ALL_ACCESS,FALSE,dwProcessID
mov hProcess,eax
; 在进程中分配空间并将DLL文件名拷贝过去,然后创建一个LoadLibrary线程
invoke VirtualAllocEx,hProcess,NULL,MAX_PATH,MEM_COMMIT,PAGE_READWRITE
mov lpDllName,eax
invoke WriteProcessMemory,hProcess,eax,offset szMyDllFull,MAX_PATH,NULL
invoke CreateRemoteThread,hProcess,NULL,0,lpLoadLibrary,lpDllName,0,NULL
ret
main endp
end main