  | 
					  | 
					  |  
					
					
	
    
 | 
                  
                    | 
                    | 					
				  
				            
					            
						            
							            
								            | 
									            
								             | 
            								
								            
									            
	C/C++本地代码调试
	帖子发起人: 手语   发起时间: 2008-12-26 17:05 下午   回复: 6
	
	
 
 
	 
		
			
				
					
						
							
							   
							
   
							
							
						 | 
						
						    
						      
						        | 
							          帖子排序: 
							            
						         | 
						        
							          
							          
						         | 
						       
						     
						 | 
					 
				 
			 | 
		 
	 
	
			
				
					
						
							
								
									
										
											
												
												 
                            2008-12-26, 17:05 下午
                        
											 | 
										 
										
											
												
													 
													
														手语
													
													
														 
														
  
  
														 注册: 2008-06-06
														
														 发 贴: 73
														
													
											  | 
											
												
													
														
															
																
																
																	LoadLibrary() 加载DLL和EXE的不同行为 
																
															
														 | 
													 
													
														| 
														  
														 | 
													 
												 
												
											 | 
										 
										
											
												
													
														
															 
														 | 
														
															
																
																	| 
																		
 上次开了个贴“请教一个.Net程序中PInvoke的问题”问了一个我遇到的奇怪现象。 问题描述有点乱,所以重开一贴讲一下我的跟踪结果。 
把问题简化一下: 我发现LoadLibrary()对待DLL和EXE有不同的行为:加载EXE时不处理Base relocation。 如果EXE输出的函数再调用其他函数,一定会出问题。因为CALL指令里的地址没有被重定位。 
VC默认是不为EXE产生重定位表的,因为一般EXE都能加载到默认基址。 强制产生重定位表的方法是在连接选项里添加“/FIXED:NO”。 但即使建立了重定位表,LoadLibrary()仍然不为EXE处理重定位。所有CALL/JMP指令里的地址还是保持原状。 
跟踪了一下LoadLibraryW()。 环境:XP SP2, VS2005 
加载DLL时:(为了看起来方便,去掉了一些无关行) 0:000> wt -l1 Tracing ntdll!LdrpMapDll to return address 7c916071     3     0 [  0] ntdll!LdrpMapDll    19     0 [  1]   ntdll!_SEH_prolog    10     0 [  1]   ntdll!LdrpEnsureLoaderLockIsHeld   120     0 [  1]   ntdll!LdrpCheckForKnownDll   150     0 [  1]   ntdll!LdrpResolveDllName    30     0 [  1]   ntdll!RtlDosPathNameToNtPathName_U    78     0 [  1]   ntdll!LdrpCreateDllSection    27     0 [  1]   ntdll!RtlFreeHeap     1     0 [  1]   ntdll!ZwMapViewOfSection     3     0 [  1]   ntdll!NtMapViewOfSection     5     0 [  1]   ntdll!RtlImageNtHeader    22     0 [  1]   ntdll!RtlpImageNtHeader    23     0 [  1]   ntdll!RtlImageDirectoryEntryToData    31     0 [  1]   ntdll!LdrpAllocateDataTableEntry    13     0 [  1]   ntdll!LdrpFetchAddressOfEntryPoint    41     0 [  1]   ntdll!LdrpInsertMemoryTableEntry    20     0 [  1]   ntdll!LdrpSendDllLoadedNotifications    23     0 [  1]   ntdll!RtlImageDirectoryEntryToData    18     0 [  1]   ntdll!RtlEqualUnicodeString    18     0 [  1]   ntdll!RtlEqualUnicodeString   167     0 [  1]   ntdll!LdrpSetProtection    13     0 [  1]   ntdll!LdrRelocateImage     1     0 [  1]   ntdll!ZwMapViewOfSection     3     0 [  1]   ntdll!NtMapViewOfSection   199     0 [  1]   ntdll!LdrpSetProtection    23     0 [  1]   ntdll!LdrpValidateImageForMp     1     0 [  1]   ntdll!NtClose     3     0 [  1]   ntdll!ZwClose     9     0 [  1]   ntdll!_SEH_epilog   367  1071 [  0] ntdll!LdrpMapDll 
加载EXE时: 0:000> wt -l1 Tracing ntdll!LdrpMapDll to return address 7c916071     3     0 [  0] ntdll!LdrpMapDll    19     0 [  1]   ntdll!_SEH_prolog    10     0 [  1]   ntdll!LdrpEnsureLoaderLockIsHeld   120     0 [  1]   ntdll!LdrpCheckForKnownDll   150     0 [  1]   ntdll!LdrpResolveDllName    30     0 [  1]   ntdll!RtlDosPathNameToNtPathName_U    78     0 [  1]   ntdll!LdrpCreateDllSection    27     0 [  1]   ntdll!RtlFreeHeap     1     0 [  1]   ntdll!ZwMapViewOfSection     3     0 [  1]   ntdll!NtMapViewOfSection     5     0 [  1]   ntdll!RtlImageNtHeader    22     0 [  1]   ntdll!RtlpImageNtHeader    23     0 [  1]   ntdll!RtlImageDirectoryEntryToData    31     0 [  1]   ntdll!LdrpAllocateDataTableEntry    13     0 [  1]   ntdll!LdrpFetchAddressOfEntryPoint    41     0 [  1]   ntdll!LdrpInsertMemoryTableEntry    20     0 [  1]   ntdll!LdrpSendDllLoadedNotifications     1     0 [  1]   ntdll!ZwMapViewOfSection     3     0 [  1]   ntdll!NtMapViewOfSection     1     0 [  1]   ntdll!NtClose     3     0 [  1]   ntdll!ZwClose     9     0 [  1]   ntdll!_SEH_epilog   310   610 [  0] ntdll!LdrpMapDll 
上面有颜色的行就是不同的部分。可以明显看到对EXE文件,LoadLibrary()没有进行基址重定位操作。 不明白为什么会有这样的差异。肯定是我少做了什么步骤。 到底还需要做什么才能正确调用EXE的导出函数呢? 
请赐教。 
 鸿鹄安知燕雀之志
																		 
																	 | 
																 
															 
														 | 
													 
													
														| 
															
														 | 
														
															IP 地址: 已记录  
														 | 
														
															报告
														 | 
													 
												 
											 | 
										 
									 
								 | 
							 
						 
					 | 
				 
			 
		
			
				
					
						
							
								
									
										
											
												
												 
                            2008-12-26, 18:17 下午
                        
											 | 
										 
										
											
												
													 
													
														手语
													
													
														 
														
  
  
														 注册: 2008-06-06
														
														 发 贴: 73
														
													
												 
											 | 
											
												
													
														
															
																
																
																	Re: LoadLibrary() 加载DLL和EXE的不同行为 
																
															
														 | 
													 
													
														| 
														  
														 | 
													 
												 
												
											 | 
										 
										
											
												
													
														
															 
														 | 
														
															
																
																	
																		
继续跟踪了一下,发现EXE/DLL的差别出在这里:
 
 0:000> kb
 ChildEBP RetAddr  Args to Child              
 0012f984 7c916071 00153598 0012fa10 0012ff38 ntdll!LdrpMapDll+0x551
 0012fc44 7c9162da 00000000 00153598 0012ff38 ntdll!LdrpLoadDll+0x1e9
 0012feec 7c801bb9 00153598 0012ff38 0012ff18 ntdll!LdrLoadDll+0x230
 0012ff54 7c80ae5c 004020f4 00000000 00000000 kernel32!LoadLibraryExW+0x18e
 0012ff68 004017f8 004020f4 00000000 0040302c kernel32!LoadLibraryW+0x11
 ......
 0012fff0 00000000 004012b2 00000000 78746341 kernel32!BaseProcessStart+0x23
 
 加载EXE时:
 7c91c499 e88f020000      call    ntdll!LdrpSendDllLoadedNotifications (7c91c72d)
 7c91c49e 81ff0e000040    cmp     edi,4000000Eh
 7c91c4a4 0f84644b0200    je      ntdll!LdrpMapDll+0x4b6 (7c94100e)
 7c91c4aa 8b45a0          mov     eax,dword ptr [ebp-60h] ss:0023:0012f924=003b00e8 //IMAGE_NT_HEADERS
 7c91c4ad f6401720        test    byte ptr [eax+17h],20h     ds:0023:003b00ff=01  //HIBYTE(Characteristics)
 7c91c4b1 7404            je      ntdll!LdrpMapDll+0x55e (7c91c4b7)
 
 加载DLL时:
 7c91c499 e88f020000      call    ntdll!LdrpSendDllLoadedNotifications (7c91c72d)
 7c91c49e 81ff0e000040    cmp     edi,4000000Eh
 7c91c4a4 0f84644b0200    je      ntdll!LdrpMapDll+0x4b6 (7c94100e)
 7c91c4aa 8b45a0          mov     eax,dword ptr [ebp-60h] ss:0023:0012f8ac=003b00f0 //IMAGE_NT_HEADERS
 7c91c4ad f6401720        test    byte ptr [eax+17h],20h     ds:0023:003b0107=21  //HIBYTE(Characteristics)
 7c91c4b1 7404            je      ntdll!LdrpMapDll+0x55e (7c91c4b7)
 
 区别行用注释标识。
 [ebp-60h]是IMAGE_NT_HEADERS的指针,byte ptr [eax+17h]是PIMAGE_NT_HEADERS->FileHeader.Characteristics(WORD)的高字节。
 实际上,DLL的Characteristics是0x2102,EXE的Characteristics是0x0102。
 
 From WINNT.h:
 #define IMAGE_FILE_EXECUTABLE_IMAGE          0x0002  // File is executable  (i.e. no unresolved externel references).
 #define IMAGE_FILE_32BIT_MACHINE             0x0100  // 32 bit word machine.
 #define IMAGE_FILE_DLL                       0x2000  // File is a DLL.
 
 差别就是EXE没有IMAGE_FILE_DLL标志。后面两条语句就是根据这个标志判断走向。
 所以,看起来LoadLibrary()确实对EXE/DLL进行了区别处理。只有对DLL才进行重定位操作。
 
 那有什么方法解决这个问题呢?让从EXE导出的函数可以正常调用其他函数。
 鸿鹄安知燕雀之志
																		 
																	 | 
																 
															 
														 | 
													 
													
														| 
															
														 | 
														
															IP 地址: 已记录  
														 | 
														
															报告
														 | 
													 
												 
											 | 
										 
									 
								 | 
							 
						 
					 | 
				 
			 
		
			
				
					
						
							
								
									
										
											
												
												 
                            2008-12-27, 16:02 下午
                        
											 | 
										 
										
											
												
													 
													
														手语
													
													
														 
														
  
  
														 注册: 2008-06-06
														
														 发 贴: 73
														
													
											  | 
											
												
													
														
															
																
																
																	Re: LoadLibrary() 加载DLL和EXE的不同行为 
																
															
														 | 
													 
													
														| 
														  
														 | 
													 
												 
												
											 | 
										 
										
											
												
													
														
															 
														 | 
														
															
																
																	
																		
LoadLibrary()加载EXE时不但不处理BaseRelocation,连EXE依赖的DLL都不会加载。
 看来只有自己动手做了。
 还有一个猜想,如果为EXE加上IMAGE_FILE_DLL标志会怎样。下周一试一下。
 鸿鹄安知燕雀之志
																		 
																	 | 
																 
															 
														 | 
													 
													
														| 
															
														 | 
														
															IP 地址: 已记录  
														 | 
														
															报告
														 | 
													 
												 
											 | 
										 
									 
								 | 
							 
						 
					 | 
				 
			 
		
			
				
					
						
							
								
									
										
											
												
												 
                            2008-12-27, 20:54 下午
                        
											 | 
										 
										
											
												
													 
													
														格蠹老雷
													
													
														 
														
  
														 注册: 2005-12-19
														
														 发 贴: 1,303
														
													
												 
											 | 
											
												
													
														
															
																
																
																	Re: LoadLibrary() 加载DLL和EXE的不同行为 
																
															
														 | 
													 
													
														| 
														  
														 | 
													 
												 
												
											 | 
										 
										
											
												
													
														
															 
														 | 
														
															
																
																	
																		
分析的很清楚,期待后面的结果。关于加载映像文件这块很值得挖掘一下。
																		 
																	 | 
																 
															 
														 | 
													 
													
														| 
															
														 | 
														
															IP 地址: 已记录  
														 | 
														
															报告
														 | 
													 
												 
											 | 
										 
									 
								 | 
							 
						 
					 | 
				 
			 
		
			
				
					
						
							
								
									
										
											
												
												 
                            2008-12-28, 13:51 下午
                        
											 | 
										 
										
											
												
													 
													
														MJ0011
													
													
														 
														
  
														 注册: 2008-04-24
														
														 发 贴: 112
														
													
											  | 
											
												
													
														
															
																
																
																	Re: LoadLibrary() 加载DLL和EXE的不同行为 
																
															
														 | 
													 
													
														| 
														  
														 | 
													 
												 
												
											 | 
										 
										
											
												
													
														
															 
														 | 
														
															
														 | 
													 
													
														| 
															
														 | 
														
															IP 地址: 已记录  
														 | 
														
															报告
														 | 
													 
												 
											 | 
										 
									 
								 | 
							 
						 
					 | 
				 
			 
		
			
				
					
						
							
								
									
										
											
												
												 
                            2008-12-30, 01:28 上午
                        
											 | 
										 
										
											
												
													 
													
														手语
													
													
														 
														
  
  
														 注册: 2008-06-06
														
														 发 贴: 73
														
													
												 
											 | 
											
												
													
														
															
																
																
																	Re: LoadLibrary() 加载DLL和EXE的不同行为 
																
															
														 | 
													 
													
														| 
														  
														 | 
													 
												 
												
											 | 
										 
										
											
												
													
														
															 
														 | 
														
															
																
																	
																		
今天试了一下,为.exe文件头添加IMAGE_FILE_DLL标志,并相应修改了CheckSum。
 失败,Windows拒绝加载。估计就不可能成功。
 
 没办法只能自己动手了。
 做个函数用来做加载.exe文件的善后工作:
 1、遍历并加载.exe所依赖的所有外部模块,建立IAT。
 2、遍历重定位数据,自己做BaseRelocation处理。
 
 函数原型和LoadLibraryW()相似:
 HMODULE
 WINAPI
 LoadExecutableW( LPCWSTR pwszExecutableFile );
 
 用法也一样:
 // 假设XXXX.exe输出ExeExportedFunction()并且用/FIXED:NO选项连接以建立重定位表,这函数内部又调用其他DLL的函数。
 HMODULE hExe = LoadExecutableW( L"XXXX.exe" );
 if( NULL != hExe ) 
 {
 	FUNCTIONPTR pFunc = (FUNCTIONPTR)GetProcAddress( hExe, "ExeExportedFunction" );
 	pFunc();
 }
 
 代码打包放在
 http://www.live-share.com/files/373906/LoadExecutable.zip.html
 很小,就俩文件.h/.cpp。
 
 简单测了几个CASE都能成功,比如:
 A.exe用LoadExecutableW()加载B.exe,并调用B输出的C(),C()调用D.dll输出的函数E()。
 如果B.exe隐含连接其他带重定位表并输出函数.exe文件,一样可以递归处理。
 经过调试一切正常,证明是可行的。
 
 为了让每个内部函数都可以单独复用,代码写得比较啰嗦。
 目前的实现不完善,只用来证明调试结论。
 
 听老雷话,要好好跟踪学习一下映像加载。^_^
 鸿鹄安知燕雀之志
																		 
																	 | 
																 
															 
														 | 
													 
													
														| 
															
														 | 
														
															IP 地址: 已记录  
														 | 
														
															报告
														 | 
													 
												 
											 | 
										 
									 
								 | 
							 
						 
					 | 
				 
			 
		
			
				
					
						
							
								
									
										
											
												
												 
                            2008-12-30, 01:35 上午
                        
											 | 
										 
										
											
												
													 
													
														手语
													
													
														 
														
  
  
														 注册: 2008-06-06
														
														 发 贴: 73
														
													
											  | 
											
												
													
														
															
																
																
																	Re: LoadLibrary() 加载DLL和EXE的不同行为 
																
															
														 | 
													 
													
														| 
														  
														 | 
													 
												 
												
											 | 
										 
										
											
												
													
														
															 
														 | 
														
															
																
																	
																		
MJ老大,第四题我想到一点解法,应该是复用.exe中的main(),让他分别扮演Dll中的DllMain()和Driver中的DriverEntry()的角色。其间要做我上面做的那些处理。
 细节可能会遇到一些难题,等我随后试试。
 
 有一事不明。
 我看到LoadDll.dll的DllMain()是这么调用自己的.exe的入口函数:
 BOOLEAN 
 WINAPI 
 DllMain( HINSTANCE hDllHandle, DWORD     nReason,  LPVOID    Reserved )
 {
 	GetDllInfo = GetProcAddress( GetModuleHandleA( NULL ), 100 );
 	if( GetDllInfo )
 	{
 		GetDllInfo( &hMod, &DllEP_RVA );	
 	}
 
 	if( DLL_PROCESS_DETACH != nReason )
 	{
 		......
 		if( nReason <= DLL_THREAD_DETACH )
 		{
 			if( !DllEP_RVA )
 				return bRet;
 			DllEP = (DWORD)hMod + DllEP_RVA;
 
 			g_dwESP = ESP;
 			bRet = DllEP( hMod, nReason, Reserved );
 			ESP = g_dwESP;
 			return bRet;
 		}
 	}
 
 	bRet = TRUE;
 	return bRet;
 }
 
 
 从LoadDll.exe调用LoadDll.dll,又从DllMain()调用A.exe的EntryPoint()。
 绕这么大一圈似乎有什么深层含义。
 好像不只是为了复用.exe中的main()。
 我比较菜,容我再想想。
 鸿鹄安知燕雀之志
																		 
																	 | 
																 
															 
														 | 
													 
													
														| 
															
														 | 
														
															IP 地址: 已记录  
														 | 
														
															报告
														 | 
													 
												 
											 | 
										 
									 
								 | 
							 
						 
					 | 
				 
			 
		
	
	
		
			| 
				高端调试 » 软件调试 » C/C++本地代码调试 » Re: LoadLibrary() 加载DLL和EXE的不同行为
			 | 
			
				
			 | 
		 
	 
	
 
								             | 
                                              | 					
								            
									            
								             | 
							             
						             
					             
				             
			          
                     |