PE学习(五)导出表,编写DLL及查看DLL的导出信息

第五章 导出表
typedef struct _IMAGE_NT_HEADERS
{
+00h DWORD Signature
+04h IMAGE_FILE_HEADER FileHeader
+18h IMAGE_OPTIONAL_HEADER32 OptionalHeader
} IMAGE_NT_HEADERS ENDS, *PIMAGE_NT_HEADERS32;
 
IMAGE_NT_HEADERS=4BYTEPE标识符 + IMAGE_FILE_HEADER + IMAGE_OPTIONAL_HEADER32 总共240字节 = 4 + 20 + FileHeader.SizeOfOptionalHeader


Windows加载器将与进程相关的DLL加载到虚拟地址空间以后,会根据导入表中登记的与该动态链接库相关的由INT指向的名称或编号来编译DLL所在虚拟地址空间,
通过函数名或编号查找导出表结构,从而确定该导出函数在虚拟地址空间中起始地址VA,并将该VA覆盖导入表的IAT相关项。
IMAGE_EXPORT_DIRECTORY STRUCT{
 Characteristics DWORD ?
 TimeDateStamp  DWORD ?
 MajorVersion  WORD ?
 MinorVersion  WORD  ?
 nName      DWORD ?  ;000ch - 指向该导出表的文件名字字符串
 nBase      DWORD ?  ;10导出函数的起始序号
 NumberOfFunctions DWORD ? ;14所有的导出函数个数
 NumberOfNames  DWORD ?  ;18以函数名导出的函数个数
 AddressOfFuctions DWORD ? ;1c导出函数地址表RVA 指向全部导出函数的入口地址的起始
 AddressOfNames  DWORD ?  ;20函数名称地址表RVA
 AddressOfNameOrdinal DWORD ? ;24函数序号地址表 AddressOfNameOrdinals指向的是单字数组
}

AddressOfFuctions DWORD ? ;导出函数地址表RVA 指向全部导出函数的入口地址的起始
nBase 并不是从0开始
AddressOfName 指向的位置是一连串的双字值,这些值指向对应定义了函数名的函数的字符串地址。
AddressOfNameOrdinal 与AddressOfName一一对应,指向AddressOfName中对应函数在AddressOfNameOrdinal的索引值 分别的偏移相等


编写DLL
1.winResult.asm // ml /c /coff winResult.asm
2.winResult.def
// link -dll -subsystem:widnows -def:winResult.def winResult.obj
上面生成 winResult.dll  winResult.lib

使用上面的DLL
1.winResult.inc
现在就可以用1.winResult.inc;2. winResult.dll  winResult.lib

2.FistWindow.asm 
//include    winResult.inc
//includelib winResult.lib

操作导出表内容可以覆盖、调整导出函数,还可以增加导出信息来导出dll中出导出的私有数据。

 

 

;------------------------
; 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

MAX_XYSTEPS equ 50
DELAY_VALUE equ 50   ; 动画效果使用的步长
X_STEP_SIZE equ 10
Y_STEP_SIZE equ 9
X_START_SIZE equ 20
Y_START_SIZE equ 10

LMA_ALPHA equ 2
LMA_COLORKEY equ 1
WS_EX_LAYERED equ 80000h

;数据段
    .data
dwCount  dd  ?
Value    dd  ?
Xsize    dd  ?
Ysize    dd  ?
sWth     dd  ?
sHth     dd  ?
Xplace   dd  ?
Yplace   dd  ?
counts   dd  ?
pSLWA    dd  ?
User32   db  'user32.dll',0
SLWA     db  'SetLayeredWindowAttributes',0
;代码段
    .code
;------------------
; DLL入口
;------------------
DllEntry   proc  _hInstance,_dwReason,_dwReserved

        mov eax,TRUE
        ret
DllEntry   endp

;-------------------------------
;  私有函数
;-------------------------------
TopXY proc wDim:DWORD,sDim:DWORD
     shr sDim,1 
     shr wDim,1
     mov eax,wDim
     sub sDim,eax
     mov eax,sDim
     ret
TopXY endp
;-----------------------------------------------------------
; 窗口抖动进入效果
;-----------------------------------------------------------
AnimateOpen proc hWin:DWORD

    LOCAL Rct:RECT

    invoke GetWindowRect,hWin,ADDR Rct
    mov Xsize,X_START_SIZE
    mov Ysize,Y_START_SIZE
    invoke GetSystemMetrics,SM_CXSCREEN
    mov sWth,eax
    invoke TopXY,Xsize,eax
    mov Xplace,eax
    invoke GetSystemMetrics,SM_CYSCREEN
    mov sHth,eax
    invoke TopXY,Ysize,eax
    mov Yplace,eax
    mov counts,MAX_XYSTEPS
aniloop:
    invoke MoveWindow,hWin,Xplace,Yplace,Xsize,Ysize,FALSE
    invoke ShowWindow,hWin,SW_SHOWNA
    invoke Sleep,DELAY_VALUE
    invoke ShowWindow,hWin,SW_HIDE
    add Xsize,X_STEP_SIZE
    add Ysize,Y_STEP_SIZE
    invoke TopXY,Xsize,sWth
    mov Xplace,eax
    invoke TopXY,Ysize,sHth
    mov Yplace,eax
    dec counts
    jnz aniloop
    mov eax,Rct.left
    mov ecx,Rct.right
    sub ecx,eax
    mov Xsize,ecx
    mov eax,Rct.top
    mov ecx,Rct.bottom
    sub ecx,eax
    mov Ysize,ecx
    invoke TopXY,Xsize,sWth
    mov Xplace,eax
    invoke TopXY,Ysize,sHth
    mov Yplace,eax
    invoke MoveWindow,hWin,Xplace,Yplace,Xsize,Ysize,TRUE 
    invoke ShowWindow,hWin,SW_SHOW
    ret 
AnimateOpen endp


;-------------------------
; 窗口抖动退出效果
;-------------------------
AnimateClose proc hWin:DWORD

    LOCAL Rct:RECT


    invoke ShowWindow,hWin,SW_HIDE
    invoke GetWindowRect,hWin,ADDR Rct
    mov eax,Rct.left
    mov ecx,Rct.right
    sub ecx,eax
    mov Xsize,ecx
    mov eax,Rct.top
    mov ecx,Rct.bottom
    sub ecx,eax
    mov Ysize,ecx
    invoke GetSystemMetrics,SM_CXSCREEN
    mov sWth,eax
    invoke TopXY,Xsize,eax
    mov Xplace,eax
    invoke GetSystemMetrics,SM_CYSCREEN
    mov sHth,eax
    invoke TopXY,Ysize,eax
    mov Yplace,eax
    mov counts,MAX_XYSTEPS
aniloop:
    invoke MoveWindow,hWin,Xplace,Yplace,Xsize,Ysize,FALSE 
    invoke ShowWindow,hWin,SW_SHOWNA
    invoke Sleep,DELAY_VALUE
    invoke ShowWindow,hWin,SW_HIDE
    sub Xsize,X_STEP_SIZE
    sub Ysize,Y_STEP_SIZE
    invoke TopXY,Xsize,sWth
    mov Xplace,eax
    invoke TopXY,Ysize,sHth
    mov Yplace,eax
    dec counts
    jnz aniloop

    ret 

AnimateClose endp

;--------------------------------------------
; 窗口淡入效果,仅运行在2000/XP以上操作系统
;--------------------------------------------
FadeInOpen proc hWin:DWORD

    invoke GetWindowLongA,hWin,GWL_EXSTYLE
    or eax,WS_EX_LAYERED
    invoke SetWindowLongA,hWin,GWL_EXSTYLE,eax
    invoke GetModuleHandleA,ADDR User32
    invoke GetProcAddress,eax,ADDR SLWA
    mov pSLWA,eax
    push LMA_ALPHA
    push 0 
    push 0
    push hWin
    call pSLWA
    mov Value,90
    invoke ShowWindow,hWin,SW_SHOWNA
doloop:
    push LMA_COLORKEY + LMA_ALPHA
    push Value
    push Value
    push hWin
    call pSLWA
    invoke Sleep,DELAY_VALUE
    add Value,15
    cmp Value,255
    jne doloop
    push LMA_ALPHA
    push 255
    push 0
    push hWin
    call pSLWA

    ret 

FadeInOpen endp

;--------------------------------------------
; 窗口淡出效果,仅运行在2000/XP以上操作系统
;--------------------------------------------
FadeOutClose proc hWin:DWORD

    invoke GetWindowLongA,hWin,GWL_EXSTYLE
    or eax,WS_EX_LAYERED
    invoke SetWindowLongA,hWin,GWL_EXSTYLE,eax
    invoke GetModuleHandleA,ADDR User32
    invoke GetProcAddress,eax,ADDR SLWA
    mov pSLWA,eax
    push LMA_ALPHA
    push 255
    push 0
    push hWin
    call pSLWA
    mov Value,255
doloop:
    push LMA_COLORKEY + LMA_ALPHA
    push Value
    push Value
    push hWin
    call pSLWA
    invoke Sleep,DELAY_VALUE
    sub Value,15
    cmp Value,0
    jne doloop
    ret
FadeOutClose endp

      End DllEntry


 

//inc file
AnimateOpen   proto  :dword
AnimateClose  proto  :dword
FadeInOpen    proto  :dword
FadeOutClose  proto  :dword


 

;------------------------
    .386
    .model flat,stdcall
    option casemap:none

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

;数据段
    .data?
hInstance  dd  ?
hWinMain   dd  ?
;常量定义
    .const
szClassName    db  'MyClass',0
szCaptionMain  db  '窗口特效演示',0
szText         db  '你好,认识我吗?^_^',0

;代码段
    .code
;------------------
; 窗口消息处理子程序
;------------------
_ProcWinMain proc uses ebx edi esi,hWnd,uMsg,wParam,lParam
      local @stPs:PAINTSTRUCT
      local @stRect:RECT
      local @hDc

      mov eax,uMsg
      
      .if eax==WM_PAINT
          invoke BeginPaint,hWnd,addr @stPs
          mov @hDc,eax
          invoke GetClientRect,hWnd,addr @stRect
          invoke DrawText,@hDc,addr szText,-1,\
                 addr @stRect,\
                 DT_SINGLELINE or DT_CENTER or DT_VCENTER
          invoke EndPaint,hWnd,addr @stPs
      .elseif eax==WM_CLOSE    ;关闭窗口
             invoke FadeOutClose,hWinMain
             invoke DestroyWindow,hWinMain
             invoke PostQuitMessage,NULL
      .else
          invoke DefWindowProc,hWnd,uMsg,wParam,lParam
          ret
      .endif
      
      xor eax,eax
      ret
_ProcWinMain endp

;----------------------
; 主窗口程序
;----------------------
_WinMain  proc
       local @stWndClass:WNDCLASSEX
       local @stMsg:MSG

       invoke GetModuleHandle,NULL
       mov hInstance,eax
       invoke RtlZeroMemory,addr @stWndClass,sizeof @stWndClass
       
       ;注册窗口类
       invoke LoadCursor,0,IDC_ARROW
       mov @stWndClass.hCursor,eax
       push hInstance
       pop @stWndClass.hInstance
       mov @stWndClass.cbSize,sizeof WNDCLASSEX
       mov @stWndClass.style,CS_HREDRAW or CS_VREDRAW
       mov @stWndClass.lpfnWndProc,offset _ProcWinMain
       mov @stWndClass.hbrBackground,COLOR_WINDOW+1
       mov @stWndClass.lpszClassName,offset szClassName
       invoke RegisterClassEx,addr @stWndClass

       ;建立并显示窗口
       invoke CreateWindowEx,WS_EX_CLIENTEDGE,\
              offset szClassName,offset szCaptionMain,\
              WS_OVERLAPPEDWINDOW,\
              100,100,600,400,\
              NULL,NULL,hInstance,NULL
       mov  hWinMain,eax
       invoke FadeInOpen,hWinMain
       ;invoke ShowWindow,hWinMain,SW_SHOWNORMAL
       invoke UpdateWindow,hWinMain ;更新客户区,即发送WM_PAINT消息

   
       ;消息循环
       .while TRUE
          invoke GetMessage,addr @stMsg,NULL,0,0
          .break .if eax==0
          invoke TranslateMessage,addr @stMsg
          invoke DispatchMessage,addr @stMsg
       .endw
       ret
_WinMain endp

start:
       call _WinMain
       invoke ExitProcess,NULL
       end start


 

;-------------------------------
; 获取指定字符串的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	;set DF=0(向高地址增长)		std is set DF=1
   repnz scasb		;scasb 在edi向高地址增长方向找al字符,找到设置ZF=1
   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		;cmpsb是用 DS:[SI] 所指的字节单元内容,减去 ES:[DI] 所指的字节单元的内容。
     .if ZERO?
       pop esi	;//if esi="fun"  edi="func"感觉要比较下长度
       jmp @F
     .endif
     pop esi
     add ebx,4
     inc edx
   .until edx>=[esi].NumberOfNames
   jmp _ret
@@:
   ;通过API名称索引获取序号索引再获取地址索引
   sub ebx,[esi].AddressOfNames		;AddressOfNames指向是的双字RVA数组
   sub ebx,_hModule		;找到相对AddressOfNames的RVA
   shr ebx,1		;DWORD 步长变为WORD步长
   add ebx,[esi].AddressOfNameOrdinals	;AddressOfNameOrdinals指向的是单字RVA数组
   add ebx,_hModule
   movzx eax,word ptr [ebx]	;无符号扩展Mov	从ebx指向地址取出索引号
   shl eax,2	;索引号*4变为AddressOfFunctions偏移量
   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


 

;--------------------
; 获取PE文件的导出表
;--------------------
_getExportInfo proc _lpFile,_lpPeHead,_dwSize
  local @szBuffer[1024]:byte
  local @szSectionName[16]:byte
  local @lpAddressOfNames,@dwIndex,@lpAddressOfNameOrdinals
  
  pushad
  mov esi,_lpPeHead
  assume esi:ptr IMAGE_NT_HEADERS
  mov eax,[esi].OptionalHeader.DataDirectory[0].VirtualAddress
  .if !eax
    invoke _appendInfo,addr szErrNoExport
    jmp _Ret
  .endif
  invoke _RVAToOffset,_lpFile,eax
  add eax,_lpFile
  mov edi,eax     ;计算导出表所在文件偏移位置
  assume edi:ptr IMAGE_EXPORT_DIRECTORY
  invoke _RVAToOffset,_lpFile,[edi].nName
  add eax,_lpFile
  mov ecx,eax	;ecx 指向该导出表的文件名字字符串的首地址
  invoke _getRVASectionName,_lpFile,[edi].nName
  invoke wsprintf,addr @szBuffer,addr szMsgExport,\
         eax,ecx,[edi].nBase,[edi].NumberOfFunctions,\
         [edi].NumberOfNames,[edi].AddressOfFunctions,\
         [edi].AddressOfNames,[edi].AddressOfNameOrdinals
  invoke _appendInfo,addr @szBuffer

  invoke _RVAToOffset,_lpFile,[edi].AddressOfNames
  add eax,_lpFile
  mov @lpAddressOfNames,eax
  invoke _RVAToOffset,_lpFile,[edi].AddressOfNameOrdinals
  add eax,_lpFile
  mov @lpAddressOfNameOrdinals,eax
  invoke _RVAToOffset,_lpFile,[edi].AddressOfFunctions
  add eax,_lpFile
  mov esi,eax   ;函数的地址表

  mov ecx,[edi].NumberOfFunctions
  mov @dwIndex,0
@@:
  pushad
  mov eax,@dwIndex
  push edi
  mov ecx,[edi].NumberOfNames
  cld
  mov edi,@lpAddressOfNameOrdinals
  repnz scasw	;scasW 在edi向高地址增长方向找aX字符,找到设置ZF=1
  .if ZERO?  ;找到函数名称
    sub edi,@lpAddressOfNameOrdinals
    sub edi,2		;lpAddressOfNameOrdinals指向是单字索引数组,减2是从零编号
    shl edi,1		;AddressOfNameOrdinals单字步长变为AddressOfNames双字步长
    add edi,@lpAddressOfNames
    invoke _RVAToOffset,_lpFile,dword ptr [edi]
    add eax,_lpFile
  .else
    mov eax,offset szExportByOrd
  .endif
  pop edi
  ;序号在ecx中
  mov ecx,@dwIndex
  add ecx,[edi].nBase
  invoke wsprintf,addr @szBuffer,addr szMsg4,\
         ecx,dword ptr [esi],eax
  invoke _appendInfo,addr @szBuffer
  popad
  add esi,4
  inc @dwIndex
  loop @B
_Ret:
  assume esi:nothing
  assume edi:nothing
  popad
  ret
_getExportInfo endp


 

//peinfo.asm 整个文件
.386
.model flat,stdcall
option casemap:none

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


ICO_MAIN equ 1000
DLG_MAIN equ 1000
IDC_INFO equ 1001
IDM_MAIN equ 2000
IDM_OPEN equ 2001
IDM_EXIT equ 2002
IDM_1    equ 4000
IDM_2    equ 4001
IDM_3    equ 4002
.data
hInstance   dd ?
hRichEdit   dd ?
hWinMain    dd ?
hWinEdit    dd ?
szFileName  db MAX_PATH dup(?)

.const
szDllEdit   db 'RichEd20.dll',0
szClassEdit db 'RichEdit20A',0
szFont      db '宋体',0
szExtPe     db 'PE File',0,'*.exe;*.dll;*.scr;*.fon;*.drv',0
            db 'All Files(*.*)',0,'*.*',0,0
szErr       db '文件格式错误!',0
szErrFormat db '这个文件不是PE格式的文件!',0
szSuccess   db '恭喜你,程序执行到这里是成功的。',0
szNotFound  db '无法查找',0
szMsg       db '文件名:%s',0dh,0ah
            db '-----------------------------------------',0dh,0ah,0dh,0ah,0dh,0ah
            db '运行平台:      0x%04x  (014c:Intel 386   014dh:Intel 486  014eh:Intel 586)',0dh,0ah
            db '节的数量:      %d',0dh,0ah
            db '文件属性:      0x%04x  (大尾-禁止多处理器-DLL-系统文件-禁止网络运行-禁止优盘运行-无调试-32位-小尾-X-X-X-无符号-无行-可执行-无重定位)',0dh,0ah
            db '建议装入基地址:  0x%08x',0dh,0ah
            db '文件执行入口(RVA地址):  0x%04x',0dh,0ah,0dh,0ah,0
szMsgSec    db '---------------------------------------------------------------------------------',0dh,0ah
            db '节的属性参考:',0dh,0ah
            db '  00000020h  包含代码',0dh,0ah
            db '  00000040h  包含已经初始化的数据,如.const',0dh,0ah
            db '  00000080h  包含未初始化数据,如 .data?',0dh,0ah
            db '  02000000h  数据在进程开始以后被丢弃,如.reloc',0dh,0ah
            db '  04000000h  节中数据不经过缓存',0dh,0ah
            db '  08000000h  节中数据不会被交换到磁盘',0dh,0ah
            db '  10000000h  数据将被不同进程共享',0dh,0ah
            db '  20000000h  可执行',0dh,0ah
            db '  40000000h  可读',0dh,0ah
            db '  80000000h  可写',0dh,0ah
            db '常见的代码节一般为:60000020h,数据节一般为:c0000040h,常量节一般为:40000040h',0dh,0ah
            db '---------------------------------------------------------------------------------',0dh,0ah,0dh,0ah,0dh,0ah
            db '节的名称  未对齐前真实长度  内存中的偏移(对齐后的) 文件中对齐后的长度 文件中的偏移  节的属性',0dh,0ah
            db '---------------------------------------------------------------------------------------------',0dh,0ah,0
szFmtSec    db '%s     %08x         %08x              %08x           %08x     %08x',0dh,0ah,0dh,0ah,0dh,0ah,0
szMsg1      db 0dh,0ah,0dh,0ah,0dh,0ah
            db '---------------------------------------------------------------------------------------------',0dh,0ah
            db '导入表所处的节:%s',0dh,0ah
            db '---------------------------------------------------------------------------------------------',0dh,0ah,0
szMsgImport db 0dh,0ah,0dh,0ah
            db '导入库:%s',0dh,0ah
            db '-----------------------------',0dh,0ah,0dh,0ah
            db 'OriginalFirstThunk  %08x',0dh,0ah
            db 'TimeDateStamp       %08x',0dh,0ah
            db 'ForwarderChain      %08x',0dh,0ah
            db 'FirstThunk          %08x',0dh,0ah
            db '-----------------------------',0dh,0ah,0dh,0ah,0
szMsg2      db '%08u         %s',0dh,0ah,0
szMsg3      db '%08u(无函数名,按序号导入)',0dh,0ah,0
szErrNoImport db  0dh,0ah,0dh,0ah
              db  '未发现该文件有导入函数',0dh,0ah,0dh,0ah,0

szMsgExport db 0dh,0ah,0dh,0ah,0dh,0ah
            db '---------------------------------------------------------------------------------------------',0dh,0ah
            db '导出表所处的节:%s',0dh,0ah
            db '---------------------------------------------------------------------------------------------',0dh,0ah
            db '原始文件名:%s',0dh,0ah
            db 'nBase               %08x',0dh,0ah
            db 'NumberOfFunctions   %08x',0dh,0ah
            db 'NuberOfNames        %08x',0dh,0ah
            db 'AddressOfFunctions  %08x',0dh,0ah
            db 'AddressOfNames      %08x',0dh,0ah
            db 'AddressOfNameOrd    %08x',0dh,0ah
            db '-------------------------------------',0dh,0ah,0dh,0ah
            db '导出序号    虚拟地址    导出函数名称',0dh,0ah
            db '-------------------------------------',0dh,0ah,0
szMsg4      db '%08x      %08x      %s',0dh,0ah,0
szExportByOrd db  '(按照序号导出)',0
szErrNoExport db 0dh,0ah,0dh,0ah
              db  '未发现该文件有导出函数',0dh,0ah,0dh,0ah,0
szMsgReloc1 db 0dh,0ah,'重定位表所处的节:%s',0dh,0ah,0
szMsgReloc2 db 0dh,0ah
            db '--------------------------------------------------------------------------------------------',0dh,0ah
            db '重定位基地址: %08x',0dh,0ah
            db '重定位项数量: %d',0dh,0ah
            db '--------------------------------------------------------------------------------------------',0dh,0ah
            db '需要重定位的地址列表(ffffffff表示对齐用,不需要重定位)',0dh,0ah
            db '--------------------------------------------------------------------------------------------',0dh,0ah,0
szMsgReloc3 db '%08x  ',0
szCrLf      db 0dh,0ah,0
szMsgReloc4 db 0dh,0ah,'未发现该文件有重定位信息.',0dh,0ah,0

.code

;----------------
;初始化窗口程序
;----------------
_init proc
  local @stCf:CHARFORMAT
  
  invoke GetDlgItem,hWinMain,IDC_INFO
  mov hWinEdit,eax
  invoke LoadIcon,hInstance,ICO_MAIN
  invoke SendMessage,hWinMain,WM_SETICON,ICON_BIG,eax       ;为窗口设置图标
  invoke SendMessage,hWinEdit,EM_SETTEXTMODE,TM_PLAINTEXT,0 ;设置编辑控件
  invoke RtlZeroMemory,addr @stCf,sizeof @stCf
  mov @stCf.cbSize,sizeof @stCf
  mov @stCf.yHeight,9*20
  mov @stCf.dwMask,CFM_FACE or CFM_SIZE or CFM_BOLD
  invoke lstrcpy,addr @stCf.szFaceName,addr szFont
  invoke SendMessage,hWinEdit,EM_SETCHARFORMAT,0,addr @stCf
  invoke SendMessage,hWinEdit,EM_EXLIMITTEXT,0,-1
  ret
_init endp

;------------------
; 错误Handler
;------------------
_Handler proc _lpExceptionRecord,_lpSEH,\
              _lpContext,_lpDispathcerContext

  pushad
  mov esi,_lpExceptionRecord
  mov edi,_lpContext
  assume esi:ptr EXCEPTION_RECORD,edi:ptr CONTEXT
  mov eax,_lpSEH
  push [eax+0ch]
  pop [edi].regEbp
  push [eax+8]
  pop [edi].regEip
  push eax
  pop [edi].regEsp
  assume esi:nothing,edi:nothing
  popad
  mov eax,ExceptionContinueExecution
  ret
_Handler endp

;---------------------
; 将内存偏移量RVA转换为文件偏移
; lp_FileHead为文件头的起始地址
; _dwRVA为给定的RVA地址
;---------------------
_RVAToOffset proc _lpFileHead,_dwRVA
  local @dwReturn
  
  pushad
  mov esi,_lpFileHead
  assume esi:ptr IMAGE_DOS_HEADER
  add esi,[esi].e_lfanew
  assume esi:ptr IMAGE_NT_HEADERS
  mov edi,_dwRVA
  mov edx,esi
  add edx,sizeof IMAGE_NT_HEADERS
  assume edx:ptr IMAGE_SECTION_HEADER
  movzx ecx,[esi].FileHeader.NumberOfSections
  ;遍历节表
  .repeat
    mov eax,[edx].VirtualAddress
    add eax,[edx].SizeOfRawData        ;计算该节结束RVA,不用Misc的主要原因是有些段的Misc值是错误的!
    .if (edi>=[edx].VirtualAddress)&&(edi<eax)
      mov eax,[edx].VirtualAddress
      sub edi,eax                ;计算RVA在节中的偏移
      mov eax,[edx].PointerToRawData
      add eax,edi                ;加上节在文件中的的起始位置
      jmp @F
    .endif
    add edx,sizeof IMAGE_SECTION_HEADER
  .untilcxz
  assume edx:nothing
  assume esi:nothing
  mov eax,-1
@@:
  mov @dwReturn,eax
  popad
  mov eax,@dwReturn
  ret
_RVAToOffset endp

;------------------------
; 获取RVA所在节的名称
;------------------------
_getRVASectionName  proc _lpFileHead,_dwRVA
  local @dwReturn
  
  pushad
  mov esi,_lpFileHead
  assume esi:ptr IMAGE_DOS_HEADER
  add esi,[esi].e_lfanew
  assume esi:ptr IMAGE_NT_HEADERS
  mov edi,_dwRVA
  mov edx,esi
  add edx,sizeof IMAGE_NT_HEADERS
  assume edx:ptr IMAGE_SECTION_HEADER
  movzx ecx,[esi].FileHeader.NumberOfSections
  ;遍历节表
  .repeat
    mov eax,[edx].VirtualAddress
    add eax,[edx].SizeOfRawData  ;计算该节结束RVA
    .if (edi>=[edx].VirtualAddress)&&(edi<eax)
      mov eax,edx
      jmp @F
    .endif
    add edx,sizeof IMAGE_SECTION_HEADER
  .untilcxz
  assume edx:nothing
  assume esi:nothing
  mov eax,offset szNotFound
@@:
  mov @dwReturn,eax
  popad
  mov eax,@dwReturn
  ret
_getRVASectionName  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	;set DF=0(向高地址增长)		std is set DF=1
   repnz scasb		;scasb 在edi向高地址增长方向找al字符,找到设置ZF=1
   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		;cmpsb是用 DS:[SI] 所指的字节单元内容,减去 ES:[DI] 所指的字节单元的内容。
     .if ZERO?
       pop esi	;//if esi="fun"  edi="func"感觉要比较下长度
       jmp @F
     .endif
     pop esi
     add ebx,4
     inc edx
   .until edx>=[esi].NumberOfNames
   jmp _ret
@@:
   ;通过API名称索引获取序号索引再获取地址索引
   sub ebx,[esi].AddressOfNames		;AddressOfNames指向是的双字RVA数组
   sub ebx,_hModule		;找到相对AddressOfNames的RVA
   shr ebx,1		;DWORD 步长变为WORD步长
   add ebx,[esi].AddressOfNameOrdinals	;AddressOfNameOrdinals指向的是单字RVA数组
   add ebx,_hModule
   movzx eax,word ptr [ebx]	;无符号扩展Mov	从ebx指向地址取出索引号
   shl eax,2	;索引号*4变为AddressOfFunctions偏移量
   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

;---------------------
; 往文本框中追加文本
;---------------------
_appendInfo proc _lpsz
  local @stCR:CHARRANGE

  pushad
  invoke GetWindowTextLength,hWinEdit
  mov @stCR.cpMin,eax  ;将插入点移动到最后
  mov @stCR.cpMax,eax
  invoke SendMessage,hWinEdit,EM_EXSETSEL,0,addr @stCR
  invoke SendMessage,hWinEdit,EM_REPLACESEL,FALSE,_lpsz
  popad
  ret
_appendInfo endp

;--------------------
; 从内存中获取PE文件的主要信息
;--------------------
_getMainInfo  proc _lpFile,_lpPeHead,_dwSize
  local @szBuffer[1024]:byte
  local @szSecName[16]:byte

  pushad
  mov edi,_lpPeHead
  assume edi:ptr IMAGE_NT_HEADERS
  movzx ecx,[edi].FileHeader.Machine          ;运行平台
  movzx edx,[edi].FileHeader.NumberOfSections ;节的数量
  movzx ebx,[edi].FileHeader.Characteristics  ;节的属性
  invoke wsprintf,addr @szBuffer,addr szMsg,\
         addr szFileName,ecx,edx,ebx,\
         [edi].OptionalHeader.ImageBase,\     ;含建议装入的地址
         [edi].OptionalHeader.AddressOfEntryPoint
  invoke SetWindowText,hWinEdit,addr @szBuffer;添加到编辑框中

  ;显示每个节的主要信息
  invoke _appendInfo,addr szMsgSec
  movzx ecx,[edi].FileHeader.NumberOfSections
  add edi,sizeof IMAGE_NT_HEADERS
  assume edi:ptr IMAGE_SECTION_HEADER
  .repeat
    push ecx
    ;获取节的名称,注意长度为8的名称并不以0结尾
    invoke RtlZeroMemory,addr @szSecName,sizeof @szSecName
    push esi
    push edi
    mov ecx,8
    mov esi,edi
    lea edi,@szSecName
    cld
    @@:
    lodsb
    .if !al  ;如果名称为0,则显示为空格
      mov al,' '
    .endif
    stosb
    loop @B
    pop edi
    pop esi
    ;获取节的主要信息
    invoke wsprintf,addr @szBuffer,addr szFmtSec,\
           addr @szSecName,[edi].Misc.VirtualSize,\
           [edi].VirtualAddress,[edi].SizeOfRawData,\
           [edi].PointerToRawData,[edi].Characteristics
    invoke _appendInfo,addr @szBuffer
    add edi,sizeof IMAGE_SECTION_HEADER
    pop ecx
  .untilcxz

  assume edi:nothing
  popad
  ret
_getMainInfo endp

;--------------------
; 获取PE文件的导入表
;--------------------
_getImportInfo proc _lpFile,_lpPeHead,_dwSize
  local @szBuffer[1024]:byte
  local @szSectionName[16]:byte
  
  pushad
  mov edi,_lpPeHead
  assume edi:ptr IMAGE_NT_HEADERS
  mov eax,[edi].OptionalHeader.DataDirectory[8].VirtualAddress
  .if !eax
    invoke _appendInfo,addr szErrNoImport
    jmp _Ret
  .endif
  invoke _RVAToOffset,_lpFile,eax
  add eax,_lpFile
  mov edi,eax     ;计算引入表所在文件偏移位置
  assume edi:ptr IMAGE_IMPORT_DESCRIPTOR
  invoke _getRVASectionName,_lpFile,[edi].OriginalFirstThunk
  invoke wsprintf,addr @szBuffer,addr szMsg1,eax  ;显示节名
  invoke _appendInfo,addr @szBuffer

  .while [edi].OriginalFirstThunk || [edi].TimeDateStamp ||\
         [edi].ForwarderChain || [edi].Name1 || [edi].FirstThunk
    invoke _RVAToOffset,_lpFile,[edi].Name1
    add eax,_lpFile
    invoke wsprintf,addr @szBuffer,addr szMsgImport,eax,\
           [edi].OriginalFirstThunk,[edi].TimeDateStamp,\
           [edi].ForwarderChain,[edi].FirstThunk
    invoke _appendInfo,addr @szBuffer
    ;获取IMAGE_THUNK_DATA列表到EBX
    .if [edi].OriginalFirstThunk
      mov eax,[edi].OriginalFirstThunk
    .else
      mov eax,[edi].FirstThunk
    .endif
    invoke _RVAToOffset,_lpFile,eax
    add eax,_lpFile
    mov ebx,eax
    .while dword ptr [ebx]
      .if dword ptr [ebx] & IMAGE_ORDINAL_FLAG32 ;按序号导入
        mov eax,dword ptr [ebx]
        and eax,0ffffh
        invoke wsprintf,addr @szBuffer,addr szMsg3,eax
      .else                                      ;按名称导入
        invoke _RVAToOffset,_lpFile,dword ptr [ebx]
        add eax,_lpFile
        assume eax:ptr IMAGE_IMPORT_BY_NAME
        movzx ecx,[eax].Hint
        invoke wsprintf,addr @szBuffer,\
              addr szMsg2,ecx,addr [eax].Name1
        assume eax:nothing
      .endif
      invoke _appendInfo,addr @szBuffer
      add ebx,4
    .endw
    add edi,sizeof IMAGE_IMPORT_DESCRIPTOR
  .endw
_Ret:
  assume edi:nothing
  popad
  ret
_getImportInfo endp

;--------------------
; 获取PE文件的导出表
;--------------------
_getExportInfo proc _lpFile,_lpPeHead,_dwSize
  local @szBuffer[1024]:byte
  local @szSectionName[16]:byte
  local @lpAddressOfNames,@dwIndex,@lpAddressOfNameOrdinals
  
  pushad
  mov esi,_lpPeHead
  assume esi:ptr IMAGE_NT_HEADERS
  mov eax,[esi].OptionalHeader.DataDirectory[0].VirtualAddress
  .if !eax
    invoke _appendInfo,addr szErrNoExport
    jmp _Ret
  .endif
  invoke _RVAToOffset,_lpFile,eax
  add eax,_lpFile
  mov edi,eax     ;计算导出表所在文件偏移位置
  assume edi:ptr IMAGE_EXPORT_DIRECTORY
  invoke _RVAToOffset,_lpFile,[edi].nName
  add eax,_lpFile
  mov ecx,eax	;ecx 指向该导出表的文件名字字符串的首地址
  invoke _getRVASectionName,_lpFile,[edi].nName
  invoke wsprintf,addr @szBuffer,addr szMsgExport,\
         eax,ecx,[edi].nBase,[edi].NumberOfFunctions,\
         [edi].NumberOfNames,[edi].AddressOfFunctions,\
         [edi].AddressOfNames,[edi].AddressOfNameOrdinals
  invoke _appendInfo,addr @szBuffer

  invoke _RVAToOffset,_lpFile,[edi].AddressOfNames
  add eax,_lpFile
  mov @lpAddressOfNames,eax
  invoke _RVAToOffset,_lpFile,[edi].AddressOfNameOrdinals
  add eax,_lpFile
  mov @lpAddressOfNameOrdinals,eax
  invoke _RVAToOffset,_lpFile,[edi].AddressOfFunctions
  add eax,_lpFile
  mov esi,eax   ;函数的地址表

  mov ecx,[edi].NumberOfFunctions
  mov @dwIndex,0
@@:
  pushad
  mov eax,@dwIndex
  push edi
  mov ecx,[edi].NumberOfNames
  cld
  mov edi,@lpAddressOfNameOrdinals
  repnz scasw	;scasW 在edi向高地址增长方向找aX字符,找到设置ZF=1
  .if ZERO?  ;找到函数名称
    sub edi,@lpAddressOfNameOrdinals
    sub edi,2		;lpAddressOfNameOrdinals指向是单字索引数组,减2是从零编号
    shl edi,1		;AddressOfNameOrdinals单字步长变为AddressOfNames双字步长
    add edi,@lpAddressOfNames
    invoke _RVAToOffset,_lpFile,dword ptr [edi]
    add eax,_lpFile
  .else
    mov eax,offset szExportByOrd
  .endif
  pop edi
  ;序号在ecx中
  mov ecx,@dwIndex
  add ecx,[edi].nBase
  invoke wsprintf,addr @szBuffer,addr szMsg4,\
         ecx,dword ptr [esi],eax
  invoke _appendInfo,addr @szBuffer
  popad
  add esi,4
  inc @dwIndex
  loop @B
_Ret:
  assume esi:nothing
  assume edi:nothing
  popad
  ret
_getExportInfo endp

;--------------------
; 获取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
    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
;--------------------
; 打开PE文件并处理
;--------------------
_openFile proc
  local @stOF:OPENFILENAME
  local @hFile,@dwFileSize,@hMapFile,@lpMemory

  invoke RtlZeroMemory,addr @stOF,sizeof @stOF
  mov @stOF.lStructSize,sizeof @stOF
  push hWinMain
  pop @stOF.hwndOwner
  mov @stOF.lpstrFilter,offset szExtPe
  mov @stOF.lpstrFile,offset szFileName
  mov @stOF.nMaxFile,MAX_PATH
  mov @stOF.Flags,OFN_PATHMUSTEXIST or OFN_FILEMUSTEXIST
  invoke GetOpenFileName,addr @stOF  ;让用户选择打开的文件
  .if !eax
    jmp @F
  .endif
  invoke CreateFile,addr szFileName,GENERIC_READ,\
         FILE_SHARE_READ or FILE_SHARE_WRITE,NULL,\
         OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,NULL
  .if eax!=INVALID_HANDLE_VALUE
    mov @hFile,eax
    invoke GetFileSize,eax,NULL
    mov @dwFileSize,eax
    .if eax
      invoke CreateFileMapping,@hFile,\  ;内存映射文件
             NULL,PAGE_READONLY,0,0,NULL
      .if eax
        mov @hMapFile,eax
        invoke MapViewOfFile,eax,\
               FILE_MAP_READ,0,0,0
        .if eax
          mov @lpMemory,eax              ;获得文件在内存的映象起始位置
          assume fs:nothing
          push ebp
          push offset _ErrFormat
          push offset _Handler
          push fs:[0]
          mov fs:[0],esp

          ;检测PE文件是否有效
          mov esi,@lpMemory
          assume esi:ptr IMAGE_DOS_HEADER
          .if [esi].e_magic!=IMAGE_DOS_SIGNATURE  ;判断是否有MZ字样
            jmp _ErrFormat
          .endif
          add esi,[esi].e_lfanew    ;调整ESI指针指向PE文件头
          assume esi:ptr IMAGE_NT_HEADERS
          .if [esi].Signature!=IMAGE_NT_SIGNATURE ;判断是否有PE字样
            jmp _ErrFormat
          .endif

          ;到此为止,该文件的验证已经完成。为PE结构文件
          ;接下来分析分件映射到内存中的数据,并显示主要参数
          invoke _getMainInfo,@lpMemory,esi,@dwFileSize
          ;显示导入表
          invoke _getImportInfo,@lpMemory,esi,@dwFileSize
          ;显示导出表
          invoke _getExportInfo,@lpMemory,esi,@dwFileSize
          ;显示重定位信息
          invoke _getRelocInfo,@lpMemory,esi,@dwFileSize


          jmp _ErrorExit
 
_ErrFormat:
          invoke MessageBox,hWinMain,offset szErrFormat,NULL,MB_OK
_ErrorExit:
          pop fs:[0]
          add esp,0ch
          invoke UnmapViewOfFile,@lpMemory
        .endif
        invoke CloseHandle,@hMapFile
      .endif
      invoke CloseHandle,@hFile
    .endif
  .endif
@@:        
  ret
_openFile endp
;-------------------
; 窗口程序
;-------------------
_ProcDlgMain proc uses ebx edi esi hWnd,wMsg,wParam,lParam
  mov eax,wMsg
  .if eax==WM_CLOSE
    invoke EndDialog,hWnd,NULL
  .elseif eax==WM_INITDIALOG  ;初始化
    push hWnd
    pop hWinMain
    call _init
  .elseif eax==WM_COMMAND     ;菜单
    mov eax,wParam
    .if eax==IDM_EXIT       ;退出
      invoke EndDialog,hWnd,NULL 
    .elseif eax==IDM_OPEN   ;打开文件
      call _openFile
    .elseif eax==IDM_1  ;以下三个菜单是7岁的儿子完成的!!
      invoke MessageBox,NULL,offset szErrFormat,offset szErr,MB_ICONWARNING
    .elseif eax==IDM_2
      invoke MessageBox,NULL,offset szErrFormat,offset szErr,MB_ICONQUESTION	
    .elseif eax==IDM_3
      invoke MessageBox,NULL,offset szErrFormat,offset szErr,MB_YESNOCANCEL
    .endif
  .else
    mov eax,FALSE
    ret
  .endif
  mov eax,TRUE
  ret
_ProcDlgMain endp

start:
  invoke LoadLibrary,offset szDllEdit
  mov hRichEdit,eax
  invoke GetModuleHandle,NULL
  mov hInstance,eax
  invoke DialogBoxParam,hInstance,\
         DLG_MAIN,NULL,offset _ProcDlgMain,NULL
  invoke FreeLibrary,hRichEdit
  invoke ExitProcess,NULL
  end start


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值