在笔者前几篇文章中我们一直在探讨如何利用Metasploit
这个渗透工具生成ShellCode
以及如何将ShellCode注入到特定进程内,本章我们将自己实现一个正向ShellCode
Shell,当进程被注入后,则我们可以通过利用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.d