DLL注入

DLL注入指的是向运行中的其它进程强制插入特定的DLL文件。从技术细节来说,DLL注入命令其他进程自行调用LoadLibrary() API,加载(Loading)用户指定的DLL文件。DLL注入与一般DLL加载的区别在于。加载的目标进程是其自身或其它进程。
WN1N8C7845RQSPTRXJII(`W.png
myhack.dll已被强制插入notepad进程,加载到notepad.exe进程中的myhack.dll与已经加载到notepad.exe进
程中的DLL(kernel32.dll、user32.dll)一样,拥有访问notepad.exe进程内存的权限,这样用户就可以做任何想做的事了。(比如:向notepad添加通信功能以实现Messenger、文本网络浏览器等)
DLL(Dynamlc Linked Library,动态链接库)
DLL被加载到进程后会自动运行DllMain()函数,用户可以把想执行的代码放到DllMain()函数,每当加载DLL时添加的代码就会自然而然得到执行。利用该特性可以修复程序Bug,或向程序添加新功能。

  1. BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpvReserved)
  2. {
  3. switch( dwReason )
  4. {
  5. case DLL_PROCESS_ATTACH:
  6. //添加想执行的代码
  7. break;
  8. case DLL_THREAD_ATTACH:
  9. break;
  10. case DLL_THREAD_DETACH:
  11. break;
  12. case DLL_PROCESS_DETACH:
  13. break;
  14. }
  15. return TRUE;
  16. }

消息钩取

Windows OS默认提供的消息钩取功能应用的就是一种DLL注入技术。与常规的DLL注入唯一的区别是,OS会直接将已注册的钩取DLL注入目标进程。

API钩取

进行API钩取时经常使用DLL注入技术。先创建好DLL形态的钩取函数,再将其轻松注入要钩取的目标进程,这样就完成了API钩取。这是灵活运用了“被注入的DLL拥有目标进程内存访问权限”这一特性。

DLL注入的实现方法

想某个进程注入DLL时主要使用以下三种方法:
#创建远程线程(CreateRemoteThread()API)
#使用注册表(AppInit_DLLs值)
#消息钩取(SetWindowsHookEx()API)

CreateRemoteThread()

分析示例源码

Myhack.cpp

#include "windows.h"
#include "tchar.h"

#pragma comment(lib, "urlmon.lib")

#define DEF_URL         (L"http://www.naver.com/index.html")//韩国naver网站
#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);

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

在DllMain()函数中可以看到,该DLL被加载(DLL_PROCESS_ATTACH)时,先输出一个调试字符串(“myhack.dll Injection!!!”)然后创建线程调用函数(ThreadProc)。在ThreadProc()函数中通过调用urlmon!URLDownloadToFile()API来下载指定网站的index.html文件。向进程注入DLL后就会调用执行该DLL的DllMain()函数。所以当myhack.dll注入notepad.exe进程后,最终会调用执行URLDownloadToFile()API。
InjectDll.cpp
该程序用来将myhack.dll注入notepad.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;

//使用dwpid获取目标进程句柄
    if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)))
    {
        _tprintf(L"OpenProcess(%d) failed!!![%d]\n", dwPID, GetLastError());
        return FALSE;
    }
    //在目标进程内存中分配szDllname大小的内存
    pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE);//分配物理存储,可读可写
    //将myhack.dll路径写入分配的内存。
    WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllPath, dwBufSize, NULL);
    //获取LoadLibraryW API的地址
    hMod = GetModuleHandle(L"Kernel32.dll");//获取已经加载模块的句柄
    pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryW");//获取函数地址

    //在目标进程中运行线程
    hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL);//创建远程线程
    _tprintf(L"%d", GetLastError());
    WaitForSingleObject(hThread, INFINITE);
    CloseHandle(hProcess);
    return TRUE;
}
int _tmain(int argc, TCHAR *argv[])
{
    if (argc != 3)
    {
        _tprintf(L"USAGE: %s pid dll_path\n", argv[2]);
        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;
}

main()函数的主要功能是检查输入程序的参数,然后调用InjectDll()函数。InjectDll()函数是用来实施DLL注入的核心函数,其功能是命令目标进程(notepad.exe)自行调用LoadLibrary(“myhack.dll”)API。下面逐行详细查看InjectDll()函数。
获取目标进程句柄
hProcess=OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)
调用OpenProcess()API,借助程序运行时以参数形式传递过来的dwPID值,获取notepad.exe进程的句柄
(PROCES_ALL_ACCESS权限)。得到PROCESS_ALL_ACCESS权限后,就可以使用获取的句柄(hProcess)控制对应进程(notepad.exe)。
将要注入的DLL路径写入目标进程内存
pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBuffSize, MEM_COMMIT, PAGE_READWRITE);
需要把即将加载的DLL文件的路径(字符串)告知目标进程(notepad.exe)。因为任何内存空间都无法进行写入操作,故先使用VirtualAllocEx() API在目标进程的内存空间中分配一块缓冲区,且指定该缓冲区的大小为DLL文件路径字符串的长度(含Terminating NULL)即可。
提示:VirtualAllocEx()函数的返回值(pRemoteBuf)为分配所得缓冲区的地址。该地址并非程序 (Inject.exe)自身进程的内存地址,而是hProcess句柄所指目标进程(notepad.exe)的内存地址。
writeProcessMemory(hProcess, pRemoteBuf,(LPVOID)szDllName, dwBufSize,NULL);
使用WriteProcessMemory() API将DLL路径字符串写入分析所得缓冲区(pRemoteBuf)地址。WriteProcessMemory() API所写的内存空间也是hProcess句柄所指的目标进程的内存空间。这样要注入的DLL文件的路径就被写入目标进程的内存空间。
调试API:Windows操作系统提供了调试API,借助他们可以访问其他进程的内存空间。其中具有代表性的有VirtualAllocEx()、VirtualFreeEx()、WriteProcessMemory()、ReadProcessMemory()等。
获取LoadLibraryW() API地址
hMod = GetModuleHandle(“kernel32.dll”);
pthreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod,“LoadLibraryW”);
调用LoadLibrary() API前先要获取其地址。
在目标进程中运行远程线程(Remote Thread)
hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL);
pThreadProc = norepad.exe进程内存中的Load LibraryW()地址
pRemoteBuf = norepad.exe进程内存中的“c\work\myhack.dll”字符串地址
一切准备就绪后。最后向notepad.exe发送一个命令,让其调用LoadLibraryW() API函数加载指定的DLL文件即可。
Windows中并未直接提供执行这一命令的API。但是我们可以使用CreateRemoteThread()这个API,用来在目标进程中执行其创建出的线程,其函数原型如下:

CreateRemoteThread()
HANDLE WINAPI CreateRemoteThread(
__in HANDLE                     hProcess,                //目标进程句柄
__in LPSECURITY_ATTRIBUTES      lpThreadAttributes,
__in SIZE_T                      dwStackSize,            //线程函数地址
__in LPTHREAD_START_ROUTINE     lpStartAddress,            //线程参数地址
__in LPVOID                     lpParameter,
__in DWORD                         dwCreationFlags,
__out LPDWORD                     lpThreadId
);

除第一个参数hProcess外,其他参数与CreateThread()函数完全一样。hProcess()参数是要执行线程的目标进程(或称“远程进程”,“宿主进程”)的句柄。lpStartAddress与lpParameter参数分别给出线程函数地址与线程参数地址。这两个地址都应该在目标进程虚拟内存空间中。
仔细观察线程函数ThreadProc()与LoadLibrary()API。

ThreadProc与LoadLibrary函数原型比较
DWORD WINAPI ThreadProcl(
    __in LPVOID            lpParameter
);

HMODULE WINAPI LoadLibrary(
    __in LPCTSTR        lpFileName
);

两函数都有一个4字节的参数,并返回一个4字节的值。也就是说二者形态结构完全一样,调用CreateRemoteThread()时,只要将LoadLibrary()函数的地址传递给第四个参数lpStartAddress,把要注入的DLL的路径字符串地址传递给第五个参数lpParameter即可(必须是目标进程的虚拟内存空间中的地址)。CreateRemoteThread()函数最主要的功能就是驱使目标进程调用LoadLibrary()函数,进而加载指定的DLL文件。

Applnit_DLLs

进行DLL注入的第二种方法是使用注册表。Windows操作系统的注册表中默认提供了Applnt_DLLs与LoadApplnit_DLLs两个注册表选项。
WUYXJ{SX}H4ZL7]JWT@IU6U.png
在注册表编辑器中,将要注入的DLL的路径字符串写入Applnit_DLLs项目,然后把Applnit_DLLs的项目值设置为1。重启后,指定DLL会注入所有运行进程。
提示:上述方法的工作原理是,User32.dll被加载到进程时,会读取Applnit_DLLs注册表项,若有值,则调用LoadLibrary()API加载用户DLL。所以,严格地说,相应DLL并不会被加载到所有进程,而只是加载至加载user32.dll的进程。

分析示例源码

myhack2.cpp

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

若当前加载自己的进程为“notepad.exe”,则以隐藏模式运行IE,连接指定网站。这样就可以根据不同目的执行多种任务。

联系示例myhack2.dll

复制文件
![WC6%VR7]2NO%ZDWSUES7B6.png
修改注册表项
运行注册表编辑器regedit.exe,进入如下路径。
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows
编辑修改Applnit_DLLs表项的值,如图所示
KK6F%77NXHZE(72V~5KQ7TT.png
然后修改LoadApplnit_DLLs注册表的值为1,如图所示。
![F_UDJFAUYFU~@Q9A@VI7JY.png
重启系统
注册表修改完毕后,重启系统,使修改生效。系统重启完成后,使用Process Explorer查看myhack.dll是否被注入所有j加载user32.dll的进程。
由图23-18可知,myhack2.dll成功注入所有加载user32.dll的进程。但由于它的目标进程仅是notepad.exe进程,所以在其他进程中不会执行任何动作。运行notepad.exe,可以看到IE正被以隐藏模式执行,如图23-19所示。
W)C1VCSP{4~X()`ZQQJ6@2G.png
8W0BWI1~11G808_6LKBAQ)1.png