Closed Oddy9923857 closed 7 years ago
Please elaborate and add reproduction steps.
Hi,
What OS did you use? Windows 7 - 64bit What process did you install to? various tried: calc.exe, cmd.exe, chrome.exe Did you try using an unmodified clone of the DoubleAgent source? Tried on unmodified checkout of latest code on Github.
Problem signature: Problem Event Name: APPCRASH Application Name: calc.exe Application Version: 6.1.7600.16385 Application Timestamp: 4a5bc9d4 Fault Module Name: DoubleAgentDll.dll Fault Module Version: 0.0.0.0 Fault Module Timestamp: 58d96f9a Exception Code: 40000015 Exception Offset: 000000000000109e OS Version: 6.1.7601.2.1.0.256.48 Locale ID: 1033 Additional Information 1: dd9b Additional Information 2: dd9b1ecc132da8a91a07259dbbb475a8 Additional Information 3: f0b7 Additional Information 4: f0b7ca28261bdb6a49d785c0e5f5fe2b
I've been struggling with this aswell.
A bit more info : If I compile using the following
cl.exe doubleagentdll.cpp /D UNICODE /GS- /LD /Od /link /ENTRY:DllMain /NODEFAULTLIB /RELEASE /SUBSYSTEM:CONSOLE kernel32.lib
then the subsequent DLL can be injected using the same technique. However I've had to ammend the original DLL code and therefore it is not a solution to this problem.
Notably; other sources (MSDN) suggest that the verifier flags registry key should be 0x80000000 but the DoubleAgent.exe program sets it to something else.
I have successfully loaded a DLL using this technique but not using DoubleAgent.exe or the provided DLL. A fresh DLL was coded and injected by hand.
If it helps, then the following code was used to create an injectable DLL (The code is not mine, found it on MSDN. It compiles with the cl.exe command line in my comment above, just make sure you use a 64-bit version if the application you are injecting is 64-bit and that the registry key is correct (i.e the flags are set to 0x8000000 etc etc. If you get an error about missing libraries then make sure to run the relevant vcvars script before issuing the command):
typedef VOID (NTAPI RTL_VERIFIER_DLL_LOAD_CALLBACK) (PWSTR DllName, PVOID DllBase, SIZE_T DllSize, PVOID Reserved); typedef VOID (NTAPI RTL_VERIFIER_DLL_UNLOAD_CALLBACK) (PWSTR DllName, PVOID DllBase, SIZE_T DllSize, PVOID Reserved); typedef VOID (NTAPI * RTL_VERIFIER_NTDLLHEAPFREE_CALLBACK) (PVOID AllocationBase, SIZE_T AllocationSize);
typedef struct _RTL_VERIFIER_THUNK_DESCRIPTOR { PCHAR ThunkName; PVOID ThunkOldAddress; PVOID ThunkNewAddress; } RTL_VERIFIER_THUNK_DESCRIPTOR, *PRTL_VERIFIER_THUNK_DESCRIPTOR;
typedef struct _RTL_VERIFIER_DLL_DESCRIPTOR { PWCHAR DllName; DWORD DllFlags; PVOID DllAddress; PRTL_VERIFIER_THUNK_DESCRIPTOR DllThunks; } RTL_VERIFIER_DLL_DESCRIPTOR, *PRTL_VERIFIER_DLL_DESCRIPTOR;
typedef struct _RTL_VERIFIER_PROVIDER_DESCRIPTOR { DWORD Length; PRTL_VERIFIER_DLL_DESCRIPTOR ProviderDlls; RTL_VERIFIER_DLL_LOAD_CALLBACK ProviderDllLoadCallback; RTL_VERIFIER_DLL_UNLOAD_CALLBACK ProviderDllUnloadCallback; PWSTR VerifierImage; DWORD VerifierFlags; DWORD VerifierDebug; PVOID RtlpGetStackTraceAddress; PVOID RtlpDebugPageHeapCreate; PVOID RtlpDebugPageHeapDestroy; RTL_VERIFIER_NTDLLHEAPFREE_CALLBACK ProviderNtdllHeapFreeCallback; } RTL_VERIFIER_PROVIDER_DESCRIPTOR, *PRTL_VERIFIER_PROVIDER_DESCRIPTOR;
typedef ULONG (__cdecl* PFN_DbgPrint)(PCH, ...); PFN_DbgPrint DbgPrint;
typedef BOOL (WINAPI* PFN_CloseHandle)(HANDLE); BOOL WINAPI ThunkCloseHandle(HANDLE hObject);
static RTL_VERIFIER_THUNK_DESCRIPTOR aThunks[] = {{"CloseHandle", NULL, ThunkCloseHandle}, {}}; static RTL_VERIFIER_DLL_DESCRIPTOR aDlls[] = {{L"kernel32.dll", 0, NULL, aThunks}, {}}; static RTL_VERIFIER_PROVIDER_DESCRIPTOR vpd = {sizeof(RTL_VERIFIER_PROVIDER_DESCRIPTOR), aDlls};
BOOL WINAPI ThunkCloseHandle(HANDLE hObject) { BOOL fRetVal = ((PFN_CloseHandle)(aThunks[0].ThunkOldAddress))(hObject); DbgPrint("CloseHandle(%p) = %s\n", hObject, fRetVal ? "TRUE" : "FALSE"); return fRetVal; }
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, PRTL_VERIFIER_PROVIDER_DESCRIPTOR pVPD) { switch(fdwReason) { case DLL_PROCESS_ATTACH: ::DisableThreadLibraryCalls(hinstDLL); break; case DLL_PROCESS_DETACH: break; case DLL_PROCESS_VERIFIER: DbgPrint = (PFN_DbgPrint)::GetProcAddress(::GetModuleHandle(TEXT("NTDLL")), "DbgPrint"); DbgPrint("CommandLine: %s\n", ::GetCommandLineA()); pVPD = &vpd; break; default: ::DebugBreak(); } return TRUE; }
funny thing is : I can't detect any real functional differences in the code apart from the DLL_Descriptor. Perhaps the issue lies in the project settings? Compiling using the command line for cl.exe gives a workable result (not tried against DoubleAgentDLL::main.c yet).
Hi Guys, Great work, I've been investigating this issue since the morning. My main conclusion is that ntdll.dll loads the verifier dll differently on Windows 7 and Windows 10. On Windows 10 - verifier dll is loaded once most of the process has been initialized. This means that verifier dll can have complicated dependencies, these dependencies would be resolved and loaded correctly and would be ready to use, that's why the sample code works on Windows 10.
On Windows 7 - verifier dll is loaded once the process is still in an intermediate stage. The first problem was caused because DoubleAgentDll was compiled with dependency libraries including the CRT. Once DoubleAgentDll has been loaded, the CRT was loaded too and failed during initialization. This problem was solved by removing all the dependency libraries (Oddy9923857 got to the same conclusion). Once I removed all the dependency libraries DoubleAgentDll gets injected and works on Windows 7. This code has been added to develop & master branches and is now available.
The second problem was caused once we tried to add any dependency library to DoubleAgentDll. I tried adding just kernel32.dll, and just calling a single function (CreateProcessW), but this caused an access violation exception when CreateProcessInternalW used an uninitialized global variable (attached). . This global variable is uninitialized because the process is still in an intermediate stage.
That leaves us with two possible solutions for Windows 7:
Great! Thanks for your analysis Michael, almost went crazy compiling different options last night!!
I'll checkout the dev branch and give it a go later tonight.
Be sure to add a "VerifierFlag" 0x80000000 value to the registry. Apparently this value limits what the verify process does (we are only interested in hooking here). This needs to be added to the installer code. To test it try manually adding it. From reg file the line is: "VerifierFlags"=dword:80000000
I had to do this for Windows 8.1, there is perhaps OS difference in how the Application Verifier stuff works.
Injection of the compiled DLL fails. The injected application crashes and recovery is not possible. The crash occurs before DLLMain is called. This occurs for all Non-AppVerifier DLLs.