无导入表无重定位表的编程,案例

本文介绍了如何使用ASM汇编语言编写代码,通过C的格式展示,实现动态加载kernel32.dll和user32.dll库,并调用GetProcAddress、LoadLibraryA和MessageBoxA函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

;这是asm汇编语言,暂定用C的格式发布 
;------------------------
; 无导入表的HelloWorld
; zyj
; 2023.11.26
;------------------------
    .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

   ;查找指令所在页的边界,以1000h对齐
   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
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值