• 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还是有重定位表的。

  • 相关阅读:
    如何在Ubuntu18.04(Bionic Beaver)系统安装Teamviewer远程控制软件
    glVertexAttribPointer 用法简介
    OpenGL ES SL 3.0规范中以前的attribute改成了in varying改成了out
    C++多线程中用临界区控制全局变量的访问冲突问题
    fatal error LNK1169: 找到一个或多个多重定义的符号或多个.c/.cpp文件想同时调用定义在.h文件里面的全局变量,防止重定义变量问题。
    WaitForSingleObject的详细用法
    [转]Win7 + Ubuntu 18.04 LTS (Bionic Beaver)双系统安装方法
    [Android Pro] Android打包一个Apk后,如何查看它的VersionCode、VersionName 等等。
    [Android] 查看Android中的AlarmManager事件
    [Android Pro] proguard
  • 原文地址:https://www.cnblogs.com/dayq/p/16001103.html
Copyright © 2020-2023  润新知