How to Obtain a RegistryKey Object for the 64-Bit Windows Registry from a 32-Bit Process without .NET 4 or Above

It is totally doable to fetch a RegistryKey object for the 64-bit and 32-bit Windows registries with .NET versions prior to .NET 4. Using reflection this can be achieved as follows, by calling some unexposed constructors that are a part of these frameworks (even previous framework editions contain them, but just don’t necessarily expose them):

public static class RegistryExtensions
{
	public enum RegistryHiveType
	{
		X86,
		X64
	}

	static Dictionary<RegistryHive, UIntPtr> _hiveKeys = new Dictionary<RegistryHive, UIntPtr> {
		{ RegistryHive.ClassesRoot, new UIntPtr(0x80000000u) },
		{ RegistryHive.CurrentConfig, new UIntPtr(0x80000005u) },
		{ RegistryHive.CurrentUser, new UIntPtr(0x80000001u) },
		{ RegistryHive.DynData, new UIntPtr(0x80000006u) },
		{ RegistryHive.LocalMachine, new UIntPtr(0x80000002u) },
		{ RegistryHive.PerformanceData, new UIntPtr(0x80000004u) },
		{ RegistryHive.Users, new UIntPtr(0x80000003u) }
	};

	static Dictionary<RegistryHiveType, RegistryAccessMask> _accessMasks = new Dictionary<RegistryHiveType, RegistryAccessMask> {
		{ RegistryHiveType.X64, RegistryAccessMask.Wow6464 },
		{ RegistryHiveType.X86, RegistryAccessMask.WoW6432 }
	};

	[Flags]
	public enum RegistryAccessMask
	{
		QueryValue = 0x0001,
		SetValue = 0x0002,
		CreateSubKey = 0x0004,
		EnumerateSubKeys = 0x0008,
		Notify = 0x0010,
		CreateLink = 0x0020,
		WoW6432 = 0x0200,
		Wow6464 = 0x0100,
		Write = 0x20006,
		Read = 0x20019,
		Execute = 0x20019,
		AllAccess = 0xF003F
	}

	[DllImport("advapi32.dll")]
	public static extern int RegOpenKeyEx(
	  UIntPtr hKey,
	  string subKey,
	  uint ulOptions,
	  uint samDesired,
	  out IntPtr hkResult);

	public static RegistryKey OpenBaseKey(RegistryHive registryHive, RegistryHiveType registryType)
	{
		UIntPtr hiveKey = _hiveKeys[registryHive];
		if (Environment.OSVersion.Platform == PlatformID.Win32NT && Environment.OSVersion.Version.Major > 5)
		{
			RegistryAccessMask flags = RegistryAccessMask.QueryValue | RegistryAccessMask.EnumerateSubKeys | RegistryAccessMask.SetValue | RegistryAccessMask.CreateSubKey | _accessMasks[registryType];
			IntPtr keyHandlePointer = IntPtr.Zero;
			int result = RegOpenKeyEx(hiveKey, null, 0, (uint)flags, out keyHandlePointer);
			if (result == 0)
			{
				var safeRegistryHandleType = typeof(SafeHandleZeroOrMinusOneIsInvalid).Assembly.GetType("Microsoft.Win32.SafeHandles.SafeRegistryHandle");
				var safeRegistryHandleConstructor = safeRegistryHandleType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(IntPtr), typeof(bool) }, null); // .NET < 4
				if (safeRegistryHandleConstructor == null)
					safeRegistryHandleConstructor = safeRegistryHandleType.GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(IntPtr), typeof(bool) }, null); // .NET >= 4
				var keyHandle = safeRegistryHandleConstructor.Invoke(new object[] { keyHandlePointer, true });
				var net3Constructor = typeof(RegistryKey).GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { safeRegistryHandleType, typeof(bool) }, null);
				var net4Constructor = typeof(RegistryKey).GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(IntPtr), typeof(bool), typeof(bool), typeof(bool), typeof(bool) }, null);
				object key;
				if (net4Constructor != null)
					key = net4Constructor.Invoke(new object[] { keyHandlePointer, true, false, false, hiveKey == _hiveKeys[RegistryHive.PerformanceData] });
				else if (net3Constructor != null)
					key = net3Constructor.Invoke(new object[] { keyHandle, true });
				else
				{
					var keyFromHandleMethod = typeof(RegistryKey).GetMethod("FromHandle", BindingFlags.Static | BindingFlags.Public, null, new[] { safeRegistryHandleType }, null);
					key = keyFromHandleMethod.Invoke(null, new object[] { keyHandle });
				}
				var field = typeof(RegistryKey).GetField("keyName", BindingFlags.Instance | BindingFlags.NonPublic);
				if (field != null)
					field.SetValue(key, String.Empty);
				return (RegistryKey)key;
			}
			else if (result == 2) // The key does not exist.
				return null;
			throw new Win32Exception(result);
		}
		throw new PlatformNotSupportedException("The platform or operating system must be Windows 2000 or later.");
	}
}

Example usage:

RegistryKey localMachineX64 = RegistryExtensions.OpenBaseKey(RegistryHive.LocalMachine, RegistryExtensions.RegistryHiveType.X64);
RegistryKey localMachineX86 = RegistryExtensions.OpenBaseKey(RegistryHive.LocalMachine, RegistryExtensions.RegistryHiveType.X86);

Once you have a RegistryKey object reference, it should work just fine with the existing RegistryKey methods.

How to Enumerate All Open Handles for All Processes on a Windows Machine

Have you ever wanted to walk through all of the open handles across all processes that are active on a Windows machine and even be able to do things like close specific handles that those processes have open? I sure have, and I did…but more specifically for my needs I wanted to know what processes had file handles open to specific files on my machine, or what processes had locked specific folders, and so forth. The following code snippet I am going to share is working code, complete with a nice unmanaged library wrapper in case you want to use it through the CLR with your favourite C# projects. It calls on some very low level APIs. These APIs are not your run of the mill, Windows application-friendly type of APIs, so be warned that like all other code on my site, you are responsible for what you do with it. I take no responsibility for any damages. If you happen to bring open handles to a close, that could result in a system-wide catastrophe, the results of which are completely unpredictable relative to what process had that handle open.

UnmanagedLibrary.h:

#include <windows.h>
#include <TlHelp32.h>
#include <iostream>
#include <sstream>
#include <map>
#include <vector>

using namespace std;

void CheckForLocks(wstring fullPath, vector<unsigned long> *processes);
bool CloseHandles(wstring fullPath, int process);

UnmanagedLibrary.cpp:

#include "UnmanagedLibrary.h"

#define NT_SUCCESS(x) ((x) >= 0)
#define STATUS_INFO_LENGTH_MISMATCH 0xc0000004
#define SystemHandleInformation 16
#define ObjectBasicInformation 0
#define ObjectNameInformation 1
#define ObjectTypeInformation 2

typedef NTSTATUS(NTAPI *_NtQuerySystemInformation)(ULONG SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength);
typedef NTSTATUS(NTAPI *_NtDuplicateObject)(HANDLE SourceProcessHandle, HANDLE SourceHandle, HANDLE TargetProcessHandle, PHANDLE TargetHandle, ACCESS_MASK DesiredAccess, ULONG Attributes, ULONG Options);
typedef NTSTATUS(NTAPI *_NtQueryObject)(HANDLE ObjectHandle, ULONG ObjectInformationClass, PVOID ObjectInformation, ULONG ObjectInformationLength, PULONG ReturnLength);

typedef struct _UNICODE_STRING
{
	USHORT Length;
	USHORT MaximumLength;
	PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;

typedef struct _SYSTEM_HANDLE
{
	ULONG ProcessId;
	BYTE ObjectTypeNumber;
	BYTE Flags;
	USHORT Handle;
	PVOID Object;
	ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE, *PSYSTEM_HANDLE;

typedef struct _SYSTEM_HANDLE_INFORMATION
{
	ULONG HandleCount;
	SYSTEM_HANDLE Handles[1];
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;

typedef enum _POOL_TYPE
{
	NonPagedPool,
	PagedPool,
	NonPagedPoolMustSucceed,
	DontUseThisType,
	NonPagedPoolCacheAligned,
	PagedPoolCacheAligned,
	NonPagedPoolCacheAlignedMustS
} POOL_TYPE, *PPOOL_TYPE;

typedef struct _OBJECT_TYPE_INFORMATION
{
	UNICODE_STRING Name;
	ULONG TotalNumberOfObjects;
	ULONG TotalNumberOfHandles;
	ULONG TotalPagedPoolUsage;
	ULONG TotalNonPagedPoolUsage;
	ULONG TotalNamePoolUsage;
	ULONG TotalHandleTableUsage;
	ULONG HighWaterNumberOfObjects;
	ULONG HighWaterNumberOfHandles;
	ULONG HighWaterPagedPoolUsage;
	ULONG HighWaterNonPagedPoolUsage;
	ULONG HighWaterNamePoolUsage;
	ULONG HighWaterHandleTableUsage;
	ULONG InvalidAttributes;
	GENERIC_MAPPING GenericMapping;
	ULONG ValidAccess;
	BOOLEAN SecurityRequired;
	BOOLEAN MaintainHandleCount;
	USHORT MaintainTypeList;
	POOL_TYPE PoolType;
	ULONG PagedPoolUsage;
	ULONG NonPagedPoolUsage;
} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;

using namespace std;

PVOID GetLibraryProcAddress(PSTR LibraryName, PSTR ProcName)
{
	return GetProcAddress(GetModuleHandleA(LibraryName), ProcName);
}

_NtQuerySystemInformation NtQuerySystemInformation = (_NtQuerySystemInformation)GetLibraryProcAddress("ntdll.dll", "NtQuerySystemInformation");
_NtDuplicateObject NtDuplicateObject = (_NtDuplicateObject)GetLibraryProcAddress("ntdll.dll", "NtDuplicateObject");
_NtQueryObject NtQueryObject = (_NtQueryObject)GetLibraryProcAddress("ntdll.dll", "NtQueryObject");

struct QueryStructure
{
	HANDLE dupHandle;
	PVOID objectNameInfo;
	ULONG objectInfoLength;
	ULONG returnLength;
	NTSTATUS result;
};

HANDLE beginQuery = CreateEvent(0, FALSE, FALSE, 0);
HANDLE endQuery = CreateEvent(0, FALSE, FALSE, 0);
QueryStructure queryStructure;

HANDLE beginQueryCloseHandle = CreateEvent(0, FALSE, FALSE, 0);
HANDLE endQueryCloseHandle = CreateEvent(0, FALSE, FALSE, 0);
QueryStructure queryStructureCloseHandle;

DWORD WINAPI queryThread(LPVOID parameter)
{
	while (WaitForSingleObject(beginQuery, INFINITE) == WAIT_OBJECT_0)
	{
		queryStructure.result = NtQueryObject(queryStructure.dupHandle, ObjectNameInformation, queryStructure.objectNameInfo, queryStructure.objectInfoLength, &queryStructure.returnLength);
		SetEvent(endQuery);
	}
	return 0;
}

DWORD WINAPI queryThreadCloseHandle(LPVOID parameter)
{
	while (WaitForSingleObject(beginQueryCloseHandle, INFINITE) == WAIT_OBJECT_0)
	{
		queryStructureCloseHandle.result = NtQueryObject(queryStructureCloseHandle.dupHandle, ObjectNameInformation, queryStructureCloseHandle.objectNameInfo, queryStructureCloseHandle.objectInfoLength, &queryStructureCloseHandle.returnLength);
		SetEvent(endQueryCloseHandle);
	}
	return 0;
}

HANDLE queryThreadHandle = CreateThread(0, 0, &queryThread, 0, 0, 0);
HANDLE queryThreadCloseHandleHandle = CreateThread(0, 0, &queryThreadCloseHandle, 0, 0, 0);

void ConvertPath(wstring *path, map<wstring, wstring> *volumes)
{
	for (map<wstring, wstring>::iterator i = (*volumes).begin(); i != (*volumes).end(); ++i)
	{
		if ((*path).compare(0, (*i).first.size(), (*i).first) == 0)
		{
			*path = (*path).replace(0, (*i).first.size(), (*i).second);
			break;
		}
	}
}

void CheckForLocks(wstring fullPath, vector<unsigned long> *processes)
{
	DWORD	CharCount = 0;
	WCHAR	DeviceName[MAX_PATH] = L"";
	HANDLE	FindHandle = INVALID_HANDLE_VALUE;
	size_t	Index = 0;
	BOOL	Success = FALSE;
	WCHAR	VolumeName[MAX_PATH] = L"";
	map<wstring, wstring> volumes;
	FindHandle = FindFirstVolumeW(VolumeName, ARRAYSIZE(VolumeName));
	while (true)
	{
		Index = wcslen(VolumeName) - 1;
		if (VolumeName[0] != L'\\' || VolumeName[1] != L'\\' || VolumeName[2] != L'?' || VolumeName[3] != L'\\' || VolumeName[Index] != L'\\')
		{
			break;
		}
		VolumeName[Index] = L'\0';
		CharCount = QueryDosDeviceW(&VolumeName[4], DeviceName, ARRAYSIZE(DeviceName));
		VolumeName[Index] = L'\\';
		if (CharCount == 0)
		{
			break;
		}
		DWORD	size = MAX_PATH + 1;
		PWCHAR	name = NULL;
		BOOL	success = FALSE;
		while (!success)
		{
			name = (PWCHAR) new BYTE[size * sizeof(WCHAR)];
			success = GetVolumePathNamesForVolumeNameW(VolumeName, name, size, &size);
			if (!success)
			{
				delete[] name;
				name = NULL;
			}
		}
		volumes[DeviceName + wstring(L"\\")] = name;
		if (name != NULL)
		{
			delete[] name;
			name = NULL;
		}
		Success = FindNextVolumeW(FindHandle, VolumeName, ARRAYSIZE(VolumeName));
		if (!Success)
		{
			if (GetLastError() != ERROR_NO_MORE_FILES)
			{
				break;
			}
			break;
		}
	}
	FindVolumeClose(FindHandle);
	FindHandle = INVALID_HANDLE_VALUE;
	NTSTATUS status;
	PSYSTEM_HANDLE_INFORMATION handleInfo;
	ULONG handleInfoSize = 0x10000;
	HANDLE processHandle;
	ULONG i;
	DWORD pid;
	HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	PROCESSENTRY32 process;
	ZeroMemory(&process, sizeof(process));
	process.dwSize = sizeof(process);
	if (Process32First(snapshot, &process))
	{
		do
		{
			pid = process.th32ProcessID;
			if (!(processHandle = OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid)))
			{
				continue;
			}
			handleInfo = (PSYSTEM_HANDLE_INFORMATION)malloc(handleInfoSize);
			while ((status = NtQuerySystemInformation(SystemHandleInformation, handleInfo, handleInfoSize, NULL)) == STATUS_INFO_LENGTH_MISMATCH)
			{
				handleInfo = (PSYSTEM_HANDLE_INFORMATION)realloc(handleInfo, handleInfoSize *= 2);
			}
			if (!NT_SUCCESS(status))
			{
				return;
			}
			for (i = 0; i < handleInfo->HandleCount; i++)
			{
				SYSTEM_HANDLE handle = handleInfo->Handles[i];
				HANDLE dupHandle = NULL;
				POBJECT_TYPE_INFORMATION objectTypeInfo;
				PVOID objectNameInfo;
				UNICODE_STRING objectName;
				ULONG returnLength = 0;
				if (handle.ProcessId != pid)
					continue;
				if (!NT_SUCCESS(NtDuplicateObject(processHandle, (HANDLE)handle.Handle, GetCurrentProcess(), &dupHandle, 0, 0, 0)))
				{
					continue;
				}
				objectTypeInfo = (POBJECT_TYPE_INFORMATION)malloc(0x1000);
				size_t currentSize = 0x1000;
				objectNameInfo = malloc(currentSize);
				queryStructure.dupHandle = dupHandle;
				queryStructure.objectNameInfo = objectNameInfo;
				queryStructure.objectInfoLength = 0x1000;
				queryStructure.returnLength = returnLength;
				queryStructure.result = -1;
				SetEvent(beginQuery);
				if (WaitForSingleObject(endQuery, 100) == WAIT_TIMEOUT)
				{
					TerminateThread(queryThreadHandle, 1);
					CloseHandle(queryThreadHandle);
					queryThreadHandle = CreateThread(0, 0, &queryThread, 0, 0, 0);
					CloseHandle(dupHandle);
					continue;
				}
				if (!NT_SUCCESS(queryStructure.result))
				{
					objectNameInfo = realloc(objectNameInfo, currentSize *= 2);
					if (!NT_SUCCESS(NtQueryObject(dupHandle, ObjectNameInformation, objectNameInfo, returnLength, NULL)))
					{
						free(objectTypeInfo);
						free(objectNameInfo);
						CloseHandle(dupHandle);
						continue;
					}
				}
				objectName = *(PUNICODE_STRING)objectNameInfo;
				if (objectName.Length)
				{
					wstring objectNameAsWString = wstring(objectName.Buffer, objectName.Length / sizeof(WCHAR));
					ConvertPath(&objectNameAsWString, &volumes);
					if ((int)objectNameAsWString.find(fullPath) >= 0)
					{
						(*processes).push_back(pid);
						free(objectTypeInfo);
						free(objectNameInfo);
						CloseHandle(dupHandle);
						break;
					}
				}
				free(objectTypeInfo);
				free(objectNameInfo);
				CloseHandle(dupHandle);
			}
			free(handleInfo);
			CloseHandle(processHandle);
		} while (Process32Next(snapshot, &process));
	}
}

bool CloseHandles(wstring fullPath, int process)
{
	DWORD	CharCount = 0;
	WCHAR	DeviceName[MAX_PATH] = L"";
	HANDLE	FindHandle = INVALID_HANDLE_VALUE;
	size_t	Index = 0;
	BOOL	Success = FALSE;
	WCHAR	VolumeName[MAX_PATH] = L"";
	map<wstring, wstring> volumes;
	FindHandle = FindFirstVolumeW(VolumeName, ARRAYSIZE(VolumeName));
	while (true)
	{
		Index = wcslen(VolumeName) - 1;
		if (VolumeName[0] != L'\\' || VolumeName[1] != L'\\' || VolumeName[2] != L'?' || VolumeName[3] != L'\\' || VolumeName[Index] != L'\\')
		{
			break;
		}
		VolumeName[Index] = L'\0';
		CharCount = QueryDosDeviceW(&VolumeName[4], DeviceName, ARRAYSIZE(DeviceName));
		VolumeName[Index] = L'\\';
		if (CharCount == 0)
		{
			break;
		}
		DWORD	size = MAX_PATH + 1;
		PWCHAR	name = NULL;
		BOOL	success = FALSE;
		while (!success)
		{
			name = (PWCHAR) new BYTE[size * sizeof(WCHAR)];
			success = GetVolumePathNamesForVolumeNameW(VolumeName, name, size, &size);
			if (!success)
			{
				delete[] name;
				name = NULL;
			}
		}
		volumes[DeviceName + wstring(L"\\")] = name;
		if (name != NULL)
		{
			delete[] name;
			name = NULL;
		}
		Success = FindNextVolumeW(FindHandle, VolumeName, ARRAYSIZE(VolumeName));
		if (!Success)
		{
			if (GetLastError() != ERROR_NO_MORE_FILES)
			{
				break;
			}
			break;
		}
	}
	FindVolumeClose(FindHandle);
	FindHandle = INVALID_HANDLE_VALUE;
	_NtQuerySystemInformation NtQuerySystemInformation = (_NtQuerySystemInformation)GetLibraryProcAddress("ntdll.dll", "NtQuerySystemInformation");
	_NtDuplicateObject NtDuplicateObject = (_NtDuplicateObject)GetLibraryProcAddress("ntdll.dll", "NtDuplicateObject");
	_NtQueryObject NtQueryObject = (_NtQueryObject)GetLibraryProcAddress("ntdll.dll", "NtQueryObject");
	NTSTATUS status;
	PSYSTEM_HANDLE_INFORMATION handleInfo;
	ULONG handleInfoSize = 0x10000;
	HANDLE processHandle;
	ULONG i;
	if (!(processHandle = OpenProcess(PROCESS_DUP_HANDLE, FALSE, process)))
	{
		return false;
	}
	handleInfo = (PSYSTEM_HANDLE_INFORMATION)malloc(handleInfoSize);
	while ((status = NtQuerySystemInformation(SystemHandleInformation, handleInfo, handleInfoSize, NULL)) == STATUS_INFO_LENGTH_MISMATCH)
	{
		handleInfo = (PSYSTEM_HANDLE_INFORMATION)realloc(handleInfo, handleInfoSize *= 2);
	}
	if (!NT_SUCCESS(status))
	{
		return false;
	}
	for (i = 0; i < handleInfo->HandleCount; i++)
	{
		SYSTEM_HANDLE handle = handleInfo->Handles[i];
		HANDLE dupHandle = NULL;
		POBJECT_TYPE_INFORMATION objectTypeInfo;
		PVOID objectNameInfo;
		UNICODE_STRING objectName;
		ULONG returnLength = 0;
		if (handle.ProcessId != process)
			continue;
		if (!NT_SUCCESS(NtDuplicateObject(processHandle, (HANDLE)handle.Handle, GetCurrentProcess(), &dupHandle, 0, 0, 0)))
		{
			continue;
		}
		objectTypeInfo = (POBJECT_TYPE_INFORMATION)malloc(0x1000);
		size_t currentSize = 0x1000;
		objectNameInfo = malloc(currentSize);
		queryStructureCloseHandle.dupHandle = dupHandle;
		queryStructureCloseHandle.objectNameInfo = objectNameInfo;
		queryStructureCloseHandle.objectInfoLength = 0x1000;
		queryStructureCloseHandle.returnLength = returnLength;
		queryStructureCloseHandle.result = -1;
		SetEvent(beginQueryCloseHandle);
		if (WaitForSingleObject(endQueryCloseHandle, 100) == WAIT_TIMEOUT)
		{
			TerminateThread(queryThreadCloseHandleHandle, 1);
			CloseHandle(queryThreadCloseHandleHandle);
			queryThreadCloseHandleHandle = CreateThread(0, 0, &queryThreadCloseHandle, 0, 0, 0);
			CloseHandle(dupHandle);
			continue;
		}
		if (!NT_SUCCESS(queryStructureCloseHandle.result))
		{
			objectNameInfo = realloc(objectNameInfo, currentSize *= 2);
			if (!NT_SUCCESS(NtQueryObject(dupHandle, ObjectNameInformation, objectNameInfo, returnLength, NULL)))
			{
				free(objectTypeInfo);
				free(objectNameInfo);
				CloseHandle(dupHandle);
				continue;
			}
		}
		objectName = *(PUNICODE_STRING)objectNameInfo;
		if (objectName.Length)
		{
			wstring objectNameAsWString = wstring(objectName.Buffer, objectName.Length / sizeof(WCHAR));
			ConvertPath(&objectNameAsWString, &volumes);
			if ((int)objectNameAsWString.find(fullPath) >= 0)
			{
				NtDuplicateObject(processHandle, (HANDLE)handle.Handle, 0, 0, 0, 0, 1);
			}
		}
		free(objectTypeInfo);
		free(objectNameInfo);
		CloseHandle(dupHandle);
	}
	free(handleInfo);
	CloseHandle(processHandle);
	return true;
}

Note: The two most important methods above are firstly, CheckForLocks, which takes either a system drive path, file path, or folder path, and returns a vector of processes with open handles at the given path, and secondly CloseHandles, which takes the same types of paths and a process ID. CloseHandles closes any open handles (containing the specified path) of a target process via a specified process ID. Below, I will even include some code to produce a CLR wrapper around this Static Library so that you can call it from C#.

Unmanaged.h:

#include "..\UnmanagedLibrary\UnmanagedLibrary.h"

using namespace System;
using namespace System::Collections::Generic;

namespace Unmanaged
{
	public ref class Handles
	{
	public:
		static List<int>^ GetProcessesWithOpenHandlesToPath(String^ path);
		static bool CloseOpenHandlesToPathInProcess(String^ path, int process);
	};
}

Unmanaged.cpp:

#include "Unmanaged.h"
#include "msclr\marshal_cppstd.h"

List<int>^ Unmanaged::Handles::GetProcessesWithOpenHandlesToPath(String^ path)
{
	List<int>^ list = gcnew List<int>();
	wstring convertedString = msclr::interop::marshal_as<wstring>(path);
	vector<unsigned long> processes;
	CheckForLocks(convertedString, &processes);
	for (vector<unsigned long>::iterator i = processes.begin(); i != processes.end(); i++) {
		list->Add(*i);
	}
	return list;
}

bool Unmanaged::Handles::CloseOpenHandlesToPathInProcess(String^ path, int process)
{
	List<int>^ list = gcnew List<int>();
	wstring convertedString = msclr::interop::marshal_as<wstring>(path);
	return CloseHandles(convertedString, process);
}

From C#, you simply call these two functions of the Unmanaged DLL:

List<int> processes = Unmanaged.Handles.GetProcessesWithOpenHandlesToPath(path);
Unmanaged.Handles.CloseOpenHandlesToPathInProcess(path, processID);

If you study the code well, you will realize that the only specialty item here is that you need to terminate threads while you enumerate handles in order to stop deadlocks from happening. There is one special, odd, weird (whatever you want to call it) case in which deadlocks happen, and that is when you call NtQueryObject on handles which reference synchronous file objects (chances are that the file object was opened by a kernel-mode driver with FILE_SYNCHRONOUS_IO_NONALERT). The thread will wait indefinitely to acquire the file lock because interrupting the wait isn’t possible, which means you have to terminate the thread.

Working with Big Numbers in C#

I recently set out to do some universal gravitation calculations in code, but I soon found that a lot of the existing or built-in data-types (Int64, Decimal, etc.) for doing so in .NET were rather inadequate (they either did not allow a high degree of precision, or they did not allow numbers large enough to suit my needs), so I searched for something that I could use to model very large numbers to an acceptably large degree of accuracy. That’s when I came across the BigRational class on CodePlex. This class decomposes large fractions into two BigInteger values (one to represent the numerator and another to represent the denominator), since any fractional number can be decomposed as such. I kept on royally butchering my by-hand calculations (since there are so many big numbers at play, along with unit conversions, etc.), and I got fed up to the point that I set out to create a code-based model in order to calculate surface gravities for the Sun and all of the planets in our Solar System using Newton’s law of universal gravitation:

using Numerics;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;

namespace UniversalPhysics
{

    public static class BigRationals
    {
        public static BigRational WithExponent(BigRational value, BigInteger exponent)
        {
            return BigRational.Multiply(value, BigRational.Pow(new BigRational(10, 1), exponent));
        }

        public static BigRational FromInteger(BigInteger value)
        {
            return new BigRational(value);
        }

        public static BigRational FromDouble(double value)
        {
            if (value == 0 || Double.IsNaN(value))
            {
                return new BigRational(0, 1);
            }
            if (Double.IsPositiveInfinity(value))
            {
                value = Double.MaxValue;
            }
            if (Double.IsNegativeInfinity(value))
            {
                value = Double.MinValue;
            }
            double fraction = value % 1;
            double numerator = value - fraction;
            double denominator = 1;
            // Notes:
            // 
            // - You are at the mercy of the precision of the double data type.
            // - If you debug this, you will see that the fraction variable changes in precision.
            // - The double data type does this to accomodate similar levels of precision.
            // - This may result in a slight skew of a very large value variable or a very small value variable.
            // - This effect is negligible when the value's precision is much less than Double.MaxValue's precision.
            while (!Double.IsNaN(fraction))
            {
                double decimalPlace = fraction * 10 * denominator - (fraction * 10 * denominator) % 1;
                if (Double.IsInfinity(numerator * 10 + decimalPlace))
                {
                    break;
                }
                if (Double.IsInfinity(denominator * 10))
                {
                    break;
                }
                denominator *= 10;
                numerator = numerator * 10 + decimalPlace;
                fraction = fraction - decimalPlace / denominator;
            }
            BigInteger numeratorAsBigInteger = new BigInteger(numerator);
            BigInteger denominatorAsBigInteger = new BigInteger(denominator);
            return new BigRational(numeratorAsBigInteger, denominatorAsBigInteger);
        }

        public static BigRational FromDoubleWithExponent(double value, BigInteger exponent)
        {
            return WithExponent(FromDouble(value), exponent);
        }
    }

    class Program
    {
        
        static SolarSystem System = new SolarSystem();

        static void Main(string[] args)
        {
            Console.WriteLine("Sun's Surface Gravity = " + (double)System.Sun.SurfaceGravity);
            Console.WriteLine("Mercury's Surface Gravity = " + (double)System.Mercury.SurfaceGravity);
            Console.WriteLine("Venus' Surface Gravity = " + (double)System.Venus.SurfaceGravity);
            Console.WriteLine("Earth's Surface Gravity = " + (double)System.Earth.SurfaceGravity);
            Console.WriteLine("Mars' Surface Gravity = " + (double)System.Mars.SurfaceGravity);
            Console.WriteLine("Jupiter's Surface Gravity = " + (double)System.Jupiter.SurfaceGravity);
            Console.WriteLine("Saturn's Surface Gravity = " + (double)System.Saturn.SurfaceGravity);
            Console.WriteLine("Uranus' Surface Gravity = " + (double)System.Uranus.SurfaceGravity);
            Console.WriteLine("Neptune's Surface Gravity = " + (double)System.Neptune.SurfaceGravity);
            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }
    }

    public class SolarSystem
    {

        public Planet Sun;
        public Planet Mercury;
        public Planet Venus;
        public Planet Earth;
        public Planet Mars;
        public Planet Jupiter;
        public Planet Saturn;
        public Planet Uranus;
        public Planet Neptune;

        public SolarSystem()
        {
            // Notes:
            //
            // - When viewed from above the Sun's north pole, the sun spins counter-clockwise on its axis.
            // - Prograde motion, when viewed from above the Sun's north pole, means counter-clockwise motion.
            // - Retrograde motion, when viewed from above the Sun's north pole, means clockwise motion.
            //
            // Putting this all together, when viewed from above the Sun's north pole:
            //
            // - The sun spins counter-clockwise.
            // - Venus and Uranus are the only two planets which have clockwise orbits.
            //      - All other planets have counter-clockwise orbits.
            // - Venus is the only planet that spins clockwise on its axis.
            //      - All other planets spin counter-clockwise on their axes.
            //      - Uranus kind-of also spins counter-clockwise, but the angle defined by its axial tilt makes it quite an oddball.
            //          - It doesn't spin like the other planets.
            //          - If you were the Sun, and a car in front of you was facing you with its front, and that car started to drive towards you, the way the wheels would spin on that car would be similar to the direction Uranus spins.
            //
            // What does this all mean?
            //
            // - Well, a planet's sidereal rotation period is really defined by axial tilt.
            // - Axial tilt (or obliquity), is the angle between an object's rotational axis and its orbital axis.
            //      - Equivalently, its the angle between a planet's equatorial plane and orbital plane.
            //      - It differs from orbital inclination.
            // - Don't take what I say for granted until you apply axial tilt for yourself, because I've made approximations with terms like clockwise and counter-clockwise.
            Sun = new Planet
            {
                Mass = BigRationals.FromDoubleWithExponent(1.98855, 30),
                MeanRadius = BigRationals.FromInteger(696342000),
                SiderealRotationPeriod = TimeSpan.FromDays(25.05)
            };
            Mercury = new Planet
            {
                Mass = BigRationals.FromDoubleWithExponent(3.3022, 23),
                MeanRadius = BigRationals.FromInteger(2439700),
                SiderealRotationPeriod = TimeSpan.FromHours(1407.5)
            };
            Venus = new Planet
            {
                Mass = BigRationals.FromDoubleWithExponent(4.8676, 24),
                MeanRadius = BigRationals.FromInteger(6051800),
                SiderealRotationPeriod = TimeSpan.FromDays(243.0185) // Retrograde (this planet orbits the Sun in the opposite spin direction of the Sun).
            };
            Earth = new Planet
            {
                Mass = BigRationals.FromDoubleWithExponent(5.97219, 24),
                MeanRadius = BigRationals.FromInteger(6371000),
                SiderealRotationPeriod = new TimeSpan(0, 23, 56, 4, 100)
            };
            Mars = new Planet
            {
                Mass = BigRationals.FromDoubleWithExponent(6.4185, 23),
                MeanRadius = BigRationals.FromInteger(3389500),
                SiderealRotationPeriod = new TimeSpan(24, 37, 22)
            };
            Jupiter = new Planet
            {
                Mass = BigRationals.FromDoubleWithExponent(1.8986, 27),
                MeanRadius = BigRationals.FromInteger(69911000),
                SiderealRotationPeriod = new TimeSpan(9, 55, 30)
            };
            Saturn = new Planet
            {
                Mass = BigRationals.FromDoubleWithExponent(5.6846, 26),
                MeanRadius = BigRationals.FromInteger(58232000),
                SiderealRotationPeriod = new TimeSpan(10, 34, 0)
            };
            Uranus = new Planet
            {
                Mass = BigRationals.FromDoubleWithExponent(8.681, 25),
                MeanRadius = BigRationals.FromInteger(25362000),
                SiderealRotationPeriod = new TimeSpan(17, 14, 24) // Retrograde (this planet orbits the Sun in the opposite spin direction of the Sun).
            };
            Neptune = new Planet
            {
                Mass = BigRationals.FromDoubleWithExponent(1.0243, 26),
                MeanRadius = BigRationals.FromInteger(24622000),
                SiderealRotationPeriod = new TimeSpan(16, 6, 36)
            };
        }
    }

    public class Planet
    {
        public BigRational Mass;
        public BigRational MeanRadius;
        public TimeSpan SiderealRotationPeriod;
        public BigRational AngularVelocity
        {
            get
            {
                return Laws.GetAngularVelocity(SiderealRotationPeriod);
            }
        }
        public BigRational SurfaceGravity
        {
            get
            {
                return Laws.GetNewtonianSurfaceGravity(Mass, MeanRadius);
            }
        }
    }

    public static class Laws
    {
        public static BigRational GetAngularVelocity(TimeSpan siderealRotationPeriod)
        {
            return BigRational.Divide(BigRational.Multiply(new BigRational(2.0), Constants.PI), BigRationals.FromDouble(siderealRotationPeriod.TotalSeconds));
        }

        public static BigRational GetNewtonianSurfaceGravity(BigRational mass, BigRational meanRadius)
        {
            return BigRational.Divide(BigRational.Multiply(Constants.GravitationalConstant, mass), BigRational.Pow(meanRadius, 2));
        }
    }

    public static class Constants
    {
        public static BigRational PI = BigRationals.FromDouble(3.141592654);
        public static BigRational GravitationalConstant = BigRationals.FromDoubleWithExponent(6.67384, -11);
        public static BigRational SpeedOfLight = BigRationals.FromInteger(299792458);
    }
}

Newton’s law of universal gravitation has been superseded by Einstein’s theory of general relativity, but it is still used as a good approximation of gravitational force. Relativity is for extreme precision, or for cases around the boundaries of Newton’s law of universal gravitation (very big masses, or very close massive objects). Alas, the output to all of that code above is as follows (the values obtained from Newton’s formulae are fairly close to the values from NASA’s Planetary Fact Sheet):

Sun's Surface Gravity = 273.695164677966
Mercury's Surface Gravity = 3.70259586050561
Venus' Surface Gravity = 8.86995750090755
Earth's Surface Gravity = 9.81960902526831
Mars' Surface Gravity = 3.72853358713613
Jupiter's Surface Gravity = 25.9249685707583
Saturn's Surface Gravity = 11.1879956428603
Uranus' Surface Gravity = 9.00696656052567
Neptune's Surface Gravity = 11.2760322511266
Press any key to exit.

What is string normalization, how do ordinal string comparisons work, and why should I care?

The following code will always return -1.

"Example\\̼".IndexOf("Example\\");

This is because the Combining Seagull Below character, U+033C, is a combining character, and it will combine with the character previous to it in order to produce a new character. Using an ordinal string comparison would fix this problem for you. As an example, the following code will always return 0:

"Example\\̼".IndexOf("Example\\", StringComparison.OrdinalIgnoreCase)

The following code will always, also return -1.

"µ".IndexOf("μ");

This is because, although µ and μ are the same symbol, they are actually two different characters that are part of the Unicode character set (one is called the Micro Sign character, U+00B5, and the other is the Greek Small Letter Mu character, U+03BC). Using an ordinal string comparison will not fix this, but normalization will. The following code will always return 0:

"µ".Normalize(NormalizationForm.FormKD).IndexOf("μ".Normalize(NormalizationForm.FormKD))

Simple Data Serialization in .NET

It certainly is possible to serialize an object in .NET without all the fuss of dealing with encodings, data contracts, the XmlSerializer class, and annotations like [DataContract] or [Serializable]. I want to illustrate a solution using Linq and XElement objects, one that I’ve grown quite fond of by the way. You do not need to use the XmlSerializer or the DataContractSerializer or any class or property annotations like [DataContract] or [Serializable] for that matter, if you do not wish to do so. In a nutshell, here it is:

using System;
using System.Linq;
using System.Xml.Linq;

namespace SerializationTesting
{

    class Person
    {

        // Notice how this object type uses private setters, something that the traditional XmlSerializer will complain about if you don't use a wrapper class..
        public string Name { get; private set; }
        public DateTime Birthday { get; private set; }
        public long HeightInMillimeters { get; private set; }
        public Gender Gendrality { get; private set; }

        // Generate a serialized XElement from this Person object.
        public XElement ToXElement()
        {
            return new XElement("person",
                new XAttribute("name", Name),
                new XAttribute("birthday", Birthday),
                new XAttribute("heightInMillimeters", HeightInMillimeters),
                new XAttribute("gendrality", (long)Gendrality)
            );
        }

        // Serialize this Person object to an XElement.
        public static Person FromXElement(XElement x)
        {
            return new Person(
                (string)x.Attribute("name"),
                (DateTime)x.Attribute("birthday"),
                (long)x.Attribute("heightInMillimeters"),
                (Gender)(long)x.Attribute("gendrality")
            );
        }

        public Person(string name, DateTime birthday, long heightInMillimeters, Gender gender)
        {
            Name = name;
            Birthday = birthday;
            HeightInMillimeters = heightInMillimeters;
            Gendrality = gender;
        }

        // You must override this in conjunction with overriding GetHashCode (below) if you want .NET collections (HashSet, List, etc.) to properly compare Person objects.
        public override bool Equals(object obj)
        {
            if (obj.GetType() == typeof(Person))
            {
                Person objAsPerson = (Person)obj;
                return Name == objAsPerson.Name && Birthday == objAsPerson.Birthday && HeightInMillimeters == objAsPerson.HeightInMillimeters && Gendrality == objAsPerson.Gendrality;
            }
            return false;
        }

        // You must override this in conjunction with overriding Equals (above) if you want .NET collections (HashSet, List, etc.) to properly compare Person objects.
        public override int GetHashCode()
        {
            return Name.GetHashCode() ^ Birthday.GetHashCode() ^ HeightInMillimeters.GetHashCode() ^ Gendrality.GetHashCode();
        }

        // This allows us to compare Person objects using the == operator.
        public static bool operator ==(Person a, Person b)
        {
            return a.Equals(b);
        }

        // This allows us to compate Person objects using the != operator.
        public static bool operator !=(Person a, Person b)
        {
            return !a.Equals(b);
        }
    }

    public enum Gender
    {
        Male,
        Female
    }

    class Program
    {
        static void Main(string[] args)
        {
            // Create first person (note how UTC time saves and loads properly when casting).
            Person personOne = new Person("Alexandru", DateTime.UtcNow, 1000, Gender.Male);
            // Save the first person to a local file on the hard disk.
            personOne.ToXElement().Save("PersonOne.dat");
            // Create second person (not using UTC time this time around).
            Person personTwo = new Person("Alexandria", DateTime.Now, 900, Gender.Female);
            // Save the second person to a local file on the hard disk.
            personTwo.ToXElement().Save("PersonTwo.dat");
            // Load the first person from a local file on the hard disk.
            XDocument personOneDocument = XDocument.Load("PersonOne.dat");
            Person personOneLoadedFromDocument = Person.FromXElement(personOneDocument.Elements().First());
            // Load the second person from a local file on the hard disk.
            XDocument personTwoDocument = XDocument.Load("PersonTwo.dat");
            Person personTwoLoadedFromDocument = Person.FromXElement(personTwoDocument.Elements().First());
            // Serialize the first person to a string and then load them from that string.
            string personOneString = personOne.ToXElement().ToString();
            XDocument personOneDocumentFromString = XDocument.Parse(personOneString);
            Person personOneLoadedFromDocumentFromString = Person.FromXElement(personOneDocumentFromString.Elements().First());
            // Check for equalities between persons (all outputs will be "true").
            Console.WriteLine(personOne.Equals(personOneLoadedFromDocument));
            Console.WriteLine(personTwo.Equals(personTwoLoadedFromDocument));
            Console.WriteLine(personOne == personOneLoadedFromDocument);
            Console.WriteLine(personTwo == personTwoLoadedFromDocument);
            Console.WriteLine(personOne != personTwo);
            Console.WriteLine(personOneLoadedFromDocument != personTwoLoadedFromDocument);
            Console.WriteLine(personOne.Equals(personOneLoadedFromDocumentFromString));
            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }
    }
}

The output of all of the equality checks in the console application above will be true, as expected. This does not suffer from annoyances like having to keep track of encodings or the way you parse data because it does all of that for you, and it does not limit your class to public setters like the XmlSerializer or some others do. This gives you much more flexibility in the long run, and will help ensure your code-base does not grow where it doesn’t need to.

Quick Tutorial on String Types in Windows and Microsoft Visual C++ (MSVC++)

If you want a simple and quick run-down of the string types in Windows and MSVC++, how they work, what they all mean, and how to program with them, I’ve put together the following snippet of code with explanations on the types of strings that MSVC++ exposes to developers and what their type-names mean, as well as a brief introduction to encoding sets and their effect on this mysterious but wonderful game of Windows string types:

#include "stdafx.h"
#include "Windows.h"

int _tmain(int argc, _TCHAR* argv[])
{
	/* Quick Tutorial on Strings in Microsoft Visual C++

	   The Unicode Character Set and Multibyte Character Set options in MSVC++ provide a project with two flavours of string encodings. They will use different encodings for characters in your project. Here are the two main character types in MSVC++ that you should be concerned about:

	   1. char <-- char characters use an 8-bit character encoding (8 bits = 1 byte) according to MSDN.
	   2. wchar_t <-- wchar_t uses a 16-bit character encoding (16 bits = 2 bytes) according to MSDN.

	   From above, we can see that the size of each character in our strings will change depending on our chosen character set.
	   
	   WARNING: Do NOT assume that any given character you append to either a Mutlibyte or Unicode string will always take up a single-byte or double-byte space defined by char or wchar_t! That is up to the discretion of the encoding used. Sometimes, characters need to be combined to define a character that the user wants in their string. In other words, take this example: Multibyte character strings take up a byte per character inside of the string, but that does not mean that a given byte will always produce the character you desire at a particular location, because even multibyte characters may take up more than a single byte. MSDN says it may take up TWO character spaces to produce a single multibyte-encoded character: "A multibyte-character string may contain a mixture of single-byte and double-byte characters. A two-byte multibyte character has a lead byte and a trail byte."

	   WARNING: Do NOT assume that Unicode contains every character for every language. For more information, please see http://stackoverflow.com/questions/5290182/how-many-bytes-takes-one-unicode-character.

	   Note: The ASCII Character Set is a subset of both Multibyte and Unicode Character Sets (in other words, both of these flavours encompass ASCII characters).
	   Note: You should always use Unicode for new development, according to MSDN. For more information, please see http://msdn.microsoft.com/en-us/library/ey142t48.aspx.
	*/
	// Strings that are Multibyte.
	LPSTR a; // Regular Multibyte string (synonymous with char *).
	LPCSTR b; // Constant Multibyte string (synonymous with const char *).
	// Strings that are Unicode.
	LPWSTR c; // Regular Unicode string (synonymous with wchar_t *).
	LPCWSTR d; // Constant Unicode string (synonymous with const wchar_t *).
	// Strings that take on either Multibyte or Unicode depending on project settings.
	LPTSTR e; // Multibyte or Unicode string (can be either char * or wchar_t *).
	LPCTSTR f; // Constant Multibyte or Unicode string (can be either const char * or const wchar_t *).
	/* From above, it is safe to assume that the pattern is as follows:

	   LP: Specifies a long pointer type (this is synonymous with prefixing this type with a *).
	   W: Specifies that the type is of the Unicode Character Set.
	   C: Specifies that the type is constant.
	   T: Specifies that the type has a variable encoding.
	   STR: Specifies that the type is a string type.
	*/
	// String format specifiers:
	e = _T("Example."); // Formats a string as either Multibyte or Unicode depending on project settings.
	e = TEXT("Example."); // Formats a string as either Multibyte or Unicode depending on project settings (same as _T).
	c = L"Example."; // Formats a string as Unicode.
	a = "Example."; // Formats a string as Multibyte.
	return 0;
}

Installing and Building Boost C++ Libraries for Microsoft Visual C++ (MSVC++) from SourceForge

Boost is an awesome tool-set for C++ developers. Here’s how you can install it and get your first application working with Boost:

  • Download the archived source libraries from SourceForge.
  • Unzip the archive to a local directory.
    • For this example, I extracted everything to “C:\Users\User\Downloads\boost_1_56_0”.
  • Launch Command Prompt as Administrator, and run these commands to build the boost libraries:
    • cd “C:\Users\User\Downloads\boost_1_56_0”
    • bootstrap.bat
    • b2.exe
  • Right click your MSVC++ project, go to Properties, and under Configuration Properties, hit VC++ Directories.
    • Add Boost to your include directories: “C:\Users\User\Downloads\boost_1_56_0”
    • Add Boost to your library directories: “C:\Users\User\Downloads\boost_1_56_0\stage\lib”
    • Here is an example:

The required project settings for building Boost projects.

Hosting an IIS Site in a Local Area Network

So, you have published your first IIS site, and now you want to try hosting, running, or testing it on your LAN (Local Area Network). Here are some things I had to do to get it working:

  • Go to Control Panel -> Network and Internet -> Network and Sharing Center -> Change advanced sharing settings, and then ensure that “Turn on network discovery” is enabled.
  • Go to Windows Firewall -> Advanced settings. In Windows Firewall with Advanced Security, go to Inbound Rules. Sort by Name and enable “World Wide Web Services (HTTPS Traffic-In)” as well as “World Wide Web Services (HTTP Traffic-In)”. These will enable Local Port 443 and 80 for inbound communications.
  • Go to Internet Information Services (IIS) Manager. Expand your server, go to Sites, and right-click the site in question. Click on “Edit Bindings…”, and give your site a binding with a simple, one word hostname, like the name of your PC and make it either an HTTP or HTTPS binding (so host it on either port 80 or 443). For example, I gave my site a name of “crapplication”. Click on Restart under Manage Website to restart the site.
  • On some other computer in your Local Area Network, try browsing to http://crapplication and you should see your site.

If you don’t see the site at this point, you may have a pretty hardcore firewall blocking your connection or some other type of routing issue. Try to disable all firewalls and then try it again. Ensure that you can also ping the machine that’s running your IIS site. Usually, routers will tell you the LAN IP of your computers, so you can try and use Command Prompt to ping between LAN IPs.

Why Public Key Cryptography is Broken

Public key cryptography is actually excellent when used correctly, but this is not the case for modern web browsers (which is mostly what it’s all designed for), and I’ll show you why. As an average Joe, you’ve probably used a web browser to visit a web site on the internet many times:

PKI1

As shown above, the site your browser talks to is hosted on a server (some computer out there, connected to the internet). You may be interested to do online banking, so you really don’t want that server you’re speaking with to be someone else pretending to be them:

PKI2

This is called a man-in-the-middle attack. Public key cryptography is supposed to stop this from happening. If I own a website, I can try and ensure this doesn’t happen by enabling SSL on my site. To enable SSL, I need to initiate a certificate signing request. IIS does a lot of this transparently, but basically it will create both public and private key pairs for my server and all the details I need to get a signed certificate. I then go to some certificate authority, send them all the details of my request (except my private key, which Windows stores internally in its Cryptographic Service Providers), pay them some money, and get them to sign my public certificate. They then return this public key certificate back to me:

PKI3

I then add that certificate to my server. The certificate only exposes the public key to the outside world and IIS will use the private key within the Cryptographic Service Providers for its key exchange mechanism:

PKI4

When Joe connects to my site using his web browser, my site gives him the signed public certificate if he connects using SSL (usually, the “https” prefix to a URL will indicate that the connection is secure, versus the unsecure “http” prefix):

PKI5

Joe’s web browser checks to see if that signed public certificate’s signature is valid by comparing it to a cache of public key certificates that it has from all of the different certificate authorities it supports:

PKI6

Joe’s browser will establish a secure communication channel with that site. If it can’t verify the signature of that certificate, it will warn him:

SSL Errors

Not being able to verify indicates the possibility of a man-in-the-middle – or does it? That’s pretty much the process, and yes – you heard right. Web browsers have a local cache of public key certificates from certificate authorities that they use to check signature accuracy on certificates that sites present to them. This is a huge flaw, for so many reasons. If a certificate authority’s private key is cracked and they need to reissue key pairs (and thus, new certificates), you may be vulnerable until your web browser manufacturers release a patch or somehow push new certificates down to your browser, if you ever end up updating the browser itself that is, or if they actually push new certificates securely. This is just one problem. But what I really wanted to get at is this – nothing is stopping a man-in-the-middle from posing as your server if he can get a certificate claiming to be you from any browser-supported certificate authority. All he needs is a signed certificate from any certificate authority that your browser supports, since your browser caches certificate authorities’ public certificates (and it will show as “Verified” for any certificate authority-signed certificate). As such, he can relay messages between you and your actual server, snooping on all the traffic, unencrypted:

PKI7

You see, nothing is really stopping an attacker from gaining a certificate like I did for my site except maybe the level of protection you pay for (meaning, if you originally paid for some sort of Extended Validation Certificate from your certificate authority, the attacker might have a harder time getting a certificate signed that claims he’s you from that same certificate authority). Extended validation doesn’t solve much of a problem, because not only can he actually obtain a signature from my certificate authority, he might be able to obtain one from any other certificate authority and claim to be my site, and you would have to know what certificate authority you’re using originally to actually detect such a thing (as well as periodically monitor your site for changes in signatures using external requests from across the globe since an attacker may only snoop based on IP geolocation):

PKI8

Not to mention the fact that a government can subpoena any certificate authority to forge a certificate claiming to be “someone else”, from the very same certificate authority that “someone else” used, and transparently monitor all traffic across that site unencrypted. So even if you do use HTTPS to connect to a site, it may still be monitored unencrypted by somebody. Are you skeptical that this could ever happen? Here’s some proof that’s its been done before. Also, some certificate authorities actually require that they generate both public and private key pairs for your server. This is a big no-no. You should never, ever expose your private key to anyone. Even certificate authorities go to extreme lengths to never let this happen to the private key they use to sign certificates.

But fear not, for there are nice and elegant ways to avoid such things. One way is to not expose vital information over the internet, physically distribute client software as applications, and have those applications use a mechanism known as certificate pinning. Essentially, this lets you become your own certificate authority…because as an average Joe, you aren’t allowed to add your own certificate authority certificate into a web browser’s cache unless you shake hands with the manufacturers and have some pretty deep connections.

So I ask you all this, armed with this knowledge, and after having read my article, do you trust certificate authorities?

If you want to read more on the underlying mechanism for key exchange, SSL certificates will usually use the RSA cryptosystem, which has been around since 1977 and has proven itself. Its based on the difficulty of factoring a number computationally. Its hard to find the factors of an extremely large number…but, I guess that’s a moving target, especially with supercomputers out there that have so many cores that threading out such a mathematical computation would likely break your private key in no time at all.

Writing Self-Replicating Code in C#

C# has some fun features under its hood, like its ability to compile an assembly from source code dynamically at run-time, allowing you to do things like write self-replicating code, code that learns (or artificial intelligence), and so forth, to whatever degree of metamorphic and polymorphic behavior you desire to get out of it.

Take this console application, for example. In each iteration, it rewrites itself under a new pseudonym and modifies its own source code every time it re-runs itself:

using Microsoft.CSharp;
using System;
using System.CodeDom.Compiler;
using System.Diagnostics;
using System.IO;
using System.Linq;

namespace Replicator
{
    class Program
    {

        static int iteration = 0;
        static string sourcePath = "../../Program.cs";

        static void Main(string[] args)
        {
            Console.WriteLine("Iteration: {0}.", iteration);
            CSharpCodeProvider provider = new CSharpCodeProvider();
            CompilerParameters parameters = new CompilerParameters(new[] { "System.dll", "System.Core.dll", "Microsoft.CSharp.dll", "System.Linq.dll" }, String.Format("Replicator{0}.exe", iteration.ToString()), true);
            parameters.GenerateExecutable = true;
            string sourceCode = File.ReadAllText(sourcePath);
            sourceCode = sourceCode.Replace(String.Format("static int iteration = {0};", iteration.ToString()), String.Format("static int iteration = {0};", (iteration + 1).ToString()));
            File.WriteAllText(sourcePath, sourceCode);
            CompilerResults results = provider.CompileAssemblyFromSource(parameters, sourceCode);
            if (results.Errors.HasErrors)
            {
                Console.WriteLine("A clone was not created. Fix your compile errors and try running this application again at a later time. Press any key to exit.");
                results.Errors.Cast<CompilerError>().ToList().ForEach(error => Console.WriteLine(error.ErrorText));
                Console.ReadLine();
            }
            else
            {
                Console.WriteLine("A clone has been created. Press any key to run it.");
                Console.ReadLine();
                Process.Start(String.Format("Replicator{0}.exe", iteration.ToString()));
            }
        }
    }
}