Thursday, December 8, 2011

kernel32.dll DeviceIoControl example in C#

Had to write some code to retrieve the partition alignment of some VM disks hosted on a NAS from within the client OS. Simple method using WMI is below, but that was a bit dull as WMI is too easy so I thought I'd create a native method using API calls to kernel32.dll and use DeviceIOControl... what a pain but below is a native method that shows how to get the native code working under C#...

WMI method - simples...
        void WMIMethod()         
  {
              string sComputer = "."; 
             ManagementScope scope = new ManagementScope("\\\\" + sComputer + "\\root\\cimv2");
             //"\\\\" + args[0] + "\\root\\cimv2");            
  scope.Connect();
             ObjectQuery query = new ObjectQuery("SELECT * FROM Win32_DiskPartition");
             ManagementObjectSearcher searcher =  new ManagementObjectSearcher(scope, query);
               ManagementObjectCollection queryCollection = searcher.Get();
               // Console.WriteLine("MachineName={0}", args[0]);
             foreach (ManagementObject m in queryCollection)
             {                 
 // Display the remote computer information                 
  Console.Write("Disk={0}", m["DiskIndex"]);                 
  Console.Write(",Partition={0}", m["Index"]);                 
  foreach (ManagementBaseObject c in m.GetRelated("Win32_LogicalDisk"))     
             {                     
   Console.Write(",Drive={0}", c["name"]);                 
   }                 
  Console.Write(",StartingOffset={0}", m["StartingOffset"]);
                 Int64 i = Convert.ToInt64(m["StartingOffset"]) + Convert.ToInt64(m["Size"]);
                 Console.Write(",NextAlignedOffset={0}", i);
                 Console.WriteLine(",Size={0}", m["Size"]);
             }
           }

Now the tricky one using the native calls... Converted this to C# from a post on XtremeVB, works like a charm...

//using Microsoft.VisualBasic;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using Microsoft.Win32.SafeHandles;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security;

static class IOCtl
{

private const int GENERIC_READ = unchecked((int)0x80000000);
private const int FILE_SHARE_READ = 1;
private const int FILE_SHARE_WRITE = 2;
private const int OPEN_EXISTING = 3;
private const int IOCTL_DISK_GET_DRIVE_LAYOUT_EX = unchecked((int)0x00070050);
private const int ERROR_INSUFFICIENT_BUFFER = 122;

private enum PARTITION_STYLE : int
{
MBR = 0,
GPT = 1,
RAW = 2
}

private enum Partition : byte
{
ENTRY_UNUSED = 0,
FAT_12 = 1,
XENIX_1 = 2,
XENIX_2 = 3,
FAT_16 = 4,
EXTENDED = 5,
HUGE = 6,
IFS = 7,
OS2BOOTMGR = 0xa,
FAT32 = 0xb,
FAT32_XINT13 = 0xc,
XINT13 = 0xe,
XINT13_EXTENDED = 0xf,
PREP = 0x41,
LDM = 0x42,
UNIX = 0x63
}

[SuppressUnmanagedCodeSecurity()]
private class NativeMethods
{

[DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern SafeFileHandle CreateFile(
string fileName,
int desiredAccess,
int shareMode,
IntPtr securityAttributes,
int creationDisposition,
int flagsAndAttributes,
IntPtr hTemplateFile);

[DllImport("kernel32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DeviceIoControl(
SafeFileHandle hVol,
int controlCode,
IntPtr inBuffer,
int inBufferSize,
IntPtr outBuffer,
int outBufferSize,
ref int bytesReturned,
IntPtr overlapped);

}

// Needs to be explicit to do the union.
[StructLayout(LayoutKind.Explicit)]
private struct DRIVE_LAYOUT_INFORMATION_EX
{
[FieldOffset(0)]
public PARTITION_STYLE PartitionStyle;
[FieldOffset(4)]
public int PartitionCount;
[FieldOffset(8)]
public DRIVE_LAYOUT_INFORMATION_MBR Mbr;
[FieldOffset(8)]
public DRIVE_LAYOUT_INFORMATION_GPT Gpt;
// Forget partition entry, we can't marshal it directly
// as we don't know how big it is.
}

private struct DRIVE_LAYOUT_INFORMATION_MBR
{
public uint Signature;
}

// Sequential ensures the fields are laid out in memory
// in the same order as we write them here. Without it,
// the runtime can arrange them however it likes, and the
// type may no longer be blittable to the C type.
[StructLayout(LayoutKind.Sequential)]
private struct DRIVE_LAYOUT_INFORMATION_GPT
{
public Guid DiskId;
// C LARGE_INTEGER is 64 bit
public long StartingUsableOffset;
public long UsableLength;
// C ULONG is 32 bit
public int MaxPartitionCount;
}

[StructLayout(LayoutKind.Sequential)]
private struct PARTITION_INFORMATION_MBR
{
public byte PartitionType;
[MarshalAs(UnmanagedType.U1)]
public bool BootIndicator;
[MarshalAs(UnmanagedType.U1)]
public bool RecognizedPartition;
public UInt32 HiddenSectors;

// helper method - is the hi bit valid - if so IsNTFT has meaning.
public bool IsValidNTFT()
{
return (PartitionType & 0xc0) == 0xc0;
}

// is this NTFT - i.e. an NTFT raid or mirror.
public bool IsNTFT()
{
return (PartitionType & 0x80) == 0x80;
}

// the actual partition type.
public Partition GetPartition()
{
const byte mask = 0x3f;
return (Partition)(PartitionType & mask);
}

}

[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)]
private struct PARTITION_INFORMATION_GPT
{
// Strangely, this works as sequential if you build x86,
// But for x64 you must use explicit.
[FieldOffset(0)]
public Guid PartitionType;
[FieldOffset(16)]
public Guid PartitionId;
[FieldOffset(32)]
//DWord64
public ulong Attributes;
[FieldOffset(40)]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 36)]
public string Name;
}

[StructLayout(LayoutKind.Explicit)]
private struct PARTITION_INFORMATION_EX
{
[FieldOffset(0)]
public PARTITION_STYLE PartitionStyle;
[FieldOffset(8)]
public long StartingOffset;
[FieldOffset(16)]
public long PartitionLength;
[FieldOffset(24)]
public int PartitionNumber;
[FieldOffset(28)]
[MarshalAs(UnmanagedType.U1)]
public bool RewritePartition;
[FieldOffset(32)]
public PARTITION_INFORMATION_MBR Mbr;
[FieldOffset(32)]
public PARTITION_INFORMATION_GPT Gpt;
}

public static void Main()
{
SendIoCtlDiskGetDriveLayoutEx(0);
Console.ReadKey();
}

private static void SendIoCtlDiskGetDriveLayoutEx(int PhysicalDrive)
{

DRIVE_LAYOUT_INFORMATION_EX lie = default(DRIVE_LAYOUT_INFORMATION_EX);
PARTITION_INFORMATION_EX[] pies = null;
using (SafeFileHandle hDevice =
NativeMethods.CreateFile("\\\\.\\PHYSICALDRIVE" + PhysicalDrive, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero))
{
if (hDevice.IsInvalid)
throw new Win32Exception();
// Must run as administrator, otherwise we get "ACCESS DENIED"

// We don't know how many partitions there are, so we have to use a blob of memory...
int numPartitions = 1;
bool done = false;
do
{
// 48 = the number of bytes in DRIVE_LAYOUT_INFORMATION_EX up to
// the first PARTITION_INFORMATION_EX in the array.
// And each PARTITION_INFORMATION_EX is 144 bytes.
int outBufferSize = 48 + (numPartitions * 144);
IntPtr blob = default(IntPtr);
int bytesReturned = 0;
bool result = false;

try
{
blob = Marshal.AllocHGlobal(outBufferSize);
result = NativeMethods.DeviceIoControl(hDevice, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, IntPtr.Zero, 0, blob, outBufferSize, refbytesReturned, IntPtr.Zero);
// We expect that we might not have enough room in the output buffer.
if (result == false)
{
// If the buffer wasn't too small, then something else went wrong.
if (Marshal.GetLastWin32Error() != ERROR_INSUFFICIENT_BUFFER)
throw new Win32Exception();
// We need more space on the next loop.
numPartitions += 1;
}
else
{
// We got the size right, so stop looping.
done = true;
// Do something with the data here - we'll free the memory before we leave the loop.
// First we grab the DRIVE_LAYOUT_INFORMATION_EX, it's at the start of the blob of memory:
lie = (DRIVE_LAYOUT_INFORMATION_EX)Marshal.PtrToStructure(blob, typeof(DRIVE_LAYOUT_INFORMATION_EX));
// Then loop and add the PARTITION_INFORMATION_EX structures to an array.
pies = new PARTITION_INFORMATION_EX[lie.PartitionCount];
for (int i = 0; i <= lie.PartitionCount - 1; i++)
{
// Where is this structure in the blob of memory?
IntPtr offset = new IntPtr(blob.ToInt64() + 48 + (i * 144));
pies[i] = (PARTITION_INFORMATION_EX)Marshal.PtrToStructure(offset, typeof(PARTITION_INFORMATION_EX));
}
}
}
finally
{
Marshal.FreeHGlobal(blob);
}
} while (!(done));
}


DumpInfo(lie, pies);

}

private static bool IsPart0Aligned(PARTITION_INFORMATION_EX[] pies)
{
try
{
if (pies[0].StartingOffset % 4096 == 0)
{
return true;
}
else
{
return false;
}
}
catch
{
return false;
}
}

private static void DumpInfo(DRIVE_LAYOUT_INFORMATION_EX lie, PARTITION_INFORMATION_EX[] pies)
{
if (IsPart0Aligned(pies) == true) {
Console.Write( "True");
}
else
{
Console.Write("false");
}
Console.WriteLine("Partition Style: {0}", lie.PartitionStyle);
Console.WriteLine("Partition Count: {0}", lie.PartitionCount);
switch (lie.PartitionStyle)
{
case PARTITION_STYLE.MBR:
Console.WriteLine("Mbr Signature: {0}", lie.Mbr.Signature);
break;
case PARTITION_STYLE.GPT:
Console.WriteLine("Gpt DiskId: {0}", lie.Gpt.DiskId);
Console.WriteLine("Gpt StartingUsableOffset: {0}", lie.Gpt.StartingUsableOffset);
Console.WriteLine("Gpt UsableLength: {0}", lie.Gpt.UsableLength);
Console.WriteLine("Gpt MaxPartitionCount: {0}", lie.Gpt.MaxPartitionCount);
break;
default:
Console.WriteLine("RAW!");
break;
}

for (int i = 0; i <= lie.PartitionCount - 1; i++)
{
Console.WriteLine();
Console.WriteLine("Partition {0} info...", i + 1);
Console.WriteLine("-------------------");
Console.WriteLine();
var _with1 = pies[i];
Console.WriteLine("Partition style: {0}", _with1.PartitionStyle);
Console.WriteLine("Starting offset: {0}", _with1.StartingOffset);
Console.WriteLine("Partition length: {0}", _with1.PartitionLength);
Console.WriteLine("Partition number: {0}", _with1.PartitionNumber);
Console.WriteLine("Rewrite partition: {0}", _with1.RewritePartition);
switch (_with1.PartitionStyle)
{
case PARTITION_STYLE.MBR:
var _with2 = _with1.Mbr;
Console.WriteLine("Mbr PartitionType - raw value: {0}", _with2.PartitionType);
// Console.WriteLine("Mbr PartitionType - validNTFT: {0}", _with2.IsValidNTFT);
// if (_with2.IsValidNTFT)
// Console.WriteLine("Mbr PartitionType - ntft: {0}", _with2.IsNTFT);
// Console.WriteLine("Mbr PartitionType - decoded: {0}", _with2.GetPartition);
Console.WriteLine("Mbr BootIndicator : {0}", _with2.BootIndicator);
Console.WriteLine("Mbr RecognizedPartition : {0}", _with2.RecognizedPartition);
Console.WriteLine("Mbr HiddenSectors : {0}", _with2.HiddenSectors);
break;
case PARTITION_STYLE.GPT:
var _with3 = _with1.Gpt;
Console.WriteLine("Gpt PartitionType: {0}", _with3.PartitionType);
Console.WriteLine("Gpt PartitionId: {0}", _with3.PartitionId);
Console.WriteLine("Gpt Attributes: {0}", _with3.Attributes);
Console.WriteLine("Gpt Name: {0}", _with3.Name);
break;
case PARTITION_STYLE.RAW:
Console.WriteLine("RAW!");
break;
}

}
}

}

Simples! lol!

Monday, October 10, 2011

Delete old profiles

Had an issue with a client where SCCM was mounting each user profile (NTUSER.DAT) everyday and the profile age was always the same for all users on the machine even if some had not logged in for months. so knocked this out that looks at the DateModified on the profile directory which only gets updated when the user logs out.

 using System;


using System.Collections.Generic;

using System.Text;

using Microsoft.Win32;

using System.IO;

using System.Security.Principal;

using System.Runtime.InteropServices;

using System.Diagnostics;







namespace DelProfs

{

class Program

{



private static string m_skeyName = "";



public static string skeyName

{

get { return m_skeyName; }

set { m_skeyName = value; }

}



private static string m_sHKLMkeyName = "";



public static string sHKLMkeyName

{

get { return m_sHKLMkeyName; }

set { m_sHKLMkeyName = value; }

}



private static string m_sFile2Check = "";



public static string sFile2Check

{

get { return m_sFile2Check; }

set { m_sFile2Check = value; }

}



[DllImport("userenv.dll", SetLastError = true)]

static extern bool DeleteProfile(string lpSidString, string lpProfilePath, string lpcomputername);

[DllImport("kernel32", SetLastError = true, CallingConvention = CallingConvention.Winapi)]

public extern static IntPtr LoadLibrary(string libraryName);

[DllImport("kernel32", SetLastError = true, CallingConvention = CallingConvention.Winapi)]

public extern static IntPtr GetProcAddress(IntPtr hwnd, string procedureName);



static void Main(string[] args)

{

//if (Is32BitProcessOn64BitProcessor())

//{

// skeyName = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList";

// sHKLMkeyName = "SOFTWARE\\Wow6432Node\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList";

//}

//else

//{

skeyName = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList";

sHKLMkeyName = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList";

//}



sFile2Check = "NTUSER.DAT";



//const string lpcomputername = null;

if (args.Length == 0)

{

System.Console.WriteLine("Please Enter the Age of Profiles to Delete in Days.");

System.Console.WriteLine("Usage: DelProfs ");

System.Console.WriteLine();

return;

//return 1;

}

string sdefUserDomain = "";

string sdefUserName = "";

int num;

bool test = int.TryParse(args[0], out num);



if (test == false)

{

System.Console.WriteLine("Please Enter the Age of Profiles to Delete in Days.");

System.Console.WriteLine("Usage: DelProfs ");

System.Console.WriteLine();

return;

//return 1;

}



if (args.Length == 3)

{

//get the default domain and username passed from smartbutton

sdefUserDomain = args[1].ToUpper();

sdefUserName = args[2].ToUpper();

//System.Console.WriteLine(sdefUser);

//return;

}



string sComputerName = "";

sComputerName = (string)Registry.GetValue("HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\ComputerName\\ActiveComputerName","Computername","");

//return;

//int iMaxAge = 10;

int iMaxAge = num;

string sProfilePath = (string)Registry.GetValue(skeyName, "ProfilesDirectory", "");

sProfilePath = Environment.ExpandEnvironmentVariables(sProfilePath);

if (sProfilePath != "")

{

try

{

foreach (string sdir in Directory.GetDirectories(sProfilePath))

{

//System.Console.WriteLine(sdir);

//string sTemp = Path.Combine(sdir, "HELPASSISTANT");

if ((sdir.ToUpper().Contains(sComputerName)) && (sComputerName != ""))

{

DeleteUserProfileKey(Path.Combine(sdir, "HELPASSISTANT"));

}



DateTime dFolderAge = getFolderDateModified(sdir);

int iAge = HowOld(dFolderAge);

if (iAge > iMaxAge)

{

string scheck = Path.Combine(sdir, sFile2Check);

if ((File.Exists(scheck))

((File.GetAttributes(scheck) & FileAttributes.Hidden) == FileAttributes.Hidden))

{

//check owner

//string sowner = "";

string sowner = getFileOwner(Path.Combine(sdir, sFile2Check));



if (sowner.IndexOf("\\") > 0)

{



string[] split = null;

char[] splitchar = { '\\' };

split = sowner.Split(splitchar);



string sDomainNaime = split[0];

string sUserName = split[1];



if ((sDomainNaime.ToUpper() == "BUILTIN")

(sDomainNaime.ToUpper() == "NT AUTHORITY")



(sDomainNaime.ToUpper() == "ERROR")

(sDomainNaime.ToUpper() == "WINDOWSLIVEID")



(sDomainNaime.ToUpper() == "IIS APPPOOL")



((sDomainNaime.ToUpper() == sdefUserDomain.ToUpper()) && (sUserName.ToUpper() == sdefUserName.ToUpper())



(sUserName.ToUpper().Contains("CLEARCASE")))

)

{

//Excluded account profiles

}

else

{

//ForceDeleteDirectory(sdir);

//DeleteUserProfileKey(sdir);

DeleteUserProfileKey(sdir);

}



}

else

{

// not a domain account

}

}

else

{

//no ntuser.dat found so delete it anyway

//ForceDeleteDirectory(sdir);

//DeleteUserProfileKey(sdir);

DeleteUserProfileKey(sdir);

}

}

}

}

catch (Exception)

{

//Console.WriteLine(excpt.Message);

}

}

else { }



}





public static int HowOld(DateTime d1)

{

TimeSpan ts = DateTime.Now - d1;

return Math.Abs(ts.Days);

}





public static void DeleteUserProfileKey(string sProfilePath)

{

//string keyName = "LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList";

Boolean bfound = false;

try

{

using (RegistryKey profileList = Registry.LocalMachine.OpenSubKey(sHKLMkeyName))

{

//Get a list of SIDs corresponding to each profile on the computer

string[] sidList = profileList.GetSubKeyNames();

foreach (string ssid in sidList)

{

try

{

string sRegPath = Path.Combine(skeyName, ssid);

string sProfileTestPath = (string)Registry.GetValue(sRegPath, "ProfileImagePath", "");



if (sProfileTestPath.ToUpper() == sProfilePath.ToUpper())

{

DeleteProfile(ssid, sProfilePath, null);

bfound = true;

ForceDeleteDirectory(sProfilePath);

DeleteProfile(ssid, sProfilePath, null);

}



// }

}

catch (Exception)

{

//Console.WriteLine(e.Message);

}

}

if (bfound == false)

{



ForceDeleteDirectory(sProfilePath);

}



}

}

catch (Exception)

{

// Console.WriteLine(e.Message);



}

}



public static void deleteRegKeySub(string skey)

{

try

{

Registry.LocalMachine.DeleteSubKeyTree(skey);

}

catch (Exception)

{

}

}



public static string getFileOwner(string sFile)

{

try

{

if (File.Exists(sFile))

{

var fs = File.GetAccessControl(sFile);



var sid = fs.GetOwner(typeof(SecurityIdentifier));

//Console.WriteLine(sid); // SID



var ntAccount = sid.Translate(typeof(NTAccount));

//Console.WriteLine(ntAccount); // DOMAIN\username



return ntAccount.ToString().ToUpper();

}

else

{

return "";

}

}

catch (Exception)

{

return "";

}

}



public static DateTime getFolderDateModified(string sFolderPath)

{

try

{

string spath = @sFolderPath;

if (!Directory.Exists(spath))

{

return DateTime.Now;

}

else

{

return Directory.GetLastWriteTime(spath);

}

}

catch (Exception)

{

return DateTime.Now;

}



}



//private static void DeleteFileSystemInfo(FileSystemInfo fsi)

//{

// fsi.Attributes = FileAttributes.Normal;

// var di = fsi as DirectoryInfo;



// if (di != null)

// {

// foreach (var dirInfo in di.GetFileSystemInfos())

// DeleteFileSystemInfo(dirInfo);

// }



// fsi.Delete();

//}



public static void ForceDeleteDirectory(string path)

{

DirectoryInfo root;

Stack fols;

DirectoryInfo fol;

try

{

fols = new Stack();

root = new DirectoryInfo(path);

fols.Push(root);

while (fols.Count > 0)

{

fol = fols.Pop();

fol.Attributes = fol.Attributes & ~(FileAttributes.Archive
FileAttributes.ReadOnly
FileAttributes.Hidden);

foreach (DirectoryInfo d in fol.GetDirectories())

{

fols.Push(d);

}

foreach (FileInfo f in fol.GetFiles())

{

f.Attributes = f.Attributes & ~(FileAttributes.Archive
FileAttributes.ReadOnly
FileAttributes.Hidden);

f.Delete();

}

}

root.Delete(true);

}

catch { }

}







private delegate bool IsWow64ProcessDelegate([In] IntPtr handle, [Out] out bool isWow64Process);



public static bool IsOS64Bit()

{

try

{

if (IntPtr.Size == 8

(IntPtr.Size == 4 && Is32BitProcessOn64BitProcessor()))

{

return true;

}

else

{

return false;

}

}

catch (Exception)

{

return false;

}



}



private static IsWow64ProcessDelegate GetIsWow64ProcessDelegate()

{

try

{



IntPtr handle = LoadLibrary("kernel32");

if (handle != IntPtr.Zero)

{

IntPtr fnPtr = GetProcAddress(handle, "IsWow64Process");



if (fnPtr != IntPtr.Zero)

{

return (IsWow64ProcessDelegate)Marshal.GetDelegateForFunctionPointer((IntPtr)fnPtr, typeof(IsWow64ProcessDelegate));

}

}

}

catch (Exception)

{ }

return null;



}



private static bool Is32BitProcessOn64BitProcessor()

{

bool isWow64 = false;

bool retVal = false;

try

{

IsWow64ProcessDelegate fnDelegate = GetIsWow64ProcessDelegate();



if (fnDelegate == null)

{

return false;

}



retVal = fnDelegate.Invoke(Process.GetCurrentProcess().Handle, out isWow64);

}

catch (Exception)

{ }



if (retVal == false)

{

return false;

}

return isWow64;

}



}

}

Monday, December 6, 2010

Get HostName from client in WCF

What a pain! but this works. Add references to:

using System.ServiceModel.Channels;
using System.Net;

.....


Then this will get the client's IP address from the incoming message header on your IIS hosted WCF Web Service and resolve to the host:

string ComputerName = "NOTFOUND";

try
{
var remp = OperationContext.Current.IncomingMessageProperties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
string[] computer_name = Dns.GetHostEntry(remp.Address).HostName.Split(new Char[] { '.' });
ComputerName = computer_name[0].ToString();
}
catch (Exception e)
{
ComputerName = "NOTFOUND";
}

Tuesday, November 9, 2010

Updating cached credentials

Updating cached credentials for full time work at home users...

I have a client that uses a VPN client and does not allow users to logon from the Windows logon screen, rather they have to already be logged in using cached credentials and then launch the VPN client and authenticate...

Now this poses a significant issue deploying applications via SCCM based on a user's groups for those users who are full time work at home or road warriors, as the user's windows security token, that stores the SIDs for all their groups, never gets updated through an interactive domain logon...

thus far the only solution I've found is... write a utility that prompts for the user's current password and then launch a new process / thread with those credentials just like runas.exe. This then updates the cached credentials... force the user to logoff and logon and voila the token has the new groups...

Search Brian Hehir's sites

Loading