Windows下通用ShellCode原理

用 C 汇编实现,编译环境VC6.0, 实测 Win all x86 x64 系统可行

0x00 原理简述

  • 利用fs寄存器,找到TEB的地址->PEB的地址。PEB的0xC偏移处为一个指向PEB_LDR_DATA结构体的指针Ldr,PEB_LDR_DATA的0xC的偏移处为一个指向LIST_ENTRY结构体的指针InLoadOrderModuleList,这是一个按加载顺序构成的双向模块链表。同时LIST_ENTRY的父结构体为LDR_DATA_TABLE_ENTRY,该结构体里有俩有用信息->0x18 DLLBase(模块基址), ->0x2c BaseDllName(指向UNICODE_STRING结构体 模块名字为unicode类型)

  • 利用上述InLoadOrderModuleList双向链表查找kernel32.dll加载到内存的位置,找到其导出表,定位kernel32.dll导出的GetProcAddress函数,使用GetProcAddress函数获取LoadLibrary的函数地址,使用LoadLibrary函数加载user32.dll动态链接库,获取user32.dll中MessageBox的函数地址,调用MessageBox函数。

  • PEB结构图片如下:

0x01 代码实现

1, 分别为GetProcAddress,MessageBox(演示),Loadlibrary 定义函数指针

2, 定义UNICODE_STRING , PEB_LDR_DATA ,LDR_DATA_TABLE_ENTRY结构体

3, 你在C/C++代码中定义一个全局变量,一个取值为“Hello world”的字符串,或直接把该字符串作为参数传递给某个函数。但是,编译器会把字符串放置在一个特定的Section中(如.rdata或.data)。所以定义模块和函数名的字符串的时候,为了使变量存在与栈中,因使用位置无关代码

注意Dll模块名称为Unicode格式。

4, 内联汇编,找到指向InLoadOrderModuleList头的指针pBeg,pPLD指向下一个模块。

5, 遍历双向链表,找到kernel32.dll。由于有些系统中的kernel32.dll大小写不一样,所以这里在遍历时考虑大小写不同的情况。

6, PE操作,遍历kernel32.dll的导出表,根据找到GetProcAddr函数地址。(AddressOfNames的偏移号 对应AddressOfNameOrdinals的偏移,找到的序号为AddressOfFunctions表的偏移)

7, 现在有了GetProcAddr的函数地址,我们可以用LoadLibrary获得任何api的函数地址,并调用。

0x02 运行结果

这里Hello World弹窗仅供测试,要实现更多的功能的话还需要你自己去挖掘噢 ╮( ̄▽  ̄)╭

0x03 完整代码

0x04 总结感悟

接下来研究的内容:

  • Hash API

  • 编码方式

  • 简单免杀

Last updated

Was this helpful?