[动态获取API地址详解_Android, Python及开发编程讨论区_Weblogic技术|Tuxedo技术|中间件技术|Oracle论坛|JAVA论坛|Linux/Unix技术|hadoop论坛_联动北方技术论坛  
网站首页 | 关于我们 | 服务中心 | 经验交流 | 公司荣誉 | 成功案例 | 合作伙伴 | 联系我们 |
联动北方-国内领先的云技术服务提供商
»  游客             当前位置:  论坛首页 »  自由讨论区 »  Android, Python及开发编程讨论区 »
总帖数
1
每页帖数
101/1页1
返回列表
0
发起投票  发起投票 发新帖子
查看: 3704 | 回复: 0   主题: [动态获取API地址详解        下一篇 
jfl
注册用户
等级:少校
经验:1112
发帖:95
精华:0
注册:2012-8-10
状态:离线
发送短消息息给jfl 加好友    发送短消息息给jfl 发消息
发表于: IP:您无权察看 2012-8-29 15:18:04 | [全部帖] [楼主帖] 楼主

;首先是一个SEH异常处理代码,这段程序是作为一个子程序来调用的,为了避免使用全局变量,这里用_lpSEH这个参数来传递需要保护的寄存器。

_SEHHandler  proc  C _lpExceptionRecord,_lpSEH,_lpContext,_lpDispatcherContext

pushad

mov  esi,_lpExceptionRecord

mov  edi,_lpContext

assume  esi:ptr EXCEPTION_RECORD,edi:ptr CONTEXT

mov  eax,_lpSEH

push  [eax + 0ch]

    pop  [edi].regEbp      ;恢复Ebp

 push  [eax + 8]

    pop  [edi].regEip      ;恢复Eip

 push  eax

pop  [edi].regEsp

assume  esi:nothing,edi:nothing

popad

mov  eax,ExceptionContinueExecution

ret

_SEHHandler  endp

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

; 在内存中扫描 Kernel32.dll 的基址,重点来了。

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

_GetKernelBase  proc  _dwKernelRet

local  @dwReturn

pushad

mov  @dwReturn,0

;********************************************************************

; 下面是地址重定位的代码,为了让这个子程序中的两个函数能用到任何地方

;********************************************************************

call  @F

@@:

pop  ebx

sub  ebx,offset @B

;********************************************************************

; 下面是创建用于错误处理的 SEH 结构

;********************************************************************

assume  fs:nothing

push  ebp

lea  eax,[ebx + offset _PageError]

push  eax

lea  eax,[ebx + offset _SEHHandler]

push  eax

push  fs:[0]

mov  fs:[0],esp

;********************************************************************

; 查找 Kernel32.dll 的基地址

;********************************************************************

mov  edi,_dwKernelRet

    and  edi,0ffff0000h    ;重点:为什么要和0ffff0000h进行and运算?因为PE文件被装入内存时是按64K对齐的,取and后刚好是64K的整数

    ;倍,这样查找的时候就更容易找到,提高效率。

 .while  TRUE

    .if  word ptr [edi] == IMAGE_DOS_SIGNATURE    ;先比较是不��一个DOS头部

 mov  esi,edi

add  esi,[esi+003ch]

    .if word ptr [esi] == IMAGE_NT_SIGNATURE  ;再看是不是一个有效的PE头

 mov  @dwReturn,edi

.break

.endif

.endif

_PageError:

    sub  edi,010000h    ;这儿为什么是减010000h,往低地址找?因为按照PE结构的顺序,导出表的地址是在PE文件地址的后面

    ;而内存的增长方向又是从低到高,所以要找kernerl32.dll的基址就必须往前面(低地址找)。  

 .break  .if edi < 070000000h

.endw

pop  fs:[0]

add  esp,0ch

popad

mov  eax,@dwReturn

ret

_GetKernelBase  endp

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

; 从内存中模块的导出表中获取某个 API 的入口地址

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

_GetApi    proc  _hModule,_lpszApi

local  @dwReturn,@dwStringLength

pushad

mov  @dwReturn,0

;********************************************************************

; 重定位

;********************************************************************

call  @F

@@:

pop  ebx

sub  ebx,offset @B

;********************************************************************

; 创建用于错误处理的 SEH 结构

;********************************************************************

assume  fs:nothing

push  ebp

lea  eax,[ebx + offset _Error]

push  eax

lea  eax,[ebx + offset _SEHHandler]

push  eax

push  fs:[0]

mov  fs:[0],esp

;********************************************************************

; 下面是计算 API 字符串的长度(带尾部的0),这段程序的原理是:将al赋值为0,然后依次与API的每一个字符进行比较,当比较到API最后一个字符'0'时

;表示API的字符串结束,然后用edi现在的地址值减去API之前的地址,之差就是API的长度。      

;********************************************************************

mov  edi,_lpszApi

mov  ecx,-1

xor  al,al

cld

    repnz  scasb    ;这条指令之前一直没弄懂,后来查看了一些资料才明白。repnz 表示在ecx!=0且ZF==0时,程序继续循环。scasb

    ;是对每一个字符进行比较(比较对象 是:al  与edi 中的字符。)用C语言可以这样翻译:

 ;while(--ecx)

;{

    ; if(*(edi++)==al)

    ; break;

;}

mov ecx,edi

sub ecx,_lpszApi

mov @dwStringLength,ecx

;********************************************************************

; 从 PE 文件头的数据目录获取导出表地址

;********************************************************************

mov  esi,_hModule

    add  esi,[esi + 3ch]    ;3ch这个长度是从DOS头部到PE头的大小,从基地址加3ch刚好到达NT结构。

 assume  esi:ptr IMAGE_NT_HEADERS

mov  esi,[esi].OptionalHeader.DataDirectory.VirtualAddress

    add  esi,_hModule    ;因为数据目录的第一个就是导出表,所以直接取其地址,就是EXPORT结构体。

 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,_lpszApi

mov  ecx,@dwStringLength

    repz  cmpsb    ;这个repz指令与上面的repnz类似,也是串循环指令。不同之处在于比较的对象不一样,

    ;这里比较的对象是si与di中的字符,若相等则继续循环,不相等则退出循环。

 .if  ZERO?

pop  esi

jmp  @F  ;找到要查找的API,则直接跳到下一步。

.endif

pop  esi

    add  ebx,4    ;没找到则继续向后找,这里,为什么是加4?因为AddressOfNames(函数名字符串地址表)是

    ;一个双字数组。

 inc  edx

.until  edx >=  [esi].NumberOfNames

jmp  _Error

@@:

;********************************************************************

; API名称索引 --> 序号索引 --> 地址索引

;********************************************************************

sub  ebx,[esi].AddressOfNames

sub  ebx,_hModule

    shr  ebx,1      ;这里为什么要右移一位?右移是除2的意思,因为AddressOfFunctions指向的是一个word数组,

    ;所以要除以2,以便可以和之后的地址相加。

 add  ebx,[esi].AddressOfNameOrdinals

    add  ebx,_hModule    ;这里为什么要加kernel32.dll的基址?因为目前ebx只是一个RVA,还不能直接用。

 movzx  eax,word ptr [ebx]

    shl  eax,2      ;这里左移一位,是恢复双字数据,为下面相加作准备。

 add  eax,[esi].AddressOfFunctions

    add  eax,_hModule    ;这个也是个RVA

;********************************************************************

; 从地址表得到导出函数地址

;********************************************************************

mov  eax,[eax]

    add  eax,_hModule    ;eax这个RVA加上kernel32.dll的基址,就是要找的API的函数地址了。

 mov  @dwReturn,eax

_Error:

pop  fs:[0]

add  esp,0ch

assume  esi:nothing

popad

mov  eax,@dwReturn

ret

_GetApi    endp

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

下面是主程序:

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

.386

.model flat,stdcall

option casemap:none

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

include    windows.inc

_ProtoGetProcAddress  typedef  proto  :dword,:dword

_ProtoLoadLibrary  typedef  proto  :dword

_ProtoMessageBox  typedef  proto  :dword,:dword,:dword,:dword

_ApiGetProcAddress  typedef  ptr  _ProtoGetProcAddress

_ApiLoadLibrary    typedef  ptr  _ProtoLoadLibrary

_ApiMessageBox    typedef  ptr  _ProtoMessageBox

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

; 数据段

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

.data?

hDllKernel32  dd  ?

hDllUser32  dd  ?

_GetProcAddress  _ApiGetProcAddress  ?

_LoadLibrary  _ApiLoadLibrary    ?

_MessageBox  _ApiMessageBox    ?

.const

szLoadLibrary  db  'LoadLibraryA',0

szGetProcAddress db  'GetProcAddress',0

szUser32  db  'user32',0

szMessageBox  db  'MessageBoxA',0

szCaption  db  'A MessageBox !',0

szText    db  'Hello, World !',0

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

; 代码段

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

.code

include    _GetKernel.asm

start:

;********************************************************************

; 从堆栈中的 Ret 地址转换 Kernel32.dll 的基址,并在 Kernel32.dll

; 的导出表中查找 GetProcAddress 函数的入口地址

;********************************************************************

invoke  _GetKernelBase,[esp]

.if  eax

mov  hDllKernel32,eax

invoke  _GetApi,hDllKernel32,addr szGetProcAddress

mov  _GetProcAddress,eax

.endif

;********************************************************************

; 用得到的 GetProcAddress 函数得到 LoadLibrary 函数地址并装入其他 Dll

;********************************************************************

.if  _GetProcAddress

invoke  _GetProcAddress,hDllKernel32,addr szLoadLibrary

mov  _LoadLibrary,eax

.if  eax

invoke  _LoadLibrary,addr szUser32

mov  hDllUser32,eax

invoke  _GetProcAddress,hDllUser32,addr szMessageBox

mov  _MessageBox,eax

.endif

.endif

;********************************************************************

.if  _MessageBox

invoke  _MessageBox,NULL,offset szText,offset szCaption,MB_OK

.endif

ret

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

end  start




赞(0)    操作        顶端 
总帖数
1
每页帖数
101/1页1
返回列表
发新帖子
请输入验证码: 点击刷新验证码
您需要登录后才可以回帖 登录 | 注册
技术讨论