Sunday, 18 November 2007

Comparing ISINST to CASTCLASS

I was reading through a corporate C# coding standard the other day for one of my clients, and noticed that one of the coding requirements in the standard related to casting of types in code.
Their standard stated that all type conversions in C# should be accomplished by using the 'as' keyword rather than using a direct cast (i.e. '(type)').
I quizzed my client on this to see what formed the basis of their choice to use 'as'. They had no explanation, but said that they felt it could be faster.

Following on from this, I was interested to see whether using 'as' or '(type)' was faster for type conversion.

In .Net IL, 'as' is compiled to 'isinst'. isinst performs a type conversion and returns null if the specified type conversion is not valid.
This differs from using '(type)', which compiles to 'castclass'. castclass also performs a type conversion but throws an Exception if the specified type conversion is not valid.
This is an important difference. For example, consider the following example using 'as' (isinst):
void Foo1(object ObjA)
{
  ClassDef ObjB;

  ObjB = ObjA as ClassDef;
  ObjB.Property = "12345";
}


In the example, we have to be certain that we know ObjA can be converted to the ClassDef type, ObjB, because if it cannot, a NullReferenceException will occur in the line where the property is set.
A better implementation of the above example could be:
void Foo2(object ObjA)
{
  ClassDef ObjB;

  ObjB = ObjA as ClassDef;
  if (ObjB != null)
    ObjB.Property = "12345";
}


If we consider the same example using '(type)' (castclass):
void Foo3(object ObjA)
{
  ClassDef ObjB;

  ObjB = (ClassDef)ObjA;
  ObjB.Property = "12345";
}


Again in this example we have to be sure that the conversion between the two types is valid, because else a TypeConversionException will occur on the line where ObjA is cast to ClassDef.
There could be two potential alternatives to the castclass example:
void Foo4(object ObjA)
{
  try
  {
    ClassDef ObjB;

    ObjB = (ClassDef)ObjA;
    ObjB.Property = "12345";
  }
  catch (Exception ex)
  {
    // Do something useful
    throw ex;
  }
}

void Foo5(object ObjA)
{
  ClassDef ObjB;

  if (ObjA is ClassDef)
  {
    ObjB = (ClassDef)ObjA;
    ObjB.Property = "12345";
  }
}


In the examples above, Foo1 and Foo3 would be appropriate to use if we knew that the object ObjA can definitely be converted to ClassDef. However, this may not always be likely, particularly in these examples becuase the parameter ObjA is an object type.
In these circumstances, it could be more appropriate to use Foo2, Foo4 or Foo5, because they offer security against failures during the conversion process.
In Foo2, we rely on isinst returning null if the conversion is not valid, and so we check for null after the conversion.
In Foo4, we handle an exception that could be raised during a failed castclass.
In Foo5, we first check to see whether ObjA is a ClassDef (using isinst - 'ObjA is ClassDef') and then perform the conversion in the knowledge that it will not cause an exception.

Now I had five potential examples for performing a type convesion, I thought it would be interesting to see whether there was a great deal of difference in the time taken to carry out the different examples.
I wrote a test project which used a high-precision StopWatch to time each of the procedures.
I timed each procedure as it performed one billion casts, repeated over 50 iterations to ensure that a good average time could be ascertained.
Following on from this I analysed the results to see which of the above five examples was fastest.

The total time for all 50 billion conversions for each test were:
Foo1 = 1,466,176 ms
Foo2 = 1,506,633 ms
Foo3 = 1,499,661 ms
Foo4 = 1,519,072 ms
Foo5 = 1,689,141 ms

And the average times per iteration (1 billion conversions) were:
Foo1 = 29,323.52 ms
Foo2 = 30,132.66 ms
Foo3 = 29,993.22 ms
Foo4 = 30,381.44 ms
Foo5 = 33,782.82 ms

Foo1 was the fastest, closely followed by Foo3 (which was around 2.284% slower).
However, as neither Foo1 or Foo3 contain any checks to ensure that the conversion was a success so they may be unsuitable for general use.

Of the remaining methods, Foo2 was fastest, which involved checking for a null value after using isinst, followed by Foo4 (the the try...catch block around castclass) and finally Foo5, which was a combination of isinst and castclass. Foo5 was significantly slower than all the other methods, being around 3.5 seconds\million conversions slower than the slowest of the other four methods.
In order to directly compare isinst and castclass, we can examine Foo1 and Foo3. Consider the IL for Foo1 and Foo3 respectively:
Foo1
...
IL_0014  ldloc.2
IL_0015  isinst    TestCastStyle.ClassDef
IL_001a  stloc.1
IL_001b  ldloc.1
IL_001c  ldstr     "12345"
IL_0021  callvirt  instance void TestCastStyle.ClassDef::set_Property(string)
...


Foo3
...
IL_0014  ldloc.2
IL_0015  castclass TestCastStyle.ClassDef
IL_001a  stloc.1
IL_001b  ldloc.1
IL_001c  ldstr     "12345"
IL_0021  callvirt  instance void TestCastStyle.ClassDef::set_Property(string)
...


The code for the methods is identical, other than IL_0015, which uses isinst in Foo1 and castclass in Foo3.

In the tests I ran, Foo1 took around 29.32ms\million conversions whereas Foo3 took 29.99ms\million, which is a difference of 0.67ms\million.
This confirms that using isinst is definitely quicker than castclass.

Considering the scenario where a check must be performed to ensure the conversion is valid, we can compare Foo2 (isinst) to Foo4 or Foo5 (castclass). The IL for these methods is:
Foo2
...
IL_0014  ldloc.2
IL_0015  isinst    TestCastStyle.ClassDef
IL_001a  stloc.1
IL_001b  ldloc.1
IL_001c  brfalse.s IL_0029
IL_001e  ldloc.1
IL_001f  ldstr     "12345"
IL_0024  callvirt  instance void TestCastStyle.ClassDef::set_Property(string)
...


Foo4
...
try
{
  IL_0014  ldloc.2
  IL_0015  castclass TestCastStyle.ClassDef
  IL_001a  stloc.1
  IL_001b  ldloc.1
  IL_001c  ldstr     "12345"
  IL_0021  callvirt  instance void TestCastStyle.ClassDef::set_Property(string)
  IL_0026  leave.s   IL_002b
}
catch [mscorlib]System.Exception
{
  IL_0028  pop
  IL_0029  rethrow
}
...


Foo5
...
IL_0014  ldloc.2
IL_0015  isinst    TestCastStyle.ClassDef
IL_001a  brfalse.s IL_002e
IL_001c  ldloc.2
IL_001d  castclass TestCastStyle.ClassDef
IL_0022  stloc.1
IL_0023  ldloc.1
IL_0024  ldstr     "12345"
IL_0029  callvirt  instance void TestCastStyle.ClassDef::set_Property(string)
...


At first glance, I had assumed that Foo4 would be slowest of these methods, but the results were:
  • Foo2 took 30.13ms\million conversions

  • Foo4 took 30.38ms\million conversions

  • Foo5 took 33.78ms\million conversions


I was surprised to see that Foo5 was by far the slowest. There does not seem to be a massive difference between Foo2 and Foo4 under the test conditions, but if an Exception was raised at IL_0015 in Foo4, this would slow the method down considerably, whereas Foo2 would continue to be very quick.

The conclusions from the tests I performed are:
  • In a direct comparison, isinst is quicker than castclass (although on slightly)

  • When having to perform checks to ensure the conversion was successful, isinst was significantly quicker than castclass

  • A combination of isinst and castclass should not be used as this was far slower than the quickest "safe" conversion (over 12% slower)


If you would like to run the tests yourself, drop me an e-mail and I will send you the test program I used. Additionally, if you would like the analysis files containing the timings and code dumps, I can also send you this.

Have fun!

Friday, 2 November 2007

Using Win32 to Manipulate the UI

Whilst working on an application at a client's site the other day, I noticed that access to certain code was being controlled by disabling buttons on a form.
The code in question should not be invoked under certain conditions (in this case, under certain security conditions), and so the application developers had assumed that disabling the button that invoked the code would prevent the user from invoking the functionality behind the button.

Sadly, this is not the case.
I demonstrated a very simple attack using Win32 API calls that enabled the button, and allowed the user to click on it, whether or not the application had disabled it or not.

In this article, I will be discussing the technique I used, and how you can write code that is protected from this kind of attack.

To demonstrate the attack to my client, I implemented my code in Microsoft Excel. I did this because most desktop PCs tend to have Microsoft Excel and I wanted to show my client that you do not need development tools like Visual Studio in order to orchestrate a simple attack like this.
In addition, I used a standard network user account to to show that this attack can be performed by a standard user, with the only requirement (in this scenario) being access to VBA in an Office product and the ability to execute macros in the product.

In order to enable the disabled button, the user will need to carry out the following actions:
- Find the window handle (HWND) of the button
- Enable the button

To start new Microsoft Excel workbook should be created, and the Visual Basic Editor (Tools -> Macros -> Visual Basic Editor) should be open.
A new Module must be added to the current workbook. The following code must go into the Module - it will not work unless it is in a VBA code Module (not WorkSheet or WorkBook code).

Find the HWND
In order to enable the button, the user needs to know the window handle (which is often referred to as the HWND) of the button.
If the user can use a tool such as Spy++ this is very easy, but as most users will not have it we need a way of discovering the window handle from Excel.

An easy way to do this is to produce a list of window handles that exist in the current session.
This is done by making an API call to EnumWindows (which lives in user32).
EnumWindows requires a callback to enumerate the window list, which is simple to achieve in Excel; We simply declare a method that matches the callback specification:
---
Private Function EnumWindowsProc(ByVal hwnd As Long, ByVal lparam As Long) As Long
---

This method matches the requirement, so we can now make the API call as required. To do this, we need to make the API method declaration and call it:
---
' This is the API method declaration
Private Declare Function EnumWindows Lib "user32" (ByVal callback As Long, ByVal lparam As Long) As Boolean

' This method, when invoked, will make the API call
Public Sub EnumerateAllWindows()
  EnumWindows AddressOf EnumWindowsProc, 0
End Sub
---

When the EnumerateAllWindows method is invoked, the API call is made to EnumWindows, and our callback method (EnumWindowsProc) will be invoked.

In EnumWindowsProc, we get passed a window handle in hwnd. The lparam parameter serves no purpose in this example and can be ignored (it will be 0).
The value in hwnd will be the first enumerated window handle. To get the next one, the EnumWindowsProc must return a true value:
---
Private Function EnumWindowsProc(ByVal hwnd As Long, ByVal lparam As Long) As Long
  Debug.Print hwnd
  EnumWindowsProc = 1
End Function
---

This will then be repeatedly invoked until all the window have been enumerated, and the Immediate window will now contain a list of a couple of hundred numbers.

However, this is not very useful as far as the user is concerned, because they do not know which of these window handles are any use to them.
To help the user, the name of the window whose handle has been displayed can also be obtained.
To do this, an API call to GetWindowTextA (which also lives in user32) can be made, which has the following API method declaration:
---
Private Declare Function GetWindowTextA Lib "user32" (ByVal hwnd As Long, ByVal Text As String, ByVal maxcount As Long) As Long
---

And now in EnumWindowsProc, the following code is added before the Debug.Print statement:
---
  Dim Length As Long
  Dim Text As String

  ' Empty string to accept a name
  Text = String(100, " ")

  ' Get the text
  Length = GetWindowTextA(hwnd, Text, 100)
  If Length > 0 Then
    Text = Mid$(Text, 1, Length)
  Else
    Text = ""
  End If

  Debug.Print Text
---

There are some important points to note when using GetWindowTextA:
- A string (named Text) of length 100, containing spaces, was created for the string to be put into. This must be done, else the string will always be empty.
- The resulting string (from GetWindowTextA) was terminated using the length returned. If this was not done the Text would contain a string terminator (null, '\0') and then spaces up to the 100th character and would be unusable.

After including the above code, invoking the method will dump the name of the window and the window handle to the immediate window.

As event this output is not ideal, Excel can be used to dump the list to the current Sheet. A member variable, RowCounter, of type Integer is added to the Module, and then in EnumerateChildWindows method, is set this value to 1.
Now in EnumWindowsProc, the Debug.Print statements are changed to:
---
  Application.ActiveSheet.Cells(RowCounter, 1).Value = Str(hwnd)
  Application.ActiveSheet.Cells(RowCounter, 2).Value = Text

  RowCounter = RowCounter + 1
---

When this code is now invoked, it dumps a list of window handles and window names to the current spreadsheet - much more useful when looking for something.

Now that is done, a list of the window handles in the system can be obtained. This can be tested by running Calc (calc.exe) and then invoking EnumerateAllWindows. The text "Calculator" will be visible next to a window handle on the list somewhere.

That's a good start, but if the user is trying to enable a button on a form, at the moment they will only have visiblity of the form's window handle.

To have a look at what is on the form, another API call, EnumChildWindows. EnumChildWindows will be used to enumerate all the child windows of a specified window handle.
Each button (and indeed most forms components) will have their own window handle.
The declaration for EnumChildWindows is:
---
Private Declare Function EnumChildWindows Lib "user32" (ByVal hwndParent As Long, ByVal callback As Long, ByVal lparam As Long) As Boolean
---

In the declaration hwndParent refers to the parent window handle, and then the callback and lparam parameters are the same as before.
To use EnumChildWindows, another callback method is required, which is declared in the same was as with EnumWindowsProc:
---
Private Function EnumChildWindowsProc(ByVal hwnd As Long, ByVal lparam As Long) As Long
---

The method body is the same as EnumWindowsProc, so it will also produce a list of window handles and associated text.

A method called EnumerateChildWindows is declared, which will read the number (window handle) in the currently selected cell and enumerate child windows of it:
---
Public Sub EnumerateChildWindows()
  If IsNumeric(Application.Selection) Then
    Dim Number As Long

    Number = Application.Selection
    If Number > 0 Then
      Application.ActiveSheet.Cells.Clear
      RowCounter = 1
      EnumChildWindows Number, AddressOf EnumChildWindowsProc, 0
    End If
  End If
End Sub
---

If the user clicks on a window handle in the worksheet and then runs the method EnumerateChildWindows, the current list will be cleared and then the child windows of the selected window handle will be displayed.

This can be repeated if necessary to pass through multiple layers of child windows, but it is most likely that the button the user is looking for has been found the first time EnumerateChildWindows is invoked.

This can be tested this by:
- Run Calc (calc.exe)
- Invoking EnumerateAllWindows
- Selecting the window handle next to "Calculator" in the list
- Invoking EnumerateChildWindows
A list that contains all of the controls on the Calculator form should now be displayed.

The user will now be able to obtain the window handle of the button they wish to enable.


Enable The Button
In the above section, the user can obtained the window handle for a control that they wish to enable.

Now the user needs to enable the button.
This is achieved by making an API call to EnableWindow, which is declared in user32:
---
Private Declare Function EnableWindow Lib "user32" (ByVal hwnd As Long, ByVal state As Boolean) As Boolean
---

When making the API call, the window handle must be supplied in hwnd, and the enabled state of the target window in state.
A method can be declared to do this:
---
Public Sub EnableSelectedWindow()
  If IsNumeric(Application.Selection) Then
  Dim Number As Long

  Number = Application.Selection
  If Number > 0 Then
  EnableWindow Number, True
    End If
  End If
End Sub
---

Now, the user can simply select a window handle in the list in the spreadsheet and invoke EnableSelectedWindow, and the window will become enabled.

This can be tested by:
- Run Calc (calc.exe)
- Putting Calc into Scientific mode (View -> Scientific). Note that the Ave, Sum, s, Dat, A, B, C, D, E and F buttons are disabled
- Invoking EnumerateAllWindows
- Selecting the window handle next to "Calculator" in the list
- Invoking EnumerateChildWindows
- Select the window handle next to one of the disabled button names (i.e. "Ave") - Invoking EnableSelectedWindow

The button will now be enabled. This can repeated as many times as necessary with other controls on the form.

This method will work on anything that has a window handle in Win32.

Conclusion
It is very easy to modify the state of controls using Win32.
This is because almost all standard controls in Windows have a window handle. This is a fundamental part of the operation of Microsoft Windows, and cannot be prevented.

It is important when designing and developing UI-invoked code that preventative steps are always taken to double-check that code execution is permitted.
As this article has demonstrated, it cannot be assumed that code is protected by simply disabling or hiding UI components.
A much more secure method would be to disable a control but to verify that execution is permitted inside the control's Click event or inside the code that is being executed.

Additionally, it is important to realise that it is not only the Enabled state of controls that can be influenced. In fact, using the same principal described in this article a user can change any of the following aspects of a window:
- Colour
- Position
- Size
- Visibility

Following the demonstration of the above to my client, they readily agreed that a better approach to protecting code from unwanted execution was required, and they have taken steps to prevent code access in this way.

If you would like to download a copy of the code used in this article, it can be downloaded here. This code is written (as per this article) for use in Microsoft Excel but can easily be changed to work with other Office products.

Have fun!