Closed phra closed 4 years ago
after review the source code and diffing latest DLLRefresher with ext_unhook, the following changes can be seen:
1.
+ // clr.dll hotpatches itself at runtime for performance reasons, so skip it
+ if (wcscmp(L"clr.dll", wszBaseDllName) == 0)
+ goto cleanup;
+
+ dprintf("[REFRESH] Opening file: %S", wszFullDllName);
+
2.
+ if (pSectionHeader[dwIdx].Characteristics & IMAGE_SCN_MEM_WRITE)
+ continue;
- // Skip writable sections
- if (pSectionHeader[dwIdx].Characteristics & IMAGE_SCN_MEM_WRITE)
- continue;
-
- ScanAndFixSection((PCHAR)pSectionHeader[dwIdx].Name, pKnown + pSectionHeader[dwIdx].VirtualAddress,
- pSuspect + pSectionHeader[dwIdx].VirtualAddress, pSectionHeader[dwIdx].Misc.VirtualSize);
+ if (wcscmp(wszBaseDllName, L"clr.dll") == 0 && strcmp(pSectionHeader[dwIdx].Name, ".text") == 0)
+ {
+ ScanAndFixSection((PCHAR)pSectionHeader[dwIdx].Name, pKnown + pSectionHeader[dwIdx].VirtualAddress,
+ pSuspect + pSectionHeader[dwIdx].VirtualAddress, pSectionHeader[dwIdx].Misc.VirtualSize);
+ }
+
in particular, in 2 we can see the following if being added that wraps ScanAndFixSection
invocation:
if (wcscmp(wszBaseDllName, L"clr.dll") == 0 && strcmp(pSectionHeader[dwIdx].Name, ".text") == 0)
the if returns true only when the DLL is crl.dll
and the section is .text
excluding all others DLL from being checked.
should this condition be negated instead? (ie. excluding only clr.dll .text
section)
if !((wcscmp(wszBaseDllName, L"clr.dll") == 0 && strcmp(pSectionHeader[dwIdx].Name, ".text") == 0))
in the original source code, the if is not present so maybe we can remove it entirely.
i tried also to compile the ReflectiveDLL version of DLLRefresher and inject it via shellcode_inject
module and that works fine, that means that the hooks are correctly removed. Unfortunately additional steps are required to make it work as extension.
full diff:
--- loader.c 2019-11-07 16:42:33.833988949 +0100
+++ refresh.c 2019-11-07 16:42:28.006062216 +0100
@@ -1,8 +1,7 @@
-#include <stdlib.h>
-
-#include "loader.h"
+#include "refresh.h"
#include "apisetmap.h"
-#include "ReflectiveDLLInjection.h"
+#include "../../common/common.h"
+#include "../../ReflectiveDLLInjection/dll/src/ReflectiveLoader.h"
void RefreshPE()
{
@@ -14,7 +13,7 @@
PLDR_DATA_TABLE_ENTRY pLdteHead = NULL;
PLDR_DATA_TABLE_ENTRY pLdteCurrent = NULL;
- OUTPUTDBGA("[*] Running DLLRefresher\n");
+ dprintf("[REFRESH] Running DLLRefresher");
pLdteHead = GetInMemoryOrderModuleList();
pLdteCurrent = pLdteHead;
@@ -26,9 +25,7 @@
wszBaseDllName = pLdteCurrent->BaseDllName.pBuffer;
pDllBase = (ULONG_PTR)pLdteCurrent->DllBase;
- OUTPUTDBGA("[*] Refreshing DLL: ");
- OUTPUTDBGW(wszBaseDllName);
- OUTPUTDBGA("\n");
+ dprintf("[REFRESH] Refreshing DLL: %S", wszFullDllName);
hModule = CustomLoadLibrary(wszFullDllName, wszBaseDllName, pDllBase);
@@ -80,14 +77,17 @@
PIMAGE_THUNK_DATA pThunkData;
FARPROC* pIatEntry;
+ // clr.dll hotpatches itself at runtime for performance reasons, so skip it
+ if (wcscmp(L"clr.dll", wszBaseDllName) == 0)
+ goto cleanup;
+
+ dprintf("[REFRESH] Opening file: %S", wszFullDllName);
+
// ----
// Step 1: Map the file into memory
// ----
- OUTPUTDBGA("[+] Opening file: ");
- OUTPUTDBGW(wszFullDllName);
- OUTPUTDBGA("\n");
- hFile = CreateFileW(wszFullDllName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ hFile = CreateFileW(wszFullDllName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
goto cleanup;
@@ -106,15 +106,15 @@
pNtHeader = (PIMAGE_NT_HEADERS)(pFile + pDosHeader->e_lfanew);
// allocate memory to copy DLL into
- OUTPUTDBGA("\t[+] Allocating memory for library\n");
+ dprintf("[REFRESH] Allocating memory for library");
pLibraryAddr = (PCHAR)VirtualAlloc(NULL, pNtHeader->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
// copy header
- OUTPUTDBGA("\t[+] Copying PE header into memory\n");
+ dprintf("[REFRESH] Copying PE header into memory");
memcpy(pLibraryAddr, pFile, pNtHeader->OptionalHeader.SizeOfHeaders);
// copy sections
- OUTPUTDBGA("\t[+] Copying PE sections into memory\n");
+ dprintf("[REFRESH] Copying PE sections into memory");
pSectionHeader = (PIMAGE_SECTION_HEADER)(pFile + pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS));
for (dwIdx = 0; dwIdx < pNtHeader->FileHeader.NumberOfSections; dwIdx++)
{
@@ -130,11 +130,10 @@
// ----
// Step 3: Calculate relocations
// ----
- OUTPUTDBGA("\t[+] Calculating file relocations\n");
+ dprintf("[REFRESH] Calculating file relocations");
pDataDir = &pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
pInitialImageBase = pNtHeader->OptionalHeader.ImageBase;
-
// set the ImageBase to the already loaded module's base
pNtHeader->OptionalHeader.ImageBase = pDllBase;
@@ -145,8 +144,8 @@
pBaseReloc = (PIMAGE_BASE_RELOCATION)(pLibraryAddr + pDataDir->VirtualAddress);
// iterate through each relocation entry
- while ((PCHAR)pBaseReloc < (pLibraryAddr + pDataDir->VirtualAddress + pDataDir->Size) && pBaseReloc->SizeOfBlock)
- {
+ while ((PCHAR)pBaseReloc < (pLibraryAddr + pDataDir->VirtualAddress + pDataDir->Size) && pBaseReloc->SizeOfBlock)
+ {
// the VA for this relocation block
pReloc = (ULONG_PTR)(pLibraryAddr + pBaseReloc->VirtualAddress);
@@ -194,7 +193,7 @@
// ----
// Step 4: Update import table
// ----
- OUTPUTDBGA("\t[+] Resolving Import Address Table (IAT) \n");
+ dprintf("[REFRESH] Resolving Import Address Table (IAT) ");
pDataDir = &pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
if (pDataDir->Size)
@@ -214,11 +213,9 @@
mbstowcs_s(&stSize, wszDllName, stDllName + 1, szDllName, stDllName);
- OUTPUTDBGA("\t\t[+] Loading library: ");
- OUTPUTDBGW(wszDllName);
- OUTPUTDBGA("\n");
+ dprintf("[REFRESH] Loading library: %S", wszDllName);
- // If the DLL starts with api- or ext-, resolve the redirected name and load it
+ // If the DLL starts with api- or ext-, resolve the redirected name and load it
if (_wcsnicmp(wszDllName, L"api-", 4) == 0 || _wcsnicmp(wszDllName, L"ext-", 4) == 0)
{
// wsRedir is not null terminated
@@ -232,6 +229,7 @@
goto next_import;
memcpy(wszDllName, wsRedir, stRedirName * sizeof(WCHAR));
+ dprintf("[REFRESH] Redirected library: %S", wszDllName);
}
}
@@ -286,9 +284,7 @@
PLDR_DATA_TABLE_ENTRY pLdteHead = NULL;
PLDR_DATA_TABLE_ENTRY pLdteCurrent = NULL;
- OUTPUTDBGA("\t\t\t[*] Searching for loaded module: ");
- OUTPUTDBGW(wszModule);
- OUTPUTDBGA(" -> ");
+ dprintf("[REFRESH] Searching for loaded module: %S", wszModule);
pLdteCurrent = pLdteHead = GetInMemoryOrderModuleList();
@@ -296,13 +292,11 @@
if (pLdteCurrent->FullDllName.Length > 2 &&
_wcsnicmp(wszModule, pLdteCurrent->BaseDllName.pBuffer, pLdteCurrent->BaseDllName.Length / 2) == 0)
{
- OUTPUTDBGA("found in memory\n");
return ((HMODULE)pLdteCurrent->DllBase);
}
pLdteCurrent = (PLDR_DATA_TABLE_ENTRY)pLdteCurrent->InMemoryOrderModuleList.Flink;
} while (pLdteCurrent != pLdteHead);
- OUTPUTDBGA("loading from disk\n");
return LoadLibraryW(wszModule);
}
@@ -315,9 +309,7 @@
DWORD dwIdx;
- OUTPUTDBGA("[*] Scanning module: ");
- OUTPUTDBGW(wszBaseDllName);
- OUTPUTDBGA("\n");
+ dprintf("[REFRESH] Scanning module: %S", wszBaseDllName);
pDosHeader = (PIMAGE_DOS_HEADER)pKnown;
pNtHeader = (PIMAGE_NT_HEADERS)(pKnown + pDosHeader->e_lfanew);
@@ -329,13 +321,15 @@
pSectionHeader = (PIMAGE_SECTION_HEADER)(pKnown + pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS));
for (dwIdx = 0; dwIdx < pNtHeader->FileHeader.NumberOfSections; dwIdx++)
{
+ if (pSectionHeader[dwIdx].Characteristics & IMAGE_SCN_MEM_WRITE)
+ continue;
- // Skip writable sections
- if (pSectionHeader[dwIdx].Characteristics & IMAGE_SCN_MEM_WRITE)
- continue;
-
- ScanAndFixSection((PCHAR)pSectionHeader[dwIdx].Name, pKnown + pSectionHeader[dwIdx].VirtualAddress,
- pSuspect + pSectionHeader[dwIdx].VirtualAddress, pSectionHeader[dwIdx].Misc.VirtualSize);
+ if (wcscmp(wszBaseDllName, L"clr.dll") == 0 && strcmp(pSectionHeader[dwIdx].Name, ".text") == 0)
+ {
+ ScanAndFixSection((PCHAR)pSectionHeader[dwIdx].Name, pKnown + pSectionHeader[dwIdx].VirtualAddress,
+ pSuspect + pSectionHeader[dwIdx].VirtualAddress, pSectionHeader[dwIdx].Misc.VirtualSize);
+ }
+
}
}
@@ -345,18 +339,16 @@
if (memcmp(pKnown, pSuspect, stLength) != 0)
{
- OUTPUTDBGA("\t[!] Found modification in: ");
- OUTPUTDBGA(szSectionName);
- OUTPUTDBGA("\n");
+ dprintf("[REFRESH] Found modification in: %s", szSectionName);
if (!VirtualProtect(pSuspect, stLength, PAGE_EXECUTE_READWRITE, &ddOldProtect))
return;
- OUTPUTDBGA("\t[+] Copying known good section into memory.\n");
+ dprintf("[REFRESH] Copying known good section into memory.");
memcpy(pSuspect, pKnown, stLength);
- if (!VirtualProtect(pSuspect, stLength, ddOldProtect, &ddOldProtect))
- OUTPUTDBGA("\t[!] Failed to reset memory permissions.\n");
+ if (!VirtualProtect(pSuspect, stLength, ddOldProtect, &ddOldProtect))
+ dprintf("[REFRESH] Unable to reset memory permissions");
}
}
@@ -468,11 +460,7 @@
{
szFwdDesc = (PCHAR)(uiLibraryAddress + uiFuncVA);
- OUTPUTDBGA("\t\t\t[*] Found a redirected entry: ");
- OUTPUTDBGA(szFwdDesc);
- OUTPUTDBGA("\n");
-
- // Find the first character after "."
+ // Find the first character after "."
szRedirFunc = strstr(szFwdDesc, ".") + 1;
stDllName = (SIZE_T)(szRedirFunc - szFwdDesc);
I wonder if @mrjefftang is around?
I can't remember why clr.dll is specifically excluded but I'm pretty sure it's because there was an issue when loading into powershell.
Description
The included
ext_server_unhook.x64.dll
doesn't work as expected since the user-land hooks are not removed.Steps to reproduce
Expected behavior
The user-land hooks are removed
Current behavior
The user-land hooks are preserved
Additional info
Using manually https://github.com/CylanceVulnResearch/ReflectiveDLLRefresher, the hooks are correctly removed.
System stuff
Metasploit version
I installed Metasploit with:
OS
Kali Linux