Cybellum / DoubleAgent

Zero-Day Code Injection and Persistence Technique

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Application Crash on Injection

Oddy9923857 opened this issue · comments

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.

Please elaborate and add reproduction steps.

  • What OS did you use?
  • What process did you install to?
  • Did you try using an unmodified clone of the DoubleAgent source?

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

commented

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):

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>

#define DLL_PROCESS_VERIFIER 4

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).
kernel32.
This global variable is uninitialized because the process is still in an intermediate stage.

That leaves us with two possible solutions for Windows 7:

  1. Only using ntdll.dll - That's what Microsoft's verifier dll does, and that's what we did too. Note that developing against the Native API may be tricky and undocumented.
  2. Delay code execution - Delay code execution and avoid "heavy" operations from main_DllMainProcessAttach, this can be done in a verity of ways, including setting a hook on the end of the process boot and only then executing the "heavy" code.

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.