最简单的ShellCode生成
本文为看雪论坛优秀文章
看雪论坛作者ID:wx_墨雪妖莲
ShellCode初级版:
什么是ShellCode?
shellcode实际上是一段可以独立执行的代码,常指一段二进制数据,这段二进制数据独立存在,不像常规PE数据那样加载运行。它没有导入表导出表之类的结构,这使得shellcode极为灵活,同时也加大了它的编写难度。一段健壮的shellcode应具备很好的兼容性。因此,要编写出一个完整的ShellCode需要以下几个重要的技术要点:
1、GetPC(用于准确定位ShellCode所定义的数据)
在x86中即为获取当前代码执行的EIP(也就是程序执行到了哪一行代码)。
(1)CALL方式
利用
E8 00000000 #CALL xxxx 58 #POP EAX
(2)FSTEVV方式
重点关注这个结构体中的fpu_instruction_pointer:
struct FpuSaveState
{
uint32 control_word; // 控制码
uint32 status_word; // 状态码
uint32 tag_word; // 标志位
uint32 fpu_instruction_pointer; // *指向上条FPU指令*
uint16 fpu_instruction_selector;
uint16 fpu_opcode;
uint32 fpu_operand_pointer;
uint16 fpu_operand_selector;
uint16 reserved;
}
D9 EE #FLDZD9 74 24 F4 #FNSTENV[ESP-0CH]5B #POP EBX
2、模拟导入表,获取函数地址
大致代码原理如下:
xor eax,eaxmov eax,fs:[0x30] #获取PEB地址mov eax,[eax+0xC] #获取PEB_LDR_DATA的结构体指针mov esi,[eax+0x1C] #通过PEB_LDR_DATA获取到结构体成员:模块链表的头部地址mov eax,[esi] #获取第一个加载的模块信息(根据环境的不同而不同)mov eax,[eax] #获取第二个加载的模块信息(根据环境的不同而不同),我这里是kernel32.dllmov eax,[eax+0x8] #获取kernel32.dll的模块基址mov kernelBase,eax 将基址保存到定义的变量进行接收
获取到了指定的模块基址后,我们就可以通过其来获取我们想要的函数地址了,在这里,需要对PE结构有一个基本的了解。
// 获取DOS头
IMAGE_DOS_HEADER* DosHeader = (IMAGE_DOS_HEADER*)kernelBase;
// 获取NT头
IMAGE_NT_HEADERS* NtHeader = (IMAGE_NT_HEADERS*)(DosHeader->e_lfanew + kernelBase);
// 获取扩展头
IMAGE_OPTIONAL_HEADER OPHeader = NtHeader->OptionalHeader;
// 获取导出表
IMAGE_EXPORT_DIRECTORY* ExportList= (IMAGE_EXPORT_DIRECTORY*)(OPHeader.DataDirectory[0].VirtualAddress + kernelBase);
// 获取函数地址表
DWORD* FuncAddList = (DWORD*)(ExportList->AddressOfFunctions+kernelBase);
// 获取函数名称表
DWORD* NameAddList = (DWORD*)(ExportList->AddressOfNames+kernelBase);
// 获取函数序号表
SHORT* OriList = (SHORT*)(ExportList->AddressOfNameOrdinals + kernelBase);
// 循环遍历,获取LoadLibrary函数地址以及GetProceAddress地址
for (int i = 0; i < ExportList->NumberOfNames; i++)
{
if (g_strcmp((char*)(NameAddList[i] + kernelBase), g_GetProcAddress) == TRUE)
{
My_GetProcAddress = (MyGetProcAddress)((FuncAddList[OriList[i]]+kernelBase));
}
}
char g_GetProcAddress[] = { 'G','e','t','P','r','o','c','A','d','d','r','e','s','s',0 };
inline BOOL g_strcmp(char* str1, char* str2)
{
// 判断对比字符的长度,防止访问越界
int length = 0;
for (length;; length++)
{
if (str1[length] == 0 || str2[length] == 0)
break;
}
// 进行对比
for (int i = 0;i<length; i++)
{
if (str1[i] != str2[i])
return FALSE;
else if (str1[i] == str2[i] && str2[i + 1] == 0&&str1[i+1]==0)
return TRUE;
}
};
// 定义函数指针
typedef HMODULE(WINAPI* MyLoadLibrary)(_In_ LPCSTR lpFileName);
typedef FARPROC(WINAPI* MyGetProcAddress)(_In_ HMODULE hModule, _In_ LPCSTR lpProcName);
typedef int(WINAPI* MyMessageBox)(_In_opt_ HWND hWnd, _In_opt_ LPCSTR lpText, _In_opt_ LPCSTR lpCaption, _In_ UINT uType);
// 通过遍历导出表进行获取函数地址(这里我就不详细写了,具体情况就是上面的代码修改一下)
HMODULE hModule = NULL;
hModule=My_LoadLibrary(g_user32);
My_MessageBox=(MyMessageBox)My_GetProcAddress(hModule, g_MessageBox);
My_MessageBox(0, 0, 0, 0);
3、生成ShellCode
上述写的代码是用C语言结合嵌入汇编进行编写的,现在我们要做的就是将程序进行生成(Release版本我用的是),然后用x32dbg进行调试,找到程序main函数的起点和终点,进行二进制代码复制,这段代码就是我们的ShellCode:
4、调试生成的ShellCode
#include<Windows.h>
#include<iostream>
// 使数据段可读可写可执行
#pragma comment(linker, "/section:.data,RWE")
// 生成的ShellCode
char arr[] = { "\x55\x8B\xEC\x81\xEC\x2C\x01\x00\x00\x53\x33\xC0\xC7\x45\xD8\x4C\x6F\x61\x64\x56\x57"\
"\xC7\x45\xDC\x4C\x69\x62\x72\xC7\x45\xE0\x61\x72\x79\x41\x88\x45\xE4\xC7\x45\xC8\x47"\
"\x65\x74\x50\xC7\x45\xCC\x72\x6F\x63\x41\xC7\x45\xD0\x64\x64\x72\x65\x66\xC7\x45\xD4"\
"\x73\x73\x88\x45\xD6\xC7\x45\xF4\x75\x73\x65\x72\xC7\x45\xF8\x33\x32\x2E\x64\x66\xC7"\
"\x45\xFC\x6C\x6C\x88\x45\xFE\xC7\x45\xE8\x4D\x65\x73\x73\xC7\x45\xEC\x61\x67\x65\x42"\
"\xC7\x45\xF0\x6F\x78\x41\x00\x33\xC0\x64\xA1\x30\x00\x00\x00\x8B\x40\x0C\x8B\x70\x1C"\
"\x8B\x06\x8B\x00\x8B\x40\x08\x89\x45\xC4\x8B\x5D\xC4\x8D\xBD\xD4\xFE\xFF\xFF\x6A\x38"\
"\x59\x8D\x73\x18\x03\x73\x3C\xF3\xA5\x8B\x85\x34\xFF\xFF\xFF\x33\xFF\x33\xF6\x8B\x4C"\
"\x18\x1C\x8B\x54\x18\x20\x03\xCB\x89\x4D\xBC\x03\xD3\x8B\x4C\x18\x24\x8B\x44\x18\x18"\
"\x03\xCB\x89\x55\xB4\x89\x4D\xC0\x89\x45\xB8\x85\xC0\x74\x2E\x8D\x45\xC8\x50\x8B\x04"\
"\xB2\x03\xC3\x50\xE8\x40\x00\x00\x00\x59\x59\x83\xF8\x01\x75\x0F\x8B\x45\xC0\x8B\x4D"\
"\xBC\x0F\xBF\x04\x70\x8B\x3C\x81\x03\xFB\x8B\x55\xB4\x46\x3B\x75\xB8\x72\xD2\x8D\x45"\
"\xD8\x50\x53\xFF\xD7\x8D\x4D\xF4\x51\xFF\xD0\x8D\x4D\xE8\x51\x50\xFF\xD7\x33\xC9\x51"\
"\x51\x51\x51\xFF\xD0\x5F\x5E\x5B\xC9\xC3\x55\x8B\xEC\x53\x8B\x5D\x0C\x33\xD2\x56\x8B"\
"\x75\x08\x57\x8B\xFA\x38\x16\x74\x11\x8B\xCB\x8B\xC6\x2B\xCE\x38\x14\x01\x74\x06\x47"\
"\x40\x38\x10\x75\xF5\x85\xFF\x7E\x30\x8B\xC6\x2B\xC3\x89\x45\x08\x8D\x0C\x1A\x8A\x04"\
"\x08\x3A\x01\x75\x1D\x80\x7C\x1A\x01\x00\x75\x07\x80\x7C\x32\x01\x00\x74\x0A\x42\x3B"\
"\xD7\x7D\x0C\x8B\x45\x08\xEB\xDE\x33\xC0\x40\xEB\x02\x33\xC0\x5F\x5E\x5B\x5D\xC3" };
int main()
{
_asm {
lea eax,arr
push eax
jmp eax
}
}
end
看雪ID:wx_墨雪妖莲
https://bbs.pediy.com/user-home-916882.htm
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!