Some categories of applications started crashing after no obvious system changes had been. As an initial step user mode crash dumps were collecting following the guide at Collecting User-Mode Dumps – Win32 apps | Microsoft Docs with DumpType set to 2.
Opening dump file in WinDbg ( WinDbg Preview – Installation – Windows drivers | Microsoft Docs ) the initial dump file showed a CLR runtime exception, so we know we have a .NET exception.
This dump file has an exception of interest stored in it. The stored exception information can be accessed via .ecxr. (6f8.2358): CLR exception - code e0434352 (first/second chance not available) For analysis of this file, run !analyze -v eax=00000000 ebx=00000000 ecx=00000000 edx=00000000 esi=00000003 edi=00000003 eip=77a723dc esp=00afc4a8 ebp=00afc638 iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200202 ntdll!NtWaitForMultipleObjects+0xc: 77a723dc c21400 ret 14h 0:000>
As this was a .NET 4.0+ app we can load the SOS debugging extension with the following cmd, and evaluate the exceptions thrown
0:000> .loadby sos clr 0:000> !pe Exception object: 02c75fdc Exception type: System.TypeInitializationException Message: The type initializer for '<Module>' threw an exception. InnerException: System.TypeInitializationException, Use !PrintException ad4b1278 to see more. StackTrace (generated): SP IP Function 00000000 00000001 MyApp!ns10.App.OnStartup(System.Windows.StartupEventArgs)+0x2 00AFEA94 54E02EF2 PresentationFramework_ni!System.Windows.Application.<.ctor>b__1_0(System.Object)+0x2e 00AFEAA4 56C4EE42 WindowsBase_ni!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate, System.Object, Int32)+0x52 00AFEAC4 56C4ED85 WindowsBase_ni!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(System.Object, System.Delegate, System.Object, Int32, System.Delegate)+0x35 00AFEB08 56C510CD WindowsBase_ni!System.Windows.Threading.DispatcherOperation.InvokeImpl()+0xdd 00AFEB40 56C4F56F WindowsBase_ni!System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(System.Object)+0x3f 00AFEB48 72F68604 mscorlib_ni!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)+0xc4 00AFEBB4 72F68537 mscorlib_ni!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)+0x17 00AFEBC8 72F684F4 mscorlib_ni!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)+0x44 00AFEBE0 56C50F83 WindowsBase_ni!MS.Internal.CulturePreservingExecutionContext.Run(MS.Internal.CulturePreservingExecutionContext, System.Threading.ContextCallback, System.Object)+0xe3 00AFEC10 56C50D80 WindowsBase_ni!System.Windows.Threading.DispatcherOperation.Invoke()+0x50 00AFEC44 56C4D346 WindowsBase_ni!System.Windows.Threading.Dispatcher.ProcessQueue()+0x176 00AFEC84 56C4C57C WindowsBase_ni!System.Windows.Threading.Dispatcher.WndProcHook(IntPtr, Int32, IntPtr, IntPtr, Boolean ByRef)+0x5c 00AFECD0 56C4E661 WindowsBase_ni!MS.Win32.HwndWrapper.WndProc(IntPtr, Int32, IntPtr, IntPtr, Boolean ByRef)+0xa1 00AFED0C 56C4E94C WindowsBase_ni!MS.Win32.HwndSubclass.DispatcherCallbackOperation(System.Object)+0x6c 00AFED1C 56C4EE42 WindowsBase_ni!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate, System.Object, Int32)+0x52 00AFED3C 56C4ED85 WindowsBase_ni!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(System.Object, System.Delegate, System.Object, Int32, System.Delegate)+0x35 00AFED80 56C4CF62 WindowsBase_ni!System.Windows.Threading.Dispatcher.LegacyInvokeImpl(System.Windows.Threading.DispatcherPriority, System.TimeSpan, System.Delegate, System.Object, Int32)+0x142 00AFEDDC 56C4E4B4 WindowsBase_ni!MS.Win32.HwndSubclass.SubclassWndProc(IntPtr, Int32, IntPtr, IntPtr)+0xf4 00000000 00000001 WindowsBase_ni!MS.Win32.UnsafeNativeMethods.DispatchMessage(System.Windows.Interop.MSG ByRef)+0x2 00AFF010 56C4B3D7 WindowsBase_ni!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame)+0xbb 00AFF058 56C4B319 WindowsBase_ni!System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame)+0x4d 00AFF064 54E02EBC PresentationFramework_ni!System.Windows.Application.RunDispatcher(System.Object)+0x60 00AFF074 54E02A7A PresentationFramework_ni!System.Windows.Application.RunInternal(System.Windows.Window)+0x7a 00AFF094 54E0286E PresentationFramework_ni!System.Windows.Application.Run(System.Windows.Window)+0x2e 00AFF0A4 02A90885 MyApp!ns10.App.Main()+0x3d StackTraceString: <none> HResult: 80131534 0:000> !PrintException /d 02c746dc Exception object: 02c746dc Exception type: System.TypeInitializationException Message: The type initializer for 'A.c0d8b631f41631263c838860696e4f989' threw an exception. InnerException: System.IO.FileLoadException, Use !PrintException ad4b1278 to see more. StackTrace (generated): SP IP Function 00000000 00000001 UNKNOWN!A.c0d8b631f41631263c838860696e4f989.c930e960a62160e11b812c53c9297a8e3()+0x2 00AFCDA0 02A9C979 UNKNOWN!<Module>..cctor()+0x9 StackTraceString: <none> HResult: 80131534 0:000> !PrintException /d 02c72cf4 Exception object: 02c72cf4 Exception type: System.IO.FileLoadException Message: Could not load file or assembly 'MyApp&' or one of its dependencies. Exception from HRESULT: 0xD0000003 InnerException: System.IO.FileLoadException, Use !PrintException ad4b1278 to see more. StackTrace (generated): SP IP Function 00000000 00000001 mscorlib_ni!System.Reflection.RuntimeAssembly._nLoad(System.Reflection.AssemblyName, System.String, System.Security.Policy.Evidence, System.Reflection.RuntimeAssembly, System.Threading.StackCrawlMark ByRef, IntPtr, Boolean, Boolean, Boolean)+0xffffffff8bf056e1 00AFC590 72F70776 mscorlib_ni!System.Reflection.RuntimeAssembly.nLoad(System.Reflection.AssemblyName, System.String, System.Security.Policy.Evidence, System.Reflection.RuntimeAssembly, System.Threading.StackCrawlMark ByRef, IntPtr, Boolean, Boolean, Boolean)+0x26 00AFC5B4 72F5B610 mscorlib_ni!System.Reflection.RuntimeAssembly.InternalLoadAssemblyName(System.Reflection.AssemblyName, System.Security.Policy.Evidence, System.Reflection.RuntimeAssembly, System.Threading.StackCrawlMark ByRef, IntPtr, Boolean, Boolean, Boolean)+0xa0 00AFC5EC 72F5EEF5 mscorlib_ni!System.Reflection.RuntimeAssembly.InternalLoad(System.String, System.Security.Policy.Evidence, System.Threading.StackCrawlMark ByRef, IntPtr, Boolean)+0x51 00AFC610 72F5EE9F mscorlib_ni!System.Reflection.RuntimeAssembly.InternalLoad(System.String, System.Security.Policy.Evidence, System.Threading.StackCrawlMark ByRef, Boolean)+0x17 00AFC61C 72F5AFF6 mscorlib_ni!System.Reflection.Assembly.Load(System.String)+0x1e 00AFC628 02A9CA35 UNKNOWN!A.c0d8b631f41631263c838860696e4f989..cctor()+0xa5 StackTraceString: <none> HResult: d0000003 0:000> !PrintException /d 02c6fcb0 Exception object: 02c6fcb0 Exception type: System.IO.FileLoadException Message: Could not load file or assembly '4608 bytes loaded from MyApp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. Exception from HRESULT: 0xD0000003 InnerException: System.Runtime.InteropServices.COMException, Use !PrintException ad4b1278 to see more. StackTrace (generated): SP IP Function 00000000 00000001 mscorlib_ni!System.Reflection.RuntimeAssembly.nLoadImage(Byte[], Byte[], System.Security.Policy.Evidence, System.Threading.StackCrawlMark ByRef, Boolean, Boolean, System.Security.SecurityContextSource)+0xffffffff8baefbd1 00AF9BE4 73705AC7 mscorlib_ni!System.Reflection.Assembly.Load(Byte[])+0x2f 00AF9BF4 02A9CB26 UNKNOWN!A.c0d8b631f41631263c838860696e4f989.c38bdfea66bcae865ec581807b0e136d1(System.Object, System.ResolveEventArgs)+0x56 00AF9C08 72F97305 mscorlib_ni!System.AppDomain.OnAssemblyResolveEvent(System.Reflection.RuntimeAssembly, System.String)+0x99 StackTraceString: <none> HResult: d0000003
A few things we can learn from this:
- We are dealing with an obfuscated .NET module due to the random GUID function names
- Due to “filename” in message Could not load file or assembly containing a xxx bytes loaded message that tells us that a .NET assembly is trying to be loaded from memory, probably extracted as a resource
- The resource name we can find from message Could not load file or assembly ‘MyApp&’
- Couldn’t find any documented reference for meaning of HRESULT d0000003
We can try and extract the module from the dump file with SaveModule command of sos extension:
0:000> lmvm MyApp Browse full module list start end module name 001b0000 006ac000 MyApp (no symbols) Loaded symbol image file: MyApp.exe Image path: C:\Program Files (x86)\Application\MyApp.exe Image name: MyApp.exe Browse all global symbols functions data Has CLR image header, track-debug-data flag not set Timestamp: Thu Dec 11 05:18:31 2018 (605347F4) CheckSum: 005E6981 ImageSize: 004FC000 File version: 1.0.0.0 Product version: 1.0.0.0 File flags: 0 (Mask 3F) File OS: 4 Unknown Win32 File type: 1.0 App File date: 00000000.00000000 Translations: 0000.04b0 Information from resource tables: CompanyName: My Company ProductName: MyApp InternalName: MyApp.exe OriginalFilename: MyApp.exe ProductVersion: 1.0.0 FileVersion: 1.0.0.0 FileDescription: My Company LegalCopyright: © My Company 2018 0:000> !SaveModule 001b0000 C:\debug\MyApp.exe 3 sections in file section 0 - VA=2000, VASize=4ea2b4, FileAddr=200, FileSize=4ea400 section 1 - VA=4ee000, VASize=ad60, FileAddr=4ea600, FileSize=ae00 section 2 - VA=4fa000, VASize=c, FileAddr=4f5400, FileSize=200
We can now analyze the .NET assembly with standard .NET decompiling tools such as:
- icsharpcode/ILSpy: .NET Decompiler with support for PDB generation, ReadyToRun, Metadata (&more) – cross-platform! (github.com)
- Ildasm.exe (IL Disassembler) | Microsoft Docs
- dotPeek: Free .NET Decompiler & Assembly Browser by JetBrains
- JustDecompile .NET Assembly Decompiler &amp;amp; Browser – Telerik
- .NET Decompiler: Decompile Any .NET Code | .NET Reflector (red-gate.com)
With these tools we can see the EXEs have what looks to be an encrypted file matching the name shown in crash dump in the .NET resources table, with the application module’s name followed by an ampersand i.e.. MyApp&.
Generally I think it’s a good idea to have several different tools as some more complex executables get better results from different tools in different scenarios.
However the code in this functions crashing looks like complete gibberish due to the obfuscation.
Using de4dot/de4dot: .NET deobfuscator and unpacker. (github.com) we are able to “deobfuscate” the module.
De4dot detects that the app has been protected with Crypto Obfuscator For .Net – Obfuscator With Code Protection, Exception Reporting, Optimization For .Net Assemblies, WPF, Silverlight, Windows Phone 7 and ASP.Net Websites (ssware.com)
Once deobfuscated the application no longer crashes.
So why is it crashing?
To further analyze this we took a Time Travel Debugging trace from within WinDbg ( Time Travel Debugging – Introduction to Time Travel Debugging objects – Windows drivers | Microsoft Docs )
Unfortunately the trace didn’t capture the application crash exception, instead only had the following exception:
Break instruction exception - code 80000003 (first/second chance not available)
In order to avoid this we had to take the TTD trace without using WinDbg. From a machine where WinDbg Preview had been installed using Windows store I copied the executable files out and into another directory. If you are unable to access the files you may need to run from an elevated command prompt, or a command prompt running as system account.
Once copied out you should be able to use without installing on Windows 8 or Server 2012 and later.
From an elevated command prompt we could then start trace with comand
\amd64\TTD\ttd.exe -launch <application name> <application arguments>
OR
\amd64\TTD\ttd.exe -attach <process id>
This approach generated a suitable trace with many .NET exceptions present in the trace.
Once loaded we loaded sos extension as before, and in addition as with most .NET traces we also loaded Steve’s Techspot – SOSEX v4.0 Now Available (stevestechspot.com) as this is especially more intuitive at setting breakpoints than with SOS extension (And many advanced features)
To automatically break on all .NET exceptions we used command and hit g to continue
sxe e0434352 g
This took us to the same load exception we had in our crash dump file. But now being a TTD we can work backwards to get a more detailed root cause.
We want to find where the very first exception got raised. From a stack we can see function clr!UnwindAndContinueRethrowHelperAfterCatch
0:000> k # ChildEBP RetAddr 00 0095b324 769840b2 ntdll!RtlRaiseException 01 0095b38c 741528f0 KERNELBASE!RaiseException+0x62 02 0095b428 741bc1a2 clr!RaiseTheExceptionInternalOnly+0x230 03 0095b45c 7451070b clr!UnwindAndContinueRethrowHelperAfterCatch+0x6f 04 0095b518 73705ac7 clr!AssemblyNative::LoadImage+0x23c 05 0095b53c 10d80a06 mscorlib_ni!System.Reflection.Assembly.Load(Byte[])$##600426B+0x2f
We set a breakpoint on this function and went backwards. I used bm with wildcards as I found it more reliable to hit the breakpoints especially when not having access to full debugging symbols.:
0:000> bm clr!UnwindAndContinueRethrowHelperAfterCatch* 1: 741bc149 @!"clr!UnwindAndContinueRethrowHelperAfterCatch" 0:000> g- Breakpoint 1 hit Time Travel Position: 2B3419:AF5 eax=745106f9 ebx=0095b50c ecx=74033450 edx=10f47e10 esi=7476f1d0 edi=0095b50c eip=741bc149 esp=0095b460 ebp=0095b518 iopl=0 nv up ei ng nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000286 clr!UnwindAndContinueRethrowHelperAfterCatch: 741bc149 6a0c push 0Ch
I used t- command to step backwards one instruction at a time, looking for when the exception HRESULT we originally got d0000003 was being set or any other clues. Once we hit a function in VCRUNTIME140_CLR0400 we could see other exception related functions in the stack.
VCRUNTIME140_CLR0400!_SEH_epilog4+0x12: 7403e8e8 51 push ecx 0:000> K # ChildEBP RetAddr 00 00959b60 74033304 VCRUNTIME140_CLR0400!_SEH_epilog4+0x12 01 00959b88 74032ebc VCRUNTIME140_CLR0400!CatchIt+0x65 02 00959c04 74032c33 VCRUNTIME140_CLR0400!FindHandler+0x27e 03 00959c30 7403e6a6 VCRUNTIME140_CLR0400!__InternalCxxFrameHandler+0xf2 04 00959c6c 77a88e72 VCRUNTIME140_CLR0400!__CxxFrameHandler+0x26 05 00959c90 77a88e44 ntdll!ExecuteHandler2+0x26 06 00959d58 77a742d6 ntdll!ExecuteHandler+0x24 07 0095a278 00000000 ntdll!KiUserExceptionDispatcher+0x26
We went backwards to this function using the same approach as before:
:000> bm ntdll!KiUserExceptionDispatcher* 2: 77a742b0 @!"ntdll!KiUserExceptionDispatcher" 0:000> g- Breakpoint 2 hit Time Travel Position: 2B33F9:0 eax=00000000 ebx=0095a2dc ecx=00000000 edx=00000000 esi=740314a4 edi=747906d0 eip=77a742b0 esp=00959d68 ebp=0095a278 iopl=0 nv up ei pl nz ac po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000212 ntdll!KiUserExceptionDispatcher: 77a742b0 833d4ce9b17700 cmp dword ptr [ntdll!LdrDelegatedKiUserExceptionDispatcher (77b1e94c)],0 ds:002b:77b1e94c=00000000
We followed same approach using t- and then a later stack showed a suitable function in stack so we went to it.
0:000> bm VCRUNTIME140_CLR0400!_CxxThrowException* 4: 74034600 @!"VCRUNTIME140_CLR0400!_CxxThrowException" 0:000> g- (1c20.1c90): C++ EH exception - code e06d7363 (first/second chance not available) Breakpoint 4 hit Time Travel Position: 2B33F5:1BA eax=0095a2dc ebx=00004000 ecx=00004000 edx=00000000 esi=d0000003 edi=10f47e10 eip=74034600 esp=0095a2c0 ebp=0095a710 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 VCRUNTIME140_CLR0400!_CxxThrowException: 74034600 55 push ebp
Using same approach we find another target function:
0:000> bm clr!EEFileLoadException::Throw* 5: 74365044 @!"clr!EEFileLoadException::Throw" 6: 74364c7a @!"clr!EEFileLoadException::Throw" 7: 743648d1 @!"clr!EEFileLoadException::Throw" 8: 743646cf @!"clr!EEFileLoadException::Throw" 9: 74364e24 @!"clr!EEFileLoadException::Throw" 10: 74364ad0 @!"clr!EEFileLoadException::Throw" 0:000> g- Breakpoint 5 hit Time Travel Position: 2B32F8:769 eax=d0000003 ebx=10f4a320 ecx=10f27b00 edx=00000000 esi=7410fc70 edi=10f4a320 eip=74365044 esp=0095a714 ebp=0095b314 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 clr!EEFileLoadException::Throw: 74365044 6828040000 push 428h
When we go backwards from this function we finally see our HRESULT code for the first time in EAX register, within clr!PEAssembly::OpenMemory function.
0:000> t- Time Travel Position: 2B32F8:766 eax=d0000003 ebx=10f4a320 ecx=10f4a320 edx=00000000 esi=7410fc70 edi=10f4a320 eip=743adda9 esp=0095a71c ebp=0095b314 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 clr!PEAssembly::OpenMemory+0xd5: 743adda9 ff7508 push dword ptr [ebp+8] ss:002b:0095b31c=00261600 0:000> t- Time Travel Position: 2B32F8:765 eax=d0000003 ebx=10f4a320 ecx=10f4a320 edx=00000000 esi=7410fc70 edi=10f4a320 eip=743adda8 esp=0095a720 ebp=0095b314 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 clr!PEAssembly::OpenMemory+0xd4: 743adda8 50 push eax
We go back until we find where EAX was set to this HRESULT:
0:000> t- Time Travel Position: 2B32F8:763 eax=7406abf4 ebx=10f4a320 ecx=10f4a320 edx=00000000 esi=7410fc70 edi=10f4a320 eip=7410fc70 esp=0095a71c ebp=0095b314 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 clr!SecurityContextFrame::GetAssembly: 7410fc70 8b4108 mov eax,dword ptr [ecx+8] ds:002b:10f4a328=d0000003
This is telling us that memory location 10f4a328 holds our HRESULT value. To find when this is set we set a break point on memory write access to this location and go backwards. I also clear my existing breakpoints first incase these exception functions I had breakpoints on got hit multiple times while going back.
0:000> bc * 0:000> ba w 1 10f4a328 0:000> g- (1c20.1c90): C++ EH exception - code e06d7363 (first/second chance not available) (1c20.1c90): C++ EH exception - code e06d7363 (first/second chance not available) Breakpoint 0 hit Time Travel Position: 2B32BC:C8 eax=00000000 ebx=00261600 ecx=0000000c edx=00000000 esi=d0000003 edi=10f4a320 eip=7452b793 esp=0095af54 ebp=0095af74 iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202 clr!ThrowHR+0x76: 7452b793 8885fcffffff mov byte ptr [ebp-4],al ss:002b:0095af70=01 0:000> t- Time Travel Position: 2B32BC:C7 eax=00000000 ebx=00261600 ecx=0000000c edx=00000000 esi=d0000003 edi=10f4a320 eip=7452b790 esp=0095af54 ebp=0095af74 iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202 clr!ThrowHR+0x76: 7452b790 897708 mov dword ptr [edi+8],esi ds:002b:10f4a328=00000000
We now also have a more interesting stack trace:
0:000> k # ChildEBP RetAddr 00 0095af74 744cf18f clr!ThrowHR+0x51 01 0095af84 744cf5f8 clr!PEImageLayout::CheckCodeIntegrity+0x5f 02 0095b1ec 743c4127 clr!RawImageLayout::RawImageLayout+0x17d 03 0095b230 743adf4c clr!PEImage::LoadFlat+0x70 04 0095b2a4 743adccb clr!PEAssembly::DoOpenMemory+0x43 05 0095b314 7450fc23 clr!PEAssembly::OpenMemory+0x4b 06 0095b438 74510608 clr!AssemblyNative::LoadFromBuffer+0x479 07 0095b518 73705ac7 clr!AssemblyNative::LoadImage+0x166
We now set a breakpoint on clr!ThrowHR and go backwards…
0:000> bm clr!ThrowHR* 2: 7452b710 @!"clr!ThrowHR" 4: 7452b7ff @!"clr!ThrowHR" 5: 7452b8ba @!"clr!ThrowHR" 0:000> g- Breakpoint 2 hit Time Travel Position: 2B32A4:B eax=d0000003 ebx=00261600 ecx=d0000003 edx=00000000 esi=66873640 edi=10efbe90 eip=7452b710 esp=0095af78 ebp=0095af84 iopl=0 nv up ei ng nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000286 clr!ThrowHR: 7452b710 6a04 push 4
Finally when we hit t- we are now in the function where exception was thrown:
0:000> t- Time Travel Position: 2B32A4:A eax=d0000003 ebx=00261600 ecx=d0000003 edx=00000000 esi=66873640 edi=10efbe90 eip=744cf18a esp=0095af7c ebp=0095af84 iopl=0 nv up ei ng nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000286 clr!PEImageLayout::CheckCodeIntegrity+0x5a: 744cf18a e881c50500 call clr!ThrowHR (7452b710)
We can see that the exception was thrown if EAX <> 0:
Time Travel Position: 2B32A4:A eax=d0000003 ebx=00261600 ecx=d0000003 edx=00000000 esi=66873640 edi=10efbe90 eip=744cf18a esp=0095af7c ebp=0095af84 iopl=0 nv up ei ng nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000286 clr!PEImageLayout::CheckCodeIntegrity+0x5a: 744cf18a e881c50500 call clr!ThrowHR (7452b710) 0:000> t- Time Travel Position: 2B32A4:9 eax=d0000003 ebx=00261600 ecx=00000000 edx=00000000 esi=66873640 edi=10efbe90 eip=744cf188 esp=0095af7c ebp=0095af84 iopl=0 nv up ei ng nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000286 clr!PEImageLayout::CheckCodeIntegrity+0x58: 744cf188 8bc8 mov ecx,eax 0:000> t- Time Travel Position: 2B32A4:8 eax=d0000003 ebx=00261600 ecx=00000000 edx=00000000 esi=66873640 edi=10efbe90 eip=744cf17b esp=0095af7c ebp=0095af84 iopl=0 nv up ei ng nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000286 clr!PEImageLayout::CheckCodeIntegrity+0x4b: 744cf17b 780b js clr!PEImageLayout::CheckCodeIntegrity+0x58 (744cf188) [br=1] 0:000> t- Time Travel Position: 2B32A4:7 eax=d0000003 ebx=00261600 ecx=00000000 edx=00000000 esi=66873640 edi=10efbe90 eip=744cf179 esp=0095af7c ebp=0095af84 iopl=0 nv up ei ng nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000286 clr!PEImageLayout::CheckCodeIntegrity+0x49: 744cf179 85c0 test eax,eax
One more step backwards and we find what triggered our failure:
0:000> t- Time Travel Position: 2B32A4:6 eax=d0000003 ebx=00261600 ecx=00000000 edx=00000000 esi=66873640 edi=10efbe90 eip=66873696 esp=0095af6c ebp=0095af84 iopl=0 nv up ei ng nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000286 wldp!WldpQueryDynamicCodeTrust+0x56: 66873696 c20c00 ret 0Ch 0:000> lmvm wldp Browse full module list start end module name 66860000 66882000 wldp (pdb symbols) C:\ProgramData\Dbg\sym\WLDP.pdb\04DF37356E1254CDAE5C6B767C1E8B7E1\WLDP.pdb Loaded symbol image file: wldp.dll Mapped memory image file: C:\ProgramData\Dbg\sym\wldp.dll\DF4FDF2C22000\wldp.dll Image path: C:\windows\SYSTEM32\wldp.dll Image name: wldp.dll Browse all global symbols functions data Image was built with /Brepro flag. Timestamp: DF4FDF2C (This is a reproducible build file hash, not a timestamp) CheckSum: 0002D5ED ImageSize: 00022000 File version: 10.0.18362.295 Product version: 10.0.18362.295 File flags: 0 (Mask 3F) File OS: 40004 NT Win32 File type: 2.0 Dll File date: 00000000.00000000 Translations: 0409.04b0 Information from resource tables: CompanyName: Microsoft Corporation ProductName: Microsoft® Windows® Operating System InternalName: wldp.dll OriginalFilename: wldp.dll ProductVersion: 10.0.18362.295 FileVersion: 10.0.18362.295 (WinBuild.160101.0800) FileDescription: Windows Lockdown Policy LegalCopyright: © Microsoft Corporation. All rights reserved.
Essentially loading the encrypted DLL from within a resource of an EXE in memory, instead of off the disk, triggered a Windows Lockdown Policy that was configured to strictly enforce code integrity.
Looking up function documentation WldpQueryDynamicCodeTrust function (Wldp.h) – Win32 apps | Microsoft Docs we can see it states the following:
Retrieves a value that determines if the specified in-memory or on-disk .NET CRL dynamic code is trusted by Device Guard policy.
Exceptions to the lockdown policy would be required for this application to continue to work as-is or the software developer would need to either remove the obfuscation or work with the vendor providing the obfuscation software to ensure it was compatible with this Windows configuration.
Now that we know this is where the fault lies further details can be extracted from the Windows code integrity log, for example using PowerShell:
Get-WinEvent -LogName "Microsoft-Windows-CodeIntegrity/Operational" | Out-GridView
Or In Windows Event Viewer under Windows Logs -> Application and Service Logs -> Microsoft -> Windows -> CodeIntegrity -> Operational
More verbose logging can be obtained from Event Viewer by selecting View -> Show Analytic and Debug Logs and right clicking the CodeIntegrity -> Verbose log and selecting enable. It is recommended to disable once finished collecting trace as it may cause a performance hit on your system.