第二十三章 DLL注入

DLL注入,是指向运行中的其他进程强制插入特定的DLL文件。常用于渗透其他进程,DLL注入可以实现API钩取、改写程序、修复BUG等。


可以看到,notepad.exe进程本来是会加载myhack.dll的,但由于我们强制注入了myhack.dll,所以现在他会加载myhack.dll。

DLL注入实验

接下来书本上进行了一个DLL注入的实例:

首先我们准备一个notepad文件(记事本),这将是我们进行注入的文件:

和一个DLL文件(此DLL的作用是下载一个.html)已经将DLL注入进notepad的EXE文件:

首先我们打开notepad文件,利用process explorer查看notepad文件的PID:

打开cmd,输入指令:'''InjectDLL.exe 1580 c:\work\myhack.dll'''

cmd显示我们注册成功,然后我们在process explorer上查看dll是否被注入成功,在View菜单中,选择Show Lower PaneLower Pane Views - DLLs项。就可以看到:

可以看到myhack.dll已经被成功加载进去了。然后我们打开dll所在文件夹,查看url是否被成功下载。

这样,就说明我们DLL注入成功了。

接下来我们来看看dll文件和exe文件的代码:

// 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)
{
    TCHAR szPath[_MAX_PATH] = { 0, };

if (!GetModuleFileName(g_hMod, szPath, MAX_PATH))
    return FALSE;

TCHAR* p = _tcsrchr(szPath, '\\');
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)
{
    HANDLE hThread = NULL;

g_hMod = (HMODULE)hinstDLL;

switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
    OutputDebugString(L"<myhack.dll> Injection!!!");

    //创建远程线程进行download
    hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);

    // 需要注意,切记随手关闭句柄,保持好习惯
    CloseHandle(hThread);
    break;
}

return TRUE;

}

我是使用vs2019进行编译的,在创建新项目的时候,我选择的是“动态链接库(DLL)”

vs不知道从哪个本开始,会加入一个预编译头,例如这里的“pch.h”的头文件:

这个头文件,会存放一些使用者预设的一些代码。

这里如果我们删除这个头文件,则会报错提示你加入头文件:

但如果加入了这个头文件,则会报错提示很多变量找不到标识符:

解决方案就是在项目——>属性中进行更改:

在属性的预编译头中选择不使用预编译头,就可以了

然后是exe文件:

// InjectDll.cpp
#include "windows.h"
#include "tchar.h"

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. 使用OpenProcess函数获取目标进程句柄(PROCESS_ALL_ACCESS权限)
    if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)))
    {
        _tprintf(L"OpenProcess(%d) failed!!! [%d]\n", dwPID, GetLastError());
        return FALSE;
    }
    
    // #2. 使用VirtualAllocEx函数在目标进程中分配内存,大小为szDllName
          // 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的加载地址还是一样的。
    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);
    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;
    }

    // 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;

}

这一部分代码,我在编译时并没有遇到问题,编译平台也是VS2019

接下来是利用OD调试myhack.dll:

首先我们打开一个未注入myhack.dll的notepad:

然后利用OD的attach功能,将notepad(附加)加载进OD(注意,OD只能加载32的程序,我这里是64位系统,所以使用Xdbg进行调试)。

notepad加载进来后,会首先暂停,这里我们在选项中进行设置:

打开DLL加载,这样程序在加载DLL时,OD(Xdbg,虽然使用的是Xdbg,但在后面我都会用OD记录)就会自动断下来。

接着我们打开InjectDLL.exe,将myhack.dll加载进notepad,回车的瞬间,OD断了下来。

这里OD断下来的,不是myhack.dll,因为在加载myhack.dll之前,会先加载其他的dll,所以我们只需要F9运行到myhack.dll就行: