Strings Utility In PowerShell

Sometimes I’m working in environments where I can’t copy in any tools for troubleshooting and sometimes simply analyzing the strings in an .EXE gives many useful clues to how it works. Here is a simple version of strings utility that only relies on PowerShell.

The script: https://github.com/chentiangemalc/PowerShellScripts/blob/master/Extract-Strings.ps1

<#
.SYNOPSIS
Extracts strings from a file.

.DESCRIPTION
Extracts western printable character strings from a binary file, including ASCII strings and UTF16 strings of a minimum length.

.PARAMETER Path
Specifies the path to the file from which strings will be extracted.

.PARAMETER MinStringLength
Specifies the minimum length of strings to be extracted. The default value is 5.

.PARAMETER HideAsciiStrings
Specifies whether to hide ASCII strings. By default, ASCII strings are shown.

.PARAMETER HideUnicodeStrings
Specifies whether to hide Unicode strings. By default, Unicode strings are shown.

.EXAMPLE
Extract-Strings -Path "C:\Files\sample.exe"
Extracts strings from the file "c:\Files\Sample.exe".

Extract-Strings -Path "C:\Files\sample.exe" -HideUnicodeStrings

.INPUTS
None.

.OUTPUTS
Extracted strings are written to the pipeline.

.NOTES
Version: 1.0
Author: chentiangemalc

#>

[CmdletBinding()]
param (
    [Parameter(Mandatory=$true)]
    [ValidateScript({Test-Path $_ -PathType 'Leaf'})]
    [string]$Path,
    [int]$MinStringLength = 5,
    [switch]$HideAsciiStrings,
    [switch]$HideUnicodeStrings
)

$bytes = [System.IO.File]::ReadAllBytes($Path)
$currentASCIIstring = [System.Text.StringBuilder]::new()
$currentUNICODEstring = [System.Text.StringBuilder]::new()
    
for ($i = 0; $i -lt $bytes.Length; $i++) {
    if ($i + 1 -lt $bytes.Length) {
        if (($bytes[$i] -ge 0x20 -and $bytes[$i] -le 0x7E -or $bytes[$i] -eq 0x0D -or $bytes[$i] -eq 0x0A) -and $bytes[$i + 1] -eq 0x00) {
            [void]$currentUNICODEstring.Append([char]$bytes[$i])
        }
        elseif ($bytes[$i] -eq 0x00 -and $bytes[$i + 1] -eq 0x00) {
            if ($currentUNICODEstring.Length -ge $minStringLength) {
                if (!$HideUnicodeStrings)
                {
                    $currentUNICODEstring.ToString()
                }
            }

            [void]$currentUNICODEstring.Clear()
        }
    }

    if ($bytes[$i] -ge 0x20 -and $bytes[$i] -le 0x7E -or $bytes[$i] -eq 0x0D -or $bytes[$i] -eq 0x0A) {
       [void]$currentASCIIstring.Append([char]$bytes[$i])
    }
    elseif ($bytes[$i] -eq 0) {
        if ($currentASCIIstring.Length -ge $minStringLength) {
            if (!$HideAsciiStrings)
            {
                $currentASCIIstring.ToString()
            }
        }
        [void]$currentASCIIstring.Clear()
    }
    else {
        [void]$currentASCIIstring.Clear()
        [void]$currentUNICODEstring.Clear()
    }
}

Posted in Uncategorized | Leave a comment

Resolve an API Set Function Name to On Disk Module and Function or Offset

Here is a script I wrote experimenting with resolving API Set function calls to on disk module/function/offset.

<#
.SYNOPSIS
    This script resolves an API set and API name to a specific module and function name.

.DESCRIPTION
    This script uses the NativeMethods class to call Windows API functions for resolving
    a given API set and API name to the actual module and function name they correspond to.

.PARAMETER ApiSet
    The API set string to be resolved. For example: "api-ms-win-devices-query-l1-1-0".

.PARAMETER Api
    The API name string to be resolved. For example: "DevGetObjects".

.PARAMETER ShowOffset
    Will show the offset of function instead of function name.
    Use if symbols don't resolve.

.EXAMPLE
    .\Resolve-ApiSet.ps1 -ApiSet "api-ms-win-devices-query-l1-1-0" -Api "DevGetObjects"
    This command will resolve the given ApiSet and ApiName to their corresponding module and function name.

   .\Resolve-ApiSet.ps1 -ApiSet "api-ms-win-devices-query-l1-1-0" -Api "DevGetObjects" -ShowOffSet
    This command will resolve the given ApiSet and ApiName to their corresponding module and offset (based on the image base address on disk)


.NOTES
    Run as 32-bit PowerShell for 32-bit modules
    64-bit PowerShell for 64-bit modules.
    If names are not resolving you may need to configure
    Microsoft Symbol Server or place name of your desired
    Symbol server in 2nd param to SymInitialize
#>
param(
    [Parameter(Mandatory=$true)]
    [string]$ApiSet,
    [Parameter(Mandatory=$true)]
    [string]$Api,
    [switch]$ShowOffset
)

Add-Type -TypeDefinition @'
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;

public class NativeMethods
{
    [DllImport("dbghelp.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern bool SymInitialize(IntPtr hProcess, string UserSearchPath, bool fInvadeProcess);

    [DllImport("dbghelp.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern bool SymFromAddr(IntPtr hProcess, ulong Address, out ulong Displacement, IntPtr Symbol);

    [DllImport("dbghelp.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern bool SymCleanup(IntPtr hProcess);

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern IntPtr GetCurrentProcess();

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern IntPtr LoadLibrary(string lpFileName);

    [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
    private static extern IntPtr GetProcAddress(IntPtr hModule, string procName);

    private const int MAX_SYM_NAME = 2000;

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    private struct SYMBOL_INFO
    {
        public uint SizeOfStruct;
        public uint TypeIndex;
        public ulong Reserved1;
        public ulong Reserved2;
        public uint Index;
        public uint Size;
        public ulong ModBase;
        public uint Flags;
        public ulong Value;
        public ulong Address;
        public uint Register;
        public uint Scope;
        public uint Tag;
        public uint NameLen;
        public uint MaxNameLen;
        public char Name;
    }

    public static void SymInitialize()
    {
        if (!SymInitialize(GetCurrentProcess(), "", true))
        {
            // ignore errors
            // can only be called once per process instance
            //Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
        }
    }

    public static string GetSymbolName(IntPtr address)
    {
        ulong displacement = 0;
        byte[] buffer = new byte[Marshal.SizeOf(typeof(SYMBOL_INFO)) + MAX_SYM_NAME * sizeof(char)];
        GCHandle bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
        IntPtr symbolPtr = bufferHandle.AddrOfPinnedObject();
        SYMBOL_INFO symbol = (SYMBOL_INFO)Marshal.PtrToStructure(symbolPtr, typeof(SYMBOL_INFO));
        symbol.SizeOfStruct = (uint)Marshal.SizeOf(typeof(SYMBOL_INFO));
        symbol.MaxNameLen = MAX_SYM_NAME;
        
        Marshal.StructureToPtr(symbol, symbolPtr, true);
        if (SymFromAddr(GetCurrentProcess(), (ulong)address, out displacement, symbolPtr))
        {
            symbol = (SYMBOL_INFO)Marshal.PtrToStructure(symbolPtr, typeof(SYMBOL_INFO));
            string functionName = Marshal.PtrToStringUni(symbolPtr + 84);
            return functionName;
        }
        else
        {
            Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
        }

        //SymCleanup(GetCurrentProcess());
        bufferHandle.Free();

        return String.Empty;
    }

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern uint GetModuleFileName(
        IntPtr hModule,
        [MarshalAs(UnmanagedType.LPTStr)] StringBuilder lpFilename,
        uint nSize
    );

    public static string GetModuleFileName(IntPtr hModule)
    {
        uint bufferSize = 1024;
        StringBuilder fileNameBuilder = new StringBuilder((int)bufferSize);
        uint result = GetModuleFileName(hModule, fileNameBuilder, bufferSize);

        if (result == 0)
        {
            throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
        }
        else if (result == bufferSize)
        {
            throw new InvalidOperationException("The file name is too long. Increase the buffer size and try again.");
        }

        return fileNameBuilder.ToString();
    }


    [DllImport("ntdll.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int LdrLoadDll(
        string DllPath,
        uint[] DllCharacteristics,
        ref UNICODE_STRING DllName,
        out IntPtr DllHandle
    );

    [DllImport("ntdll.dll", SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
    public static extern int LdrUnloadDll(IntPtr ModuleHandle);

    [DllImport("ntdll.dll", CharSet = CharSet.Ansi, SetLastError = true)]
    public static extern int LdrGetProcedureAddress(
        IntPtr DllHandle,
        ref ANSI_STRING ProcedureName,
        uint ProcedureNumber,
        out IntPtr ProcedureAddress
    );

    [StructLayout(LayoutKind.Sequential)]
    public struct UNICODE_STRING
    {
        public ushort Length;
        public ushort MaximumLength;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string Buffer;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct ANSI_STRING
    {
        public ushort Length;
        public ushort MaximumLength;
        [MarshalAs(UnmanagedType.LPStr)]
        public string Buffer;
    }

    public static UNICODE_STRING CreateUnicodeString(string str)
    {
        return new UNICODE_STRING
        {
            Length = (ushort)(str.Length * 2),
            MaximumLength = (ushort)((str.Length * 2) + 2),
            Buffer = str
        };
    }

    public static ANSI_STRING CreateAnsiString(string str)
    {
        return new ANSI_STRING
        {
            Length = (ushort)str.Length,
            MaximumLength = (ushort)(str.Length + 1),
            Buffer = str
        };
    }
}
'@

[Uint16]$IMAGE_DOS_SIGNATURE = 0x5A4D
[Uint16]$IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b
[UInt16]$IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b
[UInt16]$IMAGE_ROM_OPTIONAL_HDR_MAGIC = 0x107
[Uint16]$IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16
[Uint16]$IMAGE_SIZEOF_SHORT_NAME = 8
[Uint16]$IMAGE_SIZEOF_SECTION_HEADER = 40
 
enum IMAGE_FILE_MACHINE {
    IMAGE_FILE_RELOCS_STRIPPED           = 0x0001  # Relocation info stripped from file.
    IMAGE_FILE_EXECUTABLE_IMAGE          = 0x0002  # File is executable  (i.e. no unresolved external references).
    IMAGE_FILE_LINE_NUMS_STRIPPED        = 0x0004  # Line nunbers stripped from file.
    IMAGE_FILE_LOCAL_SYMS_STRIPPED       = 0x0008  # Local symbols stripped from file.
    IMAGE_FILE_AGGRESIVE_WS_TRIM         = 0x0010  # Aggressively trim working set
    IMAGE_FILE_LARGE_ADDRESS_AWARE       = 0x0020  # App can handle >2gb addresses
    IMAGE_FILE_BYTES_REVERSED_LO         = 0x0080  # Bytes of machine word are reversed.
    IMAGE_FILE_32BIT_MACHINE             = 0x0100  # 32 bit word machine.
    IMAGE_FILE_DEBUG_STRIPPED            = 0x0200  # Debugging info stripped from file in .DBG file
    IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP   = 0x0400  # If Image is on removable media, copy and run from the swap file.
    IMAGE_FILE_NET_RUN_FROM_SWAP         = 0x0800  # If Image is on Net, copy and run from the swap file.
    IMAGE_FILE_SYSTEM                    = 0x1000  # System File.
    IMAGE_FILE_DLL                       = 0x2000  # File is a DLL.
    IMAGE_FILE_UP_SYSTEM_ONLY            = 0x4000  # File should only be run on a UP machine
    IMAGE_FILE_BYTES_REVERSED_HI         = 0x8000  # Bytes of machine word are reversed.
    IMAGE_FILE_MACHINE_UNKNOWN           = 0x0
    IMAGE_FILE_MACHINE_TARGET_HOST       = 0x0001  # Useful for indicating we want to interact with the host and not a WoW guest.
    IMAGE_FILE_MACHINE_I386              = 0x014c  # Intel 386.
    IMAGE_FILE_MACHINE_R3000             = 0x0162  # MIPS little-endian, = 0x160 big-endian
    IMAGE_FILE_MACHINE_R4000             = 0x0166  # MIPS little-endian
    IMAGE_FILE_MACHINE_R10000            = 0x0168  # MIPS little-endian
    IMAGE_FILE_MACHINE_WCEMIPSV2         = 0x0169  # MIPS little-endian WCE v2
    IMAGE_FILE_MACHINE_ALPHA             = 0x0184  # Alpha_AXP
    IMAGE_FILE_MACHINE_SH3               = 0x01a2  # SH3 little-endian
    IMAGE_FILE_MACHINE_SH3DSP            = 0x01a3
    IMAGE_FILE_MACHINE_SH3E              = 0x01a4  # SH3E little-endian
    IMAGE_FILE_MACHINE_SH4               = 0x01a6  # SH4 little-endian
    IMAGE_FILE_MACHINE_SH5               = 0x01a8  # SH5
    IMAGE_FILE_MACHINE_ARM               = 0x01c0  # ARM Little-Endian
    IMAGE_FILE_MACHINE_THUMB             = 0x01c2  # ARM Thumb/Thumb-2 Little-Endian
    IMAGE_FILE_MACHINE_ARMNT             = 0x01c4  # ARM Thumb-2 Little-Endian
    IMAGE_FILE_MACHINE_AM33              = 0x01d3
    IMAGE_FILE_MACHINE_POWERPC           = 0x01F0  # IBM PowerPC Little-Endian
    IMAGE_FILE_MACHINE_POWERPCFP         = 0x01f1
    IMAGE_FILE_MACHINE_IA64              = 0x0200  # Intel 64
    IMAGE_FILE_MACHINE_MIPS16            = 0x0266  # MIPS
    IMAGE_FILE_MACHINE_ALPHA64           = 0x0284  # ALPHA64
    IMAGE_FILE_MACHINE_MIPSFPU           = 0x0366  # MIPS
    IMAGE_FILE_MACHINE_MIPSFPU16         = 0x0466  # MIPS
    IMAGE_FILE_MACHINE_AXP64             = 0x0284  # IMAGE_FILE_MACHINE_ALPHA64
    IMAGE_FILE_MACHINE_TRICORE           = 0x0520  # Infineon
    IMAGE_FILE_MACHINE_CEF               = 0x0CEF
    IMAGE_FILE_MACHINE_EBC               = 0x0EBC  # EFI Byte Code
    IMAGE_FILE_MACHINE_AMD64             = 0x8664  # AMD64 (K8)
    IMAGE_FILE_MACHINE_M32R              = 0x9041  # M32R little-endian
    IMAGE_FILE_MACHINE_ARM64             = 0xAA64  # ARM64 Little-Endian
    IMAGE_FILE_MACHINE_CEE               = 0xC0EE
}
 
[Flags()] enum IMAGE_SUBSYSTEM
{
    IMAGE_SUBSYSTEM_UNKNOWN              = 0   # Unknown subsystem.
    IMAGE_SUBSYSTEM_NATIVE               = 1   # Image doesnt require a subsystem.
    IMAGE_SUBSYSTEM_WINDOWS_GUI          = 2   # Image runs in the Windows GUI subsystem.
    IMAGE_SUBSYSTEM_WINDOWS_CUI          = 3   # Image runs in the Windows character subsystem.
    IMAGE_SUBSYSTEM_OS2_CUI              = 5   # image runs in the OS/2 character subsystem.
    IMAGE_SUBSYSTEM_POSIX_CUI            = 7   # image runs in the Posix character subsystem.
    IMAGE_SUBSYSTEM_NATIVE_WINDOWS       = 8   # image is a native Win9x driver.
    IMAGE_SUBSYSTEM_WINDOWS_CE_GUI       = 9   # Image runs in the Windows CE subsystem.
    IMAGE_SUBSYSTEM_EFI_APPLICATION      = 10  #
    IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER = 11   #
    IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER   = 12  #
    IMAGE_SUBSYSTEM_EFI_ROM              = 13
    IMAGE_SUBSYSTEM_XBOX                 = 14
    IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION = 16
    IMAGE_SUBSYSTEM_XBOX_CODE_CATALOG    = 17
}
 
[Flags()] enum IMAGE_DLLCHARACTERISTICS {
    IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA = 0x0020  # Image can handle a high entropy 64-bit virtual address space.
    IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE = 0x0040     # DLL can move.
    IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY   = 0x0080     # Code Integrity Image
    IMAGE_DLLCHARACTERISTICS_NX_COMPAT    = 0x0100     # Image is NX compatible
    IMAGE_DLLCHARACTERISTICS_NO_ISOLATION = 0x0200     # Image understands isolation and doesn't want it
    IMAGE_DLLCHARACTERISTICS_NO_SEH       = 0x0400     # Image does not use SEH.  No SE handler may reside in this image
    IMAGE_DLLCHARACTERISTICS_NO_BIND      = 0x0800     # Do not bind this image.
    IMAGE_DLLCHARACTERISTICS_APPCONTAINER = 0x1000     # Image should execute in an AppContainer
    IMAGE_DLLCHARACTERISTICS_WDM_DRIVER   = 0x2000     # Driver uses WDM model
    IMAGE_DLLCHARACTERISTICS_GUARD_CF     = 0x4000     # Image supports Control Flow Guard.
    IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE     = 0x8000
}
 
enum IMAGE_DIRECTORY_ENTRY
{
    IMAGE_DIRECTORY_ENTRY_EXPORT          = 0   # Export Directory
    IMAGE_DIRECTORY_ENTRY_IMPORT          = 1   # Import Directory
    IMAGE_DIRECTORY_ENTRY_RESOURCE        = 2   # Resource Directory
    IMAGE_DIRECTORY_ENTRY_EXCEPTION       = 3   # Exception Directory
    IMAGE_DIRECTORY_ENTRY_SECURITY        = 4   # Security Directory
    IMAGE_DIRECTORY_ENTRY_BASERELOC       = 5   # Base Relocation Table
    IMAGE_DIRECTORY_ENTRY_DEBUG           = 6   # Debug Directory
    #     IMAGE_DIRECTORY_ENTRY_COPYRIGHT = 7   # (X86 usage)
    IMAGE_DIRECTORY_ENTRY_ARCHITECTURE    = 7   # Architecture Specific Data
    IMAGE_DIRECTORY_ENTRY_GLOBALPTR       = 8   # RVA of GP
    IMAGE_DIRECTORY_ENTRY_TLS             = 9   # TLS Directory
    IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    = 10   # Load Configuration Directory
    IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   = 11   # Bound Import Directory in headers
    IMAGE_DIRECTORY_ENTRY_IAT            = 12   # Import Address Table
    IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   = 13   # Delay Load Import Descriptors
    IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14   # COM Runtime descriptor
}
 
[Flags()] enum IMAGE_SCN {
#       IMAGE_SCN_TYPE_REG                   0x00000000  #  Reserved.
#       IMAGE_SCN_TYPE_DSECT                 0x00000001  #  Reserved.
#       IMAGE_SCN_TYPE_NOLOAD                0x00000002  #  Reserved.
#       IMAGE_SCN_TYPE_GROUP                 0x00000004  #  Reserved.
 IMAGE_SCN_TYPE_NO_PAD               = 0x00000008  #  Reserved.
#       IMAGE_SCN_TYPE_COPY                  0x00000010  #  Reserved.
 
 IMAGE_SCN_CNT_CODE                  = 0x00000020  #  Section contains code.
 IMAGE_SCN_CNT_INITIALIZED_DATA      = 0x00000040  #  Section contains initialized data.
 IMAGE_SCN_CNT_UNINITIALIZED_DATA    = 0x00000080  #  Section contains uninitialized data.
 
 IMAGE_SCN_LNK_OTHER                =  0x00000100  #  Reserved.
 IMAGE_SCN_LNK_INFO                 = 0x00000200  #  Section contains comments or some other type of information.
#       IMAGE_SCN_TYPE_OVER                  0x00000400  #  Reserved.
 IMAGE_SCN_LNK_REMOVE                = 0x00000800  #  Section contents will not become part of image.
 IMAGE_SCN_LNK_COMDAT                = 0x00001000  #  Section contents comdat.
#                                            0x00002000  #  Reserved.
#       IMAGE_SCN_MEM_PROTECTED - Obsolete =  0x00004000
 IMAGE_SCN_NO_DEFER_SPEC_EXC         = 0x00004000  #  Reset speculative exceptions handling bits in the TLB entries for this section.
 IMAGE_SCN_GPREL                     = 0x00008000  #  Section content can be accessed relative to GP
 IMAGE_SCN_MEM_FARDATA               = 0x00008000
#       IMAGE_SCN_MEM_SYSHEAP  - Obsolete    0x00010000
 IMAGE_SCN_MEM_PURGEABLE             = 0x00020000
 IMAGE_SCN_MEM_16BIT                 = 0x00020000
 IMAGE_SCN_MEM_LOCKED                = 0x00040000
 IMAGE_SCN_MEM_PRELOAD              =  0x00080000
 
 IMAGE_SCN_ALIGN_1BYTES             =  0x00100000  # 
 IMAGE_SCN_ALIGN_2BYTES             =  0x00200000  # 
 IMAGE_SCN_ALIGN_4BYTES             =  0x00300000  # 
 IMAGE_SCN_ALIGN_8BYTES             =  0x00400000  # 
 IMAGE_SCN_ALIGN_16BYTES            =  0x00500000  #  Default alignment if no others are specified.
 IMAGE_SCN_ALIGN_32BYTES            =  0x00600000  # 
 IMAGE_SCN_ALIGN_64BYTES            =  0x00700000  # 
 IMAGE_SCN_ALIGN_128BYTES           =  0x00800000  # 
 IMAGE_SCN_ALIGN_256BYTES           =  0x00900000  # 
 IMAGE_SCN_ALIGN_512BYTES           =  0x00A00000  # 
 IMAGE_SCN_ALIGN_1024BYTES          =  0x00B00000  # 
 IMAGE_SCN_ALIGN_2048BYTES          =  0x00C00000  # 
 IMAGE_SCN_ALIGN_4096BYTES          =  0x00D00000  # 
 IMAGE_SCN_ALIGN_8192BYTES          =  0x00E00000  # 
#  Unused                                    0x00F00000
 IMAGE_SCN_ALIGN_MASK               =  0x00F00000
 
 IMAGE_SCN_LNK_NRELOC_OVFL          =  0x01000000  #  Section contains extended relocations.
 IMAGE_SCN_MEM_DISCARDABLE          =  0x02000000  #  Section can be discarded.
 IMAGE_SCN_MEM_NOT_CACHED           =  0x04000000  #  Section is not cachable.
 IMAGE_SCN_MEM_NOT_PAGED            =  0x08000000  #  Section is not pageable.
 IMAGE_SCN_MEM_SHARED               =  0x10000000  #  Section is shareable.
 IMAGE_SCN_MEM_EXECUTE              =  0x20000000  #  Section is executable.
 IMAGE_SCN_MEM_READ                 =  0x40000000  #  Section is readable.
 IMAGE_SCN_MEM_WRITE                =  0x80000000  #  Section is writeable.
 
# 
#  TLS Characteristic Flags
# 
 IMAGE_SCN_SCALE_INDEX              =  0x00000001  #  Tls index is scaled
 
}
 
Function Convert-EnumToString([Enum]$enum)
{
    $type = [Type]$enum
}
$IMAGE_DOS_HEADER = New-Object System.Collections.Specialized.OrderedDictionary
 
$IMAGE_DOS_HEADER.Add("e_magic",[Uint16]0)                     # magic number
$IMAGE_DOS_HEADER.Add("e_cblp",[UInt16]0)                      # Bytes on last page of file
$IMAGE_DOS_HEADER.Add("e_cp",[UInt16]0)                        # Pages in file
$IMAGE_DOS_HEADER.Add("e_crlc",[UInt16]0)                      # Relocations
$IMAGE_DOS_HEADER.Add("e_cparhdr",[UInt16]0)                   # Size of header in paragraphs
$IMAGE_DOS_HEADER.Add("e_minalloc",[UInt16]0)                  # Minimum extra paragraphs needed
$IMAGE_DOS_HEADER.Add("e_maxalloc",[UInt16]0)                  # Maximum extra paragraphs needed
$IMAGE_DOS_HEADER.Add("e_ss",[UInt16]0)                        # Initial (relative) SS value
$IMAGE_DOS_HEADER.Add("e_sp",[UInt16]0)                        # Initial SP value
$IMAGE_DOS_HEADER.Add("e_csum",[UInt16]0)                      # Checksum
$IMAGE_DOS_HEADER.Add("e_ip",[UInt16]0)                        # Initial IP value
$IMAGE_DOS_HEADER.Add("e_cs",[UInt16]0)                        # Initial (relative) CS value
$IMAGE_DOS_HEADER.Add("e_lfarlc",[UInt16]0)                    # File address of relocation table
$IMAGE_DOS_HEADER.Add("e_ovno",[UInt16]0)                      # Overlay number
$IMAGE_DOS_HEADER.Add("e_res",(New-Object UInt16[] 4))         # Reserved words
$IMAGE_DOS_HEADER.Add("e_oemid",[UInt16]0)                     # OEM identifier (for e_oeminfo)
$IMAGE_DOS_HEADER.Add("e_oeminfo",[UInt16]0)                   # OEM information,[UInt16]0) e_oemid specific
$IMAGE_DOS_HEADER.Add("e_res2[10]",(New-Object UInt16[] 10))   # Reserved words
$IMAGE_DOS_HEADER.Add("e_lfanew",[Uint32]0)                    # File address of new exe header
 
$IMAGE_FILE_HEADER = New-Object System.Collections.Specialized.OrderedDictionary
$IMAGE_FILE_HEADER.Add("Machine",[UInt16]0)
$IMAGE_FILE_HEADER.Add("NumberOfSections",[UInt16]0)
$IMAGE_FILE_HEADER.Add("TimeDateStamp",[UInt32]0)
$IMAGE_FILE_HEADER.Add("PointerToSymbolTable",[UInt32]0)
$IMAGE_FILE_HEADER.Add("NumberOfSymbols",[UInt32]0)
$IMAGE_FILE_HEADER.Add("SizeOfOptionalHeader",[UInt16]0)
$IMAGE_FILE_HEADER.Add("Characteristics",[UInt16]0)
 
$IMAGE_OPTIONAL_HEADER = New-Object System.Collections.Specialized.OrderedDictionary
 
$IMAGE_OPTIONAL_HEADER.Add("Magic",[Uint16]0)
$IMAGE_OPTIONAL_HEADER.Add("MajorLinkerVersion",[Byte]0)
$IMAGE_OPTIONAL_HEADER.Add("MinorLinkerVersion",[Byte]0)
$IMAGE_OPTIONAL_HEADER.Add("SizeOfCode",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("SizeOfInitializedData",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("SizeOfUninitializedData",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("AddressOfEntryPoint",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("BaseOfCode",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("BaseOfData",[Uint32]0)
 
# NT Additional Fields
 
$IMAGE_OPTIONAL_HEADER.Add("ImageBase",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("SectionAlignment",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("FileAlignment",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("MajorOperatingSystemVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER.Add("MinorOperatingSystemVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER.Add("MajorImageVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER.Add("MinorImageVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER.Add("MajorSubsystemVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER.Add("MinorSubsystemVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER.Add("Win32VersionValue",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("SizeOfImage",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("SizeOfHeaders",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("CheckSum",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("Subsystem",[Uint16]0)
$IMAGE_OPTIONAL_HEADER.Add("DllCharacteristics",[Uint16]0)
$IMAGE_OPTIONAL_HEADER.Add("SizeOfStackReserve",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("SizeOfStackCommit",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("SizeOfHeapReserve",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("SizeOfHeapCommit",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("LoaderFlags",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("NumberOfRvaAndSizes",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("DataDirectory",(New-Object System.Collections.Specialized.OrderedDictionary[] $IMAGE_NUMBEROF_DIRECTORY_ENTRIES))
For ($i = 0; $i -lt $IMAGE_OPTIONAL_HEADER["DataDirectory"].Count;$i++)
{
    $IMAGE_OPTIONAL_HEADER["DataDirectory"][$i] = New-Object System.Collections.Specialized.OrderedDictionary
    $IMAGE_OPTIONAL_HEADER["DataDirectory"][$i].Add("VirtualAddress",[Uint32]0)
    $IMAGE_OPTIONAL_HEADER["DataDirectory"][$i].Add("Size",[UINt32]0)
}
 
 
$IMAGE_OPTIONAL_HEADER64 = New-Object System.Collections.Specialized.OrderedDictionary
$IMAGE_OPTIONAL_HEADER64.Add("Magic",[Uint16]0)
$IMAGE_OPTIONAL_HEADER64.Add("MajorLinkerVersion",[Byte]0)
$IMAGE_OPTIONAL_HEADER64.Add("MinorLinkerVersion",[Byte]0)
$IMAGE_OPTIONAL_HEADER64.Add("SizeOfCode",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("SizeOfInitializedData",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("SizeOfUninitializedData",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("AddressOfEntryPoint",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("BaseOfCode",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("ImageBase",[Uint64]0)
$IMAGE_OPTIONAL_HEADER64.Add("SectionAlignment",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("FileAlignment",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("MajorOperatingSystemVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER64.Add("MinorOperatingSystemVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER64.Add("MajorImageVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER64.Add("MinorImageVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER64.Add("MajorSubsystemVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER64.Add("MinorSubsystemVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER64.Add("Win32VersionValue",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("SizeOfImage",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("SizeOfHeaders",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("CheckSum",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("Subsystem",[Uint16]0)
$IMAGE_OPTIONAL_HEADER64.Add("DllCharacteristics",[Uint16]0)
$IMAGE_OPTIONAL_HEADER64.Add("SizeOfStackReserve",[Uint64]0)
$IMAGE_OPTIONAL_HEADER64.Add("SizeOfStackCommit",[Uint64]0)
$IMAGE_OPTIONAL_HEADER64.Add("SizeOfHeapReserve",[Uint64]0)
$IMAGE_OPTIONAL_HEADER64.Add("SizeOfHeapCommit",[Uint64]0)
$IMAGE_OPTIONAL_HEADER64.Add("LoaderFlags",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("NumberOfRvaAndSizes",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("DataDirectory",(New-Object System.Collections.Specialized.OrderedDictionary[] $IMAGE_NUMBEROF_DIRECTORY_ENTRIES))
For ($i = 0; $i -lt $IMAGE_OPTIONAL_HEADER64["DataDirectory"].Count;$i++)
{
    $IMAGE_OPTIONAL_HEADER64["DataDirectory"][$i] = New-Object System.Collections.Specialized.OrderedDictionary
    $IMAGE_OPTIONAL_HEADER64["DataDirectory"][$i].Add("VirtualAddress",[Uint32]0)
    $IMAGE_OPTIONAL_HEADER64["DataDirectory"][$i].Add("Size",[UINt32]0)
}
 
$IMAGE_SECTION_HEADER = New-Object System.Collections.Specialized.OrderedDictionary
$IMAGE_SECTION_HEADER.Add("Name",(New-Object Byte[] $IMAGE_SIZEOF_SHORT_NAME))
$IMAGE_SECTION_HEADER.Add("Misc.PhysicalAddress",[Uint32]0)
$IMAGE_SECTION_HEADER.Add("Misc.VirtualSize",[Uint32]0)
$IMAGE_SECTION_HEADER.Add("VirtualAddress",[Uint32]0)
$IMAGE_SECTION_HEADER.Add("SizeOfRawData",[Uint32]0)
$IMAGE_SECTION_HEADER.Add("PointerToRawData",[Uint32]0)
$IMAGE_SECTION_HEADER.Add("PointerToRelocations",[Uint32]0)
$IMAGE_SECTION_HEADER.Add("PointerToLinenumbers",[Uint32]0)
$IMAGE_SECTION_HEADER.Add("NumberOfRelocations",[Uint16]0)
$IMAGE_SECTION_HEADER.Add("NumberOfLinenumbers",[Uint16]0)
$IMAGE_SECTION_HEADER.Add("Characteristics",[Uint32]0)
 
Function Convert-EnumToString
{
    param($enum,$value)
     
    $builder = New-Object System.Text.StringBuilder
    $names = ($enum.DeclaredFields | Select-Object -Property Name | Where-Object { $_.Name -ne "value__" }).Name
 
    ForEach ($name in $names)
    {
        if (!($value -ne 0 -and [Enum]::Parse($enum,$name).value__ -eq 0))
        {
            if ((([Enum]::Parse($enum,$name).value__) -band $value) -eq ([Enum]::Parse($enum,$name).value__))
            {
                if ($builder.Length -gt 0)
                {
                    [void]$builder.Append(" | ")
                }
                [void]$builder.Append($name)
            }
        }
    }
 
    return $builder.ToString()
     
}
Function Read-BinaryFile
{
    param(
    [System.IO.BinaryReader][ref]$reader,
    [System.Collections.Specialized.OrderedDictionary][ref]$items)
    
    
    $keys = New-Object String[] $items.Count
    $items.Keys.CopyTo($keys,0)
   
    for ($i = 0;$i -lt $items.Count;$i++)
    {
        $item = $keys[$i]
        
        if ($items[$item] -is [Array])
        {
            if ($items[$item][0] -is [System.Collections.Specialized.OrderedDictionary])
            {
                ForEach ($subItem in $items[$item])
                {
                    Read-BinaryFile -reader ([ref]$reader) -items ([ref]$subItem)
                }
            }
            else
            {
                $currentItem = $items[$item][0]
                For ($j = 0;$j -lt $items[$item].Length;$j++)
                {
                    switch($currentItem.GetType().Name)
                    {
                        "Byte"
                        {
                            $items[$item][$j] = $reader.ReadByte()
                        }
                        "Uint16"
                        { 
                            $items[$item][$j] = $Reader.ReadUint16()
                        }
                        "Uint32"
                        {
                            $items[$item][$j] = $reader.ReadUInt32()
                        }
                        "Uint64"
                        { 
                            $items[$item][$j] = $Reader.ReadUInt64()
                        }
                        default  { "Unknown Type! $($currentItem.GetType().Name)" }
                    }
                }
            }
        }
        else
        {
            $currentItem = $items[$item]
            if ($currentItem -is [System.Collections.Specialized.OrderedDictionary])
            {
                Read-BinaryFile -reader ([ref]$reader) -items ([ref]$items[$item])
            }
            else
            {
                switch($currentItem.GetType().Name)
                {
                    "Byte"
                    {
                        $items[$item] = $reader.ReadByte()
                    }
 
                    "Uint16" { 
                        $items[$item] = $reader.ReadUint16()
 
                    }
                    "Uint32"
                    { 
                         $items[$item] = $reader.ReadUint32()
                    }
                    "Uint64"
                    {
                        $items[$item] = $reader.ReadUint64()               
                    }
                    default  { "Unknown Type! $($currentItem.GetType().Name)" }
                }
            }
        }
    }
}

[NativeMethods]::SymInitialize()

$apiSetName = [NativeMethods]::CreateUnicodeString($ApiSet)
$apiName = [NativeMethods]::CreateAnsiString($Api)

[IntPtr]$dllHandle = [IntPtr]::Zero
[IntPtr]$procAddress = [IntPtr]::Zero

if ([NativeMethods]::LdrLoadDll("",0,[ref]$apiSetName,[ref]$dllHandle) -eq 0)
{
    if ([NativeMethods]::LdrGetProcedureAddress($dllHandle,[ref]$apiName,0,[ref]$procAddress) -eq 0)
    {
        if ($procAddress -ne [IntPtr]::Zero)
        {
            $filename = [NativeMethods]::GetModuleFileName($dllHandle)
            if ($ShowOffset)
            {
                $FileStream = [System.IO.File]::Open($filename, [System.IO.FileMode]::Open,[System.IO.FileAccess]::Read,[System.IO.FileShare]::Read)
 
                $BinaryReader = New-Object System.IO.BinaryReader($FileStream)
                [void]$BinaryReader.BaseStream.Seek(0,[System.IO.SeekOrigin]::Begin)
 
                Read-BinaryFile -reader ([ref]$BinaryReader) -Items ([ref]$IMAGE_DOS_HEADER)
 
                if ($IMAGE_DOS_HEADER["e_magic"] -eq $IMAGE_DOS_SIGNATURE)
                {
                    [void]$BinaryReader.BaseStream.Seek($IMAGE_DOS_HEADER["e_lfanew"] + 24,[System.IO.SeekOrigin]::Begin)
                    $magic = $BinaryReader.ReadInt16()
                    [void]$BinaryReader.BaseStream.Seek($IMAGE_DOS_HEADER["e_lfanew"],[System.IO.SeekOrigin]::Begin)
 
                    $IMAGE_NT_HEADER = New-Object System.Collections.Specialized.OrderedDictionary
                    $IMAGE_NT_HEADER.Add("Signature",[Uint32]0)
                    $IMAGE_NT_HEADER.Add("FileHeader",$IMAGE_FILE_HEADER)
 
                    switch ($magic)
                    {
                        $IMAGE_NT_OPTIONAL_HDR32_MAGIC
                        { 
                            $IMAGE_NT_HEADER.Add("OptionalHeader",$IMAGE_OPTIONAL_HEADER) 
                        }
                        $IMAGE_NT_OPTIONAL_HDR64_MAGIC
                        {
                            $IMAGE_NT_HEADER.Add("OptionalHeader",$IMAGE_OPTIONAL_HEADER64)
                        } 
                        $IMAGE_ROM_OPTIONAL_HDR_MAGIC { "ROM File, not supported!" }
                        default { "Unknown PE file type $($magic)!" } 
                    }
                    Read-BinaryFile -reader ([ref]$BinaryReader) -Items ([ref]$IMAGE_NT_HEADER)
 
                }
                else
                {
                    Write-Host "Not a valid PE file!"
                }
 
                $dllCharacteristics = Convert-EnumToString -enum ([IMAGE_DLLCHARACTERISTICS]) -value $IMAGE_NT_HEADER["OptionalHeader"]["DllCharacteristics"]
                $subSystem = [Enum]::Parse([IMAGE_SUBSYSTEM],$IMAGE_NT_HEADER["OptionalHeader"]["Subsystem"])
                $machine = [Enum]::Parse([IMAGE_FILE_MACHINE],$IMAGE_NT_HEADER["FileHeader"]["Machine"])

                [UInt64]$originalAddress = ($procAddress.ToInt64() -$dllHandle) + $IMAGE_NT_HEADER.OptionalHeader.ImageBase
                "$($filename)!0x{0:X}" -f $originalAddress
            }
            else
            {
                $symbolName = [NativeMethods]::GetSymbolName($procAddress)
                [Void][NativeMethods]::LdrUnloadDll($dllHandle)
                "$($filename)!$symbolName"
            }
        }
    }

}
Posted in Uncategorized | Leave a comment

Get Directory Listing in Console Tree View via PowerShell

Replicating the behavior of classic command line tool tree in PowerShell. By default only displays directories, add -ShowFileNames switch to show filenames as well.

Script is here https://github.com/chentiangemalc/PowerShellScripts/blob/master/Get-TreeView.ps1

Usage:

./Get-TreeView -Path C:\scripts
./Get-TreeView -Path C:\scripts -ShowFileNames
param (
    [string]$Path = (Get-Location),
    [switch]$ShowFilenames 
)

$global:dirCount = 0
$global:fileCount = 0

Function Register-AbsolutePath {
  param($absolute)
  if (Test-Path -PathType Container $absolute) {
    $global:dirCount++
  } else {
    $global:fileCount++
  }
}

Function Get-Summary {
  return "$($global:dirCount) directories, $($global:fileCount) files"
}

Function Walk-Directory {
  param($directory, $prefix = "")
  
  if ($ShowFilenames)
  {
    $filepaths = @(Get-ChildItem -Path $directory | Sort-Object Name | Select-Object -ExpandProperty Name)
  }
  else
  {
    $filepaths = @(Get-ChildItem -Path $directory -Directory | Sort-Object Name | Select-Object -ExpandProperty Name)
  }
  for ($i = 0; $i -lt $filepaths.Length; $i++) {
    if ($filepaths[$i][0] -eq ".") {
      continue
    }
    $absolute = Join-Path -Path $directory -ChildPath $filepaths[$i]
    Register-AbsolutePath -absolute $absolute
    if ($i -eq $filepaths.Length - 1) {
      Write-Host "$prefix`└── $($filepaths[$i])"
      if (Test-Path -PathType Container $absolute) {
        Walk-Directory -directory $absolute -prefix "$prefix    "
      }
    } else {
      Write-Host "$prefix`├── $($filepaths[$i])"
      if (Test-Path -PathType Container $absolute) {
       Walk-Directory -directory $absolute -prefix "$prefix`│   "
      }
    }
  }
}

Write-Host $path

Walk-Directory -directory $path
Write-Host ""
Write-Host (Get-Summary)

Posted in Uncategorized | Leave a comment

Visual Basic 6 Runtime Debugging Symbols (PDBs)

An issue you will likely come across if debugging VB6 apps with the inbuilt VB6 runtime built into Windows 8+ is that symbols don’t seem to be available via the Microsoft Symbol Server. This makes VB6 stack traces completely bonkers and a lot of work to interpret.

In Visual Basic 5 and earlier debug symbols were provided as a separate download, long since gone from the Microsoft website, although still can be found archived across the internet in files such as VB5SP3DS.EXE. These however do not contain PDB files but only the legacy DBG format symbols.

For Visual Basic 6 the debug symbols were only provided in the ISO service pack releases, the Service Pack 6 is still available via Visual Studio subscriber downloads as mu_visual_basic_6.0_service_pack_6_x86_a783d802.iso (as of Jan 2023)

Within this ISO the DBG files are available via the language folder at root of the ISO i.e. en\us\Vs6sp6B.exe. Once extracted this contains a file msvbvm60.dbg and the matching msvbvm60.dll in Msvbvm60.cab. IDA Pro reports the DBG file mismatch with the DLL however it seems to match functions and other information correctly as far as I can tell.

However on my machine, if using this DLL from the SP6 ISO, while the debugging info is fine in IDA Pro, in WinDbg it triggers downloading a PDB file which doesn’t seem to match correctly, as the start of many functions don’t match up with the symbols in many cases.

A small selection of VB6 runtime DLLs/PDBs were published to the symbol server, but most versions of the DLL I’ve come across do not have the symbols available on the symbol server.

For best success with WinDbg I found using this DLL and PDB file from Microsoft Symbol server works.

https://msdl.microsoft.com/download/symbols/msvbvm60.dll/4802A186153000/msvbvm60.dll

https://msdl.microsoft.com/download/symbols/MSVBVM60.pdb/47193E361/MSVBVM60.pdb

I wouldn’t recommend replacing the VB6 runtime included with Windows, but you can put this in the same folder as your VB6 EXE when debugging if you need to try and get more useful stack traces. It is missing a lot of patches vs the VB6 runtime included with Windows, so this is only useful if you can reproduce the scenario with this runtime. In addition, this version of the runtime is likely missing the latest security patches.

Here is an example with correct symbols, what you will see when a control is clicked:

0:000> k
 # ChildEBP RetAddr      
00 0019f410 734d4738     MSVBVM60!_DoClick
01 0019f428 7347ce03     MSVBVM60!PushCtlProc+0x7c
02 0019f450 7347f800     MSVBVM60!CommonGizWndProc+0xae
03 0019f4ac 7347e1e6     MSVBVM60!StdCtlWndProc+0x232
04 0019f4d0 7347dc27     MSVBVM60!_DefWmCommand+0xc7
05 0019f53c 734a20e3     MSVBVM60!VBDefControlProc+0xb47
06 0019f6bc 7347ce03     MSVBVM60!FormCtlProc+0x10bd
07 0019f6e4 7347f800     MSVBVM60!CommonGizWndProc+0xae
08 0019f740 76d17d52     MSVBVM60!StdCtlWndProc+0x232
09 0019f76c 76cf711a     USER32!_InternalCallWinProc+0x2a
0a 0019f85c 76cf6822     USER32!UserCallWinProcCheckWow+0x4aa
0b 0019f8c0 76d1e29c     USER32!SendMessageWorker+0x842
0c 0019f8e4 76cf5c66     USER32!SendMessageInternal+0x2d
0d 0019f904 76d387bf     USER32!SendMessageW+0x46
0e 0019f928 76d384a7     USER32!xxxButtonNotifyParent+0x50
0f 0019f950 76d37969     USER32!xxxBNReleaseCapture+0x140
10 0019f9f4 76d36de2     USER32!ButtonWndProcWorker+0xad9
11 0019fa20 76d17d52     USER32!ButtonWndProcA+0x52
12 0019fa4c 76cf711a     USER32!_InternalCallWinProc+0x2a
13 0019fb3c 76d0d162     USER32!UserCallWinProcCheckWow+0x4aa
14 0019fb74 7347d03c     USER32!CallWindowProcA+0x82
15 0019fbe0 734d4776     MSVBVM60!VBDefControlProc+0x255
16 0019fc08 7347ce03     MSVBVM60!PushCtlProc+0xbf
17 0019fc30 7347f800     MSVBVM60!CommonGizWndProc+0xae
18 0019fc8c 76d17d52     MSVBVM60!StdCtlWndProc+0x232
19 0019fcb8 76cf711a     USER32!_InternalCallWinProc+0x2a
1a 0019fda8 76cf5a48     USER32!UserCallWinProcCheckWow+0x4aa
1b 0019fe24 76d11af0     USER32!DispatchMessageWorker+0x4b8
1c 0019fe2c 7342a6b0     USER32!DispatchMessageA+0x10
1d 0019fe6c 7342a627     MSVBVM60!ThunderMsgLoop+0xfd
1e 0019fe70 ffffffff     MSVBVM60!CMsoCMHandler::FPushMessageLoop+0x19

Without symbols the same stack trace shows as the following meaningless information. A good clue that this is useless information is the huge offsets after the function name:

0:000> k
 # ChildEBP RetAddr      
WARNING: Stack unwind information not available. Following frames may be wrong.
00 0019f428 7347ce03     MSVBVM60!EbLibraryUnload+0xdcb6
01 0019f450 7347f800     MSVBVM60!IID_IVbaHost+0x2e823
02 0019f4ac 7347e1e6     MSVBVM60!IID_IVbaHost+0x31220
03 0019f4d0 7347dc27     MSVBVM60!IID_IVbaHost+0x2fc06
04 0019f53c 734a20e3     MSVBVM60!IID_IVbaHost+0x2f647
05 0019f6bc 7347ce03     MSVBVM60!BASIC_DISPINTERFACE_GetTICount+0x1cd8b
06 0019f6e4 7347f800     MSVBVM60!IID_IVbaHost+0x2e823
07 0019f740 76d17d52     MSVBVM60!IID_IVbaHost+0x31220
08 0019f76c 76cf711a     USER32!AddClipboardFormatListener+0x52
09 0019f85c 76cf6822     USER32!CallWindowProcW+0x144a
0a 0019f8c0 76d1e29c     USER32!CallWindowProcW+0xb52
0b 0019f8e4 76cf5c66     USER32!SetWindowsHookExAW+0x10c
0c 0019f904 76d387bf     USER32!SendMessageW+0x46
0d 0019f928 76d384a7     USER32!LoadCursorFromFileW+0x1eaf
0e 0019f950 76d37969     USER32!LoadCursorFromFileW+0x1b97
0f 0019f9f4 76d36de2     USER32!LoadCursorFromFileW+0x1059
10 0019fa20 76d17d52     USER32!LoadCursorFromFileW+0x4d2
11 0019fa4c 76cf711a     USER32!AddClipboardFormatListener+0x52
12 0019fb3c 76d0d162     USER32!CallWindowProcW+0x144a
13 0019fb74 7347d03c     USER32!CallWindowProcA+0x82
14 0019fbe0 734d4776     MSVBVM60!IID_IVbaHost+0x2ea5c
15 0019fc08 7347ce03     MSVBVM60!EbLibraryUnload+0xdfaf
16 0019fc30 7347f800     MSVBVM60!IID_IVbaHost+0x2e823
17 0019fc8c 76d17d52     MSVBVM60!IID_IVbaHost+0x31220
18 0019fcb8 76cf711a     USER32!AddClipboardFormatListener+0x52
19 0019fda8 76cf5a48     USER32!CallWindowProcW+0x144a
1a 0019fe24 76d11af0     USER32!DispatchMessageW+0x4d8
1b 0019fe2c 7342a6b0     USER32!DispatchMessageA+0x10
1c 0019fe6c 7342a627     MSVBVM60!_vbaStrToAnsi+0x2f1
1d 0019feb0 00000000     MSVBVM60!_vbaStrToAnsi+0x268
Posted in Uncategorized | Leave a comment

DART Setup Wizard Doesn’t Detect Installed ADK

Trying to create a DART recovery image, got the message during the installation from Microsoft Desktop Optimization Pack 2015 running installer from \DaRT\DaRT 10\Installers\en-us\x64\MSDart100.msi

However, the latest Windows ADK + Windows PE ADK component has been installed. Suspected the issue was a specific version is required, but the download link in the setup is a dead link and just takes you to a generic Microsoft page.

We could look for components not found either through Windows Installer logging, or ProcMon, but here want to demonstrate some ways to analyze how the installer is making the checks.

We can check with ORCA how the ADK installation check is occurring.

Opening the installation MSI in Orca we can set a condition that will prevent the DaRTRecoveryImage feature from installing.;

We could just remove the condition, however was curious how the check actually ocurred…

In Custom Action we can see DetectAdk action

In Binary view we can extract this item by clicking the [Binary Data] and Write Binary to Filename to save the item to disk. Typically these will be a DLL or a Script.

As this is a 32-bit DLL we can test calling this custom action with 32-bit PowerShell

$code = @'
using System;
using System.Runtime.InteropServices;
using System.ComponentModel;
namespace NativeMethods
{
    public static class CustomActionRoutines
    {
        [DllImport("msi.dll", ExactSpelling=true)]
        public static extern IntPtr MsiCreateRecord(uint cParams);
        
        [DllImport("SetupCommonDLLCmp2.dll", SetLastError = true)]
        public static extern uint DetectAdk(IntPtr hMsiHandle);

        [DllImport("msi.dll", ExactSpelling=true)]
        public static extern uint MsiCloseHandle(IntPtr hAny);
     }
}
'@
Add-Type -TypeDefinition $code

$msiHandle = [NativeMethods.CustomActionRoutines]::MsiCreateRecord(0)
[NativeMethods.CustomActionRoutines]::DetectAdk($msiHandle)
[void][NativeMethods.CustomActionRoutines]::MsiCloseHandle($msiHandle)

When we run this while monitoring with Process Monitor we can see it triggers creating a process with the following command line:

rundll32.exe “C:\WINDOWS\SYSTEM32\SetupCommonDLLCmp2.dll”,zzzzInvokeManagedCustomActionOutOfProc SfxCA_5457953 7 Microsoft.Dart.MuCustomActions!Microsoft.Dart.CustomActions.ADKCustomActions.DetectAdk

We can see this extracts a number of files, which are deleted straight after being created. Using 7-zip was able to extract the files from the DLL so we could analyze them:

These DLLs are .NET assemblies, in Microsoft.Dart.MuCustomActions.dll we find with a .NET decompiler a class Microsoft.Dart.CustomActions.ADK.CustomActions with the following code:

// Decompiled with JetBrains decompiler
// Type: Microsoft.Dart.CustomActions.ADKCustomActions
// Assembly: Microsoft.Dart.MuCustomActions, Version=10.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
// MVID: BB93085A-8824-4EAB-996B-D904BBE67B8D
// Assembly location: D:\reverse\SetupCommonDLLCmp2\Microsoft.Dart.MuCustomActions.dll

using Microsoft.Dart.Commands.Api;
using Microsoft.Deployment.WindowsInstaller;

namespace Microsoft.Dart.CustomActions
{
  public static class ADKCustomActions
  {
    [CustomAction]
    public static ActionResult DetectAdk(Session session)
    {
      if (!WindowsAdk.IsValidAdkPath())
        return ActionResult.Failure;
      session["WINDOWSKITSINSTALLED"] = "1";
      return ActionResult.Success;
    }
  }
}

This references the following ADK related queries:

// Decompiled with JetBrains decompiler
// Type: Microsoft.Dart.Commands.Api.WindowsAdk
// Assembly: Microsoft.Dart.MuCustomActions, Version=10.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
// MVID: BB93085A-8824-4EAB-996B-D904BBE67B8D
// Assembly location: D:\reverse\SetupCommonDLLCmp2\Microsoft.Dart.MuCustomActions.dll

using Microsoft.Win32;
using System;
using System.IO;

namespace Microsoft.Dart.Commands.Api
{
  internal static class WindowsAdk
  {
    private static string adkPath;
    private static string bootFilesPathx64;
    private static string bootFilesPathx86;
    private static string oscdImagePathx64;
    private static string oscdImagePathx86;
    private static string imagePathx64;
    private static string imagePathx86;
    private static string optionalComponentPathx64;
    private static string optionalComponentPathx86;

    public static string AdkPath
    {
      get
      {
        if (WindowsAdk.adkPath == null)
        {
          string str = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "Windows Kits\\10\\");
          RegistryKey registryKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32).OpenSubKey("SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots");
          if (registryKey != null)
            WindowsAdk.adkPath = registryKey.GetValue("KitsRoot10", (object) null) as string;
          if (WindowsAdk.adkPath == null)
            WindowsAdk.adkPath = str;
        }
        return WindowsAdk.adkPath;
      }
      internal set => WindowsAdk.adkPath = value;
    }

    public static bool IsValidAdkPath() => Directory.Exists(WindowsAdk.BootFilesPathx64) && Directory.Exists(WindowsAdk.BootFilesPathx86) && Directory.Exists(WindowsAdk.OscdImagePathx64) && Directory.Exists(WindowsAdk.OscdImagePathx86) && Directory.Exists(WindowsAdk.ImagePathx64) && Directory.Exists(WindowsAdk.ImagePathx86) && Directory.Exists(WindowsAdk.OptionalComponentPathx64) && Directory.Exists(WindowsAdk.OptionalComponentPathx86);

    public static string BootFilesPathx64
    {
      get
      {
        if (WindowsAdk.bootFilesPathx64 == null)
          WindowsAdk.bootFilesPathx64 = Path.Combine(WindowsAdk.AdkPath, "Assessment and Deployment Kit\\Windows Preinstallation Environment\\amd64\\Media");
        return WindowsAdk.bootFilesPathx64;
      }
      internal set => WindowsAdk.bootFilesPathx64 = value;
    }

    public static string BootFilesPathx86
    {
      get
      {
        if (WindowsAdk.bootFilesPathx86 == null)
          WindowsAdk.bootFilesPathx86 = Path.Combine(WindowsAdk.AdkPath, "Assessment and Deployment Kit\\Windows Preinstallation Environment\\x86\\Media");
        return WindowsAdk.bootFilesPathx86;
      }
      internal set => WindowsAdk.bootFilesPathx86 = value;
    }

    public static string OscdImagePathx64
    {
      get
      {
        if (WindowsAdk.oscdImagePathx64 == null)
          WindowsAdk.oscdImagePathx64 = Path.Combine(WindowsAdk.AdkPath, "Assessment and Deployment Kit\\Deployment Tools\\amd64\\Oscdimg");
        return WindowsAdk.oscdImagePathx64;
      }
      internal set => WindowsAdk.oscdImagePathx64 = value;
    }

    public static string OscdImagePathx86
    {
      get
      {
        if (WindowsAdk.oscdImagePathx86 == null)
          WindowsAdk.oscdImagePathx86 = Path.Combine(WindowsAdk.AdkPath, "Assessment and Deployment Kit\\Deployment Tools\\x86\\Oscdimg");
        return WindowsAdk.oscdImagePathx86;
      }
      internal set => WindowsAdk.oscdImagePathx86 = value;
    }

    public static string ImagePathx64
    {
      get
      {
        if (WindowsAdk.imagePathx64 == null)
          WindowsAdk.imagePathx64 = Path.Combine(WindowsAdk.AdkPath, "Assessment and Deployment Kit\\Windows Preinstallation Environment\\amd64\\en-us");
        return WindowsAdk.imagePathx64;
      }
      internal set => WindowsAdk.imagePathx64 = value;
    }

    public static string ImagePathx86
    {
      get
      {
        if (WindowsAdk.imagePathx86 == null)
          WindowsAdk.imagePathx86 = Path.Combine(WindowsAdk.AdkPath, "Assessment and Deployment Kit\\Windows Preinstallation Environment\\x86\\en-us");
        return WindowsAdk.imagePathx86;
      }
      internal set => WindowsAdk.imagePathx86 = value;
    }

    public static string OptionalComponentPathx64
    {
      get
      {
        if (WindowsAdk.optionalComponentPathx64 == null)
          WindowsAdk.optionalComponentPathx64 = Path.Combine(WindowsAdk.AdkPath, "Assessment and Deployment Kit\\Windows Preinstallation Environment\\amd64\\WinPE_OCs");
        return WindowsAdk.optionalComponentPathx64;
      }
      internal set => WindowsAdk.optionalComponentPathx64 = value;
    }

    public static string OptionalComponentPathx86
    {
      get
      {
        if (WindowsAdk.optionalComponentPathx86 == null)
          WindowsAdk.optionalComponentPathx86 = Path.Combine(WindowsAdk.AdkPath, "Assessment and Deployment Kit\\Windows Preinstallation Environment\\x86\\WinPE_OCs");
        return WindowsAdk.optionalComponentPathx86;
      }
      internal set => WindowsAdk.optionalComponentPathx86 = value;
    }

    public static string GetWinPeOcsPath(Architecture architecture)
    {
      switch (architecture)
      {
        case Architecture.X86:
          return WindowsAdk.OptionalComponentPathx86;
        case Architecture.X64:
          return WindowsAdk.OptionalComponentPathx64;
        default:
          return string.Empty;
      }
    }
  }
}

From reviewing this we can see that all of the following paths must exist for ADK to be detected as “installed”

WindowsAdk.BootFilesPathx64
WindowsAdk.BootFilesPathx86
WindowsAdk.OscdImagePathx64
WindowsAdk.OscdImagePathx86
WindowsAdk.ImagePathx64
WindowsAdk.ImagePathx86
WindowsAdk.OptionalComponentPathx64
WindowsAdk.OptionalComponentPathx86

We could test the logic with this PowerShell code:

Add-Type -Path "Microsoft.Dart.MuCustomActions.dll"
Add-Type -Path "Microsoft.Deployment.WindowsInstaller.dll"
[Microsoft.Dart.CustomActions.ADKCustomActions]::DetectAdk($null)

This reports “Failure” as expected. However it doesn’t clearly show which of the paths missing is triggering the issue. We can break the logic of the check into some pure PowerShell and show as the result per path:

class WindowsAdk
{
    [string]$adkPath
    [string]$bootFilesPathx64
    [string]$bootFilesPathx86
    [string]$oscdImagePathx64
    [string]$oscdImagePathx86
    [string]$imagePathx64
    [string]$imagePathx86
    [string]$optionalComponentPathx64
    [string]$optionalComponentPathx86
}

$WindowsADK = New-Object WindowsADK
$WindowsADK.adkPath = [System.IO.Path]::Combine([Environment]::GetFolderPath("ProgramFilesX86"), "Windows Kits\10\")
$key = [Microsoft.Win32.RegistryKey]::OpenBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, [Microsoft.Win32.RegistryView]::Registry32).OpenSubKey("SOFTWARE\Microsoft\\Windows Kits\Installed Roots")
if ($key -ne $null)
{
    $WindowsADK.adkPath = $key.GetValue("KitsRoot10")
}


$windowsADK.bootFilesPathx64 = [System.IO.Path]::Combine($WindowsADK.adkPath, "Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\Media")
$windowsADK.bootFilesPathx86 = [System.IO.Path]::Combine($WindowsADK.adkPath, "Assessment and Deployment Kit\Windows Preinstallation Environment\x86\Media")
$windowsADK.oscdImagePathx64 = [System.IO.Path]::Combine($WindowsADK.adkPath,"Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg")
$windowsADK.oscdImagePathx86 = [System.IO.Path]::Combine($WindowsADK.adkPath, "Assessment and Deployment Kit\Deployment Tools\x86\Oscdimg")
$windowsADK.imagePathx64 = [System.IO.Path]::Combine($WindowsADK.adkPath, "Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\en-us")
$windowsADK.imagePathx86 = [System.IO.Path]::Combine($WindowsADK.adkPath, "Assessment and Deployment Kit\Windows Preinstallation Environment\x86\en-us")
$windowsADK.optionalComponentPathx64 = [System.IO.Path]::Combine($WindowsADK.adkPath,"Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs")
$windowsADK.optionalComponentPathx86 = [System.IO.Path]::Combine($WindowsADK.adkPath,"Assessment and Deployment Kit\Windows Preinstallation Environment\x86\WinPE_OCs")

"Windows ADK Path : $(Test-Path $WindowsADK.adkPath)"
"x64 Boot Files Path : $(Test-Path $WindowsADK.bootFilesPathx64)"
"x86 Boot Files Path : $(Test-Path $WindowsADK.bootFilesPathx86)"
"x64 OSCD Files Path : $(Test-Path $WindowsADK.oscdImagePathx64)"
"x86 OSCD Files Path : $(Test-Path $WindowsADK.oscdImagePathx86)"
"x64 Image Path : $(Test-Path $WindowsADK.imagePathx64)"
"x86 Image Path : $(Test-Path $WindowsADK.imagePathx86)"
"x64 Optional Components Path : $(Test-Path $WindowsADK.optionalComponentPathx64)"
"x86 Optional Components Path : $(Test-Path $WindowsADK.optionalComponentPathx86)"

On my machine this output the following:

So we ran these two commands to fix the missing folders. I didn’t want to create any 32-bit images so expected these missing shouldn’t be a problem:

mkdir "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\x86\Media"
mkdir "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\x86\WinPE_OCs"
mkdir "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\x86\en-us"

Now the installer works, and recovery images are generated fine…

Posted in Uncategorized | 3 Comments

Merge Multiple PDFs with PowerShell and PDFSharp

First you will need to download PDF Sharp and build with Visual Studio the solution “BuildAll-PdfSharp.sln” and then obtaining the output PdfSharp.dll and placing in same directory as script.

Takes a specified folder of PDFs and combines them into an output file. Modify sourcePath and outputPath as necessary.

$sourcePath = "<path to folder with PDFs to merge>" 
$outputPath = "<filename of final PDF>"

Add-Type -Path "$($PSScriptRoot)\PdfSharp.dll"
$pdfDestination = New-Object PdfSharp.Pdf.PdfDocument
$pdfFiles = Get-ChildItem -Path $sourcePath -Filter *.pdf
ForEach ($filename in $pdfFiles)
{
    "Adding '$($filename.Fullname)'"
    $pdfSource = [PdfSharp.Pdf.IO.PdfReader]::Open($filename.Fullname,[PdfSharp.Pdf.IO.PdfDocumentOpenMode]::Import)
    for ($i = 0; $i -lt $pdfSource.PageCount; $i++)
    {
        $pdfDestination.AddPage($pdfSource.Pages[$i])
    }

    $pdfSource.Close()
     
}

$pdfDestination.Save($outputPath)
Posted in Uncategorized | Leave a comment

Extract Access Database Password with PowerShell

Previously we looked at removing Office Macro Passwords with PowerShell here.

This script can be used to retrieve the master password (i.e. database design password) for many Microsoft Access Database files (.mdb) Note this does not work with databases that have multiple user/passwords associated with them. Only tested with Access 2003-2007 format. May work with older formats, or there may be some minimal tweaking required.

The script is available here https://github.com/chentiangemalc/PowerShellScripts/blob/master/Get-MdbPassword.ps1

Example usage:

<#
.SYNOPSIS

    Displays the password of an Access 2003-2007 (MDB) file

.DESCRIPTION

    Decrypts Access Database password.

.PARAMETER Path

    The access database file of which to display the password.
        
.INPUTS

  None
  
.OUTPUTS

  None
  
.NOTES

  Version:        1.0

  Author:         chentiangemalc

  Creation Date:  6 Sep 2022

  Purpose/Change: Initial script development

  
.EXAMPLE

  .\Get-MdbPassword.ps1 -Path c:\test\test.mdb

#>

[CmdletBinding()]Param(
[Parameter(Mandatory=$true)]
[ValidateScript({
    if( -Not ($_ | Test-Path) ){
        throw "File or folder does not exist"
    }

    if(-Not ($_ | Test-Path -PathType Leaf) ){
        throw "The Path argument must be a file. Folder paths are not allowed."
    }
    return $true
})]
[string]$Path)

[Byte[]]$global:decoderKey = @( 
   0xBA,0x6A,0xEC,0x37,0x61,0xD5,0x9C,0xFA,0xFA,
   0xCF,0x28,0xE6,0x2F,0x27,0x8A,0x60,0x68,0x05,
   0x7B,0x36,0xC9,0xE3,0xDF,0xB1,0x4B,0x65,0x13,
   0x43,0xF3,0x3E,0xB1,0x33,0x08,0xF0,0x79,0x5B,
   0xAE,0x24,0x7C,0x2A,0x00,0x00,0x00,0x00)

Function Decode-Data([Byte[]]$data,[System.Text.Encoding]$Encoding)
{
    switch($Encoding.EncodingName)
    {
        "Unicode" { $decodeSize = 40 }
        default: { throw "Unknown encoding type" }
    }

    $dataPosition = 0
    [Byte]$key1 = $global:decoderKey[36] -bxor $data[36]
    [Byte]$key3 = $global:decoderKey[37] -bxor $data[37]
    [byte]$key4 = 0
    
    for ($counter = 0; $counter -lt $decodeSize;$counter++)
    {
        $key4 = $data[$counter] -bxor $global:decoderKey[$counter]
        $data[$counter]=$key4
        if (!($counter % 4)) { $data[$counter] = $key1 -bxor $key4 }
        if (($counter % 4) -eq 1) { $data[$counter] = $data[$counter] -bxor $key3 }

    }

    $outString = $encoding.GetString($data)
    $outString = $outString.Substring(0,$outString.IndexOf([Char]0))
    $outString 
}

$stream = [System.IO.File]::Open($path, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
$reader = New-Object System.IO.BinaryReader($stream)
$chunk = $reader.ReadBytes(128)
if ($chunk[4] -eq 0x53 -and
    $chunk[5] -eq 0x74 -and
    $chunk[6] -eq 0x61 -and
    $chunk[13] -eq 0x4A -and
    $chunk[14] -eq 0x65)
{
    [void]$reader.BaseStream.Seek(66, [System.IO.SeekOrigin]::Begin)
    $chunk = $reader.ReadBytes(128)
    if ($chunk[90] -eq 0x34 -and
        $chunk[91] -eq 0x2E -and
        $chunk[92] -eq 0x30)
    {
        Decode-Data -Data $chunk -Encoding ([System.Text.Encoding]::Unicode)
        
    }
    else
    {
        # note: Expect ASCII encoding based file to use decode size of 85 
        # but don't have any example file to test with at the moment
        throw "Unknown Encoding"
    }
}
else
{
    throw "Unexpected data!"
}

$reader.Close()
$stream.Close()
Posted in Uncategorized | Leave a comment

Display Binary Numbers and Struct Data with Printf in WinDbg

Was comparing an application behavior between Windows XP and Windows 10 and needed to check the value of some structs, without symbol information for them. The values I wanted to check were specific bits in the struct passed as the 2nd parameter to a function. In this case, I wanted to display the contents of DCB struct, but in an easy-to-read format when comparing traces. In addition, was working in an environment where it was difficult to copy files in/out so using/writing an extension wasn’t a suitable option.

This is fairly straightforward with WinDbg Preview and the JavaScript capability, and normally have used the SyntheticTypes script to parse simple C headers with the relevant struct https://github.com/microsoft/WinDbg-Samples/tree/master/SyntheticTypes

We can use .formats command but this offers limited if any formatting capability.

0:000> .formats $t0
Evaluate expression:
  Hex:     7642b9ad
  Decimal: 1984084397
  Octal:   16620534655
  Binary:  01110110 01000010 10111001 10101101
  Chars:   vB..
  Time:    Mon Nov 15 09:33:17 2032
  Float:   low 9.87375e+032 high 0
  Double:  9.80268e-315

If you can use the JavaScript capability I would use it instead of this approach.

There may be better ways to achieve this with legacy WinDbg and native scripting, but this approach while tedious does work.

One approach can use a combination of boolean and, left shift and right shifts to extract specific bits. For a 32-bit value position “31” would be the first binary digit, and position “0” would have the final digit.

This can be done concisely with a loop, however, in WinDbg this is extremely slow, taking about a minute on my machine to run the loop. Here we will assume the value we want to display in binary is stored in $t0.

0:000> .for (r $t1 = 0x1F; $t1 >= 0 ; r $t1 = $t1-1) { .printf "%d",($t0 & ( 1 << $t1 )) >> $t1 }
01110110010000101011100110101101

A speedier version is:

0:000>  .printf "%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d",($t0 & ( 1 << 0x1F)) >> 0x1F,($t0 & ( 1 << 0x1E)) >> 0x1E,($t0 & ( 1 << 0x1D)) >> 0x1D,($t0 & ( 1 << 0x1C)) >> 0x1C,($t0 & ( 1 << 0x1B)) >> 0x1B,($t0 & ( 1 << 0x1A)) >> 0x1A,($t0 & ( 1 << 0x19)) >> 0x19,($t0 & ( 1 << 0x18)) >> 0x18,($t0 & ( 1 << 0x17)) >> 0x17,($t0 & ( 1 << 0x16)) >> 0x16,($t0 & ( 1 << 0x15)) >> 0x15,($t0 & ( 1 << 0x14)) >> 0x14,($t0 & ( 1 << 0x13)) >> 0x13,($t0 & ( 1 << 0x12)) >> 0x12,($t0 & ( 1 << 0x11)) >> 0x11,($t0 & ( 1 << 0x10)) >> 0x10,($t0 & ( 1 << 0xF)) >> 0xF,($t0 & ( 1 << 0xE)) >> 0xE,($t0 & ( 1 << 0xD)) >> 0xD,($t0 & ( 1 << 0xC)) >> 0xC,($t0 & ( 1 << 0xB)) >> 0xB,($t0 & ( 1 << 0xA)) >> 0xA,($t0 & ( 1 << 0x9)) >> 0x9,($t0 & ( 1 << 0x8)) >> 0x8,($t0 & ( 1 << 0x7)) >> 0x7,($t0 & ( 1 << 0x6)) >> 0x6,($t0 & ( 1 << 0x5)) >> 0x5,($t0 & ( 1 << 0x4)) >> 0x4,($t0 & ( 1 << 0x3)) >> 0x3,($t0 & ( 1 << 0x2)) >> 0x2,($t0 & ( 1 << 0x1)) >> 0x1,($t0 & ( 1 << 0x0)) >> 0x0
01110110010000101011100110101101

If we look at our struct debugging on a target that has debugging symbols, we can see some values are not just individual “bits” but take up multiple positions i.e. bits 15-31 for fDummy2

0:000> dx -r1 ((DcbTest!_DCB *)0x4ffb9c)
((DcbTest!_DCB *)0x4ffb9c)                 : 0x4ffb9c [Type: _DCB *]
    [+0x000] DCBlength        : 0x1c [Type: unsigned long]
    [+0x004] BaudRate         : 0x2580 [Type: unsigned long]
    [+0x008 ( 0: 0)] fBinary          : 0x1 [Type: unsigned long]
    [+0x008 ( 1: 1)] fParity          : 0x0 [Type: unsigned long]
    [+0x008 ( 2: 2)] fOutxCtsFlow     : 0x1 [Type: unsigned long]
    [+0x008 ( 3: 3)] fOutxDsrFlow     : 0x0 [Type: unsigned long]
    [+0x008 ( 5: 4)] fDtrControl      : 0x2 [Type: unsigned long]
    [+0x008 ( 6: 6)] fDsrSensitivity  : 0x1 [Type: unsigned long]
    [+0x008 ( 7: 7)] fTXContinueOnXoff : 0x0 [Type: unsigned long]
    [+0x008 ( 8: 8)] fOutX            : 0x1 [Type: unsigned long]
    [+0x008 ( 9: 9)] fInX             : 0x0 [Type: unsigned long]
    [+0x008 (10:10)] fErrorChar       : 0x1 [Type: unsigned long]
    [+0x008 (11:11)] fNull            : 0x0 [Type: unsigned long]
    [+0x008 (13:12)] fRtsControl      : 0x3 [Type: unsigned long]
    [+0x008 (14:14)] fAbortOnError    : 0x1 [Type: unsigned long]
    [+0x008 (31:15)] fDummy2          : 0x19999 [Type: unsigned long]
    [+0x00c] wReserved        : 0xcccc [Type: unsigned short]
    [+0x00e] XonLim           : 0x7b [Type: unsigned short]
    [+0x010] XoffLim          : 0x1c8 [Type: unsigned short]
    [+0x012] ByteSize         : 0x8 [Type: unsigned char]
    [+0x013] Parity           : 0x4 [Type: unsigned char]
    [+0x014] StopBits         : 0x2 [Type: unsigned char]
    [+0x015] XonChar          : 23 [Type: char]
    [+0x016] XoffChar         : 45 '-' [Type: char]
    [+0x017] ErrorChar        : 67 'C' [Type: char]
    [+0x018] EofChar          : 89 'Y' [Type: char]
    [+0x019] EvtChar          : 98 'b' [Type: char]
    [+0x01a] wReserved1       : 0xcccc [Type: unsigned short]

We can get the binary value by taking those bit positions. In this case, our value is the 2nd parameter in the 32-bit standard calling convention. As we have just hit a breakpoint where the function starts we find the struct base address at poi(@esp+8) and add 8 for the offset where our 32-bit value we are breaking apart begins.

0:000> r $t0= poi(poi(@esp+8)+8)
0:000> .printf "%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d",($t0 & ( 1 << 0x1F)) >> 0x1F,($t0 & ( 1 << 0x1E)) >> 0x1E,($t0 & ( 1 << 0x1D)) >> 0x1D,($t0 & ( 1 << 0x1C)) >> 0x1C,($t0 & ( 1 << 0x1B)) >> 0x1B,($t0 & ( 1 << 0x1A)) >> 0x1A,($t0 & ( 1 << 0x19)) >> 0x19,($t0 & ( 1 << 0x18)) >> 0x18,($t0 & ( 1 << 0x17)) >> 0x17,($t0 & ( 1 << 0x16)) >> 0x16,($t0 & ( 1 << 0x15)) >> 0x15,($t0 & ( 1 << 0x14)) >> 0x14,($t0 & ( 1 << 0x13)) >> 0x13,($t0 & ( 1 << 0x12)) >> 0x12,($t0 & ( 1 << 0x11)) >> 0x11,($t0 & ( 1 << 0x10)) >> 0x10,($t0 & ( 1 << 0xF)) >> 0xF
11001100110011001   

To display it as a number we can add the bits together and use left shifts to convert it back to a value we can display as hex or decimal.

0:000> .printf "0x%X",((($t0 & ( 1 << 0x1F)) >> 1F) << 0x10) + ((($t0 & ( 1 << 0x1E)) >> 1E) << 0xF) + ((($t0 & ( 1 << 0x1D)) >> 1D) << 0xE) + ((($t0 & ( 1 << 0x1C)) >> 1C) << 0xD) + ((($t0 & ( 1 << 0x1B)) >> 1B) << 0xC) + ((($t0 & ( 1 << 0x1A)) >> 1A) << 0xB) + ((($t0 & ( 1 << 0x19)) >> 19) << 0xA) + ((($t0 & ( 1 << 0x18)) >> 18) << 0x9) + ((($t0 & ( 1 << 0x17)) >> 17) << 0x8) + ((($t0 & ( 1 << 0x16)) >> 16) << 0x7) + ((($t0 & ( 1 << 0x15)) >> 15) << 0x6) + ((($t0 & ( 1 << 0x14)) >> 14) << 0x5) + ((($t0 & ( 1 << 0x13)) >> 13) << 0x4) + ((($t0 & ( 1 << 0x12)) >> 12) << 0x3) + ((($t0 & ( 1 << 0x11)) >> 11) << 0x2) + ((($t0 & ( 1 << 0x10)) >> 10) << 0x1) + ((($t0 & ( 1 << 0xF)) >> F) << 0x0)
0x19999

This is fairly tedious to type out so used a simple PowerShell script I can use to generate the value to type in and set the text to the clipboard to paste into WinDbg:

$bitStart = 31
$bitEnd = 15

$cmd = ""
For ($i = $bitStart;$i -ge $bitEnd;$i --)
{
    $cmd+=[String]::Format("(((`$t0 & ( 1 << 0x{0:X})) >> {0:X}) << 0x{1:X})",$i,$i-$bitEnd)
    if ($i -ne $bitEnd) { $cmd += " + " }
}

$cmd | Set-Clipboard

Combining these techniques we can print out our struct now in an easy to read format. (Well the output is easy to read, not the command…)

0:000> r $t0 = poi(poi(@esp+8)+8);.printf "DCBlength=%i\nBaudRate=%i\nfBinary=%d\nfParity=%d\nfOutxCtsFlow=%d\nfOutxDsrFlow=%d\nfDtrControl=%d\nfDsrSensitivity=%d\nfTXContinueOnXoff=%d\nfOutX=%d\nfInX=%d\nfErrorChar=%d\nfNull=%d\nfRtsControl=%d\nfAbortOnError=%d\n",poi(poi(@esp+8)),poi(poi(@esp+8)+4),($t0 & ( 1 << 0x0)) >> 0x0,($t0 & ( 1 << 0x1)) >> 0x1,($t0 & ( 1 << 0x2)) >> 0x2,($t0 & ( 1 << 0x3)) >> 0x3,((($t0 & ( 1 << 0x5)) >> 5) << 0x1) + ((($t0 & ( 1 << 0x4)) >> 4) << 0x0),($t0 & ( 1 << 0x6)) >> 0x6,($t0 & ( 1 << 0x7)) >> 0x7,($t0 & ( 1 << 0x8)) >> 0x8,($t0 & ( 1 << 0x9)) >> 0x9,($t0 & ( 1 << 0xA)) >> 0xA,($t0 & ( 1 << 0xB)) >> 0xB,((($t0 & ( 1 << 0xD)) >> D) << 0x1) + ((($t0 & ( 1 << 0xC)) >> C) << 0x0),($t0 & ( 1 << 0xE)) >> 0xE;r $t0=poi(poi(@esp+8)+0xe);r $t0=poi(poi(@esp+8)+0xe);.printf "XonLim=%i\nXoffLim=%i\n",((($t0 & ( 1 << 0xF)) >> F) << 0xF) + ((($t0 & ( 1 << 0xE)) >> E) << 0xE) + ((($t0 & ( 1 << 0xD)) >> D) << 0xD) + ((($t0 & ( 1 << 0xC)) >> C) << 0xC) + ((($t0 & ( 1 << 0xB)) >> B) << 0xB) + ((($t0 & ( 1 << 0xA)) >> A) << 0xA) + ((($t0 & ( 1 << 0x9)) >> 9) << 0x9) + ((($t0 & ( 1 << 0x8)) >> 8) << 0x8) + ((($t0 & ( 1 << 0x7)) >> 7) << 0x7) + ((($t0 & ( 1 << 0x6)) >> 6) << 0x6) + ((($t0 & ( 1 << 0x5)) >> 5) << 0x5) + ((($t0 & ( 1 << 0x4)) >> 4) << 0x4) + ((($t0 & ( 1 << 0x3)) >> 3) << 0x3) + ((($t0 & ( 1 << 0x2)) >> 2) << 0x2) + ((($t0 & ( 1 << 0x1)) >> 1) << 0x1) + ((($t0 & ( 1 << 0x0)) >> 0) << 0x0),((($t0 & ( 1 << 0x1F)) >> 1F) << 0xF) + ((($t0 & ( 1 << 0x1E)) >> 1E) << 0xE) + ((($t0 & ( 1 << 0x1D)) >> 1D) << 0xD) + ((($t0 & ( 1 << 0x1C)) >> 1C) << 0xC) + ((($t0 & ( 1 << 0x1B)) >> 1B) << 0xB) + ((($t0 & ( 1 << 0x1A)) >> 1A) << 0xA) + ((($t0 & ( 1 << 0x19)) >> 19) << 0x9) + ((($t0 & ( 1 << 0x18)) >> 18) << 0x8) + ((($t0 & ( 1 << 0x17)) >> 17) << 0x7) + ((($t0 & ( 1 << 0x16)) >> 16) << 0x6) + ((($t0 & ( 1 << 0x15)) >> 15) << 0x5) + ((($t0 & ( 1 << 0x14)) >> 14) << 0x4) + ((($t0 & ( 1 << 0x13)) >> 13) << 0x3) + ((($t0 & ( 1 << 0x12)) >> 12) << 0x2) + ((($t0 & ( 1 << 0x11)) >> 11) << 0x1) + ((($t0 & ( 1 << 0x10)) >> 10) << 0x0);r $t0=poi(poi(@esp+8)+0x12);.printf "ByteSize=%d\nParity=%d\nStopBits=%d\nXonChar='%C'\nXoffChar='%C'\nErrorChar='%C'\nEofChar='%C'\nEvtChar='%C'\n",((($t0 & ( 1 << 0x7)) >> 7) << 0x7) + ((($t0 & ( 1 << 0x6)) >> 6) << 0x6) + ((($t0 & ( 1 << 0x5)) >> 5) << 0x5) + ((($t0 & ( 1 << 0x4)) >> 4) << 0x4) + ((($t0 & ( 1 << 0x3)) >> 3) << 0x3) + ((($t0 & ( 1 << 0x2)) >> 2) << 0x2) + ((($t0 & ( 1 << 0x1)) >> 1) << 0x1) + ((($t0 & ( 1 << 0x0)) >> 0) << 0x0),((($t0 & ( 1 << 0xF)) >> F) << 0x7) + ((($t0 & ( 1 << 0xE)) >> E) << 0x6) + ((($t0 & ( 1 << 0xD)) >> D) << 0x5) + ((($t0 & ( 1 << 0xC)) >> C) << 0x4) + ((($t0 & ( 1 << 0xB)) >> B) << 0x3) + ((($t0 & ( 1 << 0xA)) >> A) << 0x2) + ((($t0 & ( 1 << 0x9)) >> 9) << 0x1) + ((($t0 & ( 1 << 0x8)) >> 8) << 0x0),((($t0 & ( 1 << 0x17)) >> 17) << 0x7) + ((($t0 & ( 1 << 0x16)) >> 16) << 0x6) + ((($t0 & ( 1 << 0x15)) >> 15) << 0x5) + ((($t0 & ( 1 << 0x14)) >> 14) << 0x4) + ((($t0 & ( 1 << 0x13)) >> 13) << 0x3) + ((($t0 & ( 1 << 0x12)) >> 12) << 0x2) + ((($t0 & ( 1 << 0x11)) >> 11) << 0x1) + ((($t0 & ( 1 << 0x10)) >> 10) << 0x0),poi(poi(@esp+8)+0x15),poi(poi(@esp+8)+0x16),poi(poi(@esp+8)+0x17),poi(poi(@esp+8)+0x18),poi(poi(@esp+8)+0x19)
DCBlength=28
BaudRate=9600
fBinary=1
fParity=0
fOutxCtsFlow=1
fOutxDsrFlow=0
fDtrControl=2
fDsrSensitivity=1
fTXContinueOnXoff=0
fOutX=1
fInX=0
fErrorChar=1
fNull=0
fRtsControl=3
fAbortOnError=1
XonLim=123
XoffLim=456
ByteSize=8
Parity=4
StopBits=2
XonChar=''
XoffChar='-'
ErrorChar='C'
EofChar='Y'
EvtChar='b'

We can also split a 32-bit number stored in $t0 into four separate 1 byte values using this approach, allowing us to print an 8-bit value as a number. And provides an alternative in older versions of WinDbg which don’t support %C for ASCII char output.

0:000> r $t0 = poi(poi(@esp+8)+0x15)
0:000> r $t1 = ((($t0 & ( 1 << 0x7)) >> 7) << 0x7) + ((($t0 & ( 1 << 0x6)) >> 6) << 0x6) + ((($t0 & ( 1 << 0x5)) >> 5) << 0x5) + ((($t0 & ( 1 << 0x4)) >> 4) << 0x4) + ((($t0 & ( 1 << 0x3)) >> 3) << 0x3) + ((($t0 & ( 1 << 0x2)) >> 2) << 0x2) + ((($t0 & ( 1 << 0x1)) >> 1) << 0x1) + ((($t0 & ( 1 << 0x0)) >> 0) << 0x0);r $t2 = ((($t0 & ( 1 << 0xF)) >> F) << 0x7) + ((($t0 & ( 1 << 0xE)) >> E) << 0x6) + ((($t0 & ( 1 << 0xD)) >> D) << 0x5) + ((($t0 & ( 1 << 0xC)) >> C) << 0x4) + ((($t0 & ( 1 << 0xB)) >> B) << 0x3) + ((($t0 & ( 1 << 0xA)) >> A) << 0x2) + ((($t0 & ( 1 << 0x9)) >> 9) << 0x1) + ((($t0 & ( 1 << 0x8)) >> 8) << 0x0);r $t3 = ((($t0 & ( 1 << 0x17)) >> 17) << 0x7) + ((($t0 & ( 1 << 0x16)) >> 16) << 0x6) + ((($t0 & ( 1 << 0x15)) >> 15) << 0x5) + ((($t0 & ( 1 << 0x14)) >> 14) << 0x4) + ((($t0 & ( 1 << 0x13)) >> 13) << 0x3) + ((($t0 & ( 1 << 0x12)) >> 12) << 0x2) + ((($t0 & ( 1 << 0x11)) >> 11) << 0x1) + ((($t0 & ( 1 << 0x10)) >> 10) << 0x0);r $t4 = ((($t0 & ( 1 << 0x1F)) >> 1F) << 0x7) + ((($t0 & ( 1 << 0x1E)) >> 1E) << 0x6) + ((($t0 & ( 1 << 0x1D)) >> 1D) << 0x5) + ((($t0 & ( 1 << 0x1C)) >> 1C) << 0x4) + ((($t0 & ( 1 << 0x1B)) >> 1B) << 0x3) + ((($t0 & ( 1 << 0x1A)) >> 1A) << 0x2) + ((($t0 & ( 1 << 0x19)) >> 19) << 0x1) + ((($t0 & ( 1 << 0x18)) >> 18) << 0x0)
0:000> .printf "%i, %i, %i, %i",$t1,$t2,$t3,$t4
23, 45, 67, 89
Posted in Uncategorized | Leave a comment

Adding A Pause Between Items in Config.NT / Config.Sys

I wanted to debug startup of a 16-bit DOS driver on 32-bit Windows 10 with NTVDM, however attempts to attach debugger / Time Travel Debugging Trace to NTVDM startup process was triggering access violations and causing NTVDM.exe to crash. Once NTVDM had started I could attach debugger fine, but was missing the driver startup code I wanted to capture.

MS-DOS 6.00 added a feature where F8 could be pressed to run autoexec.bat/config.sys entries one line at a time, but I haven’t found an alternative that works with c:\windows\system32\config.nt in Windows.

In this case using Microsoft Macro Assmbler built this driver with the following commands:

masm wait.asm
link wait
exe2bin wait.exe wait.sys
xcopy wait.sys C:\windows\system32

The code is here, this can also be used a template for a simple MS-DOS driver.

; *******************************************************************
; * Press Any Key To Continue DRIVER                                *
; *******************************************************************

cseg        segment para    public  'code'
wait        proc    far
            assume  cs:cseg,es:cseg,ds:cseg

; *******************************************************************
; * MAIN PROCEDURE CODE                                             *
; *******************************************************************

begin:

; *******************************************************************
; * DEVICE HEADER - REQUIRED BY DOS                                 *
; *******************************************************************

next_dev    dd  -1              ; no other device drivers
attribute   dw  8000h           ; character device
strategy    dw  dev_strategy    ; address of 1st dos call
interrupt   dw  dev_interrupt   ; address of 2nd dos call
dev_name    db  'WAIT$ '      ; name of the driver

; *******************************************************************
; * WORK SPACE FOR THE DEVICE DRIVER                                *
; *******************************************************************

rh_ofs      dw  ?               ; request header offset
rh_seg      dw  ?               ; request header segment
msg1        db  'Waiting...'
            db  0dh,0ah,'$'
seconds     db 0   
counter     db 0  
crlf	    db 0dh,0ah,'$'
; *******************************************************************
; * THE STRATEGY PROCEDURE                                          *
; *******************************************************************

dev_strategy:                   ; first call from DOS
    mov     cs:rh_seg,es        ; save request header ptr segment
    mov     cs:rh_ofs,bx        ; save request header ptr offset
    ret

; *******************************************************************
; * THE INTERRUPT PROCEDURE                                         *
; *******************************************************************

dev_interrupt:                  ; second call from DOS
    cld                         ; save machine state on entry
    push    ds
    push    es
    push    ax
    push    bx
    push    cx  
    push    dx
    push    di
    push    si

; perform branch based on the command passed in the req header

    mov     al,es:[bx]+2        ; get command code
    cmp     al,0                ; check for 0
    jnz     exit3               ; no - exit go to error exit
    rol     al,1                ; get offset into table
    lea     di,cmdtab           ; get address of command table
    mov     ah,0                ; clear hi order
    add     di,ax               ; add offset
    jmp     word ptr[di]        ; jump indirect

; command table
;       the command code field of the static request
;       field contains the function to be performed

cmdtab  label   byte            ;
        dw      init            ; initialization

; *******************************************************************
; *     LOCAL PROCEDURES                                            *
; *******************************************************************

initial proc    near
    lea     dx,msg1             ; initialization
    mov     ah,9                ; message
    int     21h                 ; dos call
    mov     al,30               ; number of seconds to wait
    call    sleep
    ret                         ; return
initial endp

; *******************************************************************
; *     DOS COMMAND PROCESSING                                      *
; *******************************************************************

;command    0   initialization

init:   call    initial         ; display a message
        lea     ax,exit         ; get end address (offset)
        mov     es:[bx]+0eh,ax  ; store offset address
        push    cs              ; get end
        pop     ax              ; address (segment)
        mov     es:[bx]+10h,ax  ; store in break address
        jmp     exit2

; *******************************************************************
; *     ERROR EXIT                                                  *
; *******************************************************************

; Set the done flag, error flag, and unknown command error code

exit3:  mov     es:word ptr 3[bx],8103h
        jmp     exit1                   ; restore environment

; *******************************************************************
; *     COMMON EXIT                                                 *
; *******************************************************************

; common exits fall thru code
;   2 sets status to done and no error
;   1 restore callers es:bx
;   0 restore machine state and exit

exit2:                                  ; set done flag and no error
        mov     es:word ptr 3[bx],0100h
exit1:  mov     bx,cs:rh_ofs            ; restore req hdr to bx and es
        mov     es,cs:rh_seg            ; as saved by dev_Strategy
exit0:  pop     si                      ; restore all registers
        pop     di
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        pop     es
        pop     ds
        ret
exit:

; *******************************************************************
; *     END OF PROGRAM                                              *
; *******************************************************************


wait    endp

sleep proc    near

wait_for_al_seconds:      
wait_loop:
    push ax               ; save our counter (al)
    mov     [counter],al
loop_top:
    mov ah,2
    int 1ah             ; get time

    mov ah, [seconds]   ; retrieve last good value
    cmp ah, dh          ; is it same as last good value?
    jz  loop_top         ; yup, ignore it, loop again!

    mov [seconds], dh   ; save seconds

    ; display counter - can handle range of 0-99
    mov	al, [counter]   ; retrieve counter
    cbw                 ; set AH to 0
    mov  dl, 10
    div  dl             ; Divides AX by 10: quotient in al, remainder in ah
    add  ax, "00"
    mov  dx, ax         
    mov  ah, 02h        ; Display 1st digit of counter
    int  21h
    mov  dl, dh          
    int  21h            ; Display 2nd digit of counter

    
    lea     dx,crlf             ; display carriage return
    mov     ah,9                
    int     21h                 

    pop ax              
    dec al              ; decrease al by one (does not set flags!!)
    or al,al            ; set flags
    jnz wait_loop       ; al=0?  nope, around we go again!

    ret                 ; 
sleep   endp


cseg        ends
            end     begin

; that's all folks!


Now we can add line to C:\Windows\System32\config.nt to load our driver where we want it to pause:

DEVICE=%SystemRoot%\System32\wait.sys

To test all existing ntvdm.exe process must be terminated, as config.sys is only loaded when a new ntvdm.exe instance is created. Now when launching a 16-bit DOS application you will see a count down for 30 seconds when this line of config.nt has been hit:

Posted in Uncategorized | 1 Comment

Case of the Windows 11 SystemSettings.exe Crash

Collecting user mode dumps with dumptype set to 2 via Windows Error Reporting registry configuration as documented here

Noticed two SystemSettings.exe crashes, both with similar stack traces. The following information was logged in the Windows application event log:

Faulting application name: SystemSettings.exe, version: 10.0.22000.348, time stamp: 0x27a6d211
Faulting module name: MusUpdateHandlers.dll, version: 10.0.22000.348, time stamp: 0x5aa0c31b
Exception code: 0xc0000005
Fault offset: 0x0000000000092185
Faulting process id: 0x53c0
Faulting application start time: 0x01d802f49e9d7598
Faulting application path: C:\Windows\ImmersiveControlPanel\SystemSettings.exe
Faulting module path: C:\Windows\System32\MusUpdateHandlers.dll
Report Id: 75fce6a5-9ef4-42a1-a895-83bc2a6b6c9b
Faulting package full name: windows.immersivecontrolpanel_10.0.6.1000_neutral_neutral_cw5n1h2txyewy
Faulting package-relative application ID: microsoft.windows.immersivecontrolpanel

Normally some quick potential correlation may be found with Reliability Monitor to see if issues started occurring after specific system change, however possibly due to a major update installing it seems all noted crashes/changes/etc made to system on these dates earlier in the month when crash occurred no longer had any information available.

Initial analysis pointed to culprit being a null reference exception in MusUpdateHandlers.dll which is the Modern Update Settings Handler Implementation.

0:018> !analyze -v
*******************************************************************************
*                                                                             *
*                        Exception Analysis                                   *
*                                                                             *
*******************************************************************************


KEY_VALUES_STRING: 1

    Key  : AV.Dereference
    Value: NullPtr

    Key  : AV.Fault
    Value: Read

    Key  : Analysis.CPU.mSec
    Value: 2827

    Key  : Analysis.DebugAnalysisManager
    Value: Create

    Key  : Analysis.Elapsed.mSec
    Value: 12646

    Key  : Analysis.Init.CPU.mSec
    Value: 265

    Key  : Analysis.Init.Elapsed.mSec
    Value: 6821

    Key  : Analysis.Memory.CommitPeak.Mb
    Value: 285

    Key  : Timeline.OS.Boot.DeltaSec
    Value: 201950

    Key  : Timeline.Process.Start.DeltaSec
    Value: 201231

    Key  : WER.OS.Branch
    Value: co_release

    Key  : WER.OS.Timestamp
    Value: 2021-06-04T16:28:00Z

    Key  : WER.OS.Version
    Value: 10.0.22000.1

    Key  : WER.Process.Version
    Value: 10.0.22000.348


FILE_IN_CAB:  SystemSettings.exe.16748.dmp

NTGLOBALFLAG:  400

PROCESS_BAM_CURRENT_THROTTLED: 0

PROCESS_BAM_PREVIOUS_THROTTLED: 0

APPLICATION_VERIFIER_FLAGS:  0

CONTEXT:  (.ecxr)
rax=00007107f20fec8f rbx=0000004b595ff744 rcx=0000000000000000
rdx=0000004b595ff744 rsi=0000000000000002 rdi=0000000000000001
rip=00007ffe2ebd2185 rsp=0000004b595ff680 rbp=0000004b595ff7d0
 r8=0000000000000001  r9=0000000000000001 r10=0000000000009100
r11=0000004b595ff6b0 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=00007ffe2ec40328
iopl=0         nv up ei pl nz na po nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206
MusUpdateHandlers!SystemSettings::Update::CMusOrchModel::GetSeekerUXDisplayRank+0x55:
00007ffe`2ebd2185 488b01          mov     rax,qword ptr [rcx] ds:00000000`00000000=????????????????
Resetting default scope

EXCEPTION_RECORD:  (.exr -1)
ExceptionAddress: 00007ffe2ebd2185 (MusUpdateHandlers!SystemSettings::Update::CMusOrchModel::GetSeekerUXDisplayRank+0x0000000000000055)
   ExceptionCode: c0000005 (Access violation)
  ExceptionFlags: 00000000
NumberParameters: 2
   Parameter[0]: 0000000000000000
   Parameter[1]: 0000000000000000
Attempt to read from address 0000000000000000

PROCESS_NAME:  SystemSettings.exe

READ_ADDRESS:  0000000000000000 

ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%p referenced memory at 0x%p. The memory could not be %s.

EXCEPTION_CODE_STR:  c0000005

EXCEPTION_PARAMETER1:  0000000000000000

EXCEPTION_PARAMETER2:  0000000000000000

STACK_TEXT:  
0000004b`595ff680 00007ffe`2eb8a93c     : 0000015a`dec14700 0000015a`dec14700 0000015a`dec14700 00000000`00000001 : MusUpdateHandlers!SystemSettings::Update::CMusOrchModel::GetSeekerUXDisplayRank+0x55
0000004b`595ff6d0 00007ffe`2eb6b013     : 0000015a`e0ca8110 0000015a`e0ca8110 0000015a`e0ca8110 00000000`00000000 : MusUpdateHandlers!SystemSettings::Update::CMusSeekerUpdate::InitializeState+0x38c
0000004b`595ff850 00007ffe`2eb6c0ee     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : MusUpdateHandlers!<lambda_547ec51b376960035aba27ef737bbd82>::operator()+0x257
0000004b`595ff960 00007ffe`8ad36c0c     : 0000015a`e565e280 00000000`00000000 00000000`00000000 00000000`00000000 : MusUpdateHandlers!std::thread::_Invoke<std::tuple<<lambda_547ec51b376960035aba27ef737bbd82> >,0>+0xe
0000004b`595ff990 00007ffe`8bd454e0     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ucrtbase!thread_start<unsigned int (__cdecl*)(void *),1>+0x4c
0000004b`595ff9c0 00007ffe`8cfa485b     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : kernel32!BaseThreadInitThunk+0x10
0000004b`595ff9f0 00000000`00000000     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x2b


SYMBOL_NAME:  MusUpdateHandlers!SystemSettings::Update::CMusOrchModel::GetSeekerUXDisplayRank+55

MODULE_NAME: MusUpdateHandlers

IMAGE_NAME:  MusUpdateHandlers.dll

STACK_COMMAND:  ~18s ; .ecxr ; kb

FAILURE_BUCKET_ID:  NULL_POINTER_READ_c0000005_MusUpdateHandlers.dll!SystemSettings::Update::CMusOrchModel::GetSeekerUXDisplayRank

OS_VERSION:  10.0.22000.1

BUILDLAB_STR:  co_release

OSPLATFORM_TYPE:  x64

OSNAME:  Windows 10

IMAGE_VERSION:  10.0.22000.348

FAILURE_ID_HASH:  {39aeb642-c99a-f672-5c69-96a915aec9c5}

Followup:     MachineOwner

The crash seems to be related to a class CMusOrchModel

What is that? Checking all references to the string in IDA pro we find related methods but the symbol names are mangled i.e in a format like ??_E?$_Ref_count_obj2@VCMusOrchModel@Update@SystemSettings@@@std@@UEAAPEAXI@Z so all search results are copied and pasted into an online GCC/MSVC C++ demangler here http://demangler.com/

This shows us function names involved:

public: virtual void * __ptr64 __cdecl std::_Ref_count_obj2<class SystemSettings::Update::CMusOrchModel>::`vector deleting destructor'(unsigned int) __ptr64
public: virtual void * __ptr64 __cdecl SystemSettings::Update::CMusOrchModel::`scalar deleting destructor'(unsigned int) __ptr64
public: __cdecl SystemSettings::Update::CMusOrchModel::CMusOrchModel(void) __ptr64
public: virtual __cdecl SystemSettings::Update::CMusOrchModel::~CMusOrchModel(void) __ptr64
private: virtual void __cdecl std::_Ref_count_obj2<class SystemSettings::Update::CMusOrchModel>::_Destroy(void) __ptr64
protected: long __cdecl SystemSettings::Update::CMusOrchModel::AcceptAllUpdateEulas(void) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::AddSeekerUpdateToApprovalList(class wil::com_ptr_t<struct IMoUsoUpdate,struct wil::err_returncode_policy>) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::AddSeekerUpdateToApprovalList(class std::shared_ptr<class UxUsoUpdateShim>) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::ApproveAllSeekerUpdatesFromApprovalList(void) __ptr64
protected: long __cdecl SystemSettings::Update::CMusOrchModel::ApproveSeekerFeatureUpdateForInstall(void) __ptr64
protected: long __cdecl SystemSettings::Update::CMusOrchModel::ApproveSeekerQualityUpdateForInstall(void) __ptr64
public: BOOL __cdecl SystemSettings::Update::CMusOrchModel::AreUpdatesPaused(void) __ptr64
public: BOOL __cdecl SystemSettings::Update::CMusOrchModel::AreUpdatesPausedByPolicy(void) __ptr64
public: BOOL __cdecl SystemSettings::Update::CMusOrchModel::AreUsoObjectsInitialized(void) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::CanExtendPauseUpdates(unsigned long,int * __ptr64) __ptr64
public: BOOL __cdecl SystemSettings::Update::CMusOrchModel::CanPauseUpdates(void) __ptr64
protected: long __cdecl SystemSettings::Update::CMusOrchModel::CreateNotifyPropertyChangedThread(enum SystemSettings::Update::UXUpdateReason) __ptr64
protected: long __cdecl SystemSettings::Update::CMusOrchModel::CreateUpdateResultsTaskSchedule(void) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::DecrementPauseUpdates(unsigned long) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::DoesRebootScheduleExist(BOOL * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::ExtendPauseUpdates(unsigned long) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::FixServiceUnavailable(void) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::get_ActiveHours(unsigned short * __ptr64,unsigned short * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::get_ActiveHoursIntervalLimit(unsigned short * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::get_ApplicableUpdates(class std::vector<class wil::com_ptr_t<struct IMoUsoUpdate,struct wil::err_returncode_policy>,class std::allocator<class wil::com_ptr_t<struct IMoUsoUpdate,struct wil::err_returncode_policy> > > & __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::get_ApplicableUpdates(class std::vector<class std::shared_ptr<class UxUsoUpdateShim>,class std::allocator<class std::shared_ptr<class UxUsoUpdateShim> > > & __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::get_ApplicableUpdatesPayloadInfo(struct PayloadInfo * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::get_ApprovedSeekerUpdates(class std::vector<class wil::com_ptr_t<struct IMoUsoUpdate,struct wil::err_returncode_policy>,class std::allocator<class wil::com_ptr_t<struct IMoUsoUpdate,struct wil::err_returncode_policy> > > & __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::get_ApprovedSeekerUpdates(class std::vector<class std::shared_ptr<class UxUsoUpdateShim>,class std::allocator<class std::shared_ptr<class UxUsoUpdateShim> > > & __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::get_CanScheduleUpdate(struct PayloadInfo & __ptr64,BOOL * __ptr64) __ptr64
public: enum NormalizedPolicy __cdecl SystemSettings::Update::CMusOrchModel::get_EnforcedAuPolicy(void) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::get_OptInToMu(BOOL * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::get_RebootSchedule(struct _SYSTEMTIME * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::get_SchedulePickerOption(enum SchedulePickerOption * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::get_SeekerUpdates(class std::vector<class wil::com_ptr_t<struct IMoUsoUpdate,struct wil::err_returncode_policy>,class std::allocator<class wil::com_ptr_t<struct IMoUsoUpdate,struct wil::err_returncode_policy> > > & __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::get_SeekerUpdates(class std::vector<class std::shared_ptr<class UxUsoUpdateShim>,class std::allocator<class std::shared_ptr<class UxUsoUpdateShim> > > & __ptr64) __ptr64
public: enum UxSettingType __cdecl SystemSettings::Update::CMusOrchModel::get_UpdateUxOption(void) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::get_UserChoiceActiveHoursEnd(BOOL * __ptr64,unsigned long * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::get_UserChoiceActiveHoursStart(BOOL * __ptr64,unsigned long * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::GetApprovedSeekerUpdatesCounts(unsigned long * __ptr64,unsigned long * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::GetAvailableUpdateStatusCounts(unsigned long * __ptr64,unsigned long * __ptr64,unsigned long * __ptr64,unsigned long * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::GetCompatBlockInfo(class std::optional<class std::basic_string<unsigned short,struct std::char_traits<unsigned short>,class std::allocator<unsigned short> > > & __ptr64,class std::optional<class std::vector<class std::basic_string<unsigned short,struct std::char_traits<unsigned short>,class std::allocator<unsigned short> >,class std::allocator<class std::basic_string<unsigned short,struct std::char_traits<unsigned short>,class std::allocator<unsigned short> > > > > & __ptr64,class std::optional<unsigned int> & __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::GetDaysSinceRebootRequired(unsigned long * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::GetDefaultRebootScheduleTime(struct _SYSTEMTIME * __ptr64,struct _FILETIME,struct _FILETIME) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::GetDeviceEosStatus(BOOL * __ptr64,BOOL * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::GetDowntimeEstimateInfo(unsigned long * __ptr64,int * __ptr64) __ptr64
public: static class std::shared_ptr<class SystemSettings::Update::CMusOrchModel> __cdecl SystemSettings::Update::CMusOrchModel::GetInstanceShared(void)
public: BOOL __cdecl SystemSettings::Update::CMusOrchModel::GetIsSingletonDeinitializing(void) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::GetMaximumAllowedPauseDays(unsigned long * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::GetMgmtDefaultScheduleTime(struct _SYSTEMTIME * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::GetOptionsForUpdateNotificationLevelPolicy(enum UpdateNotificationOption * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::GetOrchModelShimInstance(class std::shared_ptr<class SystemSettings::Update::OrchModelShim> & __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::GetPauseUpdatesExpiryTime(struct _FILETIME * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::GetPolicyValue(enum NormalizedPolicy,enum tagUpdatePolicyStatus * __ptr64,struct tagVARIANT * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::GetSeekerFeatureUpdateBuildNumber(class std::optional<unsigned int> & __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::GetSeekerUpdatesCounts(unsigned long * __ptr64,unsigned long * __ptr64,unsigned long * __ptr64,unsigned long * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::GetSeekerUpdateTitle(unsigned short * __ptr64 * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::GetSeekerUXDisplayRank(unsigned long * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::GetUpdateHistoryDefinition(class std::vector<class wil::com_ptr_t<struct IUsoUpdateHistoryEntry,struct wil::err_returncode_policy>,class std::allocator<class wil::com_ptr_t<struct IUsoUpdateHistoryEntry,struct wil::err_returncode_policy> > > & __ptr64,struct _FILETIME * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::GetUpdateHistoryDriver(class std::vector<class wil::com_ptr_t<struct IUsoUpdateHistoryEntry,struct wil::err_returncode_policy>,class std::allocator<class wil::com_ptr_t<struct IUsoUpdateHistoryEntry,struct wil::err_returncode_policy> > > & __ptr64,struct _FILETIME * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::GetUpdateHistoryFeature(class std::vector<class wil::com_ptr_t<struct IUsoUpdateHistoryEntry,struct wil::err_returncode_policy>,class std::allocator<class wil::com_ptr_t<struct IUsoUpdateHistoryEntry,struct wil::err_returncode_policy> > > & __ptr64,struct _FILETIME * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::GetUpdateHistoryOther(class std::vector<class wil::com_ptr_t<struct IUsoUpdateHistoryEntry,struct wil::err_returncode_policy>,class std::allocator<class wil::com_ptr_t<struct IUsoUpdateHistoryEntry,struct wil::err_returncode_policy> > > & __ptr64,struct _FILETIME * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::GetUpdateHistoryQuality(class std::vector<class wil::com_ptr_t<struct IUsoUpdateHistoryEntry,struct wil::err_returncode_policy>,class std::allocator<class wil::com_ptr_t<struct IUsoUpdateHistoryEntry,struct wil::err_returncode_policy> > > & __ptr64,struct _FILETIME * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::GetUpdatePayloadSize(enum tagUsoUpdatePayloadType,unsigned __int64 * __ptr64,unsigned __int64 * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::GetUXElementStoreForSurface(enum UXSurface,class UXElementStore * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::GetValidScheduleRange(struct _FILETIME * __ptr64,struct _FILETIME * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::GetValidScheduleRangeWithFallback(struct _FILETIME * __ptr64,struct _FILETIME * __ptr64,BOOL) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::GetWOSCOneSettingsInstance(struct IUxOneSettings * __ptr64 * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::InitializeUpdateHistory(void) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::InvokeAction(struct HWND__ * __ptr64,enum SystemSettings::Update::MusActionType const & __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::InvokeReboot(BOOL,unsigned long * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::IsActiveHourIntervalValid(unsigned short,unsigned short,BOOL * __ptr64) __ptr64
public: BOOL __cdecl SystemSettings::Update::CMusOrchModel::IsActiveHoursUXApplicable(void) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::IsAutoApproveSeekerQualityUpdatesEnabled(BOOL * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::IsAutoRestartDeadlinePolicyConfigured(BOOL * __ptr64) __ptr64
public: BOOL __cdecl SystemSettings::Update::CMusOrchModel::IsCTA(void) __ptr64
public: BOOL __cdecl SystemSettings::Update::CMusOrchModel::IsDirectEngagedReboot(void) __ptr64
public: BOOL __cdecl SystemSettings::Update::CMusOrchModel::IsDisableUXAccessPolicyEnabled(void) __ptr64
public: BOOL __cdecl SystemSettings::Update::CMusOrchModel::IsEngagedRebootAllowedByPolicy(void) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::IsEngagedRestartDeadlinePolicyConfigured(BOOL * __ptr64) __ptr64
public: BOOL __cdecl SystemSettings::Update::CMusOrchModel::IsFeatureUpdatePausedByPolicy(void) __ptr64
public: BOOL __cdecl SystemSettings::Update::CMusOrchModel::IsGraceDeadlinePolicyConfigured(void) __ptr64
public: BOOL __cdecl SystemSettings::Update::CMusOrchModel::IsMgmtPolicyValidForSchedulingReboot(void) __ptr64
public: BOOL __cdecl SystemSettings::Update::CMusOrchModel::IsNotifyToRebootPolicyApplicable(void) __ptr64
public: BOOL __cdecl SystemSettings::Update::CMusOrchModel::IsPolicyConfigured(enum NormalizedPolicy) __ptr64
public: BOOL __cdecl SystemSettings::Update::CMusOrchModel::IsPolicyConfiguredAndEnabled(enum NormalizedPolicy) __ptr64
public: BOOL __cdecl SystemSettings::Update::CMusOrchModel::IsPolicyConfiguredToMapToAutomaticReboot(void) __ptr64
public: BOOL __cdecl SystemSettings::Update::CMusOrchModel::IsQualityUpdatePausedByPolicy(void) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::IsRebootRequired(BOOL * __ptr64) __ptr64
public: BOOL __cdecl SystemSettings::Update::CMusOrchModel::IsRestartForced(void) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::IsSeekerFeatureOrQualityUpdatesAvailable(BOOL * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::IsSeekerHighCompatMessageEnabled(BOOL * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::IsSeekerOnDemandUxEnabled(BOOL * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::IsSeekerUpdateInApprovalList(class wil::com_ptr_t<struct IMoUsoUpdate,struct wil::err_returncode_policy>,BOOL * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::IsSeekerUpdateInApprovalList(class std::shared_ptr<class UxUsoUpdateShim>,BOOL * __ptr64) __ptr64
public: BOOL __cdecl SystemSettings::Update::CMusOrchModel::IsSihUpdatePendingReboot(void) __ptr64
public: BOOL __cdecl SystemSettings::Update::CMusOrchModel::IsSmartActiveHoursSuggestionNeeded(void) __ptr64
public: BOOL __cdecl SystemSettings::Update::CMusOrchModel::IsUpdateErrorIgnorable(long) __ptr64
public: BOOL __cdecl SystemSettings::Update::CMusOrchModel::IsUSOAvailable(void) __ptr64
public: BOOL __cdecl SystemSettings::Update::CMusOrchModel::IsUXCampaignApplicable(enum UXSurface) __ptr64
public: BOOL __cdecl SystemSettings::Update::CMusOrchModel::IsWindowsInsiderAttentionNeeded(void) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::LoadDynamicElementById(enum UXSurface,enum UXElementType,unsigned int,struct HSTRING__ * __ptr64 * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::LoadDynamicUXStringById(unsigned int,struct HSTRING__ * __ptr64 * __ptr64) __ptr64
public: static long __cdecl SystemSettings::Update::CMusOrchModel::LocalizeWsxUpdateTitle(class std::basic_string_view<unsigned short,struct std::char_traits<unsigned short> > const & __ptr64,unsigned short * __ptr64 * __ptr64)
public: void __cdecl SystemSettings::Update::CMusOrchModel::NotifyInit(class SystemSettings::DataModel::CSingletonHelper<struct SystemSettings::Update::MusNotification>::CCallback * __ptr64) __ptr64
public: void __cdecl SystemSettings::Update::CMusOrchModel::NotifyPropertyChanged(enum SystemSettings::Update::UXUpdateReason) __ptr64
protected: virtual void __cdecl SystemSettings::Update::CMusOrchModel::OnAsyncInitComplete(void) __ptr64
protected: virtual void __cdecl SystemSettings::Update::CMusOrchModel::OnSingletonDeinit(void) __ptr64
protected: virtual long __cdecl SystemSettings::Update::CMusOrchModel::OnSingletonInit(void) __ptr64
protected: void __cdecl SystemSettings::Update::CMusOrchModel::OrchestratorUpdateCallback(char const & __ptr64,enum SystemSettings::Update::UXUpdateReason) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::PauseUpdates(unsigned long) __ptr64
protected: static void __cdecl SystemSettings::Update::CMusOrchModel::RefreshElementStoresCallback(struct _TP_CALLBACK_INSTANCE * __ptr64,void * __ptr64,struct _TP_TIMER * __ptr64)
protected: long __cdecl SystemSettings::Update::CMusOrchModel::RefreshSeekerSessionState(BOOL * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::RemoveSeekerUpdateFromApprovalList(class wil::com_ptr_t<struct IMoUsoUpdate,struct wil::err_returncode_policy>) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::RemoveSeekerUpdateFromApprovalList(class std::shared_ptr<class UxUsoUpdateShim>) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::ResumeUpdates(void) __ptr64
private: long __cdecl SystemSettings::Update::CMusOrchModel::RunElevatedInstall(struct HWND__ * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::ScheduleReboot(struct _SYSTEMTIME,enum SchedulePickerOption) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::SendCTAApprovedData(void) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::set_ActiveHoursEnd(unsigned short) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::set_ActiveHoursStart(unsigned short) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::set_DoMicrosoftScan(BOOL) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::set_SchedulePickerOption(enum SchedulePickerOption) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::set_UserChoiceActiveHoursEnd(unsigned long) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::set_UserChoiceActiveHoursStart(unsigned long) __ptr64
protected: void __cdecl SystemSettings::Update::CMusOrchModel::SingletonDeinitialize(void) __ptr64
protected: long __cdecl SystemSettings::Update::CMusOrchModel::SingletonInitialize(void) __ptr64
protected: void __cdecl SystemSettings::Update::CMusOrchModel::StopTracing(void) __ptr64

The crash seems to have occured in function “GetSeekerUXDisplayRank” which is called by “CMusSeekerUpdate::InitializeState

References to seeker:

class WRL::Details::ComPtr<class SystemSettings::Update::CMusSeekerOnDemand> __cdecl Microsoft::WRL::Details::V::Make(void)
class WRL::Details::ComPtr<class SystemSettings::Update::CMusSeekerUpdate> __cdecl Microsoft::WRL::Details::V::Make(void)
private: long __cdecl SystemSettings::Update::CMusSeekerOnDemand::InitiateSeekerUpdateTitle(void) __ptr64
private: long __cdecl SystemSettings::Update::CMusSeekerOnDemand::MoInitiateSeekerUpdateTitle(void) __ptr64
private: long __cdecl SystemSettings::Update::CMusSeekerUpdate::InitiateSeekerUpdateTitle(void) __ptr64
private: long __cdecl SystemSettings::Update::CMusSeekerUpdate::InitiateWhatsNewUrl(void) __ptr64
private: long __cdecl UxUsoShim::GetNonSeekerOrApprovedUpdates(class std::vector<class wil::com_ptr_t<struct IMoUsoUpdate,struct wil::err_returncode_policy>,class std::allocator<class wil::com_ptr_t<struct IMoUsoUpdate,struct wil::err_returncode_policy> > > & __ptr64) __ptr64
protected: long __cdecl SystemSettings::Update::CMusOrchModel::ApproveSeekerFeatureUpdateForInstall(void) __ptr64
protected: long __cdecl SystemSettings::Update::CMusOrchModel::ApproveSeekerQualityUpdateForInstall(void) __ptr64
protected: long __cdecl SystemSettings::Update::CMusOrchModel::RefreshSeekerSessionState(BOOL * __ptr64) __ptr64
protected: virtual long __cdecl SystemSettings::Update::CMusSeekerOnDemand::InitializeState(struct SystemSettings::Update::MusNotification) __ptr64
protected: virtual long __cdecl SystemSettings::Update::CMusSeekerOnDemand::Invoke(struct HWND__ * __ptr64) __ptr64
protected: virtual long __cdecl SystemSettings::Update::CMusSeekerUpdate::InitializeState(struct SystemSettings::Update::MusNotification) __ptr64
protected: virtual long __cdecl SystemSettings::Update::CMusSeekerUpdate::Invoke(struct HWND__ * __ptr64) __ptr64
protected: virtual void __cdecl SystemSettings::Update::CMusSeekerUpdate::RaiseValueChangedEvents(void) __ptr64
public: __cdecl Microsoft::WRL::Details::MakeAllocator<class SystemSettings::Update::CMusSeekerOnDemand>::~MakeAllocator<class SystemSettings::Update::CMusSeekerOnDemand>(void) __ptr64
public: __cdecl SystemSettings::Update::CMusSeekerOnDemand::CMusSeekerOnDemand(void) __ptr64
public: __cdecl SystemSettings::Update::CMusSeekerUpdate::CMusSeekerUpdate(void) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::AddSeekerUpdateToApprovalList(class std::shared_ptr<class UxUsoUpdateShim>) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::AddSeekerUpdateToApprovalList(class wil::com_ptr_t<struct IMoUsoUpdate,struct wil::err_returncode_policy>) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::ApproveAllSeekerUpdatesFromApprovalList(void) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::get_ApprovedSeekerUpdates(class std::vector<class std::shared_ptr<class UxUsoUpdateShim>,class std::allocator<class std::shared_ptr<class UxUsoUpdateShim> > > & __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::get_ApprovedSeekerUpdates(class std::vector<class wil::com_ptr_t<struct IMoUsoUpdate,struct wil::err_returncode_policy>,class std::allocator<class wil::com_ptr_t<struct IMoUsoUpdate,struct wil::err_returncode_policy> > > & __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::get_SeekerUpdates(class std::vector<class std::shared_ptr<class UxUsoUpdateShim>,class std::allocator<class std::shared_ptr<class UxUsoUpdateShim> > > & __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::get_SeekerUpdates(class std::vector<class wil::com_ptr_t<struct IMoUsoUpdate,struct wil::err_returncode_policy>,class std::allocator<class wil::com_ptr_t<struct IMoUsoUpdate,struct wil::err_returncode_policy> > > & __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::GetApprovedSeekerUpdatesCounts(unsigned long * __ptr64,unsigned long * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::GetSeekerFeatureUpdateBuildNumber(class std::optional<unsigned int> & __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::GetSeekerUpdatesCounts(unsigned long * __ptr64,unsigned long * __ptr64,unsigned long * __ptr64,unsigned long * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::GetSeekerUpdateTitle(unsigned short * __ptr64 * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::GetSeekerUXDisplayRank(unsigned long * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::IsAutoApproveSeekerQualityUpdatesEnabled(BOOL * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::IsSeekerFeatureOrQualityUpdatesAvailable(BOOL * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::IsSeekerHighCompatMessageEnabled(BOOL * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::IsSeekerOnDemandUxEnabled(BOOL * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::IsSeekerUpdateInApprovalList(class std::shared_ptr<class UxUsoUpdateShim>,BOOL * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::IsSeekerUpdateInApprovalList(class wil::com_ptr_t<struct IMoUsoUpdate,struct wil::err_returncode_policy>,BOOL * __ptr64) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::RemoveSeekerUpdateFromApprovalList(class std::shared_ptr<class UxUsoUpdateShim>) __ptr64
public: long __cdecl SystemSettings::Update::CMusOrchModel::RemoveSeekerUpdateFromApprovalList(class wil::com_ptr_t<struct IMoUsoUpdate,struct wil::err_returncode_policy>) __ptr64
public: long __cdecl UxUsoShim::GetApplicableSeekerUpdates(class std::vector<class std::shared_ptr<class UxUsoUpdateShim>,class std::allocator<class std::shared_ptr<class UxUsoUpdateShim> > > & __ptr64) __ptr64
public: long __cdecl UxUsoShim::GetApprovedSeekerUpdates(class std::vector<class std::shared_ptr<class UxUsoUpdateShim>,class std::allocator<class std::shared_ptr<class UxUsoUpdateShim> > > & __ptr64) __ptr64
public: static long __cdecl SystemSettings::Update::CMusSeekerOnDemand::CreateInstance(struct SystemSettings::DataModel::SettingDBItem const * __ptr64,struct SystemSettings::DataModel::ISettingItem * __ptr64 * __ptr64)
public: static long __cdecl SystemSettings::Update::CMusSeekerUpdate::CreateInstance(struct SystemSettings::DataModel::SettingDBItem const * __ptr64,struct SystemSettings::DataModel::ISettingItem * __ptr64 * __ptr64)
public: static long __cdecl UpdateUtil::GetApprovedSeekerUpdatesCount(class UxUsoShim * __ptr64,unsigned long * __ptr64,unsigned long * __ptr64)
public: virtual __cdecl SystemSettings::Update::CMusSeekerOnDemand::~CMusSeekerOnDemand(void) __ptr64
public: virtual __cdecl SystemSettings::Update::CMusSeekerUpdate::~CMusSeekerUpdate(void) __ptr64
public: virtual long __cdecl SystemSettings::Update::CMusSeekerOnDemand::get_Description(struct HSTRING__ * __ptr64 * __ptr64) __ptr64
public: virtual long __cdecl SystemSettings::Update::CMusSeekerOnDemand::get_IsEnabled(unsigned char * __ptr64) __ptr64
public: virtual long __cdecl SystemSettings::Update::CMusSeekerOnDemand::GetProperty(struct HSTRING__ * __ptr64,struct IInspectable * __ptr64 * __ptr64) __ptr64
public: virtual long __cdecl SystemSettings::Update::CMusSeekerUpdate::get_Description(struct HSTRING__ * __ptr64 * __ptr64) __ptr64
public: virtual long __cdecl SystemSettings::Update::CMusSeekerUpdate::get_IsApplicable(unsigned char * __ptr64) __ptr64
public: virtual long __cdecl SystemSettings::Update::CMusSeekerUpdate::GetProperty(struct HSTRING__ * __ptr64,struct IInspectable * __ptr64 * __ptr64) __ptr64
public: virtual long __cdecl SystemSettings::Update::MoOrchModelShim::ApproveSeekerQualityUpdateForInstall(void) __ptr64
public: virtual long __cdecl SystemSettings::Update::MoOrchModelShim::GetApprovedSeekerUpdates(class std::vector<class std::shared_ptr<class UxUsoUpdateShim>,class std::allocator<class std::shared_ptr<class UxUsoUpdateShim> > > & __ptr64) __ptr64
public: virtual long __cdecl SystemSettings::Update::MoOrchModelShim::GetApprovedSeekerUpdates(class std::vector<class wil::com_ptr_t<struct IMoUsoUpdate,struct wil::err_returncode_policy>,class std::allocator<class wil::com_ptr_t<struct IMoUsoUpdate,struct wil::err_returncode_policy> > > & __ptr64) __ptr64
public: virtual long __cdecl SystemSettings::Update::MoOrchModelShim::GetApprovedSeekerUpdatesCount(unsigned long * __ptr64,unsigned long * __ptr64) __ptr64
public: virtual long __cdecl SystemSettings::Update::MoOrchModelShim::GetNonSelectableSeekerUpdates(class std::vector<class wil::com_ptr_t<struct IMoUsoUpdate,struct wil::err_returncode_policy>,class std::allocator<class wil::com_ptr_t<struct IMoUsoUpdate,struct wil::err_returncode_policy> > > & __ptr64) __ptr64
public: virtual long __cdecl SystemSettings::Update::MoOrchModelShim::GetSeekerSession(BOOL * __ptr64,BOOL * __ptr64) __ptr64
public: virtual long __cdecl SystemSettings::Update::MoOrchModelShim::GetSelectableSeekerUpdates(class std::vector<class wil::com_ptr_t<struct IMoUsoUpdate,struct wil::err_returncode_policy>,class std::allocator<class wil::com_ptr_t<struct IMoUsoUpdate,struct wil::err_returncode_policy> > > & __ptr64) __ptr64
public: virtual long __cdecl SystemSettings::Update::MoOrchModelShim::SetSeekerSession(BOOL) __ptr64
public: virtual long __cdecl SystemSettings::Update::OldOrchModelShim::ApproveSeekerQualityUpdateForInstall(void) __ptr64
public: virtual long __cdecl SystemSettings::Update::OldOrchModelShim::GetApprovedSeekerUpdates(class std::vector<class std::shared_ptr<class UxUsoUpdateShim>,class std::allocator<class std::shared_ptr<class UxUsoUpdateShim> > > & __ptr64) __ptr64
public: virtual long __cdecl SystemSettings::Update::OldOrchModelShim::GetApprovedSeekerUpdatesCount(unsigned long * __ptr64,unsigned long * __ptr64) __ptr64
public: virtual long __cdecl SystemSettings::Update::OldOrchModelShim::GetNonSelectableSeekerUpdates(class std::vector<class wil::com_ptr_t<struct IMoUsoUpdate,struct wil::err_returncode_policy>,class std::allocator<class wil::com_ptr_t<struct IMoUsoUpdate,struct wil::err_returncode_policy> > > & __ptr64) __ptr64
public: virtual long __cdecl SystemSettings::Update::OldOrchModelShim::GetSeekerSession(BOOL * __ptr64,BOOL * __ptr64) __ptr64
public: virtual long __cdecl SystemSettings::Update::OldOrchModelShim::GetSelectableSeekerUpdates(class std::vector<class std::shared_ptr<class UxUsoUpdateShim>,class std::allocator<class std::shared_ptr<class UxUsoUpdateShim> > > & __ptr64) __ptr64
public: virtual long __cdecl SystemSettings::Update::OldOrchModelShim::GetSelectableSeekerUpdates(class std::vector<class wil::com_ptr_t<struct IMoUsoUpdate,struct wil::err_returncode_policy>,class std::allocator<class wil::com_ptr_t<struct IMoUsoUpdate,struct wil::err_returncode_policy> > > & __ptr64) __ptr64
public: virtual long __cdecl SystemSettings::Update::OldOrchModelShim::SetSeekerSession(BOOL) __ptr64
public: virtual void * __ptr64 __cdecl SystemSettings::Update::CMusSeekerOnDemand::`vector deleting destructor'(unsigned int) __ptr64
public: virtual void * __ptr64 __cdecl SystemSettings::Update::CMusSeekerUpdate::`vector deleting destructor'(unsigned int) __ptr64


What is the process for this CMusSeekerUpdate::InitializeState function?

  1. A call to SystemSettings::Update::CMusSettings::InitializeState which checks for pending reboot.

It checks for pending reboot via SystemSettings::Update::CMusOrchModel::IsSihUpdatePendingReboot

I believe SIH in this case is referring to “Server Initiated Healing” which includes C:\Windows\System32\SIHClient.exe from checking C:\Windows\System32\en-US\SIHClient.exe.mui we find the following text:

This daily task launches the SIH client (server-initiated healing) to detect and fix system components that are vital to automatic updating of Windows and Microsoft software installed on the machine. This task can go online, evaluate applicability of healing actions, download necessary payloads to execute the actions, and execute healing actions.
This boot task launches the SIH client to finish executing healing actions to fix the system components vital to automatic updating of Windows and Microsoft software installed on the machine. It is enabled only when the daily SIH client task fails to complete execution of applicable healing actions. This boot task never goes online and does not evaluate applicability of healing actions.

(Note: SIH can also refer to Shell Infrastructure Host which is C:\Windows\System32\SIHost.exe)

IsSihUpdatePendingReboot calls UsoConfiguration::GetConfiguration(L”UsoServicingStack”, etc) which uses an internal function RegistryManager::GetHKLMValueOrDefault to retrieve a key with name UpdateOrchestratorConfigurationRoot

If GetConfiguration does not return a value of 1 IsSihUpdatePendingReboot exits immediately.

Otherwise it continues can calls RegistryManager::HKLMValueExists(L”Sih”, L”\UpdateStaged”, L”StagingTimeStamp”, x);

Finally IsSihUpdatePendingReboot checks SystemSettings::Update::OtaIsPendingExclusiveContent by calling GetUpdateResultsEx in UpdateAPI.dll.

OtaIsPendingExclusiveContent is true when GetUpdateResultsEx is 0 or greater.

Initialize state then calls SystemSettings::Update::CMusOrchModel::IsUSOAvailable

Here USO refers to Update Session Orchestrator (USO) which you can read about here

It checks if USO is available by checking Update Orchestrator Service (USOSvc) is available service and running.

There is then a check for SystemSettings::Update::CMusOrchModel::FixServiceUnavailable which seems to potentially update some telemetry and other stuff.

The total InitializeState function has some logic like this:

__int64 __fastcall SystemSettings::Update::CMusSettings<SystemSettings::DataModel::CActionSetting>::InitializeState(
        __int64 a1)
{
  bool IsSihUpdatePendingReboot; // bl
  char v3; // si

  IsSihUpdatePendingReboot = SystemSettings::Update::CMusOrchModel::IsSihUpdatePendingReboot(*(SystemSettings::Update::CMusOrchModel **)(a1 + 248));
  if ( !SystemSettings::Update::CMusOrchModel::IsUSOAvailable(*(SystemSettings::Update::CMusOrchModel **)(a1 + 248))
    || IsSihUpdatePendingReboot )
  {
    v3 = 0;
    if ( !IsSihUpdatePendingReboot )
      SystemSettings::Update::CMusOrchModel::FixServiceUnavailable(*(SystemSettings::Update::CMusOrchModel **)(a1 + 248));
  }
  else
  {
    v3 = 1;
  }
  EnterCriticalSection((LPCRITICAL_SECTION)(a1 + 312));
  *(_BYTE *)(a1 + 264) = v3;
  if ( a1 != -312 )
    LeaveCriticalSection((LPCRITICAL_SECTION)(a1 + 312));
  return 0i64;
}

2. Some configuration is checked related to “allow scan map” and “SeekerOnDemandScanOverride

 v7 = (char *)&SystemSettings::Update::CMusSeekerUpdate::sc_rgupeMap;
  v8 = 0i64;
  v9 = *(_DWORD *)a2;
  v10 = &SystemSettings::Update::CMusSeekerUpdate::sc_rgupeMap;
  v11 = 1;
  while ( *v10 != v9 )
  {
    v8 = (unsigned int)(v8 + 1);
    ++v10;
    if ( (unsigned int)v8 >= 0xA )
      goto LABEL_9;
  }
  v7 = (char *)&SystemSettings::Update::CMusSeekerUpdate::sc_rgupeMap + 4 * v8;
LABEL_9:
  v12 = (char *)&SystemSettings::Update::CMusSeekerUpdate::sc_rgupeAllowScanMap;
  v13 = 0i64;
  v14 = &SystemSettings::Update::CMusSeekerUpdate::sc_rgupeAllowScanMap;
  while ( *v14 != v9 )
  {
    v13 = (unsigned int)(v13 + 1);
    ++v14;
    if ( (unsigned int)v13 >= 0xB )
      goto LABEL_14;
  }
  v12 = (char *)&SystemSettings::Update::CMusSeekerUpdate::sc_rgupeAllowScanMap + 4 * v13;
LABEL_14:
  v35 = &SystemSettings::Update::CMusSeekerUpdate::sc_rgupeAllowScanMap != (_UNKNOWN *)v12;
  HKLMValueOr = RegistryManager::GetHKLMValueOrDefault<unsigned long>(
                  L"WindowsUpdateUXRoot",
                  L"\\TestHooks",
                  L"SeekerOnDemandScanOverride",
                  1);
  if ( HKLMValueOr < 0 )
  {
    v5 = 6871i64;
    goto LABEL_3;
  }
  if ( !v58[1] )
  {
    v15 = MusUpdateLogging::Provider();
    if ( *(_DWORD *)v15 > 4u )
    {
      v51 = (__int64)"Seeker on demand override is set so scan is not allowed immediately";
      _tlgWriteTemplate<long (_tlgProvider_t const *,void const *,_GUID const *,_GUID const *,unsigned int,_EVENT_DATA_DESCRIPTOR *),&long _tlgWriteTransfer_EventWriteTransfer(_tlgProvider_t const *,void const *,_GUID const *,_GUID const *,unsigned int,_EVENT_DATA_DESCRIPTOR *),_GUID const *,_GUID const *>::Write<_tlgWrapSz<char>>(
        (int)v15,
        (__int64)&v51);
    }
    v35 = 0;
  }

3. Some functions are called to check if UX access is blocked and if updates are paused i.e.

IsDisableUXAccessPolicyEnabled = SystemSettings::Update::CMusOrchModel::IsDisableUXAccessPolicyEnabled(*(SystemSettings::Update::CMusOrchModel **)(a1 + 248));
  if ( SystemSettings::Update::CMusOrchModel::AreUpdatesPaused(*(SystemSettings::Update::CMusOrchModel **)(a1 + 248))
    || (v16 = SystemSettings::Update::CMusOrchModel::AreUpdatesPausedByPolicy(*(SystemSettings::Update::CMusOrchModel **)(a1 + 248)),
        v33 = 0,
        v16) )
  {
    v33 = 1;
  }

4. Get Seeker Update counts are retrieved

HKLMValueOr = SystemSettings::Update::CMusOrchModel::GetSeekerUpdatesCounts(
                  *(SystemSettings::Update::CMusOrchModel **)(a1 + 248),
                  &v46[1],
                  &v47,
                  &v44,
                  &v45);
  if ( HKLMValueOr < 0 )
  {
    v5 = 6894i64;
    goto LABEL_3;
  }
  v17 = v46[1];
  v39 = v46[1] != 0;
  v18 = v47;
  v34 = v47 != 0;
  v19 = v45;
  if ( v44 || (v36 = 0, v45) )
    v36 = 1;
  v56 = 0;
  v57 = 0;
  HKLMValueOr = SystemSettings::Update::CMusOrchModel::GetApprovedSeekerUpdatesCounts(
                  *(SystemSettings::Update::CMusOrchModel **)(a1 + 248),
                  &v56,
                  &v57);
  if ( HKLMValueOr < 0 )
  {
    v5 = 6904i64;
    goto LABEL_3;
  }

5. Configuration is checked if quality updates are auto approved

 if ( v34 )
  {
    HKLMValueOr = SystemSettings::Update::CMusOrchModel::IsAutoApproveSeekerQualityUpdatesEnabled(
                    *(SystemSettings::Update::CMusOrchModel **)(a1 + 248),
                    &v32);
    if ( HKLMValueOr < 0 )
    {
      v5 = 6933i64;
      goto LABEL_3;
    }
    v23 = v32;
  }

6. We to spot where crash occurs, GetSeekerDisplayUXRank

 IsUXCampaignApplicable = 0;
  HKLMValueOr = SystemSettings::Update::CMusOrchModel::GetSeekerUXDisplayRank(
                  *(SystemSettings::Update::CMusOrchModel **)(a1 + 248),
                  v46);
  if ( HKLMValueOr < 0 )
  {
    v5 = 6938i64;
    goto LABEL_3;
  }
  v25 = v46[0];
  if ( v46[0] != 1 )
    IsUXCampaignApplicable = SystemSettings::Update::CMusOrchModel::IsUXCampaignApplicable(*(_QWORD *)(a1 + 248), 1i64);
  HKLMValueOr = SystemSettings::Update::CMusOrchModel::IsSeekerOnDemandUxEnabled(
                  *(SystemSettings::Update::CMusOrchModel **)(a1 + 248),
                  &v40);
  if ( HKLMValueOr < 0 )
  {
    v5 = 6948i64;
    goto LABEL_3;
  }
  v32 = 0;
  v37 = 0;
  HKLMValueOr = SystemSettings::Update::CMusOrchModel::GetDeviceEosStatus(
                  *(SystemSettings::Update::CMusOrchModel **)(a1 + 248),
                  &v32,
                  &v37);
  if ( HKLMValueOr < 0 )
  {
    v5 = 6954i64;
    goto LABEL_3;
  }
  HKLMValueOr = SystemSettings::Update::CMusOrchModel::IsSeekerHighCompatMessageEnabled(
                  *(SystemSettings::Update::CMusOrchModel **)(a1 + 248),
                  &v43);
  if ( HKLMValueOr < 0 )
  {
    v5 = 6957i64;
    goto LABEL_3;
  }

The crash occurs because a function pointer is null when attempting to call _guard_xfg_dispatch_icall_fptr

__int64 __fastcall SystemSettings::Update::CMusOrchModel::GetSeekerUXDisplayRank(
        SystemSettings::Update::CMusOrchModel *this,
        unsigned int *a2)
{
  __int64 v4; // rcx
  int v5; // eax
  unsigned int v6; // edi
  int v7; // [rsp+20h] [rbp-28h]
  unsigned int v8; // [rsp+30h] [rbp-18h] BYREF
  wil::details::in1diag3 *retaddr; // [rsp+48h] [rbp+0h]

  if ( a2 )
  {
    v4 = *((_QWORD *)this + 212);
    v8 = 1;
    v5 = _guard_xfg_dispatch_icall_fptr(v4, 90i64, 1i64, &v8);

This function is an Xtended Flow Guard (XFG) function generated by the compiler, a good introduction to this is here

It seems like the reference to this function has been overwritten with 0s preventing, causing the null reference when attempting to reference a pointer to the targeted function, although I am current unsure as to what actually caused this problem.

Posted in Uncategorized | Leave a comment