Blog Archives

PowerShell: Getting the Disk Drive from a Volume or Mount Point


So this is way harder than I thought it would be.

If you take a look at the WMI class for Win32_MountPoint

https://msdn.microsoft.com/en-us/library/aa394205%28v=vs.85%29.aspx

You’ll notice there is a remark there that says:

There may not be any way to search from Win32_MountPoint to get to the associated Win32_DiskDrive

Which also applies to the Volume.

That sends you over to this blog post which talks about a win32 api call via DeviceIoControl and I managed to find decent info on PInvoke.net and also this blog post here which basically gives some VB code for it.

So after taking all that in, I was able to write a simple C# snippet that you can toss in PS to query for a Volume

Add-Type -TypeDefinition @"
using System;
using Microsoft.Win32.SafeHandles;
using System.IO;
using System.Runtime.InteropServices;

public class GetDisk
{
 private const uint IoctlVolumeGetVolumeDiskExtents = 0x560000;

 [StructLayout(LayoutKind.Sequential)]
 public struct DiskExtent
 {
 public int DiskNumber;
 public Int64 StartingOffset;
 public Int64 ExtentLength;
 }

 [StructLayout(LayoutKind.Sequential)]
 public struct DiskExtents
 {
 public int numberOfExtents;
 public DiskExtent first;
 }

 [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
 private static extern SafeFileHandle CreateFile(
 string lpFileName,
 [MarshalAs(UnmanagedType.U4)] FileAccess dwDesiredAccess,
 [MarshalAs(UnmanagedType.U4)] FileShare dwShareMode,
 IntPtr lpSecurityAttributes,
 [MarshalAs(UnmanagedType.U4)] FileMode dwCreationDisposition,
 [MarshalAs(UnmanagedType.U4)] FileAttributes dwFlagsAndAttributes,
 IntPtr hTemplateFile);

 [DllImport("Kernel32.dll", SetLastError = false, CharSet = CharSet.Auto)]
 private static extern bool DeviceIoControl(
 SafeFileHandle hDevice,
 uint IoControlCode,
 [MarshalAs(UnmanagedType.AsAny)] [In] object InBuffer,
 uint nInBufferSize,
 ref DiskExtents OutBuffer,
 int nOutBufferSize,
 ref uint pBytesReturned,
 IntPtr Overlapped
);

 public static string GetPhysicalDriveString(string path)
 {
 //clean path up
 path = path.TrimEnd('\\');
 if (!path.StartsWith(@"\\.\"))
 path = @"\\.\" + path;

 SafeFileHandle shwnd = CreateFile(path, FileAccess.Read, FileShare.Read | FileShare.Write, IntPtr.Zero, FileMode.Open, 0,
 IntPtr.Zero);
 if (shwnd.IsInvalid)
 {
 //Marshal.ThrowExceptionForHR(Marshal.GetLastWin32Error());
 Exception e = Marshal.GetExceptionForHR(Marshal.GetLastWin32Error());
 }

 var bytesReturned = new uint();
 var de1 = new DiskExtents();
 bool result = DeviceIoControl(shwnd, IoctlVolumeGetVolumeDiskExtents, IntPtr.Zero, 0, ref de1,
 Marshal.SizeOf(de1), ref bytesReturned, IntPtr.Zero);
 shwnd.Close();
 if(result)
 return @"\\.\PhysicalDrive" + de1.first.DiskNumber;
 return null;
 }
}

"@

This will allow you to call this static method to get its Disk path, so you can do something like this.


$DeviceID = [GetDisk]::GetPhysicalDriveString('c:\')
gwmi win32_diskdrive | ? { $_.deviceid -eq $DeviceID}

Now you say, but Justin, this isnt working for a mount point path!? Which is true, but we can work around that.

We can use the Win32_MountPoint class to figure this out, or, you can use this cmdlet Get-MountPointData which basically parses the class for you. Really all you need to do is get the Volume DeviceID, which looks something like this.

Volume{ccbca757-b6c9-11e4-8275-448a5ba2d884}

So we can take that and search on it like so.


[GetDisk]::GetPhysicalDriveString('Volume{78498ddf-9a97-11e4-8252-806e6f6e6963}')

NOTE: This must be run as admin, since we’re hitting the disk subsystem (kernel)

Enjoy!

PowerShell: Remove a Drive Mount/Letter (More win32)


I’ve got a server with about 300 volumes on it and I obviously can’t use drive letters so I’ve got a bunch of mount points which works great the problem is that after the first reboot the automount kicked in and gave some of those volumes drive letters. Now all of my drive letters are taken up!

I don’t really want to use the storage manager to remove the 20 or so volume labels so I started to dig in to a PowerShell method and come to find out PSCX has a method but that was the only way. I’m not a huge fan of modules (didn’t want to deploy that to my server) so I figured if they can do it so can I.

With a little digging I found the win32 api that allows us to remove mount points (a drive letter is still a mount point) and this was easier than I would have imagined.

If you’ve read my Getting Started with Win32 API then this will make perfect sense to you.


Add-Type @"
using System;
using System.Runtime.InteropServices;

public class MountPoint
{
[DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern bool DeleteVolumeMountPoint(string mountPoint);
}
"@

[MountPoint]::DeleteVolumeMountPoint("G:\")

Once I did that for each drive I needed to remove I loaded up DiskPart and typed AutoMount Disable which prevents drives from automounting at boot.
That’s all there is to it!

PowerShell: Getting Started with Win32 API


Blog on Request!

Someone asked me to delve in to Win32 API use in PowerShell. Personally I don’t feel that I’m the best person to explain this, but I’ve done it a few times now so I think I have a decent handle on it.

Let me just start off by saying Win32 API should be your last choice (ok, second to last, I hate COM). You should do a very through search of the .NET framework before jumping in to the Win32 API pool.

Once you’ve exhausted all other options and have ended up in the Win32 world, what do you do? I’m not going to go over how to find the right WIN32 API for the task because if you are at this point, you’ve probably already found the API and just want to know how to use it.

First off load up the MSDN doc for it and the PInvoke.net doc. Usually googling the API will land you on those two pages pretty quickly.

Now for an example lets take a look at an API I’ve recently been playing with, GetSystemFileCache (pinvoke)

The Signature that MSDN gives us looks like this.

BOOL WINAPI GetSystemFileCacheSize(   __out  PSIZE_T lpMinimumFileCacheSize,   __out  PSIZE_T lpMaximumFileCacheSize,   __out  PDWORD lpFlags );

Which is the signature you’d use if you were coding in C++.  Since there is no direct way to use this in PowerShell we’re going to embed this in C# code and then consume that code in PowerShell. In many cases I’ll write the C# console app first and then apply that to my PowerShell script. This is what I did with my NetStat script, which was fairly complex. In this case we’re just calling a simple method to pull the current Cache limits.

Lets take a look at this Signature. The first thing shown is BOOL, which is the return type which the docs show is any nonzero number for success. Easy enough. bool is the same in C++ as it is in C#.

Next is WINAPI which is just a tag to show its a win32 api, this can just be removed.

After this is our method name, GetSystemFileCacheSize, easy enough.

Now this is where it gets a little tricky because we often times need to convert C++ types to something C# knows.

We see we have 3 arguments here, all 3 of which are prefixed with __out, which means they are output variables (If you don’t know how to program that well it’s basically passing a reference to a variable to return multiple things). So anytime you see __out, that means the variable needs to be passed in as a reference. We’ll get to that in a moment. Next is the type that the variable needs to be. Two of which are whats called PSIZE_T and one is a PDWORD. In both cases these are prefixed with a P, which indicates Pointer (pointer, reference, __out – all the same thing) so really the type is SIZE_T and DWORD.

How do we know what SIZE_T and DWORD are in C#? Google is really your best option here, or hopefully PInvoke.net has a signature you can use. In this case PInvoke does have it, but lets pretend it doesn’t. When we google ‘C# dword’ we get some good results that let us know this is an unsigned Int (UInt32). Another clue we have is that in the MSDN docs it tells us that it’s a flag and it can have a value of 0x1 or 0x4, both of which fit in to an int just fine. So, which do we use? It doesn’t matter, provided all possible return types can fit in.

Next is that Size_T, again googling ‘C# SIZE_T’ gives us some good info as does PInvoke, but lets just google SIZE_T.

Doing so provides a great data type reference.

We can see that PSIZE_T is in there but it just says it’s a pointer to SIZE_T which we already knew.

“The maximum number of bytes to which a pointer can point. Use for a count that must span the full range of a pointer.

This type is declared in BaseTsd.h as follows:

typedef ULONG_PTR SIZE_T;

So this says it’s a ULONG (unsigned long, a long in C# is Int64, which google will help with again) It also indicates that it’s a pointer. This is where PInoke really saves us. It lets us know about an IntPtr type that is a special pointer specifically for this.

So now our new C# signature is as follows

bool GetSystemFileCacheSize(ref IntPtr lpMinimumFileCacheSize, ref IntPtr lpMaximumFileCacheSize, ref int lpFlags);

We see that it specifies ref for all the __out variables (in this case all of them, but some times they are __in) and a data type of IntPtr and int.

We’re still in the C# side of the world. we’ll need to use the System.Runtime.InteropServices (otherwise known as pinvoke). If you don’t know C# this might be a bit tricky, but we’re going to write a small C# class to contain our method and create a wrapper function around it. Not always needed but I find it makes it easier to work with.


using System;

using System.Runtime.InteropServices;

public class test1

{

[DllImport("kernel32.dll")]

public static extern bool GetSystemFileCacheSize(ref IntPtr lpMinimumFileCacheSize, ref IntPtr lpMaximumFileCacheSize, ref int lpFlags);

public struct CacheSize

{

public Int64 Min;

public Int64 Max;

public int Flags;

}

public static CacheSize GetSize()

{

IntPtr lpMin = IntPtr.Zero;

IntPtr lpMax = IntPtr.Zero;

int lpFlags = 0;

bool b = GetSystemFileCacheSize(ref lpMin, ref lpMax, ref lpFlags);

CacheSize size = new CacheSize();

size.Min = lpMin.ToInt64();

size.Max = lpMax.ToInt64();

size.Flags = lpFlags;

return size;

}

The first part of this is just adding the pinvoke namespace and the basic system namespace which helps with the IntPtr. We then create a public class. I usually start with Test1 because once you add a type to PowerShell you can’t overwrite it.

Then you’ll see the keywords [DLLImport(“kernel32.dll”)] so depending on what DLL your function is in will determin what you put in there which is followed by the signature.

Next I created a struct to help with the output to PowerShell. You’ll see how this comes in to play as we start to work with the PowerShell side of things. For now just see that it’s a simple holder for our return data. Next is our function wrapper. I’ve called it GetSize which returns my struct. You’ll also notice that it’s prefiexed with public static for both, which is important for accessing it within PowerShell.

In my function I set my min and max pointer to zero (special pointer zero) and the flags to zero. I then call the win32 api passing it the variables I created as references (pointers) and capture the return in b which if I was trying to do something for production, I’d check the return value.

I then create an object of my type CacheSize and populate it with the return values and return that to the caller.

Now that the C# is done, how do we apply this in PowerShell?

If you read my blog you’ll notice I’ve used Add-Type a few times which is a great tool for adding in types or C# code as we are doing here so we’ll just wrap this in a here-string and pass it to Add-Type like so:


Add-Type

<span style="color: #8b0000; font-family: Lucida Console; font-size: xx-small;"><span style="color: #8b0000; font-family: Lucida Console; font-size: xx-small;"><span style="color: #8b0000; font-family: Lucida Console; font-size: xx-small;">@'</span></span></span>

using System;

using System.Runtime.InteropServices;

public class test1

{

[DllImport("kernel32.dll")]

public static extern bool GetSystemFileCacheSize(ref IntPtr lpMinimumFileCacheSize, ref IntPtr lpMaximumFileCacheSize, ref int lpFlags);

public struct CacheSize

{

public Int64 Min;

public Int64 Max;

public int Flags;

}

public static CacheSize GetSize()

{

IntPtr lpMin = IntPtr.Zero;

IntPtr lpMax = IntPtr.Zero;

int lpFlags = 0;

bool b = GetSystemFileCacheSize(ref lpMin, ref lpMax, ref lpFlags);

CacheSize size = new CacheSize();

size.Min = lpMin.ToInt64();

size.Max = lpMax.ToInt64();

size.Flags = lpFlags;

return size;

}

}

'@

NOTE: Be careful of new lines in this code, the code formatter has been doing weird things to me lately.

Once you run that its imported the class in to our running environment and we can access that method like so.


$rtn = [test1]::getsize()

$rtn

$rtn | select @{n='min';e={$_.min /1mb}}, @{n='Max';e={$_.max/1gb}},flags

The return should be the same on all systems (at least what I’ve seen from tests) as 1MB min and 1024GB max. If you have a file server that’s hosting TB’s of data, you might find this to be a problem when its eaten all of your memory in system cache!

There is a way to set it with a win32 API that im working on now. I’ll be sure to post that solution when I figure it out!

I hope this has helped you understand how to use Win32 APIs a little better!

NetStat for powershell!


I have seen this requested a few times on the forums and most people gave up on it once they tried to find the process ID (PID) associated to the connection. You can get everything else from IPGlobalProperties.GetIPGlobalProperties().

So, if you want that PID you have to call the win32 GetExtendedTcpTable and GetExtendedUdpTable, which isn’t easy to do in powershell so I created it all in C# and used Add-Type to get it in to powershell then created a small helper function in PS to use it.

Figured you guys would rather copy and paste some code than install a cmdlet, plus of course you all want to read the code :)

The first time you display the results (or, once the RemoteHostName property is accessed) a bit of a delay can be experienced while a DNS query is run (was torn on making this a property or a method) but once its called, its stored and any subsequent requests are quick.

you can take the below code and make a module out of it or just paste it in the console (minus the Export-ModuleMember)


Add-Type -TypeDefinition @"
 using System;
 using System.Net;
 using System.Runtime.InteropServices;

public class NetworkUtil
 {
 [DllImport("iphlpapi.dll", SetLastError = true)]
 static extern uint GetExtendedTcpTable(IntPtr pTcpTable, ref int dwOutBufLen, bool sort, int ipVersion, TCP_TABLE_CLASS tblClass, int reserved);
 [DllImport("iphlpapi.dll", SetLastError = true)]
 static extern uint GetExtendedUdpTable(IntPtr pUdpTable, ref int dwOutBufLen, bool sort, int ipVersion, UDP_TABLE_CLASS tblClass, int reserved);
 [StructLayout(LayoutKind.Sequential)]
 public struct MIB_TCPROW_OWNER_PID
 {
 public uint dwState;
 public uint dwLocalAddr;
 public uint dwLocalPort;
 public uint dwRemoteAddr;
 public uint dwRemotePort;
 public uint dwOwningPid;
 }
 [StructLayout(LayoutKind.Sequential)]
 public struct MIB_UDPROW_OWNER_PID
 {
 public uint dwLocalAddr;
 public uint dwLocalPort;
 public uint dwOwningPid;
 }
 [StructLayout(LayoutKind.Sequential)]
 public struct MIB_TCPTABLE_OWNER_PID
 {
 public uint dwNumEntries;
 MIB_TCPROW_OWNER_PID table;
 }
 [StructLayout(LayoutKind.Sequential)]
 public struct MIB_UDPTABLE_OWNER_PID
 {
 public uint dwNumEntries;
 MIB_UDPROW_OWNER_PID table;
 }
 enum TCP_TABLE_CLASS
 {
 TCP_TABLE_BASIC_LISTENER,
 TCP_TABLE_BASIC_CONNECTIONS,
 TCP_TABLE_BASIC_ALL,
 TCP_TABLE_OWNER_PID_LISTENER,
 TCP_TABLE_OWNER_PID_CONNECTIONS,
 TCP_TABLE_OWNER_PID_ALL,
 TCP_TABLE_OWNER_MODULE_LISTENER,
 TCP_TABLE_OWNER_MODULE_CONNECTIONS,
 TCP_TABLE_OWNER_MODULE_ALL
 }
 enum UDP_TABLE_CLASS
 {
 UDP_TABLE_BASIC,
 UDP_TABLE_OWNER_PID,
 UDP_OWNER_MODULE
 }

public static Connection[] GetTCP()
 {

MIB_TCPROW_OWNER_PID[] tTable;
 int AF_INET = 2;
 int buffSize = 0;

uint ret = GetExtendedTcpTable(IntPtr.Zero, ref buffSize, true, AF_INET, TCP_TABLE_CLASS.TCP_TABLE_OWNER_PID_ALL, 0);
 IntPtr buffTable = Marshal.AllocHGlobal(buffSize);

try
 {
 ret = GetExtendedTcpTable(buffTable, ref buffSize, true, AF_INET, TCP_TABLE_CLASS.TCP_TABLE_OWNER_PID_ALL, 0);
 if (ret != 0)
 {
 Connection[] con = new Connection[0];
 return con;
 }

MIB_TCPTABLE_OWNER_PID tab = (MIB_TCPTABLE_OWNER_PID)Marshal.PtrToStructure(buffTable, typeof(MIB_TCPTABLE_OWNER_PID));
 IntPtr rowPtr = (IntPtr)((long)buffTable + Marshal.SizeOf(tab.dwNumEntries));
 tTable = new MIB_TCPROW_OWNER_PID[tab.dwNumEntries];

for (int i = 0; i < tab.dwNumEntries; i++)
 {
 MIB_TCPROW_OWNER_PID tcpRow = (MIB_TCPROW_OWNER_PID)Marshal.PtrToStructure(rowPtr, typeof(MIB_TCPROW_OWNER_PID));
 tTable[i] = tcpRow;
 rowPtr = (IntPtr)((long)rowPtr + Marshal.SizeOf(tcpRow));   // next entry
 }
 }
 finally
 { Marshal.FreeHGlobal(buffTable);}
 Connection[] cons = new Connection[tTable.Length];

for(int i=0; i < tTable.Length; i++)
 {
 IPAddress localip = new IPAddress(BitConverter.GetBytes(tTable[i].dwLocalAddr));
 IPAddress remoteip = new IPAddress(BitConverter.GetBytes(tTable[i].dwRemoteAddr));
 byte[] barray = BitConverter.GetBytes(tTable[i].dwLocalPort);
 int localport = (barray[0] * 256) + barray[1];
 barray = BitConverter.GetBytes(tTable[i].dwRemotePort);
 int remoteport = (barray[0] * 256) + barray[1];
 string state;
 switch (tTable[i].dwState)
 {
 case 1:
 state = "Closed";
 break;
 case 2:
 state = "LISTENING";
 break;
 case 3:
 state = "SYN SENT";
 break;
 case 4:
 state = "SYN RECEIVED";
 break;
 case 5:
 state = "ESTABLISHED";
 break;
 case 6:
 state = "FINSIHED 1";
 break;
 case 7:
 state = "FINISHED 2";
 break;
 case 8:
 state = "CLOSE WAIT";
 break;
 case 9:
 state = "CLOSING";
 break;
 case 10:
 state = "LAST ACKNOWLEDGE";
 break;
 case 11:
 state = "TIME WAIT";
 break;
 case 12:
 state = "DELETE TCB";
 break;
 default:
 state = "UNKNOWN";
 break;
 }
 Connection tmp = new Connection(localip, localport, remoteip, remoteport, (int)tTable[i].dwOwningPid, state);
 cons[i] = (tmp);
 }
 return cons;
 }
 public static Connection[] GetUDP()
 {
 MIB_UDPROW_OWNER_PID[] tTable;
 int AF_INET = 2; // IP_v4
 int buffSize = 0;

uint ret = GetExtendedUdpTable(IntPtr.Zero, ref buffSize, true, AF_INET, UDP_TABLE_CLASS.UDP_TABLE_OWNER_PID, 0);
 IntPtr buffTable = Marshal.AllocHGlobal(buffSize);

try
 {
 ret = GetExtendedUdpTable(buffTable, ref buffSize, true, AF_INET, UDP_TABLE_CLASS.UDP_TABLE_OWNER_PID, 0);
 if (ret != 0)
 {//none found
 Connection[] con = new Connection[0];
 return con;
 }
 MIB_UDPTABLE_OWNER_PID tab = (MIB_UDPTABLE_OWNER_PID)Marshal.PtrToStructure(buffTable, typeof(MIB_UDPTABLE_OWNER_PID));
 IntPtr rowPtr = (IntPtr)((long)buffTable + Marshal.SizeOf(tab.dwNumEntries));
 tTable = new MIB_UDPROW_OWNER_PID[tab.dwNumEntries];

for (int i = 0; i < tab.dwNumEntries; i++)
 {
 MIB_UDPROW_OWNER_PID udprow = (MIB_UDPROW_OWNER_PID)Marshal.PtrToStructure(rowPtr, typeof(MIB_UDPROW_OWNER_PID));
 tTable[i] = udprow;
 rowPtr = (IntPtr)((long)rowPtr + Marshal.SizeOf(udprow));
 }
 }
 finally
 { Marshal.FreeHGlobal(buffTable);}
 Connection[] cons = new Connection[tTable.Length];

for (int i = 0; i < tTable.Length; i++)
 {
 IPAddress localip = new IPAddress(BitConverter.GetBytes(tTable[i].dwLocalAddr));
 byte[] barray = BitConverter.GetBytes(tTable[i].dwLocalPort);
 int localport = (barray[0] * 256) + barray[1];
 Connection tmp = new Connection(localip, localport, (int)tTable[i].dwOwningPid);
 cons[i] = tmp;
 }
 return cons;
 }
 }
 public class Connection
 {
 private IPAddress _localip, _remoteip;
 private int _localport, _remoteport, _pid;
 private string _state, _remotehost, _proto;
 public Connection(IPAddress Local, int LocalPort, IPAddress Remote, int RemotePort, int PID, string State)
 {
 _proto = "TCP";
 _localip = Local;
 _remoteip = Remote;
 _localport = LocalPort;
 _remoteport = RemotePort;
 _pid = PID;
 _state = State;
 }
 public Connection(IPAddress Local, int LocalPort, int PID)
 {
 _proto = "UDP";
 _localip = Local;
 _localport = LocalPort;
 _pid = PID;
 }
 public IPAddress LocalIP { get{ return _localip;}}
 public IPAddress RemoteIP{ get{return _remoteip;}}
 public int LocalPort{ get{return _localport;}}
 public int RemotePort{ get { return _remoteport; }}
 public int PID{ get { return _pid; }}
 public string State{ get { return _state; }}
 public string Protocol{get { return _proto; }}
 public string RemoteHostName
 {
 get {
 if (_remotehost == null)
 _remotehost = Dns.GetHostEntry(_remoteip).HostName;
 return _remotehost;
 }
 }
 public string PIDName{ get { return (System.Diagnostics.Process.GetProcessById(_pid)).ProcessName; } }
 }
 "@

function Get-NetStat
 {
 PARAM([switch]$TCPonly, [switch]$UDPonly)
 if(!$UDPonly)
 {$tcp = [NetworkUtil]::GetTCP()}
 if(!$tcponly)
 {$udp = [NetworkUtil]::GetUDP()}
 $results = $tcp + $udp
 return $results
 }
 Export-ModuleMember get-netstat

Also looking for input or corrections!

UPDATE:
Take a look at a new post for making this in to a module with a format file so its not so ugly!

https://jrich523.wordpress.com/2011/06/23/creating-a-module-for-powershell-with-a-format-file/

Enjoy

Justin