天天看點

動态加載技術

文章目錄

  • ​​一:動态庫技術​​
  • ​​1.DLL靜态調用​​
  • ​​2.DLL動态調用​​
  • ​​二:動态加載技術​​
  • ​​1.導出函數起始位址​​
  • ​​2.在程式設計中使用動态加載技術​​
  • ​​1.查找kernel32.dll的基位址​​
  • ​​2.擷取GetProcAddress位址​​
  • ​​3.在代碼中使用擷取的函數位址程式設計​​

動态加載技術可以讓程式設計者脫離複雜的導入表結構,在程式空間中構造類似于導入表的調用引入函數機制。

在了解這一知識前最好先了解windows虛拟記憶體管理,詳情見我部落格

一:動态庫技術

動态加載技術

1.DLL靜态調用

又稱隐式調用。隐式加載就是在程式編譯的時候就将dll編譯到可執行檔案中。調用一個動态連結庫的函數通常采取的方式是:把産生動态連結庫時産生的".lib"庫檔案和".inc"包含的檔案加入到應用程式的工程中,想使用DLL中的函數時,直接使用函數的名字即可,例如,加入user32.lib user32.inc調用MessageBox函數

2.DLL動态調用

又稱顯式調用。通過API函數加載和解除安裝DLL來達到調用DLL函數的目的。與動态庫調用有關的函數主要包括:

  1. LoadLibrary(或MFC的AfxLoadLibrary),裝載動态連結庫
  2. GetProcAddress,擷取要引入函數的VA(虛拟記憶體位址),将符号名或辨別号轉換為DLL内部位址
  3. FreeLibrary(或MFC的AfxFreeLibrary),釋放動态連結庫
//1.加載動态庫
    HINSTANCE  m_hDll = LoadLibrary(_T("MFCDLL1.dll"));
 
    //2.根據函數名擷取函數位址
    typedef IHpDllWin* (*hpDllFun)();
    hpDllFun pShowDlg = (hpDllFun)GetProcAddress(m_hDll, "ShowDialog");
 
    //3.擷取導出類對象指針,調用導出函數
    IHpDllWin* m_hpwin = pShowDlg();
 
    //4.解除安裝dll
    FreeLibrary(hDll);      

二:動态加載技術

1.導出函數起始位址

程式引進動态連結庫的最終目的是要調用動态連結庫裡的函數代碼,是以,擷取動态連接配接庫裡的導出函數起始位址是動态加載技術的關鍵。

現在假設user32.dll被動态裝載到記憶體的0x77DF0000處,那麼MessageBoxA的入口位址VA就是:

0x77DF000 + 0x00026544 = 0x7E16544

如果一個函數在程序空間中的VA 确定以後,最簡單拿的調用方式就是通過一下寫死方式來調用

push xx       ;顯示往棧裡壓入該函數的參數,個數由調用的函數決定
......
mov eax,77E16544;
call eax      

2.在程式設計中使用動态加載技術

1.查找kernel32.dll的基位址

.386
    .model flat,stdcall
    option casemap:none

include    windows.inc
include    user32.inc
includelib user32.lib
include    kernel32.inc
includelib kernel32.lib

;資料段
    .data
szText         db  'kernel32.dll在本程式位址空間的基位址為:%08x',0dh,0ah,0
kernel32Base   dd  ?
szBuffer       db 256 dup(0)

;代碼段
    .code
_getKernelBase  proc _dwKernelRetAddress
   local @dwRet

   pushad
   mov @dwRet,0
   
   mov edi,_dwKernelRetAddress
   and edi,0ffff0000h  ;查找指令所在頁的邊界,以1000h對齊

   .repeat
     .if word ptr [edi]==IMAGE_DOS_SIGNATURE  ;找到kernel32.dll的dos頭
        mov esi,edi
        add esi,[esi+003ch]
        .if word ptr [esi]==IMAGE_NT_SIGNATURE ;找到kernel32.dll的PE頭辨別
          mov @dwRet,edi
          .break
        .endif
     .endif
     sub edi,010000h
     .break .if edi<070000000h
   .until FALSE
   popad
   mov eax,@dwRet
   ret
_getKernelBase  endp   


start:
    mov eax,dword ptr [esp]
    invoke _getKernelBase,eax
    invoke wsprintf,addr szBuffer,addr szText,eax
    invoke MessageBox,NULL,addr szBuffer,NULL,MB_OK
    ret
    end start      

2.擷取GetProcAddress位址

;------------------------------------------------
; 從記憶體中子產品的導出表中擷取某個 API 的入口位址
;------------------------------------------------
_getApi  proc  _hModule,_lpszApi
  local @dwReturn,@dwStringLen
  
  pushad
  mov @dwReturn,0
  call @F
@@:
  pop ebx
  sub ebx,offset @B

  ;建立用于錯誤處理的SEH結構
  assume fs:nothing
  push ebp
  lea eax,[ebx+offset _ret]
  push eax
  lea eax,[ebx+offset _SEHHandler]
  push eax
  push fs:[0]
  mov fs:[0],esp

  ;計算API字元串的長度(注意帶尾部的0)
  mov edi,_lpszApi
  mov ecx,-1
  xor al,al
  cld
  repnz scasb
  mov ecx,edi
  sub ecx,_lpszApi
  mov @dwStringLen,ecx
  ;從DLL檔案頭的資料目錄中擷取導出表的位置
  mov esi,_hModule
  add esi,[esi+3ch]
  assume esi:ptr IMAGE_NT_HEADERS
  mov esi,[esi].OptionalHeader.DataDirectory.VirtualAddress
  add esi,_hModule
  assume esi:ptr IMAGE_EXPORT_DIRECTORY
  mov ebx,[esi].AddressOfNames
  add ebx,_hModule
  xor edx,edx
  .repeat
    push esi
    mov edi,[ebx]
    add edi,_hModule
    mov esi,_lpszApi
    mov ecx,@dwStringLen
    repz cmpsb
    .if ZERO?
      pop esi
      jmp @F
    .endif
    pop esi
    add ebx,4
    inc edx
  .until edx>=[esi].NumberOfNames
  jmp _ret
@@:
  ;API名稱索引->序号索引->位址索引
  sub ebx,[esi].AddressOfNames
  sub ebx,_hModule
  shr ebx,1
  add ebx,[esi].AddressOfNameOrdinals
  add ebx,_hModule
  movzx eax,word ptr [ebx]
  shl eax,2
  add eax,[esi].AddressOfFunctions
  add eax,_hModule
  ;從位址表得到導出函數位址
  mov eax,[eax]
  add eax,_hModule
  mov @dwReturn,eax
_ret:
  pop fs:[0]
  add esp,0ch
  assume esi:nothing
  popad
  mov eax,@dwReturn
  ret
_getApi  endp

start:
    invoke _getApi,hDllKernel32,addr szGetProcAddress   ;擷取GetProcAddress函數的記憶體位址
    mov _GetProcAddress,eax
    ...
    ret
    end start      

3.在代碼中使用擷取的函數位址程式設計

;聲明函數
  _QLMessageBoxA  typedef  proto :dword,:dword,:dword,:dword
  ;聲明函數引用
  _ApiMessageBoxA  typedef  ptr _QLMessageBoxA
  
  ...
  
  ;定義函數
  _messageBox  _ApiMessageBoxA  ?
  ;動态擷取_messageBox的位址
  ...
  ;調用函數
  invoke _messageBox,NULL,offset szText,NULL,MB_OK      

繼續閱讀