DLL注入
DLL注入
DLL(Dynamic Linked Library动态链接库)被加载到进程后会自动运行DllMain()函数,用户可以把想执行的代码放到DllMain()函数,每当加载DLL时,添加的代码就会自然而然得到执行。
DLL注入是指向运行中的其他进程强制插入特定的DLL文件。其工作原理是从外部促使目标进程调用LoadLibrary() API从而强制执行DLL的DllMain()函数,而且被注入的DLL拥有目标进程内存的访问权限,用户可以随意操作。与一般的DLL加载的区别在于,DLL注入加载的目标进程是其自身或其他进程。
DllMain()函数即DLL文件的入口函数:
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpvReserved){
switch( dwReason ){
case DLL_PROCESS_ATTACH:
//添加想执行的代码
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
DLL注入主要应用于:改善功能与修复Bug、消息钩取、API钩取、恶意代码、监视和管理PC用户的应用程序等。
DLL注入的实现方法:
1、创建远程线程(CreateRemoteThread() API
2、使用注册表(AppInit_DLLs值):User32.dll加载时会读取AppInit_DLLs,若有值则调用LoadLibrary() API来加载DLL。
3、消息钩取(SetWindowsHookEx() API)
创建远程线程实现DLL注入
编写将要注入notepad.exe进程的myhack.dll文件,该DLL文件用于联网访问本博客首页(https://blog.csdn.net/ski_12)并下载该网页内容保存为本地文件。
myhack.cpp
//myhack.cpp
#include "windows.h"
//Unicode编码时,tchar为uchar(双字符);ANSI编码时,tchar为char
#include "tchar.h"
//表示链接urlmon.lib这个库
#pragma comment(lib, "urlmon.lib")
//定义URL和文件名等常量
#define DEF_URL (L"https://blog.csdn.net/ski_12")
#define DEF_FILE_NAME (L"SKI12.html")
HMODULE g_hMod = NULL;
//实现下载指定URL内容
DWORD WINAPI ThreadProc(LPVOID lParam){
TCHAR szPath[_MAX_PATH] = {0,};
//获取当前进程已加载模块的文件的完整路径
if( !GetModuleFileName(g_hMod, szPath, MAX_PATH) ){
return FALSE;
}
//_tcsrchr:查找字符串中某个字符最后一次出现的位置
TCHAR *p = _tcsrchr(szPath, '\\');
if( !p ){
return FALSE;
}
//_tcscpy_s:字符拷贝函数,若是UNICODE编码则采用wcscpy_s()函数,若是多字节编码则采用strcpy_s()函数
_tcscpy_s(p + 1, _MAX_PATH, DEF_FILE_NAME);
//下载URL内容到本地文件
URLDownloadToFile(NULL, DEF_URL, szPath, 0, NULL);
return 0;
}
//DLL文件入口函数
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved){
HANDLE hThread = NULL;
g_hMod = (HMODULE)hinstDLL;
switch( fdwReason ){
//当DLL被加载时执行
case DLL_PROCESS_ATTACH:
//输出Debug信息
OutputDebugString(L"[!]myhack.dll插入成功.");
//创建线程调用ThreadProc
hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
//关闭线程句柄
CloseHandle(hThread);
break;
}
return TRUE;
}
在DllMain()函数中可以看到,该DLL被加载时(DLL_PROCESS_ATTACH),先输出一个调试字符串,再创建线程调用函数(ThreadProc)。在ThreadProc()函数中通过调用urlmon!URLDownloadToFile() API下载指定网站的文件。
InjectDll.cpp
//InjectDll.cpp
#include "windows.h"
#include "tchar.h"
//查看设置权限
BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege){
TOKEN_PRIVILEGES tp;
HANDLE hToken;
LUID luid;
//获取进程Token
if( !OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken) ){
_tprintf(L"[-]OpenProcessToken error: %u\n", GetLastError());
return FALSE;
}
//查看本地系统的权限值
if ( !LookupPrivilegeValue(NULL, lpszPrivilege, &luid) ){
_tprintf(L"[-]LookupPrivilegeValue error: %u\n", GetLastError() );
return FALSE;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
if( bEnablePrivilege ){
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
}else{
tp.Privileges[0].Attributes = 0;
}
//启用特权或禁用所有特权
if( !AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES) NULL, (PDWORD)NULL) ){
_tprintf(L"[-]AdjustTokenPrivileges error: %u\n", GetLastError() );
return FALSE;
}
if( GetLastError() == ERROR_NOT_ALL_ASSIGNED ){
_tprintf(L"[-]The token does not have the specified privilege. \n");
return FALSE;
}
return TRUE;
}
//实现DLL注入
BOOL InjectDll(DWORD dwPID, LPCTSTR szDllPath){
HANDLE hProcess = NULL;
HANDLE hThread = NULL;
HMODULE hMod = NULL;
LPVOID pRemoteBuf = NULL;
DWORD dwBufSize = (DWORD)(_tcslen(szDllPath) + 1) * sizeof(TCHAR);
LPTHREAD_START_ROUTINE pThreadProc;
//使用dwPID获取目标进程句柄
//得到PROCESS_ALL_ACCESS权限后便可以使用获取的句柄控制对应的进程
if( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) ){
_tprintf(L"[-]OpenProcess(%d) failed!!! [%d]\n", dwPID, GetLastError());
return FALSE;
}
//在目标进程hProcess内存中分配dwBufSize大小的内存空间
pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE);
//将DLL文件路径写入内存
WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllPath, dwBufSize, NULL);
//先获取模块句柄,再从句柄中获取LoadLibraryW() API的地址
//LoadLibraryW()是LoadLibrary()的Unicode字符串版本
//Win OS中,kernel32.dll在每个进程中的加载地址是一致的,故不用特意获取加载到目标进程的kernel32.dll的LoadLibraryW() API地址而直接加载本身的即可
hMod = GetModuleHandle(L"kernel32.dll");
pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryW");
//令目标进程hProcessd调用CreateRemoteThread()从而调用LoadLibrary()
//其中线程函数ThreadProc()与LoadLibrary()两者形态结构一致,即LoadLibrary()可当做线程函数来执行
hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
//关闭线程和句柄
CloseHandle(hThread);
CloseHandle(hProcess);
return TRUE;
}
//_tmain为支持Unicode所使用的main的一个别名,宏定义在tchar.h,经编译即为main
int _tmain(int argc, TCHAR *argv[]){
if(argc != 3){
_tprintf(L"[*]Usage : %s <pid> <dll_path>\n", argv[0]);
return 1;
}
//查看设置权限
if( !SetPrivilege(SE_DEBUG_NAME, TRUE) ){
return 1;
}
//注入DLL
if( InjectDll((DWORD)_tstol(argv[1]), argv[2]) ){
_tprintf(L"[+]Inject DLL (\"%s\") success!!!\n", argv[2]);
}else{
_tprintf(L"[-]Inject DLL (\"%s\") failed!!!\n", argv[2]);
}
return 0;
}
main()函数主要检查输入的参数,然后调用InjectDll()函数。InjectDll()函数用于命令目标进程调用LoadLibrary(“myhack.dll”)以实现DLL注入。
先打开notepad.exe程序,打开DebugView程序,打开Process Explorer查看notepad.exe程序的PID值,本次测试的PID值为1460。然后cmd运行InjectDll.exe:
可以在cmd看到注入DLL成功,同时,在DebugView中查看到注入成功的Debug输出信息。
切换到Process Explorer查看notepad.exe程序,可以看到notepad.exe程序被注入了myhack.dll:
接着到放置InjectDll.exe和myhack.dll的目录中查看,可以发现在本地生成了SKI12.html文件,打开查看是保存的博客首页的内容:
下面开始调试。
运行notepad.exe,然后使用Ollydbg2 Attach该进程:
再F9使其运行起来。
接着设置如下选项,即每当有新的DLL加载到notepad.exe进程时,都会在该DLL的EP处暂停:
接着使用InjectDll.exe注入myhack.dll,查看Ollydbg情况:
分析可知,调试器暂停的地方并不是myhack.dll的EP而是名为iertutil.dll模块的EP,因为在加载myhack.dll之前需要先加载它导入的所有DLL文件。不断F9直至到myhack.dll的EP处暂停:
使用注册表AppInit_DLLs实现DLL注入
Windows OS的注册表中默认提供了AppInt_DLLs和LoadAppInit DLLs两个注册表项。
在注册表编辑器中,将要注入的DLL的路径字符串写入AppInit_DLLs项目,然后把LoadAppInit DLLs的项目值设置为1。重启后,指定DLL会注入所有运行进程。
工作原理:user32.dll被加载到进程时,会读取AppInit_DLLs注册表项,若有值,则调用LoadLibrary() API加载用户DLL。严格的说,相应DLL并不会被加载到所有进程,而只是加载至加载user32.dll的进程。另外,Windows XP会忽略LoadAppInit DLLs注册表项。
myhack2.dll
//myhack2.cpp
#include "windows.h"
#include "tchar.h"
//定义常量
#define DEF_CMD L"C:\\Program Files\\Internet Explorer\\\iexplore.exe"
#define DEF_ADDR L"https://blog.csdn.net/ski_12"
#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, '\\')) ){
break;
}
if( _tcsicmp(p+1, DEF_DST_PROC) ){
break;
}
wsprintf(szCmd, L"%s %s", DEF_CMD, DEF_ADDR);
//打开IE进程
if( !CreateProcess(NULL, (LPTSTR)(LPCTSTR)szCmd, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi) ){
break;
}
if( pi.hProcess != NULL ){
CloseHandle(pi.hProcess);
}
break;
}
return TRUE;
}
若当前加载的进程为notepad.exe,则以隐藏模式运行IE,连接指定网站。
将myhack2.dll文件放入测试的目录中,然后运行regedit.exe修改AppInit_DLLs表项的值为myhack2.dll的绝对路径:
接着修改LoadAppInit_DLLs注册表项的值为1:
再重启系统,使修改生效。
重启之后,使用Process Explorer查看myhack2.dll是否被注入所有加载user32.dll的进程中:
可以看到,myhack2.dll成功注入到所有加载user32.dll的进程中,但由于该DLL文件的目标是notepad.exe进程,因而其它进程不会执行任何操作。
运行notepad.exe,可以看到IE被以隐藏模式执行:
消息钩取实现DLL注入
和《Windows消息钩取》那篇博客一样,这里buzai
上一篇: Dll注入