Window环境下编写Shellcode(入门篇)
Posted Emra
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Window环境下编写Shellcode(入门篇)相关的知识,希望对你有一定的参考价值。
Window环境下编写Shellcode
一、 什么是shellcode
a) 定义
是一段可注入的指令(opcode),可以在被攻击的程序内运行。
b) 特点
独立存在,无需任何文件格式的包装,因为shellcode直接操作寄存器和函数,所以opcode必须是16进制形式。因此也不能用高级语言编写shellcode。
在内存中运行,无需运行在固定的宿主进程上。
c) 作用
我们想让目标程序以不同于设计者预期的方式运行,或者是按照和我们的意图形式。
d) 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
三、 相关工具
a) Visual studio2012-代码编写
b) PEID-PE结构扫描
查看可执行文件的入口点
c) Winhex-16进制查看工具
查找对应位置的字符串,并保存为bin文件
d) IDA-汇编调试工具
e) MSDN Library-库函数查询工具
安装离线库文档
可查函数的具体描述,包括调用的系统api
四、 Shellcode编写原则
a) 避免使用任何全局变量,因为全局变量运行时属于独立地内存空间,违背了shellcode使用绝对地址的原则
b) 不能使用static来定义变量,与全局变量的意义类似
c) 确保已加载所需使用的API动态链接库,确保所调用的链接库在内存中,保证正常的跳转。
五、 第一种shellcode的编写实例
#include <windows.h>
#pragma comment(linker,"/entry:EntryMain")
/**
例子:message这个弹框文件的存在
*/
int EntryMain()
{
// MessageBox(NULL,NULL, NULL,NULL);
//例子2:如何获取MessageBox的地址,并通过这个返回的地址调用这个函数
// 方法:通过函数的地址,嵌入汇编->压栈
//几个常用的动态链接库:kernel32.dll user32.dll gdi32.dll msvcrt.dll
LPVOID lp = GetProcAddress(LoadLibraryA("user32.dll"),"MessageBoxA");
char * pdata = "hello word";
_asm
{
push 0
push 0
push pdata
push 0 // 压入4个参数:相当于例子1中的4个为NULL的参数
call lp //调用改地址
} //能够实现直接调用MessageBox的例子
return 0;
}
六、 函数生成的位置规律
a) 单文件函数生成的位置规律
1、规律:单文件函数的生成规律,与函数实现的先后顺序有关,而与函数的定义顺序无关
2、例子:
#include <stdio.h>
#include <windows.h>
int FuncA(int a ,int b);
int FuncB(int a ,int b);
int main()
{
DWORD dwSize = (DWORD)FuncB - (DWORD)FuncA;
return 0;
}
int FuncB(int a ,int b)
{
puts("FuncB...");
return a+b;
}
int FuncA(int a ,int b)
{
puts("FuncA...");
return a+b;
}
3、结果
b) 多文件函数生成的位置规律
1、 规律:与包含文件的位置无关,与实际调用的顺序有关
2、 例子:
文件目录:
|-头文件:头文件夹
|-A.h
|-B.h
|-源文件:源文件夹
|-main,cpp
/********A.h*****************/
#include <stdio.h>
void A()
{
puts("AAA");
}
/********B.h*****************/
#include <stdio.h>
void B()
{
puts("BBB");
}
/*********main.cpp*********/
#include "B.h"
#include "A.h"
#include <stdio.h>
int main()
{
A();
B();
return 0;
}
3、 结果
七、 第二种shellcode的编写实例
a) Shellcode编写的基本框架
编写shellcode的基本逻辑框架:先用调用标准库中的各种函数实现shellcode的工程,再深入所调用的函数,对其进行通过基地址函数的调用改写。
以下代码由【第一种shellcode的实现】改写而来,仔细对比,发现其中的规律。
#include <stdio.h>
#include <windows.h>
#pragma comment(linker, "/entry:EntryMain")
//获取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
}
}
FARPROC getProcAddress(HMODULE hMoudleBase)
{
PIMAGE_DOS_HEADER lpDosHeader = (PIMAGE_DOS_HEADER)hMoudleBase;
PIMAGE_NT_HEADERS32 lpNtHeader = (PIMAGE_NT_HEADERS)((DWORD)hMoudleBase+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)hMoudleBase+(DWORD)lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
PDWORD lpdwFunName = (PDWORD)((DWORD)hMoudleBase +(DWORD)lpExports->AddressOfNames);
PWORD lpwOrd = (PWORD)((DWORD)hMoudleBase +(DWORD)lpExports->AddressOfFunctions);
PDWORD lpdwFunAddr = (PDWORD)((DWORD)hMoudleBase +(DWORD)lpExports->AddressOfFunctions);
DWORD dwLoop = 0;
FARPROC pRet = NULL;
for(;dwLoop<=lpExports->NumberOfNames-1; dwLoop++)
{
char* pFunName = (char*)(lpdwFunName[dwLoop]+(DWORD)hMoudleBase);
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)hMoudleBase);
break;
}
}
return pRet;
}
int EntryMain()
{
//CreateFileA("1.txt",GENERIC_WRITE,0,NULL,CREATE_ALWAYS,0,NULL);
typedef FARPROC(WINAPI* FN_GetProcAddress)
(
_In_ HMODULE hModule,
_In_ LPCSTR lpProcName
);
FN_GetProcAddress fn_GetProcAddress = (FN_GetProcAddress)getProcAddress((HMODULE)getKernel32());//得到GetProAddress的真实地址
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
);
char sFile[] = {'C','r','e','a','t','e','F','i','l','e','A',0};
FN_CreateFileA fn_CreateFileA = (FN_CreateFileA)fn_GetProcAddress((HMODULE)getKernel32(),sFile);
char sNewFile[] = {'1','.','t','x','t','