动态获取API入口地址,使exe文件的导入表为空

本文出自罗云彬的win32汇编那本书,我稍微做了一下修改

Windows在执行一个exe文件的时候会调用Kernel32.dll中的CreateProcess来启动应用程序的主程序,在执行应用程序之前windows会push返回地址。这个返回地址通过程序入口的[esp]就能得到。为什么要说这个返回地址呢,因为这个返回地址在Kernel32.dll模块中,而GetProcAddress和LoadLibrary函数也在Kernel32.dll中,我们完全可以通过这个返回地址,间接地得到这两个函数的地址。

PE文件在执行时,Kernel32.dll肯定是被装入内存的,而且PE文件装入内存的时候是按64K对齐的,每页的间隔是4K。
我们将这个返回地址按64K对齐,然后以每次一个页的间隔在内存中寻找DOS MZ文件头标识和PE文件头标识,如果找到的话,标示这个页的起始地址就是Kernel32.dll模块的基址。


程序主要思路:
(1)动态获取Kernel32.dll载入基地址.
(2)根据Kernel32.dll的导出表找到GetProcAddress函数的地址
(3)调用GetProcAddress函数得到LoadLibrary函数的地址。

(4)获得LoadLibrary,GetProcAddress这两个关键函数后,就可以调用其他DLL文件的API了。

如果你用Dependency 查看,会看不到这个exe文件引用了哪些DLL文件


代码如下:

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Sample code for < Win32ASM Programming 4th Edition>
; by 罗云彬, luoyunbin@hotmail.com
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; NoImport.asm
; 以从内存中动态获取的办法使用 API
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 使用 nmake 或下列命令进行编译和链接:
; ml /c /coff NoImport.asm
; Link /subsystem:windows NoImport.com
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
		.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

_GetKernel.asm是

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Sample code for < Win32ASM Programming 4th Edition>
; by 罗云彬, luoyunbin@hotmail.com
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 公用模块:_GetKernel.asm
; 根据程序被调用的时候堆栈中有个用于 Ret 的地址指向 Kernel32.dll
; 而从内存中扫描并获取 Kernel32.dll 的基址
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;
;
;
;
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 错误 Handler
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_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
		push	[eax + 8]
		pop	[edi].regEip
		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
		.while	TRUE
			.if	word ptr [edi] == IMAGE_DOS_SIGNATURE
				mov	esi,edi
				add	esi,[esi+003ch]
				.if word ptr [esi] == IMAGE_NT_SIGNATURE
					mov	@dwReturn,edi
					.break
				.endif
			.endif
			_PageError:
			sub	edi,010000h
			.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)
;********************************************************************
		mov	edi,_lpszApi
		mov	ecx,-1
		xor	al,al
		cld
		repnz	scasb
		mov	ecx,edi
		sub	ecx,_lpszApi
		mov	@dwStringLength,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,_lpszApi
			mov	ecx,@dwStringLength
			repz	cmpsb
			.if	ZERO?
				pop	esi
				jmp	@F
			.endif
			pop	esi
			add	ebx,4
			inc	edx
		.until	edx >=	[esi].NumberOfNames
		jmp	_Error
@@:
;********************************************************************
; 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	@dwReturn,eax
_Error:
		pop	fs:[0]
		add	esp,0ch
		assume	esi:nothing
		popad
		mov	eax,@dwReturn
		ret

_GetApi		endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值