邪恶八进制信息安全团队技术讨论组's Archiver

金州 2005-12-19 02:52

[转载]API函数地址的获取

文章作者:Hit


;以下是利用SEH和PE文件结构搜索API地址的例子
;由Hit于2003年8月7日改写Hume(CVC.GB)的例子而成
;程序原理及原版程序参见:
;《CVC内部杂志 Issue 1 特别版》中《API函数地址的获取(2)》
;CVC(中国毒客公社)论坛地址: [url]http://bbs.logincom.com/bbs/cgi-bin/leoboard.cgi[/url]

;原理:
;利用遍历SEH链表取得API的步骤如下:
;a)遍历SEH链,找到prev等于0xFFFFFFFF的EXCEPTION_REGISTER结构,获取handler值;
;b)利用PE文件特征在内存中搜索kernel32.dll的基地址;
;c)搜索kernel32.dll的IAT,获取GetProcAddress的地址;
;d)以GetProceAddress获取其他任何Win32 API函数地址。

;代码说明:
; === 以内为关键代码,以外为演示代码
; *** 以内为移植时可能需要修改的关键代码

.386
.model flat, stdcall
option casemap:none



.data

;=========================================================================
KernelBase dd      ?                  ;Kernel32.dll的基地址
UserBase  dd      ?                  ;User32.dll的基地址
GdiBase   dd      ?                  ;Gdi32.dll的基地址

LLA_Addr  dd      ?                  ;LoadLibraryA的地址
GPA_Addr  dd      ?                  ;GetProcAddress的地址
;=========================================================================


APIName1  db      'MessageBoxA',0
APIName2  db      'ExitProcess',0

Text     db      'Success!',0
Caption   db      'Message',0
      
      
      
.code
Start:   

;=========================================================================
       cld                        ;清方向标志
       xor     esi,esi              ;将esi置0
       assume   fs:nothing            ;使用FS时,编译器不报错
       lods    dword ptr fs:[esi]       ;将SEH起始节点地址存入eax
@@:      inc     eax                 ;┓ 判断eax是否为-1,
       jz      @F                  ;┣ 如果是,则跳出循环
       dec     eax                 ;┛
       xchg    esi,eax              ;┓ 指针移向下一个节点
       lodsd                      ;┛
       jmp     short @B              ;一直循环
@@:      lodsd                      ;┓
       xchg    esi,eax              ;┛ 得到handler值
                                
                                ;以下开始搜索Kernel32.dll
                                ;的基地址
                                
@@:      dec     esi                 ;┓ Kernel32.dll的基地址
       xor     si,si                ;┛ 是64K的倍数,esi为指针
       mov     eax,[esi]             ;读取指针指向的内容
       add     ax,0a5b3h             ;0a5b3h就是'MZ'的相反数
       jnz     @B                  ;判断内容是不是'MZ'
       mov     edi,[esi+3ch]          ;edi存放PE头的RVA
       mov     eax,[esi+edi]          ;eax存放PE头的起始内容
       add     eax,0ffffbab0h          ;0ffffbab0h就是"PE\0\0"的相反数
       jnz     @B                  ;判断内容是不是"PE\0\0"
      
      ;************************
       mov     KernelBase,esi          ;保存Kernel32.dll的基地址
      ;************************
      
       mov     ebp,esi              ;┓edi为PE结构中DataDirectory
       mov     edi,[ebp+edi+78h]        ;┛的RVA
     
       push    edi                 ;保存DataDirectory的RAV
       push    esi                 ;保存Kernel32.dll的基地址

       mov     eax,[ebp+edi+20h]        ;eax为AddressOfNames的地址
       mov     edx,[ebp+edi+24h]        ;edx为AddressOfNameOrdinals地址
       call    @F                  ;将下面字符串地址压入堆栈
       db      "GetProcAddress",0       ;API的字符串
@@:
       pop     edi                 ;从堆栈中弹出字符串地址
       mov     ecx,15               ;字符串长15字节
       sub     eax,4                ;┓
@@:                               ;┃
       add     eax,4                ;┃
       add     edi,ecx              ;┃
       sub     edi,15               ;┣查找"GetProcAddress"字符串
       mov     esi,[ebp+eax]          ;┃
       add     esi,ebp              ;┃
       mov     ecx,15               ;┃
       repz    cmpsb                ;┃
       jnz     @B                  ;┛

       pop     esi                 ;恢复Kernel32.dll的基地址
       pop     edi                 ;恢复DataDirectory的RAV

       sub     eax,[ebp+edi+20h]        ;┓
       shr     eax,1                ;┃
       add     edx,ebp              ;┣ ebp为GetProcAddress的地址
       movzx    eax,word ptr [edx+eax]    ;┃
       add     esi,[ebp+edi+1ch]        ;┃
       add     ebp,[esi+eax*4]         ;┛
      
      ;************************
       mov     GPA_Addr,ebp           ;保存GetProcAddress的地址
      ;************************
      
       call    @F                  ;┓
       db      'LoadLibraryA',0        ;┃
@@:     ;************************          ;┃
       push    KernelBase            ;┃ 调用API函数GetProcAddress
      ;************************          ;┣ 从Kernel32.dll中获取API函
                                ;┃ 数LoadLibraryA的地址
       call    ebp                 ;┃
      ;************************          ;┃
       mov     LLA_Addr,eax           ;┛
      ;************************
      
       call     @F                 ;┓
       db      'User32.dll',0         ;┃
@@:      call     eax                ;┣ 如上,调用API函数LoadLibraryA
      ;*************************         ;┃ 获取User32.dll的基地址
       mov      UserBase,eax          ;┛
      ;*************************
      
       call     @F                 ;┓
       db      'Gdi32.dll',0          ;┃
@@:     ;*************************         ;┣ 如上,调用API函数LoadLibraryA
       call     dword ptr [LLA_Addr]     ;┃ 获取Gdi32.dll的基地址
       mov      GdiBase,eax           ;┛
      ;*************************

;=========================================================================

       push    offset APIName1
       push    UserBase
       call    dword ptr [GPA_Addr]      ;获取MessageBoxA的地址
      
       push    0                  ;一个"确定"按钮
       push    offset Caption          ;标题栏内容
       push    offset Text            ;消息内容
       push    0                  ;消息框没有父窗口
       call    eax                 ;调用MessageBoxA显示消息框
      
       push    offset APIName2
       push    KernelBase
       call    dword ptr [GPA_Addr]      ;获取ExitProcess的地址
      
       push    0
       call    eax                 ;退出程序
      
       end     Start

;以上程序开发工具和测试环境:
;编译器:  MASM32   7.00.9466
;编辑器:  NoteXPad  1.4.0.0 RC6
;操作系统: Windows XP SP1

;程序编译后运行,出现Success!字样的消息框,但在源程序中没有引入
;任何头文件(*.inc)和库文件(*.lib)。程序的目的达到了。

;如转载本文,请保持文章所有内容完整,谢谢!

页: [1]
© 1999-2008 EvilOctal Security Team