Unable to Connect to iSCSI Targets with Authentication Type in PowerShell

Attempting to connect to iSCSI targets in PowerShell was failing when specifying Authentication Type. The authentication type was being specified as per Microsoft documentation here :

-AuthenticationType.
Specifies the type of authentication to use when logging into the target. The acceptable values for this parameter are:
  • None
  • OneWayCHAP
  • MutualCHAP

The default value is None.

Let’s check how the command makes a connection. Using PowerShell cmd

Get-Command -Type All -Name Connect-IscsiTarget | Format-List *

We can see in ScriptBlock it is using WMI Microsoft.Management.Infrastructure.CimInstance#MSFT_iSCSISession

Using cmd

find "MSFT_iSCSISession" C:\windows\system32\wbem\*.mof

We can see this is defined in MOF file C:\WINDOWS\SYSTEM32\WBEM\ISCSIWMIV2.MOF
We can open the MOF file in a text editor and note the GUID here

ClsId = “{52C84ACA-027A-4536-A74A-E0BB50C44782}”;

We can now use this to find the DLL that implements the WMI method:

C:\Windows>reg query hkcr\clsid\{52C84ACA-027A-4536-A74A-E0BB50C44782}\InProcServer32

HKEY_CLASSES_ROOT\clsid\{52C84ACA-027A-4536-A74A-E0BB50C44782}\InProcServer32
    (Default)    REG_EXPAND_SZ    %systemroot%\system32\iscsiwmiv2.dll
    ThreadingModel    REG_SZ    Both

Analysis of DLL %systemroot%\system32\iscsiwmiv2.dll shows the authentication types are all in upper case.

  • NONE
  • ONEWAYCHAP
  • MUTUALCHAP

.text:1000E437 loc_1000E437: ; CODE XREF: MSFT_iSCSITarget_Invoke_Connect(x,x,x,x,x,x,x)+27Bj
.text:1000E437 ; MSFT_iSCSITarget_Invoke_Connect(x,x,x,x,x,x,x)+280j
.text:1000E437 cmp byte ptr [ebx+54h], 0
.text:1000E43B jz loc_1000E50F
.text:1000E441 mov edx, [ebx+50h]
.text:1000E444 or eax, 80h
.text:1000E449 mov [esp+0B38h+var_B10], eax
.text:1000E44D mov esi, offset Str2 ; “NONE”
.text:1000E452 mov [esp+0B38h+Memory], edi
.text:1000E456 mov ecx, edx
.text:1000E458
.text:1000E458 loc_1000E458: ; CODE XREF: MSFT_iSCSITarget_Invoke_Connect(x,x,x,x,x,x,x)+2C8j
.text:1000E458 mov di, [ecx]
.text:1000E45B cmp di, [esi]
.text:1000E45E jnz short loc_1000E480
.text:1000E460 test di, di
.text:1000E463 jz short loc_1000E47A
.text:1000E465 mov di, [ecx+2]
.text:1000E469 cmp di, [esi+2]
.text:1000E46D jnz short loc_1000E480
.text:1000E46F add ecx, 4
.text:1000E472 add esi, 4
.text:1000E475 test di, di
.text:1000E478 jnz short loc_1000E458
.text:1000E47A
.text:1000E47A loc_1000E47A: ; CODE XREF: MSFT_iSCSITarget_Invoke_Connect(x,x,x,x,x,x,x)+2B3j
.text:1000E47A xor esi, esi
.text:1000E47C mov ecx, esi
.text:1000E47E jmp short loc_1000E487
.text:1000E480 ; —————————————————————————
.text:1000E480
.text:1000E480 loc_1000E480: ; CODE XREF: MSFT_iSCSITarget_Invoke_Connect(x,x,x,x,x,x,x)+2AEj
.text:1000E480 ; MSFT_iSCSITarget_Invoke_Connect(x,x,x,x,x,x,x)+2BDj
.text:1000E480 sbb ecx, ecx
.text:1000E482 or ecx, 1
.text:1000E485 xor esi, esi
.text:1000E487
.text:1000E487 loc_1000E487: ; CODE XREF: MSFT_iSCSITarget_Invoke_Connect(x,x,x,x,x,x,x)+2CEj
.text:1000E487 neg ecx
.text:1000E489 sbb ecx, ecx
.text:1000E48B and ecx, esi
.text:1000E48D mov esi, offset aMutualchap ; “MUTUALCHAP”
.text:1000E492 mov [esp+0B38h+Memory], ecx
.text:1000E496 mov ecx, edx
.text:1000E498
.text:1000E498 loc_1000E498: ; CODE XREF: MSFT_iSCSITarget_Invoke_Connect(x,x,x,x,x,x,x)+308j
.text:1000E498 mov di, [ecx]
.text:1000E49B cmp di, [esi]
.text:1000E49E jnz short loc_1000E4C0
.text:1000E4A0 test di, di
.text:1000E4A3 jz short loc_1000E4BA
.text:1000E4A5 mov di, [ecx+2]
.text:1000E4A9 cmp di, [esi+2]
.text:1000E4AD jnz short loc_1000E4C0
.text:1000E4AF add ecx, 4
.text:1000E4B2 add esi, 4
.text:1000E4B5 test di, di
.text:1000E4B8 jnz short loc_1000E498
.text:1000E4BA
.text:1000E4BA loc_1000E4BA: ; CODE XREF: MSFT_iSCSITarget_Invoke_Connect(x,x,x,x,x,x,x)+2F3j
.text:1000E4BA xor edi, edi
.text:1000E4BC mov ecx, edi
.text:1000E4BE jmp short loc_1000E4C7
.text:1000E4C0 ; —————————————————————————
.text:1000E4C0
.text:1000E4C0 loc_1000E4C0: ; CODE XREF: MSFT_iSCSITarget_Invoke_Connect(x,x,x,x,x,x,x)+2EEj
.text:1000E4C0 ; MSFT_iSCSITarget_Invoke_Connect(x,x,x,x,x,x,x)+2FDj
.text:1000E4C0 sbb ecx, ecx
.text:1000E4C2 or ecx, 1
.text:1000E4C5 xor edi, edi
.text:1000E4C7
.text:1000E4C7 loc_1000E4C7: ; CODE XREF: MSFT_iSCSITarget_Invoke_Connect(x,x,x,x,x,x,x)+30Ej
.text:1000E4C7 test ecx, ecx
.text:1000E4C9 jnz short loc_1000E4D3
.text:1000E4CB mov [esp+0B38h+Memory], 2
.text:1000E4D3
.text:1000E4D3 loc_1000E4D3: ; CODE XREF: MSFT_iSCSITarget_Invoke_Connect(x,x,x,x,x,x,x)+319j
.text:1000E4D3 mov ecx, offset aOnewaychap ; “ONEWAYCHAP”
.text:1000E4D8
.text:1000E4D8 loc_1000E4D8: ; CODE XREF: MSFT_iSCSITarget_Invoke_Connect(x,x,x,x,x,x,x)+348j
.text:1000E4D8 mov si, [edx]
.text:1000E4DB cmp si, [ecx]
.text:1000E4DE jnz short loc_1000E4FE
.text:1000E4E0 test si, si
.text:1000E4E3 jz short loc_1000E4FA
.text:1000E4E5 mov si, [edx+2]
.text:1000E4E9 cmp si, [ecx+2]
.text:1000E4ED jnz short loc_1000E4FE
.text:1000E4EF add edx, 4
.text:1000E4F2 add ecx, 4
.text:1000E4F5 test si, si
.text:1000E4F8 jnz short loc_1000E4D8
.text:1000E4FA
.text:1000E4FA loc_1000E4FA: ; CODE XREF: MSFT_iSCSITarget_Invoke_Connect(x,x,x,x,x,x,x)+333j
.text:1000E4FA mov ecx, edi
.text:1000E4FC jmp short loc_1000E503
.text:1000E4FE ; —————————————————————————
.text:1000E4FE
.text:1000E4FE loc_1000E4FE: ; CODE XREF: MSFT_iSCSITarget_Invoke_Connect(x,x,x,x,x,x,x)+32Ej
.text:1000E4FE ; MSFT_iSCSITarget_Invoke_Connect(x,x,x,x,x,x,x)+33Dj
.text:1000E4FE sbb ecx, ecx
.text:1000E500 or ecx, 1
.text:1000E503
.text:1000E503 loc_1000E503: ; CODE XREF: MSFT_iSCSITarget_Invoke_Connect(x,x,x,x,x,x,x)+34Cj
.text:1000E503 test ecx, ecx
.text:1000E505 jnz short loc_1000E50F
.text:1000E507 mov [esp+0B38h+Memory], 1
.text:1000E50F
.text:1000E50F loc_1000E50F: ; CODE XREF: MSFT_iSCSITarget_Invoke_Connect(x,x,x,x,x,x,x)+28Bj
.text:1000E50F ; MSFT_iSCSITarget_Invoke_Connect(x,x,x,x,x,x,x)+355j
.text:1000E50F cmp byte ptr [ebx+5Ch], 0
.text:1000E513 jz short loc_1000E54A
.text:1000E515 or eax, 20h
.text:1000E518 mov [esp+50h], edi
.text:1000E51C push ecx
.text:1000E51D mov ecx, [ebx+58h]
.text:1000E520 lea edx, [esp+54h]
.text:1000E524 mov [esp+0B3Ch+var_B10], eax
.text:1000E528 call _DiscpUnicodeToAnsi@12 ; DiscpUnicodeToAnsi(x,x,x)
.text:1000E52D mov ecx, [ebx+58h]
.text:1000E530 lea edx, [ecx+2]
.text:1000E533
.text:1000E533 loc_1000E533: ; CODE XREF: MSFT_iSCSITarget_Invoke_Connect(x,x,x,x,x,x,x)+38Cj
.text:1000E533 mov ax, [ecx]
.text:1000E536 add ecx, 2
.text:1000E539 cmp ax, di
.text:1000E53C jnz short loc_1000E533
.text:1000E53E mov eax, [esp+0B38h+var_B10]
.text:1000E542 sub ecx, edx
.text:1000E544 sar ecx, 1
.text:1000E546 mov dword ptr [esp+0B38h+var_AF0], ecx
.text:1000E54A
.text:1000E54A loc_1000E54A: ; CODE XREF: MSFT_iSCSITarget_Invoke_Connect(x,x,x,x,x,x,x)+363j
.text:1000E54A cmp byte ptr [ebx+64h], 0
.text:1000E54E jz short loc_1000E581
.text:1000E550 or eax, 40h
.text:1000E553 mov [esp+54h], edi
.text:1000E557 push ecx
.text:1000E558 mov ecx, [ebx+60h]
.text:1000E55B lea edx, [esp+58h]
.text:1000E55F mov [esp+0B3Ch+var_B10], eax
.text:1000E563 call _DiscpUnicodeToAnsi@12 ; DiscpUnicodeToAnsi(x,x,x)
.text:1000E568 mov ecx, [ebx+60h]
.text:1000E56B lea edx, [ecx+2]
.text:1000E56E
.text:1000E56E loc_1000E56E: ; CODE XREF: MSFT_iSCSITarget_Invoke_Connect(x,x,x,x,x,x,x)+3C7j
.text:1000E56E mov ax, [ecx]
.text:1000E571 add ecx, 2
.text:1000E574 cmp ax, di
.text:1000E577 jnz short loc_1000E56E
.text:1000E579 sub ecx, edx
.text:1000E57B sar ecx, 1
.text:1000E57D mov [esp+4Ch], ecx
.text:1000E581
.text:1000E581 loc_1000E581: ; CODE XREF: MSFT_iSCSITarget_Invoke_Connect(x,x,x,x,x,x,x)+39Ej
.text:1000E581 cmp byte ptr [ebx+4Bh], 0
.text:1000E585 mov al, 1
.text:1000E587 jz short loc_1000E58C
.text:1000E589 mov al, [ebx+4Ah]

Changing the PowerShell cmd to specify AuthenticationType in all upper case resolves the connection problem.

This can also be observed if running PowerShell cmd

Get-IscsiSession

And noting that AuthenticationType is always listed in UPPERCASE.

Posted in Uncategorized | Leave a comment

Extracting Office Upload Center Info with PowerShell

Office 2016 Upload Center stores data about currently syncing files and errors in an Access Database file (.accdb)

With Click2Run Office installed by default we can’t programatically query Microsoft Access database, so we need to install the Access Database engines. However with Click2Run Office installed these will fail to install with an error similar to this:

 You cannot install the 64-bit version of Microsoft Access Database Engine 2016 because you currently have 32-bit Office products installed. If you want to install the 64-bit Microsoft Access Datbase Engine 2016, you will first need to remove the 32-bit installation of Office products. After uninstalling the following product(s), rerun setup in order to install 64-bit version of Microsoft Access Database Engine 2016:
Office 16 Click-to-Run Extensibility Component

You will get a similar error if you try the 32-bit installer.

Download Microsoft Access Database Engine 2016 Redistributable from https://www.microsoft.com/en-us/download/details.aspx?id=54920

To successfully install with Click2Run Office isntalled use the /quiet switch. Run the following commands from an elevated command prompt.

For use with 64-bit PowerShell:

AccessDatabaseEngine_X64.exe /quiet

For use with 32-bit PowerShell:

AccessDatabaseEngine.exe /quiet

With the engine installed we can onw query the upload center datbase. The purpose of each database table and column is left as an excercise for the reader.

$path = Join-Path -Path $env:LOCALAPPDATA `
    -ChildPath "Microsoft\Office\16.0\OfficeFileCache\CentralTable.accdb"

$tables = @( 
    "CacheProperties", 
    "EventClients",
    "EventMetaInfo"
    "IncomingEvents",
    "MasterFile",
    "OutgoingEvents",
    "ServerTarget",
    "Subcache" )

$results = @{}
$conn = New-Object System.Data.Odbc.OdbcConnection
$conn.ConnectionString = `
    "Driver={Microsoft Access Driver (*.mdb, *.accdb)};Dbq=$path"

$conn.Open()
$cmd = New-Object System.Data.Odbc.OdbcCommand
$cmd.Connection = $conn
ForEach ($table in $tables)
{
    Write-Host "Reading $table"
    $cmd.CommandText = "SELECT * FROM $table"
    $reader = $cmd.ExecuteReader()
    $results[$table] = New-Object System.Data.DataTable
    $results[$table].Load($reader)
}

ForEach ($table in $tables)
{
    Write-Host "Saving to $($table).csv"
    $results[$table] | Export-Csv -NoTypeInformation -Path "$($table).csv"
}
Posted in Uncategorized | Leave a comment

PowerShell Script to Backup / Restore Clipboard Contents

When Windows programs save data to the clipboard, the data can be saved in multiple formats. Analysing this process is easy with API Monitor and monitoring the Clipboard APIs such as OpenClipboard, CloseClipboard, SetClipboardData, GetClipboardData. An example of using this to diagnose a clipboard problem is here

In investigating an application that on some machines when pasting selected an undesired format, despite the correct format also being on the clipboard, I wanted to analyse the clipboard contents more closely and repeat testing on my own test machine. As I didn’t have access to the source application that put the content onto the clipboard I wrote this PowerShell script which will save the clipboard contents for all formats into an XML file which can be restored at a later time to the clipboard.

Usage is:

Backup Clipboard

Save-Clipboard -Path C:\support\clipboard_data.xml

Restore Clipboard

Load-Clipboard -Path C:\support\clipboard_data.xml

This can also be used to analyse the raw contents of the clipboard if required.

Add-Type @'
    using System;
    using System.Text;
    using System.Runtime.InteropServices;
    using System.Collections.Generic;

public static class NativeMethods
{       

    [DllImport("user32.dll", SetLastError=true)]
    public static extern int SetClipboardData(uint uFormat, IntPtr hMem);

    [DllImport("user32.dll", SetLastError=true)]
    public static extern uint EnumClipboardFormats(uint format);

    [DllImport("user32.dll")]
    public static extern bool IsClipboardFormatAvailable(uint format);

    [DllImport("user32.dll")]
    public static extern IntPtr GetClipboardData(uint uFormat);

    [DllImport("user32.dll", SetLastError=true)]
    public static extern bool OpenClipboard(IntPtr hWndNewOwner);

    [DllImport("kernel32.dll")]
    public static extern IntPtr GlobalSize(IntPtr hMemory);

    [DllImport("kernel32.dll")]
    public static extern IntPtr GlobalLock(IntPtr hMem);

    [DllImport("user32.dll", SetLastError=true)]
    public static extern bool CloseClipboard();

    public static List GetClipboardFormats()
    {
        List formats = new List();
        uint currentFormat = 0;

        IntPtr Handle = IntPtr.Zero;
        OpenClipboard(Handle);

        do
        {
            currentFormat = EnumClipboardFormats(currentFormat);
            if (currentFormat!=0)
            {
                formats.Add(currentFormat);
            }
        } while (currentFormat != 0);
        return formats;
    }

    public static byte[] GetClipboardDataBytes(uint SelectedFormat)
    {
         if (SelectedFormat != 0)
         {
         IntPtr Handle = IntPtr.Zero;
         OpenClipboard(Handle);

         //Get pointer to clipboard data in the selected format
         IntPtr ClipboardDataPointer = GetClipboardData(SelectedFormat);

         //Do a bunch of crap necessary to copy the data from the memory
         //the above pointer points at to a place we can access it.
         IntPtr Length = GlobalSize(ClipboardDataPointer);
         IntPtr gLock = GlobalLock(ClipboardDataPointer);

         //Init a buffer which will contain the clipboard data
         byte[] Buffer = new byte[(int)Length];

         //Copy clipboard data to buffer
         Marshal.Copy(gLock, Buffer, 0, (int)Length);
         CloseClipboard();

         return Buffer;
         }

         return new byte[] {};
    }
}
'@

Function Load-Clipboard
{
    param([string]$Path)
    [xml]$Doc = Get-Content $Path
    $handle = [System.IntPtr]::Zero
    [NativeMethods]::OpenClipboard($handle)
    ForEach ($clipboard in $doc.Clipboards.Clipboard)
    {
        [UInt32]$format = $clipboard.Format
        $data = [Convert]::FromBase64String($clipboard.Data)
        Write-Host "Loading $($data.Length) bytes into clipboard format $format"
        $ptr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($data.Length)
        [void][System.Runtime.InteropServices.Marshal]::Copy($data, 0, $ptr, $data.Length);
        [NativeMethods]::SetClipboardData($format,$ptr)
        [System.Runtime.InteropServices.Marshal]::FreeHGlobal($ptr)

    }
    [NativeMethods]::CloseClipboard()

}

Function Save-Clipboard
{
    param([string]$Path)
    [xml]$Doc = New-Object System.Xml.XmlDocument
    $dec = $Doc.CreateXmlDeclaration("1.0","UTF-8",$null)
    [void]$doc.AppendChild($dec)
    [void]$doc.CreateComment("Clipboard Data from $($env:COMPUTERNAME) for $($env:USERNAME) on $(Get-Date)")
    $root = $doc.CreateNode("element","Clipboards",$null)
    $formats = [NativeMethods]::GetClipboardFormats()

    ForEach($format in $formats)
    {
        Write-Host "Capturing clipboard format $format"
        $data = [NativeMethods]::GetClipboardDataBytes($format)
        $c = $doc.CreateNode("element","Clipboard",$null)
        [void]$c.SetAttribute("Format",$format)
        [void]$c.SetAttribute("Data",[Convert]::ToBase64String($data))
        [void]$root.AppendChild($c)
    }

    $doc.AppendChild($root)
    $doc.Save($Path)
}
Posted in Uncategorized | Leave a comment

Time Travel Debugging in WinDbg Preview

The functionality similar to Microsoft’s internal Time Travel Tracing Diagnostic Tool has now added into WinDbg Preview, available only from Windows Store here

This version of WinDbg requires Windows 10 Anniversary update or later, and Windows Store access.

Time Travel Debugging is a powerful feature, allowing you to record program execution. Then you can go backwards or forwards through this execution to hit breakpoints or  perform stack analysis.

Documentation is available here

Note: You must use Run As Administrator when launching WinDbg to use Time Travel tracing.

Currently only user mode process is supported, not kernel debugging.

To test this out a friend launched a batch file that was failing, but couldn’t work out why.

They launched the application with WinDbg and selected “Record Process with Time Travel Debugging

You can also use Attach to process and select option to record process with time travel debugging.

image

Once the trace was created, they sent me the trace, and unlike a normal dmp file, I can now set breakpoints, and move forwards and backwards through the program execution with g and g- command.

Here I used tt to set time travel position, bp to set a breakpoint on SetLastError, !positions to show position in time travel trace, !error @r8 to translate error code passed to SetLastError into a human readable message, k for stack trace, and g to continue.

0:000> !tt 0
Setting position to the beginning of the trace
Setting position: 11:0
(6e00.14c4): Break instruction exception – code 80000003 (first/second chance not available)
Time Travel Position: 11:0
ntdll!NtQueryVolumeInformationFile+0x14:
00007ffb`a6ca5cc4 c3              ret
0:000> bp ntdll!RtlSetLastWin32Error “!positions;!error @r8;k;g”
0:000> g
>Thread ID=0x14C4 – Position: 5F:4EF
Error code: (Win32) 0 (0) – The operation completed successfully.
# Child-SP          RetAddr           Call Site
00 0000004b`8c94f048 00007ffb`a60ca93a ntdll!RtlSetLastWin32Error
01 0000004b`8c94f050 00007ffb`a6091040 msvcrt!getptd_noexit+0x6e
02 0000004b`8c94f080 00007ffb`a60eaa56 msvcrt!_LocaleUpdate::_LocaleUpdate+0x20
03 0000004b`8c94f0b0 00007ff7`aa04ffc6 msvcrt!wcsicmp_l+0x26
04 0000004b`8c94f110 00007ff7`aa04f5b9 cmd!FindAndFix+0x156
05 0000004b`8c94f3a0 00007ff7`aa04fcb5 cmd!FindFixAndRun+0x99
06 0000004b`8c94f840 00007ff7`aa05cf85 cmd!Dispatch+0xa5
07 0000004b`8c94f8d0 00007ff7`aa0568c9 cmd!_chkstk+0x5205
08 0000004b`8c94f970 00007ffb`a56f2774 cmd!wil::details_abi::ProcessLocalStorage<wil::details_abi::ProcessLocalData>::~ProcessLocalStorage<wil::details_abi::ProcessLocalData>+0x289
09 0000004b`8c94f9b0 00007ffb`a6c70d51 KERNEL32!BaseThreadInitThunk+0x14
0a 0000004b`8c94f9e0 00000000`00000000 ntdll!RtlUserThreadStart+0x21
>Thread ID=0x14C4 – Position: 90:1F
Error code: (NTSTATUS) 0x569a0150 (1452933456) – <Unable to get error code text>
# Child-SP          RetAddr           Call Site
00 0000004b`8c94ea98 00007ffb`a3354357 ntdll!RtlSetLastWin32Error
01 0000004b`8c94eaa0 00007ff7`aa0497f5 KERNELBASE!CreateDirectoryW+0x3d867
02 0000004b`8c94eb90 00007ff7`aa049aa7 cmd!MdWork+0xe5
03 0000004b`8c94ee10 00007ff7`aa04995a cmd!LoopThroughArgs+0x13f
04 0000004b`8c94f370 00007ff7`aa04f7d3 cmd!eMkdir+0x1a
05 0000004b`8c94f3a0 00007ff7`aa04fcb5 cmd!FindFixAndRun+0x2b3
06 0000004b`8c94f840 00007ff7`aa05cf85 cmd!Dispatch+0xa5
07 0000004b`8c94f8d0 00007ff7`aa0568c9 cmd!_chkstk+0x5205
08 0000004b`8c94f970 00007ffb`a56f2774 cmd!wil::details_abi::ProcessLocalStorage<wil::details_abi::ProcessLocalData>::~ProcessLocalStorage<wil::details_abi::ProcessLocalData>+0x289
09 0000004b`8c94f9b0 00007ffb`a6c70d51 KERNEL32!BaseThreadInitThunk+0x14
0a 0000004b`8c94f9e0 00000000`00000000 ntdll!RtlUserThreadStart+0x21
>Thread ID=0x14C4 – Position: B6:5CB
Error code: (Win32) 0xce (206) – The filename or extension is too long.
# Child-SP          RetAddr           Call Site
00 0000004b`8c94e7b8 00007ffb`a56f2d6e ntdll!RtlSetLastWin32Error
01 0000004b`8c94e7c0 00007ffb`a32e7ecd KERNEL32!BasepNotifyLoadStringResource+0x7e
02 0000004b`8c94e810 00007ffb`a32e7c97 KERNELBASE!BaseDllFormatMessage+0x22d
03 0000004b`8c94e940 00007ff7`aa0542c9 KERNELBASE!FormatMessageW+0x37
04 0000004b`8c94e990 00007ff7`aa054191 cmd!FindMsg+0x6d
05 0000004b`8c94ea90 00007ff7`aa05439c cmd!PutMsg+0x81
06 0000004b`8c94eb50 00007ff7`aa059ab2 cmd!PutStdErr+0x2c
07 0000004b`8c94eb90 00007ff7`aa049aa7 cmd!_chkstk+0x1d32
08 0000004b`8c94ee10 00007ff7`aa04995a cmd!LoopThroughArgs+0x13f
09 0000004b`8c94f370 00007ff7`aa04f7d3 cmd!eMkdir+0x1a
0a 0000004b`8c94f3a0 00007ff7`aa04fcb5 cmd!FindFixAndRun+0x2b3
0b 0000004b`8c94f840 00007ff7`aa05cf85 cmd!Dispatch+0xa5
0c 0000004b`8c94f8d0 00007ff7`aa0568c9 cmd!_chkstk+0x5205
0d 0000004b`8c94f970 00007ffb`a56f2774 cmd!wil::details_abi::ProcessLocalStorage<wil::details_abi::ProcessLocalData>::~ProcessLocalStorage<wil::details_abi::ProcessLocalData>+0x289
0e 0000004b`8c94f9b0 00007ffb`a6c70d51 KERNEL32!BaseThreadInitThunk+0x14
0f 0000004b`8c94f9e0 00000000`00000000 ntdll!RtlUserThreadStart+0x21
>Thread ID=0x14C4 – Position: B6:5E5
Error code: (Win32) 0xce (206) – The filename or extension is too long.
# Child-SP          RetAddr           Call Site
00 0000004b`8c94e7b8 00007ffb`a56f2d76 ntdll!RtlSetLastWin32Error
01 0000004b`8c94e7c0 00007ffb`a32e7ecd KERNEL32!BasepNotifyLoadStringResource+0x86
02 0000004b`8c94e810 00007ffb`a32e7c97 KERNELBASE!BaseDllFormatMessage+0x22d
03 0000004b`8c94e940 00007ff7`aa0542c9 KERNELBASE!FormatMessageW+0x37
04 0000004b`8c94e990 00007ff7`aa054191 cmd!FindMsg+0x6d
05 0000004b`8c94ea90 00007ff7`aa05439c cmd!PutMsg+0x81
06 0000004b`8c94eb50 00007ff7`aa059ab2 cmd!PutStdErr+0x2c
07 0000004b`8c94eb90 00007ff7`aa049aa7 cmd!_chkstk+0x1d32
08 0000004b`8c94ee10 00007ff7`aa04995a cmd!LoopThroughArgs+0x13f
09 0000004b`8c94f370 00007ff7`aa04f7d3 cmd!eMkdir+0x1a
0a 0000004b`8c94f3a0 00007ff7`aa04fcb5 cmd!FindFixAndRun+0x2b3
0b 0000004b`8c94f840 00007ff7`aa05cf85 cmd!Dispatch+0xa5
0c 0000004b`8c94f8d0 00007ff7`aa0568c9 cmd!_chkstk+0x5205
0d 0000004b`8c94f970 00007ffb`a56f2774 cmd!wil::details_abi::ProcessLocalStorage<wil::details_abi::ProcessLocalData>::~ProcessLocalStorage<wil::details_abi::ProcessLocalData>+0x289
0e 0000004b`8c94f9b0 00007ffb`a6c70d51 KERNEL32!BaseThreadInitThunk+0x14
0f 0000004b`8c94f9e0 00000000`00000000 ntdll!RtlUserThreadStart+0x21
>Thread ID=0x14C4 – Position: 13B:625
Error code: (Win32) 0 (0) – The operation completed successfully.
# Child-SP          RetAddr           Call Site
00 0000004b`8c94ee08 00007ffb`a60ca93a ntdll!RtlSetLastWin32Error
01 0000004b`8c94ee10 00007ffb`a6091040 msvcrt!getptd_noexit+0x6e
02 0000004b`8c94ee40 00007ffb`a60e50a2 msvcrt!_LocaleUpdate::_LocaleUpdate+0x20
03 0000004b`8c94ee70 00007ffb`a60da39c msvcrt!woutput_l+0x72
04 0000004b`8c94f380 00007ffb`a60da301 msvcrt!vsnwprintf_l+0x8c
05 0000004b`8c94f3f0 00007ff7`aa051c39 msvcrt!vsnwprintf+0x11
06 0000004b`8c94f430 00007ff7`aa051c03 cmd!StringCchPrintfW+0x79
07 0000004b`8c94f460 00007ff7`aa0685c4 cmd!StringCchPrintfW+0x43
08 0000004b`8c94f4a0 00007ff7`aa05b6ed cmd!PrintPrompt+0x230
09 0000004b`8c94f7a0 00007ff7`aa04ddbc cmd!_chkstk+0x396d
0a 0000004b`8c94f800 00007ff7`aa04d6b6 cmd!Lex+0x47c
0b 0000004b`8c94f870 00007ff7`aa04d3f8 cmd!GeToken+0x26
0c 0000004b`8c94f8a0 00007ff7`aa05cf4b cmd!Parser+0x118
0d 0000004b`8c94f8d0 00007ff7`aa0568c9 cmd!_chkstk+0x51cb
0e 0000004b`8c94f970 00007ffb`a56f2774 cmd!wil::details_abi::ProcessLocalStorage<wil::details_abi::ProcessLocalData>::~ProcessLocalStorage<wil::details_abi::ProcessLocalData>+0x289
0f 0000004b`8c94f9b0 00007ffb`a6c70d51 KERNEL32!BaseThreadInitThunk+0x14
10 0000004b`8c94f9e0 00000000`00000000 ntdll!RtlUserThreadStart+0x21
TTD: End of trace reached.
(6e00.14c4): Break instruction exception – code 80000003 (first/second chance not available)
Time Travel Position: 15D:0
ntdll!NtDeviceIoControlFile+0x12:
00007ffb`a6ca5482 0f05            syscall

Now I could click the time travel position, or type cmd tt B6:5E5 to go position of interest. We could then set further breakpoints, or trace instruction by instruction use t command.

The great thing is you can also set breakpoints and go backwards (g-) or start again to explore alternative debugging approach.

Continuing on…

0:000> !tt B6:5E5
Setting position: B6:5E5
(6e00.14c4): Break instruction exception – code 80000003 (first/second chance not available)
Time Travel Position: B6:5E5
ntdll!RtlSetLastWin32Error:
00007ffb`a6c5a630 894c2408        mov     dword ptr [rsp+8],ecx ss:0000004b`8c94e7c0=000000ce
0:000> k
# Child-SP          RetAddr           Call Site
00 0000004b`8c94e7b8 00007ffb`a56f2d76 ntdll!RtlSetLastWin32Error
01 0000004b`8c94e7c0 00007ffb`a32e7ecd KERNEL32!BasepNotifyLoadStringResource+0x86
02 0000004b`8c94e810 00007ffb`a32e7c97 KERNELBASE!BaseDllFormatMessage+0x22d
03 0000004b`8c94e940 00007ff7`aa0542c9 KERNELBASE!FormatMessageW+0x37
04 0000004b`8c94e990 00007ff7`aa054191 cmd!FindMsg+0x6d
05 0000004b`8c94ea90 00007ff7`aa05439c cmd!PutMsg+0x81
06 0000004b`8c94eb50 00007ff7`aa059ab2 cmd!PutStdErr+0x2c
07 0000004b`8c94eb90 00007ff7`aa049aa7 cmd!_chkstk+0x1d32
08 0000004b`8c94ee10 00007ff7`aa04995a cmd!LoopThroughArgs+0x13f
09 0000004b`8c94f370 00007ff7`aa04f7d3 cmd!eMkdir+0x1a
0a 0000004b`8c94f3a0 00007ff7`aa04fcb5 cmd!FindFixAndRun+0x2b3
0b 0000004b`8c94f840 00007ff7`aa05cf85 cmd!Dispatch+0xa5
0c 0000004b`8c94f8d0 00007ff7`aa0568c9 cmd!_chkstk+0x5205
0d 0000004b`8c94f970 00007ffb`a56f2774 cmd!wil::details_abi::ProcessLocalStorage<wil::details_abi::ProcessLocalData>::~ProcessLocalStorage<wil::details_abi::ProcessLocalData>+0x289
0e 0000004b`8c94f9b0 00007ffb`a6c70d51 KERNEL32!BaseThreadInitThunk+0x14
0f 0000004b`8c94f9e0 00000000`00000000 ntdll!RtlUserThreadStart+0x21
0:000> bp cmd!_chkstk+0x1d32
0:000> g
Breakpoint 2 hit
Time Travel Position: D6:42
cmd!_chkstk+0x1d32:
00007ff7`aa059ab2 90              nop
0:000> t
Time Travel Position: D6:43
cmd!_chkstk+0x1d33:
00007ff7`aa059ab3 e943fdfeff      jmp     cmd!MdWork+0xeb (00007ff7`aa0497fb)
0:000> t
Time Travel Position: D6:44
cmd!MdWork+0xeb:
00007ff7`aa0497fb 8bdf            mov     ebx,edi
0:000> t
Time Travel Position: D6:45
cmd!MdWork+0xed:
00007ff7`aa0497fd 488b8d38010000  mov     rcx,qword ptr [rbp+138h] ss:0000004b`8c94edc8=0000019f56bf0780
0:000> du 0000019f56bf0780
0000019f`56bf0780  “C:\long\long\long\long\long\long”
0000019f`56bf07c0  “\long\long\long\long\long\long\l”
0000019f`56bf0800  “ong\long\long\long\long\long\lon”
0000019f`56bf0840  “g\long\long\long\long\long\long\”
0000019f`56bf0880  “long\long\long\long\long\long\lo”
0000019f`56bf08c0  “ng\long\long\long\long\long\long”
0000019f`56bf0900  “\long\long\long\long\long\long\l”
0000019f`56bf0940  “ong\long\long\long\long\long”

From this analysis we worked out the long file path that was being supplied in a mkdir command, and was able to modify it into a permitted size.

Posted in WinDbg | Leave a comment

Case of the Win10 HyperV Fail – Property ‘MaxInternalSize’ does not exist in class ‘Msvm_VirtualHardDiskSettingData’

Trying to copy a huge file out of a HyperVM late at night I naively went and right clicked “Mount” in Windows Explorer on the VHDX file. Unfortunately my VM had multiple checkpoints, which aren’t visible when browsing the VHDX. Realised my mistake then unmounted the VHDX. Unfortunately now the VM stopped booting…

Can’t access disk, opening the VM properties selecting disk and clicking Inspect I get error:

[Window Title]
Virtual Hard Disk Properties

[Main Instruction]
There was a problem with one of the command line parameters. Either ‘DESKTOP-RTTN04O’ could not be found, or ‘C:\Users\Public\Documents\Hyper-V\Virtual hard disks\Win7_31AB2236-D8AC-4CF9-8A75-2DC321590C8C.Avhdx’ is not a valid path.

[Content]
Property ‘MaxInternalSize’ does not exist in class ‘Msvm_VirtualHardDiskSettingData’.

[Close]

This VM had the “parent” and two subsequent checkpoints..

  • Parent = Win7.vhdx
  • 1st checkpoint = Win7_5A0F4E19-3212-4D0A-8A43-83853D468B0B.avhdx
  • 2nd checkpoint = Win7_31AB2236-D8AC-4CF9-8A75-2DC321590C8C.avhdx

To fix this I used the PowerShell cmd Set-VHD

Set-VHD "C:\Users\Public\Documents\Hyper-V\Virtual hard disks\Win7_31AB2236-D8AC-4CF9-8A75-2DC321590C8C.Avhdx" -ParentPath "C:\users\public\documents\Hyper-V\Virtual hard disks\Win7_5A0F4E19-3212-4D0A-8A43-83853D468B0B.avhdx"
Set-VHD "C:\users\public\documents\Hyper-V\Virtual hard disks\Win7_5A0F4E19-3212-4D0A-8A43-83853D468B0B.avhdx" -ParentPath "C:\users\public\documents\Hyper-V\Virtual hard disks\Win7.vhdx"

Unfortunately the final command failed:

Set-VHD : Failed to set new parent for the virtual disk.
There exists ID mismatch between the differencing virtual hard disk and the parent disk.

To resolve this error I added –ignoreidmismatch

Set-VHD "C:\users\public\documents\Hyper-V\Virtual hard disks\Win7_5A0F4E19-3212-4D0A-8A43-83853D468B0B.avhdx" -ParentPath "C:\users\public\documents\Hyper-V\Virtual hard disks\Win7.vhdx" -ignoreidmismatch

I reset my VM hard disk to use the 2nd checkpoint disk file, I received a warning data loss could occur if I continued, but I risked everything and continued anyway.  Clicking “Inspect Disk” now succeeded and my VM now booted fine, and seemed to have the recent data I had added into it…

Posted in Uncategorized | 2 Comments

Faster Where-Object In PowerShell

One of the worst features of PowerShell for me is extremely slow performance in loops, especially using items like Where-Object { $_.Name –eq “value” }

For performance critical loops I use inline C#

Here is an example to search data imported from CSV more quickly, to retrieve the relevant line of data:

$Source = @"

using System;
using System.Management.Automation;
namespace FastSearch
{
    
    public static class Search
    {
        public static object Find(PSObject[] collection, string column, string data)
        {
            foreach(PSObject item in collection)
            {
                if (item.Properties[column].Value.ToString() == data) { return item; }
            }

            return null;
        }
    }
}
"@ 

Add-Type -ReferencedAssemblies $Assem -TypeDefinition $Source -Language CSharp 

$data = Import-Csv "songwriters.csv"

$row = [FastSearch.Search]::Find($data,"Name","Bob Dylan")

In test CSV file to search the CSV data 30k times the C# version took a few minutes, using $data | Where-Object { Name –eq “<name>”} took over 12 hours.

Posted in Performance, PowerShell | Leave a comment

Export Large Number of Users from ConfigMgr to CSV with PowerShell

Had a scenario where I required export of 50k+ users from ConfigMgr to do some analysis.

However PowerShell cmdlet Get-CMUser just hung for an hour or more. Maybe it would eventually come back with a result, I don’t know…

So I tried using WMI query:

$SiteName="<site code>"
$SCCMServer="<configmgr server>"
$SCCMNameSpace="root\sms\site_$SiteName"
$result = Get-WmiObject -namespace $SCCMNameSpace -computer $SCCMServer -query "select * from sms_r_user"
$result | Export-Csv UserIDs.csv -NoTypeInformation

However this also hung for quite some time until eventually failing:

Get-WmiObject : Shutting down
At line:4 char:11
+ $result = Get-WmiObject -namespace $SCCMNameSpace -computer $SCCMServer -query ” …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [Get-WmiObject], ManagementException
    + FullyQualifiedErrorId : GetWMIManagementException,Microsoft.PowerShell.Commands.GetWmiObjectCommand

 

To work around this, did a search filtering by first character of username. In this case I just looked for usernames starting with a-z or 0-9:

$SiteName="<site code>"
$SCCMServer="<configmgr server>"
$SCCMNameSpace="root\sms\site_$SiteName"

$result = @()
ForEach ($letter in "a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","0","1","2","3","4","5","6","7","8","9")
{
    Write-Host "Searching for usernames starting with $letter"
    $result += Get-WmiObject -namespace $SCCMNameSpace -computer $SCCMServer -query "select * from sms_r_user WHERE UserName LIKE '$letter%'"
}

$result | Export-Csv UserIDs.csv -NoTypeInformation

This worked perfectly and extracted all the users into CSV format within a few minutes.

Posted in ConfigMgr | Tagged | Leave a comment