kevoreilly / capemon

capemon: CAPE's monitor
GNU General Public License v3.0
102 stars 49 forks source link

ntdll write protection preventing AppV hooking in Office 2016 32bit #21

Closed michaelweiser closed 3 years ago

michaelweiser commented 3 years ago

Starting Winword and Excel 2016 32 bit with capemon loaded on recent Windows 10 quickly ends in an error message The operating system is not presently configured to run this application: error-message Enabling loader snaps shows that various DLLs can not be found:

DebugString: "0cb8:0cc8 @ 00250593 - LdrGetDllHandleEx - ENTER: DLL name: mso20win32client.dll"
DebugString: "0cb8:0cc8 @ 00250593 - LdrpFindLoadedDllInternal - RETURN: Status: 0xc0000135"
DebugString: "0cb8:0cc8 @ 00250593 - LdrGetDllHandleEx - RETURN: Status: 0xc0000135"
DebugString: "0cb8:0cc8 @ 00250609 - LdrLoadDll - ENTER: DLL name: C:\Program Files (x86)\Common Files\Microsoft Shared\Office16\mso20win32client.dll"
DebugString: "0cb8:0cc8 @ 00250609 - LdrpLoadDllInternal - ENTER: DLL name: C:\Program Files (x86)\Common Files\Microsoft Shared\Office16\mso20win32client.dll"
DebugString: "0cb8:0cc8 @ 00250609 - LdrpResolveDllName - ENTER: DLL name: C:\Program Files (x86)\Common Files\Microsoft Shared\Office16\mso20win32client.dll"
DebugString: "0cb8:0cc8 @ 00250609 - LdrpResolveDllName - RETURN: Status: 0xc0000135"
DebugString: "0cb8:0cc8 @ 00250609 - LdrpProcessWork - ERROR: Unable to load DLL: "C:\Program Files (x86)\Common Files\Microsoft Shared\Office16\mso20win32client.dll", Parent Module: "(null)", Status: 0xc0000135"
DebugString: "0cb8:0cc8 @ 00250609 - LdrpLoadDllInternal - RETURN: Status: 0xc0000135"
DebugString: "0cb8:0cc8 @ 00250609 - LdrLoadDll - RETURN: Status: 0xc0000135"

... and so on for mso30win32client.dll, mso40uiwin32client.dll, mso99Lwin32client.dll and more.

Looking at the supposed location they indeed do not exist there. Instead they live at C:\Program Files (x86)\Microsoft Office\root\VFS\ProgramFilesCommonX86\Microsoft Shared\OFFICE16.

This is confirmed by looking at the loader snaps of an unmonitored Winword.exe in x32dbg which read:

DebugString: "19a8:0580 @ 00954453 - LdrGetDllHandleEx - RETURN: Status: 0xc0000135"
DebugString: "19a8:0580 @ 00954453 - LdrLoadDll - ENTER: DLL name: C:\Program Files (x86)\Common Files\Microsoft Shared\Office16\mso20win32client.dll"
DebugString: "19a8:0580 @ 00954453 - LdrpLoadDllInternal - ENTER: DLL name: C:\Program Files (x86)\Common Files\Microsoft Shared\Office16\mso20win32client.dll"
DebugString: "19a8:0580 @ 00954453 - LdrpResolveDllName - ENTER: DLL name: C:\Program Files (x86)\Common Files\Microsoft Shared\Office16\mso20win32client.dll"
DebugString: "19a8:0580 @ 00954453 - LdrpResolveDllName - RETURN: Status: 0x00000000"
DebugString: "19a8:0580 @ 00954453 - LdrpMinimalMapModule - ENTER: DLL name: C:\Program Files (x86)\Common Files\Microsoft Shared\Office16\mso20win32client.dll"
DLL Loaded: 5AE10000 C:\Program Files (x86)\Microsoft Office\root\VFS\ProgramFilesCommonX86\Microsoft Shared\OFFICE16\Mso20win32client.dll
DebugString: "19a8:0580 @ 00954453 - LdrpMinimalMapModule - RETURN: Status: 0x00000000"

The mechanism behind that apparent redirection is reverse engineered and explained at https://lucasg.github.io/2018/08/22/magic-behind-appvisv/.

Indeed, in the unhooked Winword.exe, disassembly of ntdll exports contain hooks redirecting into module appvisvsubsystems32: ntopenfile-appv-hook

In the monitored process, they do not. This is likely explained by the following earlier debug output and exceptions:

DebugString: "0cb8:0cc8 @ 00250500 - LdrGetDllHandleEx - ENTER: DLL name: AppVIsvSubsystems32.dll"
DebugString: "0cb8:0cc8 @ 00250500 - LdrpFindLoadedDllInternal - RETURN: Status: 0x00000000"
DebugString: "0cb8:0cc8 @ 00250500 - LdrGetDllHandleEx - RETURN: Status: 0x00000000"
DebugString: "0cb8:0cc8 @ 00250500 - LdrGetDllHandleEx - ENTER: DLL name: AppVIsvSubsystems32.dll"
DebugString: "0cb8:0cc8 @ 00250500 - LdrpFindLoadedDllInternal - RETURN: Status: 0x00000000"
DebugString: "0cb8:0cc8 @ 00250500 - LdrGetDllHandleEx - RETURN: Status: 0x00000000"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtOpenKey" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtOpenKeyEx" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtOpenKeyTransacted" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtOpenKeyTransactedEx" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtDeleteKey" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtFlushKey" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtCreateKey" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtCreateKeyTransacted" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtEnumerateKey" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtQueryKey" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtQueryObject" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtSetInformationKey" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtQueryValueKey" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtEnumerateValueKey" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtSetValueKey" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtDeleteValueKey" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtRenameKey" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtQueryMultipleValueKey" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtNotifyChangeKey" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtNotifyChangeMultipleKeys" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtQuerySecurityObject" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtSetSecurityObject" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtDuplicateObject" by name"
DebugString: "0cb8:0cc8 @ 00250515 - LdrpGetProcedureAddress - INFO: Locating procedure "NtClose" by name"
EXCEPTION_DEBUG_INFO:
           dwFirstChance: 1
           ExceptionCode: C0000005 (EXCEPTION_ACCESS_VIOLATION)
          ExceptionFlags: 00000000
        ExceptionAddress: 5CCC212B appvisvsubsystems32.5CCC212B
        NumberParameters: 2
ExceptionInformation[00]: 00000001 Write
ExceptionInformation[01]: 776D2AA0 <ntdll.ZwClose> Inaccessible Address
First chance exception on 5CCC212B (C0000005, EXCEPTION_ACCESS_VIOLATION)!
EXCEPTION_DEBUG_INFO:
           dwFirstChance: 1
           ExceptionCode: C0000005 (EXCEPTION_ACCESS_VIOLATION)
          ExceptionFlags: 00000000
        ExceptionAddress: 5CCC2130 appvisvsubsystems32.5CCC2130
        NumberParameters: 2
ExceptionInformation[00]: 00000001 Write
ExceptionInformation[01]: 776D2AA1 ntdll.776D2AA1 Inaccessible Address
First chance exception on 5CCC2130 (C0000005, EXCEPTION_ACCESS_VIOLATION)!
EXCEPTION_DEBUG_INFO:
           dwFirstChance: 1
           ExceptionCode: C0000005 (EXCEPTION_ACCESS_VIOLATION)
          ExceptionFlags: 00000000
        ExceptionAddress: 5CCC212B appvisvsubsystems32.5CCC212B
        NumberParameters: 2
ExceptionInformation[00]: 00000001 Write
ExceptionInformation[01]: 776D2D90 <ntdll.ZwDuplicateObject> Inaccessible Address
First chance exception on 5CCC212B (C0000005, EXCEPTION_ACCESS_VIOLATION)!
EXCEPTION_DEBUG_INFO:
           dwFirstChance: 1
           ExceptionCode: C0000005 (EXCEPTION_ACCESS_VIOLATION)
          ExceptionFlags: 00000000
        ExceptionAddress: 5CCC2130 appvisvsubsystems32.5CCC2130
        NumberParameters: 2
ExceptionInformation[00]: 00000001 Write
ExceptionInformation[01]: 776D2D91 ntdll.776D2D91 Inaccessible Address

This in my interpretation shows how AppVIsvSubsystems32.dll is locating the exports of the functions it wants to hook and then trying to patch them, which is denied.

In a jumping conclusion this lead me to NtProtectVirtualMemory where I had seen an ntdll protection functionality. And indeed, setting ntdll-protect=0 in options of analysis.conf of the CAPEv2 analyzer makes all the above misbehaviour disappear. Disassembly of the ntdll entrypoints shows that AppVIsvSubsystems32.dll is once again able to install its hooks and the loader snaps show the DLLs being loaded successfully from their actual locations. (Word and Excel still don't start up successfully but that seems to be an unrelated problem for another day.)

Should ntdll-protect=0 perhaps become part of the special office settings profile? Or could/should there be a more general detection of AppV Detour hooking attempts as explained in above article?

(I would suspect that 64bit Office suffers from the same problem but can not easiliy test that because the only workaround for #12 I currently have incidentally consists of disabling the hooking of NtProtectVirtualMemory which also disables ntdll write protection.)

kevoreilly commented 3 years ago

Hi Michael, and thank you. Certainly ntdll-protect=0 can be set in Office settings. Another possibility is in-monitor exe path check similar to how browser hook modes are set, there is even a monitor yara sig that sets this option (IcedID). Still better might be to hook LdrpDetectDetour and enable this option in that hook - I will look into this last option and let you know how I get on.

kevoreilly commented 3 years ago

This is now fixed - the monitor now checks process path and enables Office settings including disabling ntdll write-protection.

Sorry it's taken me so long to publish this.