GetProcAddressWithHash原理和C代码实现

GetProcAddressWithHash原理和C代码实现

Created
Oct 9, 2021 10:07 AM
Tags
攻防
C
这是在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
    }
}