P文件之重定位编程

PE权威指南第六章之重定位编程

1.无导入表的HelloWorld

	.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 ?

_getKernelBase proc _dwKernelRetAddress
	local @dwRet

	pushad
	mov @dwRet,0
	mov edi,_dwKernelRetAddress

	and edi,0ffff0000h

	.repeat
		.if word ptr [edi] == IMAGE_DOS_SIGNATURE
			mov esi,edi
			add esi,[esi+003ch]
			.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

_getApi proc _hModule,_lpApi
	local @ret
	local @dwLen

	pushad
	mov @ret,0
	mov edi,_lpApi
	mov ecx,-1
	xor al,al
	cld
	repnz scasb
	mov ecx,edi
	sub ecx,_lpApi
	mov @dwLen,ecx

	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
@@:
	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]
	invoke _getKernelBase,eax
	mov hKernel32Base,eax

	invoke _getApi,hKernel32Base,addr szGetProcAddr
	mov lpGetProcAddr,eax
	mov _getProcAddress,eax
	invoke _getProcAddress,hKernel32Base,addr szLoadLib
	mov _loadLibrary,eax
	invoke _loadLibrary ,addr user32_DLL
	mov hUser32Base,eax
	invoke _getProcAddress,hUser32Base,addr szMessageBox
	mov _messageBox,eax
	invoke _messageBox,NULL,offset szText,NULL,MB_OK
	ret
end start

将源程序保存为hello1.asm,然后普通的编译链接,发现没有按照预期弹出信息框,经过调试,发现运行后不久就抛出异常。

因为我们把变量定义在代码区,而代码区是不可写的,所以需要修改编译生成的exe的代码区的属性。或者,改变链接的指令。

方便起见,我们直接修改链接指令。

完整指令如下:

C:\Users\17724\Desktop\214>ml -c -coff hello1.asm
Microsoft (R) Macro Assembler Version 12.00.21005.1
Copyright (C) Microsoft Corporation.  All rights reserved.

 Assembling: hello1.asm

***********
ASCII build
***********

C:\Users\17724\Desktop\214>link -subsystem:windows -section:.text,ERW hello1.obj
Microsoft (R) Incremental Linker Version 12.00.21005.1
Copyright (C) Microsoft Corporation.  All rights reserved.

C:\Users\17724\Desktop\214>hello1.exe

如此,就会弹出信息框了。

使用PeInfo查看,可见是没有导入表的。

但是有重定位信息,简单理解就是,手动修改PE文件的加载基址以后,程序就不能正常运行。

为了改善这个方案,可以采用重定位编程。

2.无重定位的HelloWorld

	.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
szExitProcess db 'ExitProcess',0

user32_DLL db 'user32.dll',0,0

_getProcAddress _ApiGetProcAddress ?
_loadLibrary _ApiLoadLib ?
_messageBox _ApiMessageBoxA ?

hKernel32Base dd ?
hUser32Base dd ?
lpGetProcAddr dd ?
lpLoadLib dd ?

_getKernelBase proc _dwKernelRetAddress
	local @dwRet

	pushad
	mov @dwRet,0
	mov edi,_dwKernelRetAddress

	and edi,0ffff0000h

	.repeat
		.if word ptr [edi] == IMAGE_DOS_SIGNATURE
			mov esi,edi
			add esi,[esi+003ch]
			.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

_getApi proc _hModule,_lpApi
	local @ret
	local @dwLen

	pushad
	mov @ret,0
	mov edi,_lpApi
	mov ecx,-1
	xor al,al
	cld
	repnz scasb
	mov ecx,edi
	sub ecx,_lpApi
	mov @dwLen,ecx

	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
@@:
	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]
	push eax
	call @F
@@:
	pop ebx
	sub ebx,offset @B
	pop eax

	;获取kernel32.dll的基址
	invoke _getKernelBase,eax
	
	mov [ebx + offset hKernel32Base],eax


	;获取ProcAddress的函数地址
	mov edx,ebx
	add edx,offset szGetProcAddr
	invoke _getApi,eax,edx

	mov [ebx + offset _getProcAddress],eax
	
	;获取LoadLibrary的函数地址
	mov edx,ebx
	add edx,offset szLoadLib
	push edx
	push [ebx + offset hKernel32Base]
	call eax


	;下面这一段和上面等效,其实自己写的_getApi和系统库的GetProcAddress一个效果
	;mov edx,ebx
	;add edx,offset szLoadLib
	;invoke _getApi,[ebx + offset hKernel32Base],edx


	mov [ebx+offset _loadLibrary],eax

	;加载user32.dll
	mov edx,ebx
	add edx,offset user32_DLL
	push edx
	call eax

	mov [ebx + offset hUser32Base],eax  

	;找到MessageBox的函数地址
	mov edx,ebx
	add edx,offset szMessageBox
	invoke _getApi,eax,edx
	
	;下面这一段和上面等效
	;mov ecx,ebx
	;add ecx,offset szMessageBox
	;push ecx
	;push eax
	;call [ebx + offset _getProcAddress]

	mov ecx,ebx
	add ecx,offset szText

	push MB_OK
	push NULL
	push ecx
	push NULL
	call eax

	;不加上ExitProcess的话,关闭信息框后进程还挂在后台
	mov edx,ebx
	add edx,offset szExitProcess
	invoke _getApi,[ebx + offset hKernel32Base],edx

	push NULL
	call eax
end start

保存为hello2.asm文件,和之前的一样编译链接即可。

取巧之处在于,利用call @F等一系列指令,将start函数的指令地址保存到了ebx寄存器里面,以后使用变量,就可以用偏移的方式,这样就不会引入绝对地址了。

效果,也就是修改PE文件的基址,也能正常运行。

但是,虽然如此,hello2.exe还是有重定位表的。

posted @ 2022-03-13 17:30  念秋  阅读(94)  评论(0编辑  收藏  举报