shellcode加载器

本文介绍了如何在编写shellcode时避免使用绝对地址,通过动态获取函数地址来实现调用,如GetProcAddress和LoadLibraryA。示例中展示了如何调用MessageBoxA并传递字符串。此外,还提到了利用 CreatFileA 函数来创建自定义函数类型以增加灵活性。文章还探讨了PEB、TEB结构体和FS寄存器在查找kernel32.dll基址中的作用,以解决GetProcAddress本身的绝对地址问题。
摘要由CSDN通过智能技术生成

不能有全局变量 (其实也是使用了绝对地址)

不能使用常量字符串

char szBuffer[] = "ShellCode"; //会使用常量区,所以不可用

//写成
char szBuffer[] = {
   'S', 'h', 'e', 'l', '\0'}; //这个使用堆栈

char szBuffer[] = {
   'K', '0', 'e', '0', '\0'}; //UNICODE格式,高字节为零,占2个字符,一般函数名为UinCode格式

编写shellcode不能使用绝对地址,因为当我们加载到其他函数中时绝对地址储存的可能什么也没有,甚至是其他函数,所以当我们需要调用一个函数时,需要动态获取函数的地址实现调用。
想要动态获取函数地址,可以使用以下函数
GetProcAddress 从dll中获取函数的地址
LoadLibraryA 将指定的模块加载到调用进程的地址空间中
举个例子:

LPVOID lp = GetProcAddress(LoadLibraryA("user32.dll"),"MessageBoxA");
_asm
{
   
	push 0
	push 0
	push 0
	push 0
	call lp
}

这样就可以获取到MessageBoxA函数的地址,并且通过汇编代码实现了调用。
当然如果我们想要在MessageBoxA函数中输出一个字符串的话,可以设置一个字符串指针:
char * pszData =“helloworld”;
并且修改对应的push 参数就可以了。
这种方法可行,但是并不灵活,再看一下更加灵活的方式
用 CreatFileA 函数来演示一下

int entrymain()
{
   
	typedef HANDLE (WINAPI* FN_CreateFileA)(
	__in	 LPCSTR lpFileName,
	__in	 DWORD dwDesiredAccess,
	__in	 DWORD dwShareMode,
	__in	 LPSECURITY_ATTRIBUTES lpSecruityAttributes,
	__in	 DWORD dwCreationDisposition,
	__in	 DWORD dwFlagsAndAttributes,
	__in_opt HANDLE hTemplateFile
	);
	FN_CreatFileA fn_CreateFileA;
	fn_CreateFileA = (FN_CreatFileA)GetProcAddress(LoadLibraryA("kernel32.dll"),"CreateFileA");
	CreatFileA("hello.txt",GENERIC_WRITE,0,NULL,CREAT_ALWAYS,0,NULL);
}

我们可以自定义一个与想调用的函数一样的新类型,这样我们就可以随便使用,因为这个函数是我们自定义的函数。
当然,明眼的人可能已经发现,我们在使用GetProcAddress函数去获得函数的地址时,GetProcAddress函数本身不就已经是调用的绝对地址了吗?当然LoadLibraryA函数也一样。
这里我们就需要了解一下PEB和TEB与FS寄存器,FS0偏移处是teb结构体,30h偏移处是PEB结构体,而PEB结构体中有三个成员链表分别储存了三个顺序,代码中有详解。通过这三个顺序我们分别可以使用三个方法获得kernel32的基址。获取这个基址的方法还有很多,推荐一篇博客

__declspec(nacked) DWORD getKernel32()
{
   
	__asm
	{
   
		mov eax,fs:[30]   //fs:[30] 纯存的是PEB,也就是进程环境块,操作系统在加载进程的过程中会自动初始化一个PEB结构体用来初始化该进程的各种信息的结构体
		mov eax,[eax+0ch]    //也就是PEB 0ch处的偏移,该结构体的三个成员链表都可以获取kernel32的基址
		mov eax,[eax+14h]    //获取初始化顺序链表的地址,首地址是第一个模块
		mov eax,[eax]        //第二个模块
		mov eax,[eax]        //第三个模块
		mov eax,[eax+10h]    // 10h偏移处就是kernel32的基地址
	}
}

看完上面的代码,会有疑惑,为什么是第三个参数呢?看下图,是因为kernel32.dll是第三个被加载的dll文件。
pe文件运行时加载的链接库

FARPROC _GetProcAddress(HMODULE hModuleBase) 
{
   
    PIMAGE_DOS_HEADER lpDosHeader = (PIMAGE_DOS_HEADER)hModuleBase;
    PIMAGE_NT_HEADERS32 lpNtHeader = (PIMAGE_NT_HEADERS)((DWORD)hModuleBase + lpDosHeader->e_lfanew);
    if (!lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size){
   
        return NULL;
    }
    if (!lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress) {
   
        return NULL;
    }
    PIMAGE_EXPORT_DIRECTORY lpExports = (PIMAGE_EXPORT_DIRECTORY)((DWORD)hModuleBase + (DWORD)lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
    PDWORD lpdwFunName = (PDWORD)((DWORD)hModuleBase + 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值