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()));
            }
        }
    }
}

Calculating the Core Frequencies of a Modern Intel CPU with Clock-Varying Features in Visual C++ on a Windows Machine

Note that the complete solution and download link is at the bottom. Modern Intel CPU’s have features such as Turbo Boost, which will vary the frequency of your CPU. Please note that (and I have seen this on multiple machines I own) you should disable the Hyper-V feature in Windows before trying out the code below, as Hyper-V seems to stop clock-variations from taking place on Intel CPU’s. In other words, it seems to stop Turbo Boost. To disable Hyper-V, go to Control Panel -> Uninstall a program -> Turn Windows features on or off (from the menu to the left of the window), and then deselect the Hyper-V check-box, and hit OK.

Before the introduction of Turbo Boost, functions such as QueryPerformanceFrequency in conjunction with some other methods provided an effective means of detecting a CPU’s clock speed. This is no longer the case. QueryPerformanceFrequency only seems to return the maximum frequency before clock-varying technologies start to kick in. However, this function is still going to be important (and still going to be used in our calculations). The actual formula for calculating CPU frequency is the maximum CPU frequency before clock-varying technologies multiplied by two Intel-specific registers which give you the ratio of actual performance over maximum performance (actualFrequency = maximumFrequencyWithoutClockSpeedVariations * APERF / MPERF). There registers are known as APERF and MPERF, and the ratio of APERF / MPERF is what we need to fetch.

To obtain the values of model-specific registers such as APERF or MPERF, we need to generate the rdmsr instruction using an Intrinsic Function known as __readmsr. Unfortunately, calling this function is not as simple as just, well…calling it. As the remark for it on MSDN goes, “This function is only available in kernel mode, and the routine is only available as an intrinsic.” When you write an application in Windows, like a WPF application or a Console Application, it runs as a user-mode application, meaning that it has some security restrictions in what you can do with it and that it runs in a ring of security called Ring 3, the least privileged security level. However, a kernel-mode application runs in Ring 0, the most privileged security level. Wikipedia has a great article on Protection Rings if you’d like to read more about them. As far as I know, the only way to get into kernel-mode is to write a kernel-mode driver. What this all means is that we need to have a user-mode application that talks to a kernel-mode driver in order to fetch the values of APERF and MPERF.

I started by downloading the Windows Driver Kit, which you will also need, so go ahead and grab it. I spent a lot of time trying to find examples of kernel-mode drivers that communicated with user-mode applications, and I came across a nice example on CodeProject. I noticed that the example in question uses driver source code that is only for WDM-based drivers, whereas most driver templates in the Windows Driver Kit are WDF-based. In fact, the latest Windows Driver Kit has no WDM driver with source code in it (only a blank template with absolutely no code). You can read about the differences between WDF and WDM on MSDN. WDM is clearly what we are after, as this article states, “The WDM model is closely tied to the operating system. Drivers interact directly with the operating system by calling system service routines and manipulating operating system structures. Because WDM drivers are trusted kernel-mode components, the system provides limited checks on driver input.” After spending some time looking through the Windows Driver Kit samples, I found the IOCTL sample driver which is a perfect sample skeleton driver that we can alter to fit our needs. Its description reads, “This sample driver is not a Plug and Play driver. This is a minimal driver meant to demonstrate a feature of the operating system. Neither this driver nor its sample programs are intended for use in a production environment. Instead, they are intended for educational purposes and as a skeleton driver.” I began modifying that driver to fit my actual needs. I also learned a few things along the way. For one, kernel drivers don’t like floating point arithmetic. I’m not even entirely sure that they will compile if you try to make use of a double or float type unless you find a means of explicitly including that type definition inside of your driver. Also, you’re limited because you cannot use the Windows header, and must use a special kernel-mode header known as the Ntddk header. I’ve modified the IOCTL sample skeleton as follows:

driver.c

//
// Include files.
//

#include <ntddk.h>          // various NT definitions
#include <string.h>
#include <intrin.h>

#include "driver.h"

#define NT_DEVICE_NAME      L"\\Device\\KernelModeDriver"
#define DOS_DEVICE_NAME     L"\\DosDevices\\KernelModeDriver"

#if DBG
#define DRIVER_PRINT(_x_) \
                DbgPrint("KernelModeDriver.sys: ");\
                DbgPrint _x_;

#else
#define DRIVER_PRINT(_x_)
#endif

//
// Device driver routine declarations.
//

DRIVER_INITIALIZE DriverEntry;

_Dispatch_type_(IRP_MJ_CREATE)
_Dispatch_type_(IRP_MJ_CLOSE)
DRIVER_DISPATCH DriverCreateClose;

_Dispatch_type_(IRP_MJ_DEVICE_CONTROL)
DRIVER_DISPATCH DriverDeviceControl;

DRIVER_UNLOAD DriverUnloadDriver;

VOID
PrintIrpInfo(
    PIRP Irp
    );
VOID
PrintChars(
    _In_reads_(CountChars) PCHAR BufferAddress,
    _In_ size_t CountChars
    );

#ifdef ALLOC_PRAGMA
#pragma alloc_text( INIT, DriverEntry )
#pragma alloc_text( PAGE, DriverCreateClose)
#pragma alloc_text( PAGE, DriverDeviceControl)
#pragma alloc_text( PAGE, DriverUnloadDriver)
#pragma alloc_text( PAGE, PrintIrpInfo)
#pragma alloc_text( PAGE, PrintChars)
#endif // ALLOC_PRAGMA


NTSTATUS
DriverEntry(
    _In_ PDRIVER_OBJECT   DriverObject,
    _In_ PUNICODE_STRING      RegistryPath
    )
/*++

Routine Description:
    This routine is called by the Operating System to initialize the driver.

    It creates the device object, fills in the dispatch entry points and
    completes the initialization.

Arguments:
    DriverObject - a pointer to the object that represents this device
    driver.

    RegistryPath - a pointer to our Services key in the registry.

Return Value:
    STATUS_SUCCESS if initialized; an error otherwise.

--*/

{
    NTSTATUS        ntStatus;
    UNICODE_STRING  ntUnicodeString;    // NT Device Name "\Device\KernelModeDriver"
    UNICODE_STRING  ntWin32NameString;    // Win32 Name "\DosDevices\KernelModeDriver"
    PDEVICE_OBJECT  deviceObject = NULL;    // ptr to device object

    UNREFERENCED_PARAMETER(RegistryPath);

    RtlInitUnicodeString( &ntUnicodeString, NT_DEVICE_NAME );

    ntStatus = IoCreateDevice(
        DriverObject,                   // Our Driver Object
        0,                              // We don't use a device extension
        &ntUnicodeString,               // Device name "\Device\KernelModeDriver"
        FILE_DEVICE_UNKNOWN,            // Device type
        FILE_DEVICE_SECURE_OPEN,		// Device characteristics
        FALSE,                          // Not an exclusive device
        &deviceObject );                // Returned ptr to Device Object

    if ( !NT_SUCCESS( ntStatus ) )
    {
        DRIVER_PRINT(("Couldn't create the device object\n"));
        return ntStatus;
    }

    //
    // Initialize the driver object with this driver's entry points.
    //

	DriverObject->MajorFunction[IRP_MJ_CREATE] = DriverCreateClose;
	DriverObject->MajorFunction[IRP_MJ_CLOSE] = DriverCreateClose;
	DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DriverDeviceControl;
	DriverObject->DriverUnload = DriverUnloadDriver;

    //
    // Initialize a Unicode String containing the Win32 name
    // for our device.
    //

    RtlInitUnicodeString( &ntWin32NameString, DOS_DEVICE_NAME );

    //
    // Create a symbolic link between our device name  and the Win32 name
    //

    ntStatus = IoCreateSymbolicLink(
                        &ntWin32NameString, &ntUnicodeString );

    if ( !NT_SUCCESS( ntStatus ) )
    {
        //
        // Delete everything that this routine has allocated.
        //
        DRIVER_PRINT(("Couldn't create symbolic link\n"));
        IoDeleteDevice( deviceObject );
    }


    return ntStatus;
}


NTSTATUS
DriverCreateClose(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp
    )
/*++

Routine Description:

    This routine is called by the I/O system when the KernelModeDriver is opened or
    closed.

    No action is performed other than completing the request successfully.

Arguments:

    DeviceObject - a pointer to the object that represents the device
    that I/O is to be done on.

    Irp - a pointer to the I/O Request Packet for this request.

Return Value:

    NT status code

--*/

{
    UNREFERENCED_PARAMETER(DeviceObject);

    PAGED_CODE();

    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;

    IoCompleteRequest( Irp, IO_NO_INCREMENT );

    return STATUS_SUCCESS;
}

VOID
DriverUnloadDriver(
    _In_ PDRIVER_OBJECT DriverObject
    )
/*++

Routine Description:

    This routine is called by the I/O system to unload the driver.

    Any resources previously allocated must be freed.

Arguments:

    DriverObject - a pointer to the object that represents our driver.

Return Value:

    None
--*/

{
    PDEVICE_OBJECT deviceObject = DriverObject->DeviceObject;
    UNICODE_STRING uniWin32NameString;

    PAGED_CODE();

    //
    // Create counted string version of our Win32 device name.
    //

    RtlInitUnicodeString( &uniWin32NameString, DOS_DEVICE_NAME );


    //
    // Delete the link from our device name to a name in the Win32 namespace.
    //

    IoDeleteSymbolicLink( &uniWin32NameString );

    if ( deviceObject != NULL )
    {
        IoDeleteDevice( deviceObject );
    }



}

NTSTATUS
DriverDeviceControl(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp
    )

/*++

Routine Description:

    This routine is called by the I/O system to perform a device I/O
    control function.

Arguments:

    DeviceObject - a pointer to the object that represents the device
        that I/O is to be done on.

    Irp - a pointer to the I/O Request Packet for this request.

Return Value:

    NT status code

--*/

{
    PIO_STACK_LOCATION  irpSp;// Pointer to current stack location
    NTSTATUS            ntStatus = STATUS_SUCCESS;// Assume success
    ULONG               inBufLength; // Input buffer length
	ULONG               outBufLength; // Output buffer length
	void				*inBuf; // pointer to input buffer
	unsigned __int64    *outBuf; // pointer to the output buffer

    UNREFERENCED_PARAMETER(DeviceObject);

    PAGED_CODE();

    irpSp = IoGetCurrentIrpStackLocation( Irp );
	inBufLength = irpSp->Parameters.DeviceIoControl.InputBufferLength;
	outBufLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength;

	if (!inBufLength || !outBufLength || outBufLength != sizeof(unsigned __int64)*2)
    {
        ntStatus = STATUS_INVALID_PARAMETER;
        goto End;
    }

    //
    // Determine which I/O control code was specified.
    //

    switch ( irpSp->Parameters.DeviceIoControl.IoControlCode )
    {
    case IOCTL_SIOCTL_METHOD_BUFFERED:

        //
        // In this method the I/O manager allocates a buffer large enough to
        // to accommodate larger of the user input buffer and output buffer,
        // assigns the address to Irp->AssociatedIrp.SystemBuffer, and
        // copies the content of the user input buffer into this SystemBuffer
        //

        DRIVER_PRINT(("Called IOCTL_SIOCTL_METHOD_BUFFERED\n"));
        PrintIrpInfo(Irp);

        //
        // Input buffer and output buffer is same in this case, read the
        // content of the buffer before writing to it
        //

        inBuf = (void *)Irp->AssociatedIrp.SystemBuffer;
		outBuf = (unsigned __int64 *)Irp->AssociatedIrp.SystemBuffer;

        //
        // Read the data from the buffer
        //

        DRIVER_PRINT(("\tData from User :"));
        //
        // We are using the following function to print characters instead
        // DebugPrint with %s format because we string we get may or
        // may not be null terminated.
        //
        PrintChars(inBuf, inBufLength);

        //
        // Write to the buffer
        //

		unsigned __int64 data[sizeof(unsigned __int64) * 2];
		data[0] = __readmsr(232);
		data[1] = __readmsr(231);

		DRIVER_PRINT(("data[0]: %d", data[0]));
		DRIVER_PRINT(("data[1]: %d", data[1]));

		RtlCopyBytes(outBuf, data, outBufLength);

        //
        // Assign the length of the data copied to IoStatus.Information
        // of the Irp and complete the Irp.
        //

		Irp->IoStatus.Information = sizeof(unsigned __int64)*2;

        //
        // When the Irp is completed the content of the SystemBuffer
        // is copied to the User output buffer and the SystemBuffer is
        // is freed.
        //

       break;

    default:

        //
        // The specified I/O control code is unrecognized by this driver.
        //

        ntStatus = STATUS_INVALID_DEVICE_REQUEST;
        DRIVER_PRINT(("ERROR: unrecognized IOCTL %x\n",
            irpSp->Parameters.DeviceIoControl.IoControlCode));
        break;
    }

End:
    //
    // Finish the I/O operation by simply completing the packet and returning
    // the same status as in the packet itself.
    //

    Irp->IoStatus.Status = ntStatus;

    IoCompleteRequest( Irp, IO_NO_INCREMENT );

    return ntStatus;
}

VOID
PrintIrpInfo(
    PIRP Irp)
{
    PIO_STACK_LOCATION  irpSp;
    irpSp = IoGetCurrentIrpStackLocation( Irp );

    PAGED_CODE();

    DRIVER_PRINT(("\tIrp->AssociatedIrp.SystemBuffer = 0x%p\n",
        Irp->AssociatedIrp.SystemBuffer));
    DRIVER_PRINT(("\tIrp->UserBuffer = 0x%p\n", Irp->UserBuffer));
    DRIVER_PRINT(("\tirpSp->Parameters.DeviceIoControl.Type3InputBuffer = 0x%p\n",
        irpSp->Parameters.DeviceIoControl.Type3InputBuffer));
    DRIVER_PRINT(("\tirpSp->Parameters.DeviceIoControl.InputBufferLength = %d\n",
        irpSp->Parameters.DeviceIoControl.InputBufferLength));
    DRIVER_PRINT(("\tirpSp->Parameters.DeviceIoControl.OutputBufferLength = %d\n",
        irpSp->Parameters.DeviceIoControl.OutputBufferLength ));
    return;
}

VOID
PrintChars(
    _In_reads_(CountChars) PCHAR BufferAddress,
    _In_ size_t CountChars
    )
{
    PAGED_CODE();

    if (CountChars) {

        while (CountChars--) {

            if (*BufferAddress > 31
                 && *BufferAddress != 127) {

                KdPrint (( "%c", *BufferAddress) );

            } else {

                KdPrint(( ".") );

            }
            BufferAddress++;
        }
        KdPrint (("\n"));
    }
    return;
}

And finally, the user-mode Win32 Console Application that loads and also runs this driver:

FrequencyCalculator.cpp

#include "stdafx.h"
#include <iostream>
#include <windows.h>
#include <winioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strsafe.h>
#include <process.h>
#include "..\KernelModeDriver\driver.h"

using namespace std;

BOOLEAN
ManageDriver(
_In_ LPCTSTR  DriverName,
_In_ LPCTSTR  ServiceName,
_In_ USHORT   Function
);

HANDLE hDevice;
TCHAR driverLocation[MAX_PATH];

void InstallDriver()
{
	DWORD errNum = 0;
	GetCurrentDirectory(MAX_PATH, driverLocation);
	_tcscat_s(driverLocation, _T("\\KernelModeDriver.sys"));

	std::wcout << "Trying to install driver at " << driverLocation << std::endl;

	//
	// open the device
	//

	if ((hDevice = CreateFile(_T("\\\\.\\KernelModeDriver"),
		GENERIC_READ | GENERIC_WRITE,
		0,
		NULL,
		CREATE_ALWAYS,
		FILE_ATTRIBUTE_NORMAL,
		NULL)) == INVALID_HANDLE_VALUE) {

		errNum = GetLastError();

		if (errNum != ERROR_FILE_NOT_FOUND) {

			printf("CreateFile failed!  ERROR_FILE_NOT_FOUND = %d\n", errNum);

			return;
		}

		//
		// The driver is not started yet so let us the install the driver.
		// First setup full path to driver name.
		//

		if (!ManageDriver(_T(DRIVER_NAME),
			driverLocation,
			DRIVER_FUNC_INSTALL
			)) {

			printf("Unable to install driver. \n");

			//
			// Error - remove driver.
			//

			ManageDriver(_T(DRIVER_NAME),
				driverLocation,
				DRIVER_FUNC_REMOVE
				);

			return;
		}

		hDevice = CreateFile(_T("\\\\.\\KernelModeDriver"),
			GENERIC_READ | GENERIC_WRITE,
			0,
			NULL,
			CREATE_ALWAYS,
			FILE_ATTRIBUTE_NORMAL,
			NULL);

		if (hDevice == INVALID_HANDLE_VALUE){
			printf("Error: CreatFile Failed : %d\n", GetLastError());
			return;
		}
	}
}

void UninstallDriver()
{
	//
	// close the handle to the device.
	//

	CloseHandle(hDevice);

	//
	// Unload the driver.  Ignore any errors.
	//
	ManageDriver(_T(DRIVER_NAME),
		driverLocation,
		DRIVER_FUNC_REMOVE
		);
}

double GetPerformanceRatio()
{
	BOOL bRc;
	ULONG bytesReturned;

	int input = 0;
	unsigned __int64 output[2];
	memset(output, 0, sizeof(unsigned __int64) * 2);

	//printf("InputBuffer Pointer = %p, BufLength = %d\n", &input, sizeof(&input));
	//printf("OutputBuffer Pointer = %p BufLength = %d\n", &output, sizeof(&output));

	//
	// Performing METHOD_BUFFERED
	//

	//printf("\nCalling DeviceIoControl METHOD_BUFFERED:\n");

	bRc = DeviceIoControl(hDevice,
		(DWORD)IOCTL_SIOCTL_METHOD_BUFFERED,
		&input,
		sizeof(&input),
		output,
		sizeof(unsigned __int64)*2,
		&bytesReturned,
		NULL
		);

	if (!bRc)
	{
		//printf("Error in DeviceIoControl : %d", GetLastError());
		return 0;

	}
	//printf("    OutBuffer (%d): %d\n", bytesReturned, output);
	if (output[1] == 0)
	{
		return 0;
	}
	else
	{
		return (float)output[0] / (float)output[1];
	}
}

struct Core
{
	int CoreNumber;
};

int GetNumberOfProcessorCores()
{
	SYSTEM_INFO sysinfo;
	GetSystemInfo(&sysinfo);
	return sysinfo.dwNumberOfProcessors;
}

float GetCoreFrequency()
{
	// __rdtsc: Returns the processor time stamp which records the number of clock cycles since the last reset.
	// QueryPerformanceCounter: Returns a high resolution time stamp that can be used for time-interval measurements.
	// Get the frequency which defines the step size of the QueryPerformanceCounter method.
	LARGE_INTEGER frequency;
	QueryPerformanceFrequency(&frequency);
	// Get the number of cycles before we start.
	ULONG cyclesBefore = __rdtsc();
	// Get the Intel performance ratio at the start.
	float ratioBefore = GetPerformanceRatio();
	// Get the start time.
	LARGE_INTEGER startTime;
	QueryPerformanceCounter(&startTime);
	// Give the CPU cores enough time to repopulate their __rdtsc and QueryPerformanceCounter registers.
	Sleep(1000);
	ULONG cyclesAfter = __rdtsc();
	// Get the Intel performance ratio at the end.
	float ratioAfter = GetPerformanceRatio();
	// Get the end time.
	LARGE_INTEGER endTime;
	QueryPerformanceCounter(&endTime);
	// Return the number of MHz. Multiply the core's frequency by the mean MSR (model-specific register) ratio (the APERF register's value divided by the MPERF register's value) between the two timestamps.
	return ((ratioAfter + ratioBefore) / 2)*(cyclesAfter - cyclesBefore)*pow(10, -6) / ((endTime.QuadPart - startTime.QuadPart) / frequency.QuadPart);
}

struct CoreResults
{
	int CoreNumber;
	float CoreFrequency;
};

CRITICAL_SECTION printLock;

static void printResult(void *param)
{
	EnterCriticalSection(&printLock);
	CoreResults coreResults = *((CoreResults *)param);
	std::cout << "Core " << coreResults.CoreNumber << " has a speed of " << coreResults.CoreFrequency << " MHz" << std::endl;
	delete param;
	LeaveCriticalSection(&printLock);
}

bool closed = false;

static void startMonitoringCoreSpeeds(void *param)
{
	Core core = *((Core *)param);
	SetThreadAffinityMask(GetCurrentThread(), 1 << core.CoreNumber);
	while (!closed)
	{
		CoreResults *coreResults = new CoreResults();
		coreResults->CoreNumber = core.CoreNumber;
		coreResults->CoreFrequency = GetCoreFrequency();
		_beginthread(printResult, 0, coreResults);
	}
	delete param;
}

int _tmain(int argc, _TCHAR* argv[])
{
	InitializeCriticalSection(&printLock);
	InstallDriver();
	for (int i = 0; i < GetNumberOfProcessorCores(); i++)
	{
		Core *core = new Core{ 0 };
		core->CoreNumber = i;
		_beginthread(startMonitoringCoreSpeeds, 0, core);
	}
	std::cin.get();
	closed = true;
	UninstallDriver();
	DeleteCriticalSection(&printLock);
}

To have the user-mode application actually install this driver, you need to either:

  • Sign the driver with a Code Signing certificate you’ve obtained from some third party issuer. To sign the driver, right click the driver’s project file, go to Configuration Properties -> Driver Signing -> General, and change the Sign Mode, setting the Production Certificate to the Code Signing certificate you have installed into your machine from the third party issuer.
  • Do not sign the driver and either disable Driver Signature Verification from the advanced Windows start-up boot options or disable Secure Boot from your BIOS and enable test signing, and generate a new test certificate from the drop-down menu under the driver’s Configuration Properties -> Driver Signing -> General -> Test Certificate.

If you’ve completed the above steps, the code should run just fine (if you run it under Administrator credentials). Finally, you can download the complete source code of my implementation. Note that, you use this code at your own risk and are entirely liable for any problems that may arise out of it, whatsoever. I do ask that if you use my driver, you give me a little credit. Just throw my name in a comment somewhere. Here is the download link: FrequencyCalculator.zip.

Debugging Heap Corruptions in Production (Release Mode) MSVC++ Windows Applications with Global Flags

Say you have a Release mode MSVC++ Win32 Console Application with the following code:

#include "stdafx.h"
#include <iostream>

using namespace std;

void CorruptTheHeap()
{
	// Create a pointer to an integer.
	int *reallyBadlyCodedIntegerAllocation;
	// Allocate insufficient space for this memory space to store an actual integer.
	cout << "The expected memory size of this type is " << sizeof(int) << " bytes, which is " << 8*sizeof(int) << " bits." << endl;
	int sizeToAllocateForThisType = 0;
	cout << "The actual size of this integer will be set to " << sizeToAllocateForThisType << " bytes." << endl;
	reallyBadlyCodedIntegerAllocation = (int *)malloc(sizeToAllocateForThisType);
	// Try to set the integer to an integer value (causing a memory leak at this point since we are writing over the bounds of the memory we actually own).
	*reallyBadlyCodedIntegerAllocation = 0;
	// Trying to free this integer would cause a heap corruption because it will try to free the size of the type, but we have allocated it to be nothing. In a Release build, this will cause a crash.
	free(reallyBadlyCodedIntegerAllocation);
}

int _tmain(int argc, _TCHAR* argv[])
{
	CorruptTheHeap();
	cout << "Press any key to exit." << endl;
	cin.get();
	return 0;
}

Make sure the application’s project settings build it so that its *.pdb file(s) is or are in the same folder as where you execute it. Now get WinDbg, which contains Global Flags. For this example, my application is built in Release (must be in Release) as an x64 architecture application, so the next step for me is to start Global Flags (X64). A simple Windows start screen search for Global Flags should uncover it, but for me it installed at C:\Program Files (x86)\Windows Kits\8.1\Debuggers\x64\gflags.exe. Open it up, go to the Image File tab. Type in the name of your Image file. For me, my Console Application compiles and runs as HeapCorruptor.exe, so I typed that in, and pressed the TAB key on my keyboard. You’ll notice that when running Global Flags, it seems to keep track of executables based on their name, so when you restart and go through the same process of typing in the executable’s name and press the TAB key, it loads your last configuration (I know, its kind of weird and takes a bit of getting used to).

Check off Debugger and set its path to the path of your WinDbg executable. For me, a default setup of the debugging tools installed WinDbg at “C:\Program Files (x86)\Windows Kits\8.1\Debuggers\x64\windbg.exe”. Then set up any other necessary flags, as I have in the screenshot below, hit Apply, and then OK to close it down.

What you need to to set in Global Flags to catch heap corruptions in production.
Settings for detecting heap corruptions using Global Flags.

Now, run your executable. Global Flags will start up along with it. It starts paused, so type in g (for go) to get it to continue, or hit F5 in Global Flags. After you type in g and hit enter, you should see an error as the application starts up and runs:

0:000> g


===========================================================
VERIFIER STOP 000000000000000F: pid 0x3DC0: corrupted suffix pattern 

	0000003E3B071000 : Heap handle
	0000003E3C191500 : Heap block
	0000000000000001 : Block size
	0000003E3C191501 : corruption address
===========================================================
This verifier stop is not continuable. Process will be terminated 
when you use the `go' debugger command.

We have caught a heap corruption at memory address 0000003E3C191500. Type in !heap –p –a 0000003E3C191500 to get more information on the error:

    address 0000003e3c191500 found in
    _HEAP @ 3e3c150000
              HEAP_ENTRY Size Prev Flags            UserPtr UserSize - state
        0000003e3c1914b0 0008 0000  [00]   0000003e3c191500    00001 - (busy)
        7ffc110a83f3 verifier!VerifierDisableFaultInjectionExclusionRange+0x00000000000022ff
        7ffc4e438e98 ntdll!RtlpNtMakeTemporaryKey+0x0000000000000330
        7ffc4e3f333a ntdll!memset+0x00000000000148fa
        7ffc4e3774e7 ntdll!RtlAllocateHeap+0x0000000000000187
        7ffc110c17ab verifier!VerifierGetPropertyValueByName+0x00000000000133cf
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\SYSTEM32\MSVCR120.dll - 
        7ffc10e96a57 MSVCR120!malloc+0x000000000000005b
*** WARNING: Unable to verify checksum for HeapCorruptor.exe
        7ff7698d1316 HeapCorruptor!CorruptTheHeap+0x00000000000000a6
        7ff7698d1339 HeapCorruptor!wmain+0x0000000000000009
        7ff7698d1e37 HeapCorruptor!__tmainCRTStartup+0x000000000000010f
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\system32\KERNEL32.DLL - 
        7ffc4bc216ad KERNEL32!BaseThreadInitThunk+0x000000000000000d
        7ffc4e3b4629 ntdll!RtlUserThreadStart+0x000000000000001d

This is great because it shows us some important bits of information, like the call-stack that caused our corruption. In particular, we can see that CorruptTheHeap() was called and that, while running it, Global Flags was then unable to verify a check-sum, so we can pinpoint the problem to be in that function…sometimes Global Flags may show you even more granular methods of underlying framework code that you are using to help you drill down to the actual function causing problems, but in a nutshell that’s essentially it. Make sure to follow the steps above and disable all check-boxes for the context of that executable’s name in Global Flags, or else it will always start with your executable.

Visual Studio’s Out-of-the-Box Low-Level Debugging Tools: An IL Disassembler and IL Assembler How-To

Ildasm.exe (IL Disassembler) is an out-of-the box disassembler packaged with Visual Studio, and Ilasm.exe (IL Assembler) is an out-of-the-box assembler packaged with Visual Studio. Let’s try using Ildasm.exe and Ilasm.exe to disassemble and then re-assemble a portable executable from a C# Console Application. Take the following C# Console Application for example:

using System;

namespace PrintSomething
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Started the console application. Press any key to exit.");
            Console.ReadLine();
        }
    }
}

Build this code in Debug mode and run it. Figure out where the code was built and copy the path to your executable for this C# Console Application. Then do a search on your computer for the VS2012 x86 Native Tools Command Prompt and open it up (it will help us run the IL Assembler and Disassembler more easily). Try to change the directory somewhere where you have permissions…for me, the desktop is an alright place so the first command I ran was this:

cd "C:\Users\YourUserAccountName\Desktop"

Here comes the sweet stuff. To disassemble your executable, run this command on it (PrintSomething.exe is the name of the Console Application I’ve shown the code for above, but you can paste whatever path you have copied to your own executable):

ildasm "C:\PathToYourExecutable\PrintSomething.exe" /out:"Disassembly.asm"

You can inspect the assembly code in Disassembly.asm now by opening it up in your favourite text editor (you will find Disassembly.asm in your working directory, which I changed to be my desktop earlier):


//  Microsoft (R) .NET Framework IL Disassembler.  Version 4.0.30319.18020
//  Copyright (c) Microsoft Corporation.  All rights reserved.



// Metadata version: v4.0.30319
.assembly extern mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                         // .z\V.4..
  .ver 4:0:0:0
}
.assembly PrintSomething
{
  .custom instance void [mscorlib]System.Runtime.Versioning.TargetFrameworkAttribute::.ctor(string) = ( 01 00 1C 2E 4E 45 54 46 72 61 6D 65 77 6F 72 6B   // ....NETFramework
                                                                                                        2C 56 65 72 73 69 6F 6E 3D 76 34 2E 35 2E 31 01   // ,Version=v4.5.1.
                                                                                                        00 54 0E 14 46 72 61 6D 65 77 6F 72 6B 44 69 73   // .T..FrameworkDis
                                                                                                        70 6C 61 79 4E 61 6D 65 14 2E 4E 45 54 20 46 72   // playName..NET Fr
                                                                                                        61 6D 65 77 6F 72 6B 20 34 2E 35 2E 31 )          // amework 4.5.1
  .custom instance void [mscorlib]System.Reflection.AssemblyTitleAttribute::.ctor(string) = ( 01 00 0E 50 72 69 6E 74 53 6F 6D 65 74 68 69 6E   // ...PrintSomethin
                                                                                              67 00 00 )                                        // g..
  .custom instance void [mscorlib]System.Reflection.AssemblyDescriptionAttribute::.ctor(string) = ( 01 00 00 00 00 ) 
  .custom instance void [mscorlib]System.Reflection.AssemblyConfigurationAttribute::.ctor(string) = ( 01 00 00 00 00 ) 
  .custom instance void [mscorlib]System.Reflection.AssemblyCompanyAttribute::.ctor(string) = ( 01 00 00 00 00 ) 
  .custom instance void [mscorlib]System.Reflection.AssemblyProductAttribute::.ctor(string) = ( 01 00 0E 50 72 69 6E 74 53 6F 6D 65 74 68 69 6E   // ...PrintSomethin
                                                                                                67 00 00 )                                        // g..
  .custom instance void [mscorlib]System.Reflection.AssemblyCopyrightAttribute::.ctor(string) = ( 01 00 12 43 6F 70 79 72 69 67 68 74 20 C2 A9 20   // ...Copyright .. 
                                                                                                  20 32 30 31 34 00 00 )                            //  2014..
  .custom instance void [mscorlib]System.Reflection.AssemblyTrademarkAttribute::.ctor(string) = ( 01 00 00 00 00 ) 
  .custom instance void [mscorlib]System.Runtime.InteropServices.ComVisibleAttribute::.ctor(bool) = ( 01 00 00 00 00 ) 
  .custom instance void [mscorlib]System.Runtime.InteropServices.GuidAttribute::.ctor(string) = ( 01 00 24 36 37 36 65 36 33 32 34 2D 38 65 62 34   // ..$676e6324-8eb4
                                                                                                  2D 34 33 33 33 2D 39 33 37 37 2D 39 61 65 37 39   // -4333-9377-9ae79
                                                                                                  38 62 33 31 61 30 39 00 00 )                      // 8b31a09..
  .custom instance void [mscorlib]System.Reflection.AssemblyFileVersionAttribute::.ctor(string) = ( 01 00 07 31 2E 30 2E 30 2E 30 00 00 )             // ...1.0.0.0..

  // --- The following custom attribute is added automatically, do not uncomment -------
  //  .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) 

  .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) 
  .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78   // ....T..WrapNonEx
                                                                                                             63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 )       // ceptionThrows.
  .hash algorithm 0x00008004
  .ver 1:0:0:0
}
.module PrintSomething.exe
// MVID: {5750456D-A154-4CDE-A849-0BC5414E34EE}
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003       // WINDOWS_CUI
.corflags 0x00020003    //  ILONLY 32BITPREFERRED
// Image base: 0x009C0000


// =============== CLASS MEMBERS DECLARATION ===================

.class private auto ansi beforefieldinit PrintSomething.Program
       extends [mscorlib]System.Object
{
  .method private hidebysig static void  Main(string[] args) cil managed
  {
    .entrypoint
    // Code size       19 (0x13)
    .maxstack  8
    IL_0000:  nop
    IL_0001:  ldstr      "Started the console application. Press any key to "
    + "exit."
    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_000b:  nop
    IL_000c:  call       string [mscorlib]System.Console::ReadLine()
    IL_0011:  pop
    IL_0012:  ret
  } // end of method Program::Main

  .method public hidebysig specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    // Code size       7 (0x7)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
    IL_0006:  ret
  } // end of method Program::.ctor

} // end of class PrintSomething.Program


// =============================================================

// *********** DISASSEMBLY COMPLETE ***********************
// WARNING: Created Win32 resource file Disassembly.res

Try changing the text in this file. For example, change this line:

IL_0001:  ldstr      "Started the console application. Press any key to "
    + "exit."

To this line:

IL_0001:  ldstr      "That just happened."

Now assemble the code with the following command:

ilasm "Disassembly.asm"

You should see a new executable pop out of this file in your current working directory (for me, its the desktop as I’ve shown before): Disassembly.exe. Run it, and voila, you’ll see that the assembler compiled your assembly (and included any changes you’ve made). Pretty nice for touching up some very low-level code in Visual Studio applications, or even for dynamically changing your application’s manifest information.