首页 > 代码库 > hash算法搜索获得api函数地址的实现

hash算法搜索获得api函数地址的实现

  我们一般要获得一个函数的地址,通常采用的是明文,例如定义一个api函数字符串"MessageBoxA",然后在GetProcAddress函数中一个字节一个字节进行比较。这样弊端很多,例如如果我们定义一个杀毒软件比较敏感的api函数字符串,那么可能就会增加杀毒软件对我们的程序的判定值,而且定义这些字符串还有一个弊端是占用的字节数较大。我们想想如何我们的api函数字符串通过算法将它定义成一个4字节的值,然后在GetProcAddress中把AddressOfNames表中的每个地址指向的api字符串通过我们的算法压缩成4字节值后,与我们之前定义的4字节值进行判断,如果匹配成功则读取函数地址。

  我们来看一种rol 3移位算法,这个算法是每次将目的地址循环向左移动3位,然后将低字节与源字符串的每个字节进行异或。算法很精巧方便。还有很多方便小巧的算法,例如ROR 13等算法,其实它和我们上面的基本一样,只不过它将函数名表的字符串通过我们的算法过程获得hash值后与我们之前定义的hash值进行匹配,匹配成功则获得对应函数的地址。

  下面的代码通过hash搜索WinExec函数来运行Clac:

 .386  
    .model flat, stdcall  
    option casemap:none  
      
include windows.inc  
include user32.inc  
include kernel32.inc  
includelib user32.lib  
includelib kernel32.lib  
  
    .const  
szCalc      db calc.exe, 0  
  
    .code  
      
_GetKrnl32 proc  

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 获取kernel32.dll的地址,因为xp和win7不一样
; 故采用比较名称的方式获取地址,具体见Kernel32基地址获得学习
; http://blog.csdn.net/programmingring/article/details/11357393
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    assume fs:nothing  
    mov eax, fs:[30h]  
    mov eax, [eax + 0ch]  
    mov eax, [eax + 1ch]        ; 第一个LDR_MODULE
    
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 压入kernel32.dll的unicode字符串
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  
    push dword ptr 006ch  
    push dword ptr 6c0064h  
    push dword ptr 2e0032h  
    push dword ptr 33006ch  
    push dword ptr 65006eh  
    push dword ptr 720065h  
    push word ptr 006bh  
    mov ebx, esp              ; ebx指向字符串
    
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 开始比较
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>      
_Search:  
    cmp eax, 0  
    jz _NotFound  
    cmp eax, 0ffffffffh  
    jz _NotFound  
    lea esi, [eax + 1ch]          ; esi指向UNICODE_STRING结构
    xor ecx, ecx              
    mov cx, 13              ; 比较的长度
    mov esi, [esi + 4]          ; 获得UNICODE_STRING结构的Buffer成员
    mov edi, ebx              ; edi指向kernel32unicode字符串
    repz cmpsw  
    or ecx, ecx              ; ecx减完说明相等
    jz _Found  
    mov eax, [eax]              ; 获取下一个LDR_MODULE
    jmp _Search  
      
_NotFound:  
    mov eax, 0ffffffffh  
    jmp _Over  
      
_Found:  
    mov eax, [eax + 08h]          ; 获得地址
      
_Over:  
    add esp, 26  
    ret  
      
_GetKrnl32 endp  
  
_GetRolHash proc _lpApiString  
      
    mov eax, _lpApiString 
    push esi  
    xor edx, edx  
    xchg eax, esi              ; esi = _lpApiString
    cld                  ; 递增
      
_Next:      
    lodsb                  ; 从esi指向的地址中取一个字符
    test al, al              ; 比较是否为0
    jz _Ret  
    rol edx, 3              ; 左循环移位3位
    xor dl, al              ; 低字节和字符异或
    jmp _Next  
      
_Ret:  
    xchg eax, edx              ; eax = 处理后的_lpApiString的hash
    pop esi  
    ret  
      
_GetRolHash endp  
  
_GetApi proc _hDllHandle, _iHashApi  
      
    push ebp  
    mov eax, _hDllHandle  
    mov ecx, _iHashApi  
    mov ebx, eax  
    mov edi, ecx  
      
    mov eax, [ebx + 3ch]  
    mov esi, [ebx + eax + 78h]          ; Get Export RVA  
    lea esi, [esi + ebx + IMAGE_EXPORT_DIRECTORY.NumberOfNames]  
    lodsd  
    xchg eax, edx                   ; edx = NumberOfNames  
    lodsd  
    push eax                        ; [esp] = AddressOfFunctions  
    lodsd  
    xchg eax, ebp                      ; ebp = AddressOfNames  
    lodsd  
    xchg eax, ebp                   ; ebp = AddressOfNameOrdinals, eax = AddressOfNames  
    add eax, ebx  
    xchg eax, esi                   ; esi = AddressOfNames  
      
_LoopScas:  
    dec edx  
    js _Ret  
    lodsd                  ; eax = api名称的rva
    add eax, ebx              ; eax = api名称的实际地址
    push edx  
    invoke _GetRolHash, eax         ; 计算hash字符串  
      
    pop edx  
    cmp eax, edi              ; 比较和传入的hash参数是否相等
    jz _GetAddr  
      
    add ebp, 2              ; 递增,和esi相对应
    jmp _LoopScas  
      
_GetAddr:  
    movzx eax, word ptr [ebp + ebx]      ; 取得序号值
    shl eax, 2              ; 乘4
    add eax, [esp]              ; 在AddressOfFunctions取得相应序号值位置的值
    mov eax, [ebx + eax]          ; 取得函数地址
    add eax, ebx  
      
_Ret:  
    pop ecx  
    pop ebp  
    ret  
      
_GetApi endp  
  
_WinMain proc  
  
    invoke _GetKrnl32  
    invoke _GetApi, eax, 016ef74bh      ; "WinExec"的hash  
    push SW_SHOW  
    lea ebx, szCalc  
    push ebx  
    call eax  
    ret  
      
_WinMain endp
  
start:  
    call _WinMain  
    invoke ExitProcess, 0  
      
    end start  

 

hash算法搜索获得api函数地址的实现