Window中的shellcode编写框架(入门篇)
Posted Thresh|的博客
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Window中的shellcode编写框架(入门篇)相关的知识,希望对你有一定的参考价值。
Shellcode
- 定义
是一段可注入的指令(opcode),可以在被攻击的程序内运行。
- 特点
短小精悍,灵活多变,独立存在,无需任何文件格式的包装,因为shellcode直接操作寄存器和函数,所以opcode必须是16进制形式。因此也不能用高级语言编写shellcode。在内存中运行,无需运行在固定的宿主进程上。
- Shellcode的利用原理
将shellcode注入缓冲区,然后欺骗目标程序执行它。而将shellcode注入缓冲区最常用的方法是利用目标系统上的缓冲区溢出漏洞。
Shellcode生成方法
- 编程语言编写:汇编语言,C语言
- shellcode生成器
Shellcode编写原则
- 杜绝双引号字符串的直接使用
关闭VS自动优化没有使用到的变量
自定义函数入口
- 动态获取函数的地址
GetProAddress 从dll中获取函数的地址
参数1:调用dll的句柄,参数2:函数名
Bug:error C2760: 语法错误: 意外的令牌“标识符”,预期的令牌为“类型说明符”
打开项目工程-> 属性 -> c/c++ --> 语言 -> 符合模式 修改成否即可 如果这样设置将无法使用c函数。
这个比较关键,否则使用printf就直接崩溃或者是编译报错
最佳方案是:修改平台工具集
- 通过获得Kernel32基址来获取GetProcAddres基址
- 避免全局变量的使用
因为vs会将全局变量编译在其他区段中 结果就是一个绝对的地址不能使用static定义变量(变量放到内部函数使用)
- 确保已加载使用API的动态链接库
编写shellcode前的准备
- 修改程序入口点:链接器-高级。作用:去除自动生成的多余的exe代码
- 关闭缓冲区安全检查,属性->C/C++ ->代码生成->安全检查禁用
- 设置工程兼容window XP :代码生成 ->运行库 选择 debug MTD release MT
- 清除资源:链接器->调试->清单文件
- 关闭调试功能
如下:
属性->常规->平台工具集 选择xp版本
C/C++->代码生成->运行库选择MT
安全检查禁用
链接器->高级->入口点修改为EntryMain
函数动态链接调用
在编写shellcode时,所有用到的函数都需要动态调用,通过LoadLibrary函数加载动态链接库,GetProAddress获取动态链接库中函数的地址。所以获取到GetProAddress和LoadLibrary地址时非常重要的。通过GetProAddress和LoadLibrary,可以获取到已加载的动态链接库的地址。
动态获取Kernel32.dll基址和GetProAddress 地址
在正常情况下,我们不知道LoadLibraryA的地址,所以不能直接使用该函数。GetProAddress是动态链接库Kernel32.dll中的函数。每个进程内部加载都会加载Kernel32.dll,获取到加载Kernel32.dll地址就可以获取到GetProAddress函数的地址。通过汇编内嵌获取到Kernel32的基址。获取到GetProAddress地址后,就可以获取到所有需要的函数的地址。
pE文件运行时加载的链接库
代码如下:
#include <windows.h> #include <stdio.h> //内嵌汇编获取Kernel32的地址 __declspec(naked) DWORD getKernel32() { __asm { mov eax,fs:[30h] mov eax,[eax+0ch] mov eax,[eax+14h] mov eax,[eax] mov eax,[eax] mov eax,[eax+10h] ret } } //通过kernel32基址获取GetProcAddress的地址 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 + (DWORD)lpExports->AddressOfNames); PWORD lpword = (PWORD)((DWORD)hModuleBase + (DWORD)lpExports->AddressOfNameOrdinals); PDWORD lpdwFunAddr = (PDWORD)((DWORD)hModuleBase + (DWORD)lpExports->AddressOfFunctions); DWORD dwLoop = 0; FARPROC pRet = NULL; for (; dwLoop <= lpExports->NumberOfNames - 1; dwLoop++) { char* pFunName = (char*)(lpdwFunName[dwLoop] + (DWORD)hModuleBase); if (pFunName[0] == \'G\'&& pFunName[1] == \'e\'&& pFunName[2] == \'t\'&& pFunName[3] == \'P\'&& pFunName[4] == \'r\'&& pFunName[5] == \'o\'&& pFunName[6] == \'c\'&& pFunName[7] == \'A\'&& pFunName[8] == \'d\'&& pFunName[9] == \'d\'&& pFunName[10] == \'r\'&& pFunName[11] == \'e\'&& pFunName[12] == \'s\'&& pFunName[13] == \'s\') { pRet = (FARPROC)(lpdwFunAddr[lpword[dwLoop]] + (DWORD)hModuleBase); break; } } return pRet; } int main() { //kernel32.dll 基址的动态获取 HMODULE hLoadLibrary = LoadLibraryA("kernel32.dll"); //使用内嵌汇编来获取基址 HMODULE _hLoadLibrary = (HMODULE)getKernel32(); //效果是一样的 printf("LoadLibraryA动态获取的地址: 0x%x\\n", hLoadLibrary); printf("内嵌汇编获取的地址: 0x%x\\n", _hLoadLibrary); //声明定义,先转到到原函数定义,然后重新定义 typedef FARPROC(WINAPI *FN_GetProcAddress)( _In_ HMODULE hModule, _In_ LPCSTR lpProcName ); FN_GetProcAddress fn_GetProcAddress; fn_GetProcAddress = (FN_GetProcAddress)_GetProcAddress(_hLoadLibrary); printf("动态获取GetProcAddress地址: 0x%x\\n",fn_GetProcAddress); printf("内置函数获取: 0x%x\\n",GetProcAddress); }
可以看到,动态获取的地址和系统函数获取的是一致的
Shellcode框架(一)
引用前面kernel32.dll和GetProcaddress的获取地址。对于每一个要引用的函数,通过查看定义来声明定义函数,通过GetProcaddress动态获取地址。
具体代码如下:
原C代码:
#include <windows.h> #include <stdio.h> #include <windows.h> int EntryMain() { CreateFileA(“1.txt”, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); MessageBoxA(NULL, “hello world”, tip, MB_OK); return 0; }
Shellcode编写:
#include <windows.h> #include <stdio.h> FARPROC getProcAddress(HMODULE hModuleBase); DWORD getKernel32(); int EntryMain() { //声明定义GetProcAddress typedef FARPROC(WINAPI *FN_GetProcAddress)( _In_ HMODULE hModule, _In_ LPCSTR lpProcName ); //获取GetProcAddress真实地址 FN_GetProcAddress fn_GetProcAddress = (FN_GetProcAddress)getProcAddress((HMODULE)getKernel32()); //声明定义CreateFileA typedef HANDLE(WINAPI *FN_CreateFileA)( __in LPCSTR lpFileName, __in DWORD dwDesiredAccess, __in DWORD dwShareMode, __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes, __in DWORD dwCreationDisposition, __in DWORD dwFlagsAndAttributes, __in_opt HANDLE hTemplateFile ); //将来的替换,地址全部动态获取 //FN_CreateFileA fn_CreateFileA = (FN_CreateFileA)GetProcAddress(LoadLibrary("kernel32.dll"), "CreateFileA"); //带引号的字符串打散处理 char xyCreateFile[] = { \'C\',\'r\',\'e\',\'a\',\'t\',\'e\',\'F\',\'i\',\'l\',\'e\',\'A\',0 }; //动态获取CreateFile的地址 FN_CreateFileA fn_CreateFileA = (FN_CreateFileA)fn_GetProcAddress((HMODULE)getKernel32(), xyCreateFile); char xyNewFile[] = { \'1\',\'.\',\'t\',\'x\',\'t\',\'\\0\'}; fn_CreateFileA(xyNewFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); //定义LoadLibraryA typedef HMODULE(WINAPI *FN_LoadLibraryA)( __in LPCSTR lpLibFileName ); char xyLoadLibraryA[] = { \'L\',\'o\',\'a\',\'d\',\'L\',\'i\',\'b\',\'r\',\'a\',\'r\',\'y\',\'A\',0}; //动态获取LoadLibraryA的地址 FN_LoadLibraryA fn_LoadLibraryA = (FN_LoadLibraryA)fn_GetProcAddress((HMODULE)getKernel32(), xyLoadLibraryA); //定义MessageBoxA typedef int (WINAPI *FN_MessageBoxA)( __in_opt HWND hWnd, __in_opt LPCSTR lpText, __in_opt LPCSTR lpCaption, __in UINT uType); //原来的:MessageBoxA(NULL, "Hello world", "tip", MB_OK); char xy_user32[] = { \'u\',\'s\',\'e\',\'r\',\'3\',\'2\',\'.\',\'d\',\'l\',\'l\',0 }; char xy_MessageBoxA[] = { \'M\',\'e\',\'s\',\'s\',\'a\',\'g\',\'e\',\'B\',\'o\',\'x\',\'A\',0 }; FN_MessageBoxA fn_MessageBoxA = (FN_MessageBoxA)fn_GetProcAddress(fn_LoadLibraryA(xy_user32), xy_MessageBoxA); char xy_Hello[] = { \'H\',\'e\',\'l\',\'l\',\'o\',\' \',\'w\',\'o\',\'r\',\'l\',\'d\',0 }; char xy_tip[] = { \'t\',\'i\',\'p\' }; fn_MessageBoxA(NULL, xy_Hello, xy_tip, MB_OK); return 0; } //内嵌汇编获取Kernel32的地址 __declspec(naked) DWORD getKernel32() { __asm { mov eax, fs:[30h] mov eax, [eax + 0ch] mov eax, [eax + 14h] mov eax, [eax] mov eax, [eax] mov eax, [eax + 10h] ret } } //获取GetProcAddress的地址 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 + (DWORD)lpExports->AddressOfNames); PWORD lpword = (PWORD)((DWORD)hModuleBase + (DWORD)lpExports->AddressOfNameOrdinals); PDWORD lpdwFunAddr = (PDWORD)((DWORD)hModuleBase + (DWORD)lpExports->AddressOfFunctions); DWORD dwLoop = 0; FARPROC pRet = NULL; for (; dwLoop <= lpExports->NumberOfNames - 1; dwLoop++) { char* pFunName = (char*)(lpdwFunName[dwLoop] + (DWORD)hModuleBase); if (pFunName[0] == \'G\'&& pFunName[1] == \'e\'&& pFunName[2] == \'t\'&& pFunName[3] == \'P\'&& pFunName[4] == \'r\'&& pFunName[5] == \'o\'&& pFunName[6] == \'c\'&& pFunName[7] == \'A\'&& pFunName[8] == \'d\'&& pFunName[9] == \'d\'&& pFunName[10] == \'r\'&& pFunName[11] == \'e\'&& pFunName[12] == \'s\'&& pFunName[13] == \'s\') { pRet = (FARPROC)(lpdwFunAddr[lpword[dwLoop]] + (DWORD)hModuleBase); break; } } return pRet; }
结果:运行exe文件创建了1.txt文件,并出现弹框。
函数生成的位置规律
- 单文件函数生成的位置规律
规律:单文件函数的生成规律,与函数实现的先后顺序有关,而与函数的定义顺序无关。
例如:
#include<windows.h> #include<stdio.h> int FuncA(int a, int b) { puts("AAAA"); return a + b; } int FuncB(int a, int b) { puts("BBB"); return a + b; } int main() { FuncA(1, 2); FuncB(2, 3); return 0; }
结果:在IDA中看到生成的exe文件中的函数顺序为FuncA,FuncB,main,与函数实现的先后顺序有关。通过函数的位置,可以得到两个函数之间的空间大小。
2.多文件函数生成的位置规律
规律:与包含文件的位置无关,与实际调用的顺序有关
//A.h #include<stdio.h> void A() { puts("AAA"); } //B.h #include<stdio.h> void B() { puts("BBB"); } //main #include"A.h" #include"B.h" int main() {
A(); B(); return 0; }
结果:
工程下的后缀名为vcxproj文件
修改顺序
可以看到生成的顺序发生了改变
Shellcode框架(二)
Shellcode代码执行过程
ShellcodeStart-> ShellcodeEntry-> ShellcodeEnd
工程文件:
api.h文件:存放定义的的函数
header.h文件:存放定义的功能函数
0.entry.cpp文件:shellcode的入口点
a.start.cpp文件:shellcode执行(实现逻辑功能)
b.work.cpp文件:存放具体功能实现
z.end.cpp文件:shellcode结束
a.start.cpp文件和b.work.cpp文件分别管理逻辑功能和具体实现,更方便管理。
生成的文件顺序:
代码:
api.h
#pragma once #include<windows.h> //声明定义GetProcAddress typedef FARPROC(WINAPI *FN_GetProcAddress)( _In_ HMODULE hModule, _In_ LPCSTR lpProcName ); //定义LoadLibraryA typedef HMODULE(WINAPI *FN_LoadLibraryA)( __in LPCSTR lpLibFileName ); //定义MessageBoxA typedef int (WINAPI *FN_MessageBoxA)( __in_opt HWND hWnd, __in_opt LPCSTR lpText, __in_opt LPCSTR lpCaption, __in UINT uType); //定义CreateFileA typedef HANDLE(WINAPI *FN_CreateFileA)( __in LPCSTR lpFileName, __in DWORD dwDesiredAccess, __in DWORD dwShareMode, __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes, __in DWORD dwCreationDisposition, __in DWORD dwFlagsAndAttributes, __in_opt HANDLE hTemplateFile ); //定义一个指针 typedef struct _FUNCTIONS { FN_GetProcAddress fn_GetProcAddress; FN_LoadLibraryA fn_LoadLibraryA; FN_MessageBoxA fn_MessageBoxA; FN_CreateFileA fn_CreateFileA; }FUNCTIONS,*PFUNCTIONS;
header.h
#pragma once #include<windows.h> #include<stdio.h> #include"api.h" void ShellcodeStart(); void ShellcodeEntry(); void ShellcodeEnd(); void CreateShellcode(); void InitFunctions(PFUNCTIONS pFn); void CreateConfigFile(PFUNCTIONS pFn);
0.entry.cpp
#include "header.h" int EntryMain() { CreateShellcode(); return 0; } void CreateShellcode() { HMODULE hMsvcrt = LoadLibraryA("msvcrt.dll"); //定义printf typedef int (__CRTDECL *FN_printf)( _In_z_ _Printf_format_string_ char const* const _Format, ...); FN_printf fn_printf = (FN_printf)GetProcAddress(hMsvcrt,"printf"); HANDLE hBin = CreateFileA("sh.bin", GENERIC_ALL, 0, NULL, CREATE_ALWAYS, 0, NULL); if (hBin == INVALID_HANDLE_VALUE) { //这里不能使用printf函数,因为修改了函数入口,找不到printf的地址,所有需要动态调用printf函数 //printf("create file error:%d\\n", GetLastError()); fn_printf("create file error:%d\\n", GetLastError()); return ; } DWORD dwSize = (DWORD)ShellcodeEnd - (DWORD)ShellcodeStart; DWORD dwWrite; WriteFile(hBin, ShellcodeStart, dwSize, &dwWrite, NULL); CloseHandle(hBin); }
a.start.cpp文件
#include "header.h" #include "api.h" __declspec(naked) void ShellcodeStart() { __asm { jmp ShellcodeEntry } } //内嵌汇编获取Kernel32的地址 __declspec(naked) DWORD getKernel32() { __asm { mov eax, fs:[30h]; test eax, eax; js finished; mov eax, [eax + 0ch]; mov eax, [eax + 14h]; mov eax, [eax]; mov eax, [eax] mov eax, [eax + 10h] finished: ret } } //获取GetProcAddress的地址 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 + (DWORD)lpExports->AddressOfNames); PWORD lpword = (PWORD)((DWORD)hModuleBase + (DWORD)lpExports->AddressOfNameOrdinals); PDWORD lpdwFunAddr = (PDWORD)((DWORD)hModuleBase + (DWORD)lpExports->AddressOfFunctions); DWORD dwLoop = 0; FARPROC pRet = NULL; for (; dwLoop <= lpExports->NumberOfNames - 1; dwLoop++) { char* pFunName = (char*)(lpdwFunName[dwLoop] + (DWORD)hModuleBase); if (pFunName[0] == \'G\'&& pFunName[1] == \'e\'&& pFunName[2] == \'t\'&& pFunName[3] == \'P\'&& pFunName[4] == \'r\'&& pFunName[5] == \'o\'&& pFunName[6] == \'c\'&& pFunName[7] == \'A\'&& pFunName[8] == \'d\'&& pFunName[9] == \'d\'&& pFunName[10] == \'r\'&& pFunName[11] == \'e\'&& pFunName[12] == \'s\'&& pFunName[13] == \'s\') { pRet = (FARPROC)(lpdwFunAddr[lpword[dwLoop]] + (DWORD)hModuleBase); break; } } return pRet; } //动态调用地址 void InitFunctions(PFUNCTIONS pFn) { //获取GetProcAddress真实地址 pFn->fn_GetProcAddress = (FN_GetProcAddress)getProcAddress((HMODULE)getKernel32()); //动态获取LoadLibraryA的地址 char xyLoadLibraryA[] = { \'L\',\'o\',\'a\',\'d\',\'L\',\'i\',\'b\',\'r\',\'a\',\'r\',\'y\',\'A\',0 }; pFn->fn_LoadLibraryA = (FN_LoadLibraryA)pFn->fn_GetProcAddress((HMODULE)getKernel32(), xyLoadLibraryA); //动态获取MessageBoxA的地址 char xy_user32[] = { \'u\',\'s\',\'e\',\'r\',\'3\',\'2\',\'.\',\'d\',\'l\',\'l\',0 }; char xy_MessageBoxA[] = { \'M\',\'e\',\'s\',\'s\',\'a\',\'g\',\'e\',\'B\',\'o\',\'x\',\'A\',0 }; pFn->fn_MessageBoxA = (FN_MessageBoxA)pFn->fn_GetProcAddress(pFn->fn_LoadLibraryA(xy_user32), xy_MessageBoxA); //动态获取CreateFile的地址 char xyCreateFile[] = { \'C\',\'r\',\'e\',\'a\',\'t\',\'F\',\'i\',\'l\',\'e\',\'A\',0 }; pFn->fn_CreateFileA = (FN_CreateFileA)pFn->以上是关于Window中的shellcode编写框架(入门篇)的主要内容,如果未能解决你的问题,请参考以下文章