Window环境下编写Shellcode(入门篇)
2021-03-17 20:26
标签:长度 程序 特点 控制台应用程序 comm 系统 检查 产生 定义变量 Window环境下编写Shellcode 一、 什么是shellcode a) 定义 b) 特点 c) 作用 d) Shellcode的利用原理 a) Visual studio2012-代码编写 b) PEID-PE结构扫描 c) Winhex-16进制查看工具 d) IDA-汇编调试工具 e) MSDN Library-库函数查询工具 a) 避免使用任何全局变量,因为全局变量运行时属于独立地内存空间,违背了shellcode使用绝对地址的原则 b) 不能使用static来定义变量,与全局变量的意义类似 c) 确保已加载所需使用的API动态链接库,确保所调用的链接库在内存中,保证正常的跳转。 六、 函数生成的位置规律 a) 单文件函数生成的位置规律 3、结果 b) 多文件函数生成的位置规律 3、 结果 七、 第二种shellcode的编写实例 a) Shellcode编写的基本框架 b) Shellcode编写框架的优化 八、 编写shellcode加载器 例子: Window环境下编写Shellcode(入门篇) 标签:长度 程序 特点 控制台应用程序 comm 系统 检查 产生 定义变量 原文地址:https://www.cnblogs.com/Erma/p/12378401.html
是一段可注入的指令(opcode),可以在被攻击的程序内运行。
独立存在,无需任何文件格式的包装,因为shellcode直接操作寄存器和函数,所以opcode必须是16进制形式。因此也不能用高级语言编写shellcode。
在内存中运行,无需运行在固定的宿主进程上。
我们想让目标程序以不同于设计者预期的方式运行,或者是按照和我们的意图形式。
将shellcode注入缓冲区,然后欺骗目标程序执行它。而将shellcode注入缓冲区最常用的方法是利用目标系统上的缓冲区溢出漏洞。
二、 环境的基本搭建
Windows下Visio studio2012 ?visual c++ ?控制台应用程序的设置:
1、 设置入口点,去除自动生成的多余的exe代码
操作:项目?属性?链接器?高级?入口点(可自定义,或加上#pragma comment(linker,"/entry:EntryMain"))
实验:可在更改配置的前后,将用IDA打开生成的exe文件,
结果: 前=》很多杂七杂八的没在代码里调用的函数 后=》只有自己在源代码中编写的函数
2、 去除安全检查
项目->属性->C/C++->代码生成->安全检查
3、 兼容xp平台
设置字符集:未设置
4、 设置运行库
MT选项:链接LIB版的C和C++运行库。在链接时就会在将C和C++运行时库集成到程序中成为程序中的代码,程序体积会变大。
MTd选项:LIB的调试版。
MD选项:使用DLL版的C和C++运行库,这样在程序运行时会动态的加载对应的DLL,程序体积会减小,缺点是在系统没有对应DLL时程序无法运行。
MDd选项:表示使用DLL的调试版。
动态版(DLL)和静态版(LIB)C和C++运行库的优缺点
动态版(DLL):优点=exe程序小,缺点=没有对应的dll就无法运行。
静态版(LIB):缺点=因为静态版必须把C和C++运行库复制到目标程序中,所以产生的可执行问会比较大。并且对于大型的项目,在程序运行时会产生对个运行库,在链接时可能会出现重复定义的问题。
5、 关闭生成清单
操作:链接器-》清单文件--》关闭
实验:PEID打开配置前后的exe文件,查看其各个段
结果:前=》只有代码段 后=》只有数据段
6、 关闭调试信息
链接器-》调试器-》FALSE
三、 相关工具
查看可执行文件的入口点
查找对应位置的字符串,并保存为bin文件
安装离线库文档
可查函数的具体描述,包括调用的系统api
四、 Shellcode编写原则
五、 第一种shellcode的编写实例#include
1、规律:单文件函数的生成规律,与函数实现的先后顺序有关,而与函数的定义顺序无关
2、例子:#include
1、 规律:与包含文件的位置无关,与实际调用的顺序有关
2、 例子:
文件目录:
|-头文件:头文件夹
|-A.h
|-B.h
|-源文件:源文件夹
|-main,cpp/********A.h*****************/
#include
/********B.h*****************/
#include
/*********main.cpp*********/
#include "B.h"
#include "A.h"
#include
编写shellcode的基本逻辑框架:先用调用标准库中的各种函数实现shellcode的工程,再深入所调用的函数,对其进行通过基地址函数的调用改写。
以下代码由【第一种shellcode的实现】改写而来,仔细对比,发现其中的规律。#include
1、 文件目录及说明
|-头文件:头文件夹
|--------api.h :文件中所调用的api,在此定义,放入一个结构体中
|--------header.h : 文件中所有的定义都放在这里,便于统一的管理
|-源文件:源文件目录
|-------- 0.entry.cpp :入口文件,提取shellcode,提取a.start.cpp和z.end.cpp中间的代码。
|---
----- a.start.cpp : shellcode的开始位置,跳转到shellcode的入口位置,并调用执行shellcode的功能
|--------b.word.cpp :这是中间文件,不会影响shellcode提取
|-------- z.end.cpp : shellcode的结束位置
2、 例子#pragma once
#include
#pragma once
#include "api.h"
#include
#include "header.h"
#pragma comment(linker,"/entry:EntryMain")
int EntryMain()
{
CreateShellcode();
return 0;
}
void CreateShellcode()
{
HMODULE hMsvcrt=LoadLibraryA("msvcrt.dll");
typedef int( __cdecl *fn_printf)(_In_z_ _Printf_format_string_ const char * _Format, ...);
fn_printf xprintf=(fn_printf)GetProcAddress(hMsvcrt,"printf");
xprintf("1");
HANDLE hBin=CreateFileA("sh.bin",GENERIC_ALL,0,NULL,CREATE_ALWAYS,0,NULL);
if(hBin ==INVALID_HANDLE_VALUE)
{
xprintf("create file error:%d\n",GetLastError());
return ;
}
DWORD dwSize=(DWORD)ShellcodeEnd-(DWORD)ShellcodeStart;//计算程序ShellcodeEnd到ShellcodeStart之间的字节长度
DWORD dwWriten;
WriteFile(hBin,ShellcodeStart,dwSize,&dwWriten,NULL);//将这段程序写入sh.bin文件中。
CloseHandle(hBin);
//ShellcodeEntry();
}
#include "api.h"
#include "header.h"
_declspec(naked) void ShellcodeStart()
{
_asm
{
jmp ShellcodeEntry
}
}
//获取 kernel32库地址
_declspec(naked) DWORD getKernel32()//获取Kernel32地址指针
{
_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(;dwLoopNumberOfNames-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)
{
pFn->fn_GetProcAddress=(FN_GetProcAddress)getProcAddress((HMODULE)getKernel32());
char szLoadLibraryA[]={'L','o','a','d','L','i','b','r','a','r','y','A',0};
pFn->fn_LoadLibraryA=(FN_LoadLibraryA)pFn->fn_GetProcAddress((HMODULE)getKernel32(),szLoadLibraryA);//通过GetProcAddress获取 LoadLibraryA地址
char szUser32[]={'U','s','e','r','3','2','.','d','l','l',0};
char szMessageBoxA[]={'M','e','s','s','a','g','e','B','o','x','A',0};
pFn->fn_MessageBoxA=(Fn_MessageBoxA)pFn->fn_GetProcAddress(pFn->fn_LoadLibraryA(szUser32),szMessageBoxA);//通过LoadLibraryA加载User32.dll后获取MessageBoxA方法地址
char szCreateFileA[]={'C','r','e','a','t','e','F','i','l','e','A',0};
pFn->fn_CreateFileA=(FN_CreateFileA)pFn->fn_GetProcAddress((HMODULE)getKernel32(),szCreateFileA);//获取CreateFileA方法地址
}
void ShellcodeEntry()
{
FUNCTIONS fn;
InitFunctions(&fn);
ShowMessage(&fn);
CreateConfigFile(&fn);
}
#include "api.h"
#include "header.h"
void CreateConfigFile(PFUNCTIONS pFn)
{
char fileName[]={'1','.','t','x','t',0};
pFn->fn_CreateFileA(fileName,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,0,NULL);
}
void ShowMessage(PFUNCTIONS pFn)
{
char szHello[]={'H','e','l','l','o',' ','w','o','r','l','l','d',0};
char szTip[]={'t','i','p',0};
pFn->fn_MessageBoxA(NULL,szHello,szTip,MB_OK);
}
#include "header.h"
void ShellcodeEnd()
{
}
作用:实现两种方式的shellcode的执行
Bin-》exe
Exe-》shellcode
基本操作:
1、 打开文件
2、 读取文件大小
3、 申请与文件大小相等的内存空间
4、 将文件读入3申请的空间
5、 调用shellcode#include
文章标题:Window环境下编写Shellcode(入门篇)
文章链接:http://soscw.com/index.php/essay/65469.html