《逆向工程核心原理》学习笔记: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

《逆向工程核心原理》学习笔记:64位 & Windows 内核6

《逆向工程核心原理》学习笔记:API钩取