2018-07-13 DLL注入(一)

DLL注入是把指定的DLL加载到另一个进程的内存空间中去。


DLL注入技术:1、通过远程线程注入

                         2、通过全局消息钩子注入

                         3、通过注册表注入

                         4、通过输入法注入

                         5、驱动注入

                         6、在启动进程时挂起主线程,写入指令后再恢复线程

                         7、DLL劫持

                         8、修改PE文件导入表

                         9、使用NtMapViewOfSection注入

                         10、使用SetThreadContext注入


通过远程线程注入:1、用OpenProcess打开要注入进程的句柄

                                2、用VirtualAllocEx在远程进程中申请一段内存,长度为DLL路径长度+1(多出来的一字节用于存放\0)

                                3、用WriteProcessMemory将DLL的路径远程写入申请的内存中

                                4、用CreateRemoteThread将LoadLibraryA作为线程启动函数,参数为DLL的路径,远程创建线程

                                5、用CloseHandle关闭线程句柄

                                6、调用DllMain函数,在函数里完成要做的事。

实现代码:

BOOL InjectDll(DWORD dwProcessID, char* dllPath){//参数:目标进程ID、DLL路径

FARPROC FuncAddr = NULL;

HMODULE hdll = LoadLibrary(TEXT("Kernel32.dll"));//加载DLL

if (hdll != NULL){

FuncAddr = GetProcAddress(hdll, "LoadLibraryA");//获取LoadLibraryA函数地址

if (FuncAddr == NULL)return FALSE;

}

HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ | PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION, FALSE, dwProcessID);//获取进程句柄

if (hProcess == NULL)return FALSE;

DWORD dwSize = strlen(dllPath) + 1;

LPVOID RemoteBuf = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);//远程申请内存

DWORD dwRealSize;

if (WriteProcessMemory(hProcess, RemoteBuf, dllPath, dwSize, &dwRealSize))//远程写内存

{

DWORD dwThreadId;

HANDLE hRemoteThread = CreateRemoteThread(hProcess, 0, 0, (LPTHREAD_START_ROUTINE)FuncAddr, RemoteBuf, 0, &dwThreadId);//创建远程线程

if (hRemoteThread == NULL)

{

VirtualFreeEx(hProcess, RemoteBuf, dwSize, MEM_COMMIT);

CloseHandle(hProcess);

return FALSE;

}

//释放资源

WaitForSingleObject(hRemoteThread, INFINITE);

CloseHandle(hRemoteThread);

VirtualFreeEx(hProcess, RemoteBuf, dwSize, MEM_COMMIT);

CloseHandle(hProcess);

return TRUE;

}

else

{

VirtualFreeEx(hProcess, RemoteBuf, dwSize, MEM_COMMIT);

CloseHandle(hProcess);

return FALSE;

}

}


通过全局消息钩子注入

通过设置全局消息钩子来实现dll注入,然后窗体有相关消息请求会自动加载注入dll,在入口处做处理。

dll注入期间注入程序不可以退出,否则dll内核句柄有可能被释放

源码:

DLL调用

/*

HMODULE h = LoadLibrary(L"xx.dll");

_SetHook SetHook = (_SetHook)GetProcAddress(h,"SetHook");

_UnHook UnHook = (_UnHook)GetProcAddress(h,"UnHook");

SetHook();

Sleep(10000);

UnHook();

CloseHandle(h);

*/


//DLL相关代码 

#include

#include

#include


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


#pragma data_seg("Yrrehs")

HHOOK HT = NULL;

#pragma data_seg()

HINSTANCE DLLhinst = NULL;

LRESULT CALLBACK CProc(int nCode,WPARAM wParam,LPARAM lParam){

return CallNextHookEx(HT,nCode,wParam,lParam);

}

//安装钩子

extern "C" __declspec(dllexport) BOOL SetHook(){

HT = SetWindowsHookEx(WH_CALLWNDPROC,CProc,DLLhinst,0);

if(HT == NULL){

return false;

}

return true;

}

//卸载钩子

extern "C" __declspec(dllexport) BOOL UnHook(){

BOOL HM_BOOL = FALSE;

if(HT != NULL){

HM_BOOL = UnhookWindowsHookEx(HT);

}

return HM_BOOL;

}

//获得进程名

wchar_t* GetProcessName(DWORD processID){

HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,FALSE,processID);

wchar_t *procName = new wchar_t[MAX_PATH];

GetModuleFileNameEx(hProcess,NULL,procName,MAX_PATH);

CloseHandle(hProcess);

return procName;

}

//获得进程名

wchar_t* GetProcessName(wchar_t *FileName){

size_t len = wcslen(FileName);

size_t i = len-1;

for(;i>=0;i--){

if(FileName[i] == L'\\'){

break;

}

}

wchar_t *temp = FileName + i + 1;

return temp;

}

BOOL WINAPI DllMain(HINSTANCE hinstDll,DWORD fdwReason,LPVOID lpvReserved){

DLLhinst = hinstDll;

if(DLL_PROCESS_ATTACH == fdwReason){

wchar_t *procName = GetProcessName(GetCurrentProcessId());

if(_wcsicmp(L"xxx.exe",GetProcessName(procName))==0){

//XXXXXX

}

}

if(DLL_PROCESS_DETACH == fdwReason){

}

return TRUE;

}



通过注册表注入

在系统中每一个进程加载User32.dll时,会受到DLL_PROCESS_ATTACH通知,当User32.dll对其进行处理时,会取得注册表键值HKEY_LOCAL_MACHINE\Software\Microsoft\windowsNT\CurrentVersion\Windows\AppInit_Dlls,并调用LoadLibrary来载入这个字符串指定的每个DLL。被调用的DLL会在系统调用它们的DllMain函数,并将参数fdwReason的值设为DLL_PROCESS_ATTACH时,对自己进行初始化。所以我们在这个键值中添加我们的Dll路径,即可实现注入。

注入流程:

1、打开注册表键值如下:HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows\

2、在上面的注册表项中操作AppInit_DLLs键值,在该键值中添加自己的DLL的全路径加dll名,多个DLL以逗号或者空格分开(因此在文件名中应该尽量不要存在空格),该键值只有第一个dll文件名可以包含路径,后面的都不能包含,因此最好将dll放在系统路径,这样可以不用包含路径也能被加载。

3、在该注册表项中添加键值LoadAppInit_DLLs,类型为DWORD,并将其值置为1。

关于注册表操作的API:

RegOpenKeyEx                         打开注册表键值

RegQueryValueEx                      查询键值

RegSetValueEx                           设置键值

RegCloseKey                               关闭键值


源码:

//打开键值

    nReg = RegOpenKeyEx(

        HKEY_LOCAL_MACHINE,

        m_szRegPath,

        0,

        KEY_ALL_ACCESS,

        &hKey);

    if(nReg != ERROR_SUCCESS)

    {

        return FALSE;

    } 

    //查询键值

    DWORD dwReadType;

    DWORD dwReadCount;

    TCHAR szReadBuff[1000] = {0};

    nReg = RegQueryValueEx(hKey,

        _T("AppInit_DLLs"),

        NULL,

        &dwReadType,

        (BYTE*)&szReadBuff,

        &dwReadCount);

    if(nReg != ERROR_SUCCESS)

    {

        return FALSE;

    }

    //是否dll名称已经在内容中

    tstring strCmpBuff;

    strCmpBuff = szReadBuff;

    if (!strCmpBuff.find(InjectFilePath))

    {

        return FALSE;

    }

    //有字符串就加入空格

    if (0 != _tcscmp(szReadBuff,_T("")))

    {

        _tcscat_s(szReadBuff,_T(" "));

    }

    _tcscat_s(szReadBuff,InjectFilePath);

    //把dll路径设置到注册表中

    nReg = RegSetValueEx(hKey,

        _T("AppInit_DLLs"),

        0,

        REG_SZ,

        (CONST BYTE*)szReadBuff,

        (_tcslen(szReadBuff)+1)*sizeof(TCHAR));


完成注册表注入之后,并不是希望所有程序都运行DLL里面的内容,需要在DLL中过滤窗口名称,让指定窗口名称的EXE文件运行DLL里的线程。所需API如下所示:

CreateThread                              创建线程

Sleep                                           睡眠

EnumWindows                            遍历窗口

GetWindowsText                         得到窗口名称

GetCurrentProcessId                  得到当前进程ID

GetWindowThreadProcessId      由HWND获得进程ID

为了实现此功能,需要在注入的DLL中创建线程,并在线程中执行遍历窗口函数,我们需要先获取窗口名称,与我们想运行的EXE名称进行对比,并进行进程ID对比,因为不光只有一个EXE文件的运行实例,经过这些过滤后,可以在指定的EXE文件中运行代码。

源码:

BOOL CALLBACK lpEnumFunc(HWND hwnd, LPARAM lParam)

{

    TCHAR str[MAXBYTE] = {0};

    //得到窗口名称

    GetWindowText(hwnd,str,sizeof(str));

    //是否名称是计算器

    if(0 == _tcscmp(str,_T("计算器")))

    {

        //由于存在可能多个计算器,需要过滤线程ID

        //得到本身线程的ID

        DWORD dwCurrentProcessId = GetCurrentProcessId();

        DWORD dwFindCurrentProcessId = 0;

        //得到窗口线程ID

        GetWindowThreadProcessId(hwnd,&dwFindCurrentProcessId);

        //比较

        if (dwCurrentProcessId == dwFindCurrentProcessId)

        {

            *(PDWORD)lParam = 1;

            return FALSE;

        }

    }

    return TRUE;

}

DWORD ThreadProc(LPVOID lParam)

{

    //等待1秒时间以便于让windows创建窗口

    Sleep(1000);

    DWORD dwFind = 0;

    //遍历窗口,过滤窗口名称

    EnumWindows(lpEnumFunc,(LPARAM)&dwFind);

    if (!dwFind) return 0;


// 运行代码

    return 0;

}

BOOL InitInstance()

{

    DWORD dwThreadId;

    m_hThread = ::CreateThread(NULL, NULL,

        (LPTHREAD_START_ROUTINE)ThreadProc,

        this, NULL,&dwThreadId);

    return TRUE;

}

你可能感兴趣的:(2018-07-13 DLL注入(一))