样正常工作,你可以利用C提供的全套功能。确保数据为局部变量,一切都OK了。
使用库函数
我将这篇文章专注于Windows环境的shellcode。上面所提及的一些规则也适用于Unix系统。Windows环境下的shellcode会更复杂一点,因为我们没有一致公开的方法进行系统调用,就像在Unix中仅需几条汇编代码就可以的那样(对int 80h的调用)。我们需要利用DLL中提供的API函数,来进行系统调用做些像读写文件、网络通信这样的事。这些DLL最终会进行必要的系统调用,而它的实现细节几乎随着每次Windows的发布而变化。像《The Shellcoder’s Handbook》这样的标榜性著作描绘了搜寻内存中DLL和函数的方法。如果想将shellcode做到在不同Windows版本间的可移植性,有两个函数是必须的:1、查找kernel32.dll的函数;2、实现GetProcAddress()函数或者查找GetProcAddress()地址的函数。我所提供的实现是基于hash的而非字符串的比较,下面我将提供用于shellcode的hash实现,并做个简短的说明。
Hash函数
在shellcode中,使用hash进行函数的查询是比较普遍的。较流行的ROR13 hash方法是最常用的,它的实现也用在了《The Shellcoder’s Handbook》中。它的基本思想是当我们要查询一个名为“MyFunction”的函数时,不是将字符串存放在内存中,对每个函数名进行字符串的比对,而是生成一个32位的hash值,将每个函数名进行hash比对。这并不能减小运行时间,但是可以节省shellcode的空间,也具有一定的反逆向功效。下面我提供了ASCII和Unicode版本的ROR13 hash实现:
DWORD __stdcall unicode_ror13_hash(const WCHAR *unicode_string)
{
DWORD hash = 0;
while (*unicode_string != 0)
{
DWORD val = (DWORD)*unicode_string++;
hash = (hash >> 13) | (hash << 19); // ROR 13
hash += val;
}
return hash;
}
DWORD __stdcall ror13_hash(const char *string)
{
DWORD hash = 0;
while (*string) {
DWORD val = (DWORD) *string++;
hash = (hash >> 13)|(hash << 19); // ROR 13
hash += val;
}
return hash;
}
查找DLL
有3个链表可以用来描述内存中加载的DLL:
InMemoryOrderModuleList、InInitializationOrderModuleList和InLoadOrderModuleList。它们都在PEB(进程环境块)中。在你的shellcode中,用哪个都可以,我所用的是InMemoryOrderModuleList。需要如下的两条内联汇编来访问PEB:
PPEB __declspec(naked) get_peb(void)
{
__asm {
mov eax, fs:[0x30]
ret
}
}
现在我们已经获取了PEB,可以查询内存中的DLL了。唯一的一直存在于Windows进程内存中的DLL是ntdll.dll,但kernel32.dll会更方便一点,并且在99.99%的Windows进程中(Win32子系统)都可用。下面我提供的代码实现会查询module列表,利用unicode的ROR13 hash值查到kernel32.dll。
HMODULE __stdcall find_kernel32(void)
{
return find_module_by_hash(0x8FECD63F);
}
HMODULE __stdcall find_module_by_hash(DWORD hash)
{
PPEB peb;
LDR_DATA_TABLE_ENTRY *module_ptr, *first_mod;
peb = get_peb();
module_ptr = (PLDR_DATA_TABLE_ENTRY)peb->Ldr->InMemoryOrderModuleList.Flink;
first_mod = module_ptr;
do {
if (unicode_ror13_hash((WCHAR *)module_ptr->FullDllName.Buffer) == hash)
return (HMODULE)module_ptr->Reserved2[0];
else
module_ptr = (PLDR_DATA_TABLE_ENTRY)module_ptr->Reserved1[0];
} while (module_ptr && module_ptr != first_mod); // because the list wraps,
return INVALID_HANDLE_VALUE;
}
这里所提供的find_module_by_hash函数可以利用dll名称的hash值找到任意的加载在内存中的DLL。如果要加载一个新的原本不再内存中的DLL,就需要使用kernel32.dll中的LoadLibrary函数。要找到LoadLibrary函数,我们就需要实现GetProcAddress函数。下面的代码实现利用函数名的hash值在加载的dll中查找函数:
FARPROC __stdcall find_function(HMODULE module, DWORD hash)
{
IMAGE_DOS_HEADER *dos_header;
IMAGE_NT_HEADERS *nt_headers;
IMAGE_EXPORT_DIRECTORY *export_dir;
DWORD *names, *funcs;
WORD *nameords;
int i;
dos_header = (IMAGE_DOS_HEADER *)module;
nt_headers = (IMAGE_NT_HEADERS *)((char *)module + dos_header->e_lfanew);
export_dir = (IMAGE_EXPORT_DIRECTORY *)((char *)module + nt_headers->OptionalHeade