PE学习(十一)第十一章:动态加载技术


kernel32.dll默认很早加载进去,其中有函数用于加载进程,否则进程创建不出。
获取kernel32.dll的基地址:
1.使用vs cmd自带的dumpbin工具:dumpbin /headers kernel32.dll
 还有PEinfo,onlydbg
 XP OS下ntdll.dll,kernel32.dll不会被重定位。
2.从进程地址空间开始搜索
 寻找PE特征字符串,分析导出表,查找GetProcAddress"特征函数“,再对齐即可。
3.从SEH框架开始查找
 OS默认的结构化异常处理程度指向kernel32._except_handler3函数。
4.从PEB开始查找
5.直接从main函数入口时esp指向地址为kernel.dll中的一个值

 

//通过main函数入口里esp指向的返回地址为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


 

;------------------------
; 获取kernel32.dll的基址
; 从PEB结构中搜索kernel32.dll的基地址
; 戚利
; 2010.6.27
;------------------------
    .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',0
szOut   db '%08x',0dh,0ah,0
szBuffer db 256 dup(0)

;代码段
    .code

start:

   assume fs:nothing
   mov eax,fs:[30h] ;获取PEB所在地址
   mov eax,[eax+0ch] ;获取PEB_LDR_DATA 结构指针
   mov esi,[eax+1ch] ;获取InInitializationOrderModuleList 链表头
                     ;第一个LDR_MODULE节点InInitializationOrderModuleList成员的指针
   lodsd             ;获取双向链表当前节点后继的指针
   mov eax,[eax+8]   ;获取kernel32.dll的基地址

   ;输出模块基地址
   invoke wsprintf,addr szBuffer,addr szText,eax
   invoke MessageBox,NULL,addr szBuffer,NULL,MB_OK
   ret
   end start


 

;------------------------
; 获取kernel32.dll的基址
; 从SEH框架空间中搜索kernel32.dll的基地址
; 戚利
; 2010.6.27
;------------------------
    .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',0
szOut   db '%08x',0dh,0ah,0
szBuffer db 256 dup(0)

;代码段
    .code

start:

   assume fs:nothing
   mov eax,fs:[0]
   inc eax   ; 如果eax=0FFFFFFFFh,则设置为0
loc1:  
   dec eax
   mov esi,eax ;ESI指向EXCEPTION_REGISTRATION
   mov eax,[eax]  ;eax=EXCEPTION_REGISTRATION.prev
   inc eax        ;如果eax=0FFFFFFFFh,则设置为0
   jne loc1
   lodsd          ;跳过0FFFFFFFFh
   lodsd          ;获取kernel32._except_handler地址
   xor ax,ax      ;按照10000h对齐,舍入
   jmp loc3

loc2:
   sub eax,10000h         
loc3:

   cmp dword ptr [eax],905A4Dh
   jne loc2

   ;输出模块基地址
   invoke wsprintf,addr szBuffer,addr szText,eax
   invoke MessageBox,NULL,addr szBuffer,NULL,MB_OK
   ret
   end start


 

;------------------------
; 获取kernel32.dll的基址
; 从进程地址空间搜索kernel32.dll的基地址
; 戚利
; 2010.6.27
;------------------------
    .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',0
szOut   db '%08x',0dh,0ah,0
szBuffer db 256 dup(0)

;代码段
    .code

start:

   call loc0
   db 'GetProcAddress',0  ;特征函数名

loc0:
   pop edx            ;edx中存放了特征函数名所在地址
   push edx
   mov ebx,7ffe0000h  ;从高地址开始

loc1:
   cmp dword ptr [ebx],905A4Dh
   JE loc2   ;判断是否为MS DOS头标志

loc5:
   sub ebx,00010000h

   pushad         ;保护寄存器1
   invoke IsBadReadPtr,ebx,2
   .if eax
     popad        ;恢复寄存器1
     jmp loc5
   .endif
   popad          ;恢复寄存器1

   jmp loc1



loc2:   ;遍历导出表
   mov esi,dword ptr [ebx+3ch] 
   add esi,ebx ;ESI指向PE头
   mov esi,dword ptr [esi+78h]
   nop
 
   .if esi==0
     jmp loc5
   .endif
   add esi,ebx ;ESI指向数据目录中的导出表
   mov edi,dword ptr [esi+20h] ;指向导出表的AddressOfNames
   add edi,ebx ;EDI为AddressOfNames数组起始位置
   mov ecx,dword ptr [esi+18h] ;指向导出表的NumberOfNames
   push esi


   xor eax,eax
loc3:
   push edi
   push ecx
   mov edi,dword ptr [edi]
   add edi,ebx  ;edi指向了第一个函数的字符串名起始
   mov esi,edx  ;esi指向了特征函数名起始
   xor ecx,ecx
   mov cl,0eh  ;特征函数名的长度
   repe cmpsb
   pop ecx
   pop edi
   je loc4    ;找到特征函数,转移
   add edi,4  ;edi移动到下一个函数名所在地址
   inc eax    ;eax为计数
   loop loc3

   jmp loc5
loc4:
   ;特征函数匹配成功,输出模块基地址
    
    invoke wsprintf,addr szBuffer,addr szText,ebx
    invoke MessageBox,NULL,addr szBuffer,NULL,MB_OK
    ret
    end start


 

;------------------------
; 无导入表的HelloWorld
; 戚利
; 2010.6.27
;------------------------
    .386
    .model flat,stdcall
    option casemap:none

include    windows.inc


;声明函数
_QLGetProcAddress typedef proto :dword,:dword  
;声明函数引用    
_ApiGetProcAddress  typedef ptr _QLGetProcAddress

_QLLoadLib        typedef proto :dword
_ApiLoadLib       typedef ptr _QLLoadLib

_QLMessageBoxA    typedef proto :dword,:dword,:dword,:dword
_ApiMessageBoxA   typedef ptr _QLMessageBoxA

;数据段
    .data

szText         db  'HelloWorldPE',0
szGetProcAddr  db  'GetProcAddress',0
szLoadLib      db  'LoadLibraryA',0
szMessageBox   db  'MessageBoxA',0

user32_DLL     db  'user32.dll',0,0

;定义函数
_getProcAddress _ApiGetProcAddress  ?             
_loadLibrary    _ApiLoadLib         ?
_messageBox     _ApiMessageBoxA     ?


hKernel32Base   dd  ?
hUser32Base     dd  ?
lpGetProcAddr   dd  ?
lpLoadLib       dd  ?


;代码段
    .code
;------------------------------------
; 根据kernel32.dll中的一个地址获取它的基地址
;------------------------------------
_getKernelBase  proc _dwKernelRetAddress
   local @dwRet

   pushad
   mov @dwRet,0

   ;查找指令所在页的边界,以1000h对齐   
   mov edi,_dwKernelRetAddress
   and edi,0ffff0000h  

   .repeat

     ;找到kernel32.dll的dos头
     .if word ptr [edi]==IMAGE_DOS_SIGNATURE  
        mov esi,edi
        add esi,[esi+003ch]

        ;找到kernel32.dll的PE头标识
        .if word ptr [esi]==IMAGE_NT_SIGNATURE 
          mov @dwRet,edi
          .break
        .endif
     .endif
     sub edi,010000h
     .break .if edi<070000000h
   .until FALSE
   popad
   mov eax,@dwRet
   ret
_getKernelBase  endp   

;-------------------------------
; 获取指定字符串的API函数的调用地址
; 入口参数:_hModule为动态链接库的基址
;           _lpApi为API函数名的首址
; 出口参数:eax为函数在虚拟地址空间中的真实地址
;-------------------------------
_getApi proc _hModule,_lpApi
   local @ret
   local @dwLen

   pushad
   mov @ret,0
   ;计算API字符串的长度,含最后的零
   mov edi,_lpApi
   mov ecx,-1
   xor al,al
   cld
   repnz scasb
   mov ecx,edi
   sub ecx,_lpApi
   mov @dwLen,ecx

   ;从pe文件头的数据目录获取导出表地址
   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,_lpApi
     mov ecx,@dwLen
     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 @ret,eax

_ret:
   assume esi:nothing
   popad
   mov eax,@ret
   ret
_getApi endp

start:
    ;取当前函数的堆栈栈顶值
    mov eax,dword ptr [esp]
    ;获取kernel32.dll的基地址
    invoke _getKernelBase,eax
    mov hKernel32Base,eax

    ;从基地址出发搜索GetProcAddress函数的首址
    invoke _getApi,hKernel32Base,addr szGetProcAddr
    mov lpGetProcAddr,eax
    mov _getProcAddress,eax   ;为函数引用赋值 GetProcAddress

    ;使用GetProcAddress函数的首址
    ;传入两个参数调用GetProcAddress函数
    ;获得LoadLibraryA的首址

    invoke _getProcAddress,hKernel32Base,addr szLoadLib
    mov _loadLibrary,eax

    ;使用LoadLibrary获取user32.dll的基地址
    invoke _loadLibrary,addr user32_DLL
    mov hUser32Base,eax

    ;使用GetProcAddress函数的首址,获得函数MessageBoxA的首址
    invoke _getProcAddress,hUser32Base,addr szMessageBox
    mov _messageBox,eax   ;调用函数MessageBoxA
    invoke _messageBox,NULL,offset szText,NULL,MB_OK

    ret
    end start


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值