Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
michaelweiser opened this issue Jul 5, 2021 · 2 comments
Closed

Comments

@michaelweiser
Copy link

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
Copy link
Owner

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.

Jack28 pushed a commit to Jack28/CAPEv2 that referenced this issue Aug 31, 2021
There is an issue regarding the "ntdll write protection preventing AppV hooking
in Office 2016 32bit kevoreilly#21" in the capemon repository.
kevoreilly/capemon#21

It states "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".

"... in the unhooked Winword.exe, disassembly of ntdll exports contain hooks
redirecting into module appvisvsubsystems32. In the monitored process, they do
not"
Jack28 added a commit to Jack28/CAPEv2 that referenced this issue Aug 31, 2021
There is an issue regarding the "ntdll write protection preventing AppV hooking
in Office 2016 32bit kevoreilly#21" in the capemon repository.
kevoreilly/capemon#21

It states "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".

"... in the unhooked Winword.exe, disassembly of ntdll exports contain hooks
redirecting into module appvisvsubsystems32. In the monitored process, they do
not"
@kevoreilly
Copy link
Owner

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants