超简单的C++编写Windows平台ShellCode的框架
2020-04-13 02:50:37 Author: bbs.pediy.com(查看原文) 阅读量:338 收藏

背景

来看雪很长时间了,在这里学到了不少东西,本人菜鸡一只,一直在潜水。

周末整理了一下笔记,整理出这个ShellCode编写框架,取名EasyShellCode,不敢独享,分享给大家,希望有用。

功能

可以向写普通C++代码一样写ShellCode,写完直接编译即可以生成相应的代码,直接引用即可。( 字符串还是要拆开写的)

DWORD WINAPI EasyShellCodeMain(LPVOID pParam)
{
	char out[] = { 'a', 'b' , 'c', '\0' };
	API(KERNEL32, OutputDebugStringA)(out);

	return 0;
}

所有的API都可以像上面这样直接调用,程序会自动生成相应的库的代码

编译后,程序会自动生成Code.h头文件,直接引用即可.

#include "Code.h"

typedef DWORD (WINAPI* TStartFun)(LPVOID lpParam);
int main()
{
	PBYTE lpBuffer = (PBYTE)VirtualAlloc(0, sizeof(g_ShellCode),  MEM_COMMIT,PAGE_EXECUTE_READWRITE);
	CopyMemory(lpBuffer, g_ShellCode, sizeof(g_ShellCode));
	return ((TStartFun)lpBuffer)(0);
}

原理

首先是API这个宏:

#define API(DLLNAME, FUNNAME) ((decltype(&FUNNAME))WinApi::ForceCalc<WinApi::ELFNoCaseHash(#DLLNAME ".dll"), WinApi::CalcHash(#FUNNAME)>())

这里用到了 C++11的decltype(下面还会用到C++14的constexpr,这也是要求高版本编译器的原因)如果不用高版本编译器,函数类型的声明就要自己写.

整体思想就是通过计算字符串的hash值去找到对应的模块.其中WinApi::ForceCalc是一个模板函数,定义如下:

template<DWORD dwDllName, DWORD dwFunName>
__forceinline PVOID ForceCalc()
{
	return GetApiAddr(dwDllName, dwFunName);
}

这个函数看起来没什么用, 但是把它去掉就能发现问题了. 为什么呢? 得说完hash计算方法后再说.

下面继续看 ELFNoCaseHash 函数,因为Dll的名字是不区分大小写的,所以这里用了一个不区分大小写的hash生成函数

constexpr UINT ELFNoCaseHash(LPCSTR str, UINT nLen = UINT_MAX)
{
	if (nullptr == str) {
		return -1;
	}
	UINT hash = 0;
	UINT test = 0;
	while (*str && nLen) {
		hash = (hash << 4) + MiniToUpper((*str++));
		if ((test = hash & 0xF0000000) != 0) {
			hash = ((hash ^ (test >> 24)) & (~test));
		}
		nLen--;
	}

	return hash;
}

精髓就是constexpr, 这个关键字会让编译器尽可能在编译器计算值,因为字符串是个常量,所以在编译器计算hash是可能的,这也就导致了编译到最后代码中的不是一个字符串常量,而是一个hash整数.

再来说为什么要有WinApi::ForceCalc,简单来说就是  constexpr编译器不保证真的在编译器计算出值(有的是因为根本计算不出来值,有的是因为它偷懒了)。怎么办呢?  就是通过这个函数模板让它强制计算(它不计算就不知道怎么生成模板函数),也就是名字的由来。

剩下的技巧就没什么了,就是一个根据hash值找对应函数的方法了.  主要靠 GetApiAddr函数实现.

这个函数先去PEB中查找已经加载的模块(请查看GetModuleHandleFromPeb 函数实现), 如果找到了,那么直接去模块中找函数地址. 反之如果没找到,那么就去加载它(请查看 GetModuleHandleFromDllNum 函数实现).具体去看下代码吧,就没什么技巧了.

项目支持多文件,需要添加文件.

如之前所说,Main.cpp中的 EasyShellCodeMain就是ShellCode的入口函数,如果用到其他文件,直接添加即可.

但要保证AAAAAStart.cpp是第一个,ZZZZZEnd.cpp是最后一个(一般只要保证文件名是大写字母开头就行)。


支持X86和X64。

代码

https://gitee.com/on_my_note/EasyShellCode

PS

代码开放给大家学习,

[推荐]看雪企服平台,提供项目众包、安全分析、定制项目开发、APP等级保护、渗透测试等安全服务!

最后于 10小时前 被独—行编辑 ,原因:


文章来源: https://bbs.pediy.com/thread-258775.htm
如有侵权请联系:admin#unsafe.sh