这是在shellcode种常见到的技术,用来通过函数的hash值来获取到某些API的内存地址,避免直接只用函数名,因为某些检测软件就是通过函数名来check的。
输出表结构:
IMAGE_EXPORT_DIRECTORY STRUCT
{
Characteristics
TimeDateStamp
MajorVersion
MinorVersion
Name ->模块的真实名称
Base ->基数;序数减去这个基数就是函数地址数组的索引值
NumberOfFunctions ->AddressOfFunctions阵列中的元素个数
NumberOfNames ->AddressOfNames阵列中的元素个数
AddressOfFunctions ->指向函数地址数组
AddressOfNames ->函数名字的指针地址
AddressOfNameOrdinals ->指向输出序列号数组
}
GetProcAddress函数实现原理(参数是:模块句柄,函数名):
- 通过EAT结构找到AddressOfNames字段,遍历这个链表,依次与函数名比较,是否是我们要找的函数名,如果是,记下当前索引
- 待着索引到AddressOfNameOrdinals数组中寻找函数在AddressOfFunctions的下标,AddressOfNameOrdinals[index],就得到了函数地址在AddressOfFunctions的下标
- 然后AddressOfFunctions中查找地址,最后返回该地址
ps:函数输出有两种方式,函数名输出和下标输出
Hash寻找API地址和GetProcAddress函数实现原理大致是一样的,只不过在遍历AddressOfNames表时进行函数名对比时用的是hash,先把获取到的函数名进行hash转换,然后与我们的hash值进行比较,若一直则返回地址。
x64实现
1、获取kernel32.dll在内存中的地址。
- 通过gs寄存器找到TEB的位置:gs:[0x30]
- 在TEB结构中找到PEB:TEB+0x60
- 在PEB结构中找到Ldr:PEB+0x18
- 在Ldr中找到InInitializationOrderModuleList:Ldr+0x30
- 接着在InInitializationOrderModuleList列表中找到kernel32.dll的地址
HMODULE GetKernel32Base() {
HMODULE hKer32 = NULL;
__asm
{
xor rdx, rdx
mov rdx, gs: [rdx + 0x60]//peb
mov rdx, [rdx + 0x18]//LDR
mov rdx, [rdx + 0x30] //
mov rdx,[rdx]
mov rdx,[rdx]
mov rdx,[rdx+0x10]
mov hKer32, rdx
}
return hKer32;
}
2、通过遍历Addressofname来筛选出我们想要的到的API
GetProcHash算法并不是唯一的。但是寻找时算法需要一致。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<Windows.h>
IMAGE_DOS_HEADER* dos_header;
IMAGE_NT_HEADERS* nt_header;
IMAGE_SECTION_HEADER* section_header;
IMAGE_IMPORT_DESCRIPTOR* iid;
HMODULE GetKernel32Base() {
HMODULE hKer32 = NULL;
__asm
{
xor rdx, rdx
mov rdx, gs: [rdx + 0x60]//peb
mov rdx, [rdx + 0x18]//LDR
mov rdx, [rdx + 0x30] //
mov rdx,[rdx]
mov rdx,[rdx]
mov rdx,[rdx+0x10]
mov hKer32, rdx
}
return hKer32;
}
DWORD GetProcHash(char* lpProcName) {
char* p = lpProcName;
DWORD result = 0;
do {
result = (result << 7) - result;
result += *p;
} while (*p++);
return result;
}
int main()
{
DWORD szLoadLibraryA = 0x0148be54;
HMODULE Handle_Kernel32 = GetKernel32Base();
printf("hKernel32: 0x%llx\n", Handle_Kernel32);
PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)Handle_Kernel32;
PIMAGE_NT_HEADERS NtHeader = (PIMAGE_NT_HEADERS)((char*)DosHeader + DosHeader->e_lfanew);//通过Dos头找到Nt头
PIMAGE_EXPORT_DIRECTORY ExportTable = (PIMAGE_EXPORT_DIRECTORY)((char*)Handle_Kernel32 + NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
//解析导出表
//获取函数地址数组
PDWORD pAddressOfFunc = (PDWORD)((char*)Handle_Kernel32 + ExportTable->AddressOfFunctions);
//获取名字数组
PDWORD pAddressOfName = (PDWORD)((char*)Handle_Kernel32 + ExportTable->AddressOfNames);
//获取序号导出表
PWORD pAddressOfNameOrdinal = (PWORD)((char*)Handle_Kernel32 + ExportTable->AddressOfNameOrdinals);
int Funcnumber = ExportTable->NumberOfFunctions;
printf("Functione Number:%llx\n", Funcnumber);
for (int i = 0; i < Funcnumber; i++)
{
//printf("%llx,%s\n", GetProcHash((char*)Handle_Kernel32 + pAddressOfName[i]), (char*)Handle_Kernel32 + pAddressOfName[i]);
if (szLoadLibraryA == GetProcHash((char*)Handle_Kernel32 + pAddressOfName[i])) {
printf("index:%x\n", i);
printf("addr,%llx\n", (char*)Handle_Kernel32+pAddressOfFunc[pAddressOfNameOrdinal[i]]);
}
}
return 0;
}
X86实现
#include<stdio.h>
#include<Windows.h>
int main_1() {
printf("begin");
_asm {
mov eax, fs: [0x18] ;//找到teb
mov eax, [eax + 0x30];//peb
mov eax, [eax + 0x0c];//PEB_LDR_DATA
mov eax, [eax + 0x0c];//LIST_ENTRY 主模块
mov eax, [eax];//ntdll
mov eax, [eax];//kernel32
mov eax, dword ptr[eax + 0x18];//kernel32基址
mov ebx, eax;
mov ecx, [ebx + 0x3c];//DosHeader->e_lfanew
add ecx, ebx; //ecx = NT header
add ecx, 0x18;//_IMAGE_OPTIONAL_HEADER
add ecx, 0x60;//datadirectory
mov edx, dword ptr[ecx];//VirtualAddress
add edx, ebx;//_IMAGE_EXPORT_DIRECTORY
add edx, 0x1c;
mov ecx, dword ptr[edx];//addressofFunc
add ecx, ebx;
mov ecx, dword ptr[ecx + 4 * 0x3c6]
add ecx, ebx;
push 0x6c;
push 0x6c642e31;
push esp;
call ecx;//call LoadLibrary
add esp, 0xc;
mov eax, fs: [0x18] ;//找到teb
mov eax, [eax + 0x30];//peb
mov eax, [eax + 0x0c];//PEB_LDR_DATA
mov eax, [eax + 0x0c];//LIST_ENTRY 主模块
mov eax, [eax];//ntdll
mov eax, [eax];//kernel32
mov eax, dword ptr[eax + 0x18];//kernel32基址
mov ebx, eax;
mov ecx, [ebx + 0x3c];//DosHeader->e_lfanew
add ecx, ebx; //ecx = NT header
add ecx, 0x18;//_IMAGE_OPTIONAL_HEADER
add ecx, 0x60;//datadirectory
mov edx, dword ptr[ecx];//VirtualAddress
add edx, ebx;//_IMAGE_EXPORT_DIRECTORY
add edx, 0x1c;
mov ecx, dword ptr[edx];//addressofFunc
add ecx, ebx;
mov ecx, dword ptr[ecx + 4 * 0x582]
add ecx, ebx;
push 0xffffff;
call ecx;//call Sleep
}
}