第23章:DLL注入
使用LoadLibrary()API加载某个Dll时,Dll会被加载到进程,然后会自动运行DllMain()函数.
OS会将已注册的Dll直接注入目标进程,这也是上一章能成功的原因.
Dll注入主要使用以下三种方法:
1.创建远程线程 CreatRemoteThread().
2.使用注册表( AppInit_DLLs 值 ).
3.消息钩取 SetWindowsHookEx().
第一种是:创建远程线程
下面的代码来自 https://www.52pojie.cn/thread-1142368-1-1.html ,自己做了一些补充修改
// InjectDll.cpp #include "windows.h" #include "tchar.h" BOOL InjectDll(DWORD dwPID, LPCTSTR szDllPath) //myhack.dll 的路径被作为参数传入. { HANDLE hProcess = NULL, hThread = NULL; HMODULE hMod = NULL; LPVOID pRemoteBuf = NULL; //确定路径需要占用的缓冲区大小 DWORD dwBufSize = (DWORD)(_tcslen(szDllPath) + 1) * sizeof(TCHAR); //求Unicode字符个数 LPTHREAD_START_ROUTINE pThreadProc; // #1. 使用OpenProcess函数获取目标进程句柄(PROCESS_ALL_ACCESS权限) if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) ) //通过PID,获得待注入进程的句柄 { _tprintf(L"OpenProcess(%d) failed!!! [%d]\n", dwPID, GetLastError()); return FALSE; } // #2. 使用VirtualAllocEx函数在目标进程中分配内存,大小为 dwBufSize 字节 // VirtualAllocEx函数返回的是hProcess指向的目标进程的分配所得缓冲区的内存地址 pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE); // #3. 将myhack.dll路径 ("c:\\..../myhack.dll")写入目标进程中分配到的内存 WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllPath, dwBufSize, NULL); // #4. 获取LoadLibraryA() API的地址 // 这里主要利用了kernel32.dll文件在每个进程中的加载地址都相同这一特点,所以不管是获取加载到 // InjectDll.exe还是notepad.exe进程的kernel32.dll中的LoadLibraryW函数的地址都是一样的。这里的加载地 // 址相同指的是在同一次系统运行中,如果再次启动系统kernel32.dll的加载地址会变,但是每个进程的 // kernerl32.dll的加载地址还是一样的。
// 系统Dll首次进入内存后,Windows为减少不必要的加载,会直接将其它进程系统Dll映射到要加载的进程中.
hMod = GetModuleHandle(L"kernel32.dll"); pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryW"); // #5. 在目标进程notepad.exe中运行远程线程 // pThreadProc = notepad.exe进程内存中的LoadLibraryW()地址 // pRemoteBuf = notepad.exe进程内存中待加载注入dll的路径字符串的地址 hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL); //使得可以在 hprocess 中执行 pThreadProc 线程,参数是 pRemoteBuf . WaitForSingleObject(hThread, INFINITE); //同样,记得关闭句柄 CloseHandle(hThread); CloseHandle(hProcess); return TRUE; } int _tmain(int argc, TCHAR *argv[]) // 文件名(default)、pid、Dll路径 一共三个参数 { if( argc != 3) { _tprintf(L"USAGE : %s <pid> <dll_path>\n", argv[0]); return 1; } // change privilege if( !SetPrivilege(SE_DEBUG_NAME, TRUE) ) //获得权限 return 1; // inject dll if( InjectDll((DWORD)_tstol(argv[1]), argv[2]) ) //调用Dll函数 _tprintf(L"InjectDll(\"%s\") success!!!\n", argv[2]); else _tprintf(L"InjectDll(\"%s\") failed!!!\n", argv[2]); return 0; }
// myhack.cpp
#include "windows.h"
#include "tchar.h" //解决兼容字符集,国际化字符集
#pragma comment(lib, "urlmon.lib") //引入静态库
#define DEF_URL (L"http://www.naver.com/index.html")
#define DEF_FILE_NAME (L"index.html")
HMODULE g_hMod = NULL; //实例句柄
DWORD WINAPI ThreadProc(LPVOID lParam) //WINAPI == _stdcall
{
TCHAR szPath[_MAX_PATH] = {0,};
if( !GetModuleFileName( g_hMod, szPath, MAX_PATH ) ) //获取当前进程已加载模块的文件的完整路径
return FALSE; //g_hMod在DllMain中以被赋值
TCHAR *p = _tcsrchr( szPath, '\\' ); //返回第一次在字符串中出现的该字符的指针,如果要查找的字符再串中没有出现,则返回NULL
if( !p )
return FALSE;
_tcscpy_s(p+1, _MAX_PATH, DEF_FILE_NAME); //参数准备,完善下载文件的绝对地址
URLDownloadToFile(NULL, DEF_URL, szPath, 0, NULL); //调用函数进行URL下载
return 0;
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) //LPVOID -> 任意类型
{
HANDLE hThread = NULL;
g_hMod = (HMODULE)hinstDLL; //HMODULE表示模块句柄。代表应用程序载入的模块,win32系统下通常是被载入模块的线性地址。
//HINSTANCE 在win32下与HMODULE是相同的东西
switch( fdwReason )
{
case DLL_PROCESS_ATTACH :
OutputDebugString(L"<myhack.dll> Injection!!!");
//创建远程线程进行download,调用 ThreadProc 函数
hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
// 需要注意,切记随手关闭句柄,保持好习惯
CloseHandle(hThread);
break;
}
return TRUE;
}
调试时,先打开notepad.exe程序,在OD中附加该程序,按F9跑起来,并且选中在DLL入口处断住.然后在cmd中加参数运行InjectDll.exe程序.
在OD中继续按F9直到看见myhack.dll载入.
第二种是修改注册表.
在注册表编辑器中,将要注入的DLL的路径字符写入AppInint_DLLs项目,然后把LoadAppInint_DLLs的值设置为1.重启后指定DLL会注入所有运行进程.
// myhack2.cpp #include "windows.h" #include "tchar.h" #define DEF_CMD L"c:\\Program Files\\Internet Explorer\\iexplore.exe" #define DEF_ADDR L"http://www.naver.com" #define DEF_DST_PROC L"notepad.exe" BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { TCHAR szCmd[MAX_PATH] = {0,}; TCHAR szPath[MAX_PATH] = {0,}; TCHAR *p = NULL; STARTUPINFO si = {0,}; PROCESS_INFORMATION pi = {0,}; si.cb = sizeof(STARTUPINFO); si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; switch( fdwReason ) { case DLL_PROCESS_ATTACH : if( !GetModuleFileName( NULL, szPath, MAX_PATH ) ) break; if( !(p = _tcsrchr(szPath, '\\')) ) //返回最后一次在字符串中出现的该字符的指针,如果要查找的字符再串中没有出现,则返回NULL break; if( _tcsicmp(p+1, DEF_DST_PROC) ) break; wsprintf(szCmd, L"%s %s", DEF_CMD, DEF_ADDR); //将数据写入缓冲区szCmd if( !CreateProcess(NULL, (LPTSTR)(LPCTSTR)szCmd, //LPCTSTR == 判断字符是否为Unicode NULL, NULL, FALSE, //LPSTR == 指向字符或字符串的指针 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi) ) //以参数 DEF_ADDR 打开 explore 程序 break; if( pi.hProcess != NULL ) CloseHandle(pi.hProcess); break; } return TRUE; }
重启系统后就可以自动加载