PE学习(六)第六章 栈与重定位表 实例栈溢出、模拟加载器加载DLL、遍历重定位表

第六章 栈与重定位表
16bit OS 存在长调用 lcall push cs,ip    相应的iret pop ip, cs  而call/ret only focus ip register
32bit OS 因为32寄存器可以访问4G空间,可以长短调用被忽略,只关注eip

实模式:段地址+程序偏移地址  SS::SP= SS<<16+SP
保护模式:段选择子+程序偏移地址 原来的段寄存器称为段选择子,指向是 GDT/LDT(全局描述符表/局部描述符表)

call 0xXXXXXXXX   ;//push 后面的返回地址  再 jump 0xXXXXXXXX

push ebp
mov ebp,esp  ;//这两句是masm32中伪关键字proc,和C中的函数一样,翻译为汇编时自动会加上这两行还有leave
...
leave 等价 mov esp,ebp pop ebp
上面这3行是 编译器为proc做的

ret  ;//pop eip

//当只有一个要访问.text段中的数据时,需要设置段属性
C:\testmasm\example\666>ml /coff HelloWorld1.asm /link -subsystem:windows -section:.text,ERW

window加载器发现IMAGE_OPTIONAL_HEADER32.ImageBase被占用时,会根据重定位表修改所有的重定位信息

//栈溢出导致部分代码未执行
    .386
    .model flat,stdcall
    option casemap:none

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

;数据段
    .data
szTextHelloWord     db  'HelloWorld',0
szTextOverData		 db  'Test overwrite ret address to simulate stack overflow!',0
szShellCode				db	'oneArg',0

;代码段
    .code
_overWrite proc _lpSrc
	;_lpSrc,retAddress,ebp
	pushad
	mov [ebp+4],offset someAddress
	popad
	ret
_overWrite endp

start:
    invoke _overWrite,addr szShellCode
    invoke MessageBox,NULL,offset szTextHelloWord,NULL,MB_OK
someAddress:
    invoke MessageBox,NULL,offset szTextOverData,NULL,MB_OK
    invoke ExitProcess,NULL
end start


 

//no_exist_import_realloc_data.asm
;------------------------
; 无导入表、无数据段、无重定位信息的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


;代码段
    .code


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  ?


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

   pushad

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

   .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]
    push eax
    call @F   ; 免去重定位
@@:
    pop ebx
    sub ebx,offset @B

    pop eax
    ;获取kernel32.dll的基地址
    invoke _getKernelBase,eax
    mov [ebx+offset hKernel32Base],eax

    ;从基地址出发搜索GetProcAddress函数的首址
    mov eax,offset szGetProcAddr
    add eax,ebx

    mov edi,offset hKernel32Base
    mov ecx,[ebx+edi]


    invoke _getApi,ecx,eax
    mov [ebx+offset lpGetProcAddr],eax

    ;为函数引用赋值 GetProcAddress
    mov [ebx+offset _getProcAddress],eax   

    ;使用GetProcAddress函数的首址
    ;传入两个参数调用GetProcAddress函数,获得LoadLibraryA的首址
    mov eax,offset szLoadLib
    add eax,ebx
   
    mov edi,offset hKernel32Base
    mov ecx,[ebx+edi]
    
    mov edx,offset _getProcAddress
    add edx,ebx
    
    ;模仿调用 invoke GetProcAddress,hKernel32Base,addr szLoadLib
    push eax
    push ecx
    call dword ptr [edx]   

    mov [ebx+offset _loadLibrary],eax

    ;使用LoadLibrary获取user32.dll的基地址

    mov eax,offset user32_DLL
    add eax,ebx

    mov edi,offset _loadLibrary
    mov edx,[ebx+edi]
    
    push eax
    call edx   ; invoke LoadLibraryA,addr _loadLibrary

    mov [ebx+offset hUser32Base],eax

    ;使用GetProcAddress函数的首址,获得函数MessageBoxA的首址
    mov eax,offset szMessageBox
    add eax,ebx
   
    mov edi,offset hUser32Base
    mov ecx,[ebx+edi]
    
    mov edx,offset _getProcAddress
    add edx,ebx


    ;模仿调用 invoke GetProcAddress,hUser32Base,addr szMessageBox
    push eax
    push ecx
    call dword ptr [edx]   
    mov [ebx+offset _messageBox],eax

    ;调用函数MessageBoxA
    mov eax,offset szText
    add eax,ebx

    mov edx,offset _messageBox
    add edx,ebx

    ;模仿调用 invoke MessageBoxA,NULL,addr szText,NULL,MB_OK    
    push MB_OK
    push NULL
    push eax
    push NULL
    call dword ptr [edx]   

    ret
    end start


 

//遍历重定位表
;--------------------
; 获取PE文件的重定位信息
;--------------------
_getRelocInfo proc  _lpFile,_lpPeHead,_dwSize
  local @szBuffer[1024]:byte
  local @szSectionName[16]:byte

  pushad
  mov esi,_lpPeHead
  assume esi:ptr IMAGE_NT_HEADERS
  mov eax,[esi].OptionalHeader.DataDirectory[8*5].VirtualAddress
  .if !eax
    invoke _appendInfo,addr szMsgReloc4
    jmp _ret
  .endif
  push eax
  invoke _RVAToOffset,_lpFile,eax
  add eax,_lpFile
  mov esi,eax
  pop eax
  invoke _getRVASectionName,_lpFile,eax
  invoke wsprintf,addr @szBuffer,addr szMsgReloc1,eax
  invoke _appendInfo,addr @szBuffer
  assume esi:ptr IMAGE_BASE_RELOCATION
  ;循环处理每个重定位块
  .while [esi].VirtualAddress
    cld			;//set DF=0 向高地址步增
    lodsd   ;eax=[esi].VirtualAddress
    mov ebx,eax
    lodsd   ;eax=[esi].SizeofBlock
    sub eax,sizeof IMAGE_BASE_RELOCATION  ;块总长度-两个dd
    shr eax,1                             ;然后除以2,得到重定位项数量
                                          ;除以2是因为重定位项是word
    push eax
    invoke wsprintf,addr @szBuffer,addr szMsgReloc2,ebx,eax
    invoke _appendInfo,addr @szBuffer
    pop ecx                               ;重定位项数量
    xor edi,edi
    .repeat
      push ecx
      lodsw
      mov cx,ax
      and cx,0f000h    ;得到高四位
      .if cx==03000h   ;重定位地址指向的双字的32位都需要休正
        and ax,0fffh
        movzx eax,ax
        add eax,ebx    ;得到修正以前的偏移,该偏移加上装入时的基址就是绝对地址
      .else            ;该重定位项无意义,仅用来作为对齐
        mov eax,-1
      .endif
      invoke wsprintf,addr @szBuffer,addr szMsgReloc3,eax
      inc edi
      .if edi==8       ;每显示8个项目换行
        invoke lstrcat,addr @szBuffer,addr szCrLf
        xor edi,edi
      .endif
      invoke _appendInfo,addr @szBuffer
      pop ecx
    .untilcxz
    .if edi
      invoke _appendInfo,addr szCrLf
    .endif
  .endw
_ret:
  assume esi:nothing
  popad
  ret
_getRelocInfo endp


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值