Continuing Case of ByRef Corruption–.NET Patching

A new revision of app we previously met here https://chentiangemalc.wordpress.com/2014/05/22/case-of-the-invalid-base-key-error/ was out…

The “Invalid Base Key” error had been patched, but now we had another. It looked like another case of variables being unexpectedly modified due to liberal and incorrect use of By Reference.

I also noticed the recommendation to change ByRef into ByVal hadn’t been followed. Instead…

if (szBaseKey.ToString() == “”)
{
  Information.Err().Raise(-2147220989, “RegIni Class”, “Invalid Base Key”, null, null);
}

had been replaced with

if (szBaseKey.ToString() == “”)
{
}

Certainly eliminated the error message though.

In this new case however it seemed multiple API calls may be impacted. Majority of them were called by a function called QueryValue. So I wanted to change that function to use it’s parameters “by value” instead of “by reference”

Using ILDasm from Windows SDK I decompiled the EXE, and it was easy enough to patch the function definition to use “by value” I just had to remove the ampersands:

From

.method public instance object  QueryValue(int32& lhKey,
                                             string& szKeyName,
                                             string& szValueName,
                                             [opt] bool& bSupressErrorMessage) cil managed noinlining nooptimization
 

to

.method public instance object  QueryValue(int32 lhKey,
                                             string szKeyName,
                                             string szValueName,
                                             [opt] bool bSupressErrorMessage) cil managed noinlining nooptimization
 


However this also requires updating all the calls to the method. To easily do this you want a text editor that supports multi-line search & replace. In this case I used UltraEdit (http://www.ultraedit.com/downloads/ultraedit_download.html)

image

I had to do this twice as calls to this function had been formatted in two different ways. To ensure none were let I searched for QueryValue(int32&

With that we recompiled our IL file. This was .NET 2.0 app so I used .NET 2.0 version of ilasm:

c:\windows\microsoft.net\framework\v2.0.50727\ilasm.exe qfbox2.il /resource=qfbox2.res

However the fresh EXE didn’t work:

image

Using Visual Studio I set Debug –> Exceptions and ticked all boxes

image

Then launched EXE with Visual Studio .NET Reflector add-in “Debug an Executable”

image

then

image

Looking at this we can see our bSupressErrorMessage had been turned into an array. This is because accessing the “by reference” value different code gets generated.

For example this C# code:

public object QueryValue(ref int lhKey,ref string szKeyName, ref string szValueName,ref bool bSupressErrorMessage) { if (!bSupressErrorMessage) { Console.WriteLine("by reference: bSupressErrorMessage NOT true"); } return null; } public object QueryValue(int lhKey, string szKeyName, string szValueName, bool bSupressErrorMessage) { if (!bSupressErrorMessage) { Console.WriteLine("by value: bSupressErrorMessage NOT true"); } return null; }

Generates the following IL code:

.method public hidebysig instance object QueryValue(int32& lhKey, string& szKeyName, string& szValueName, bool& bSupressErrorMessage) cil managed { .maxstack 1 .locals init ( [0] object obj2, [1] bool flag) L_0000: nop L_0001: ldarg.s bSupressErrorMessage L_0003: ldind.i1 L_0004: stloc.1 L_0005: ldloc.1 L_0006: brtrue.s L_0015 L_0008: nop L_0009: ldstr "by reference: bSupressErrorMessage NOT true" L_000e: call void [mscorlib]System.Console::WriteLine(string) L_0013: nop L_0014: nop L_0015: ldnull L_0016: stloc.0 L_0017: br.s L_0019 L_0019: ldloc.0 L_001a: ret } .method public hidebysig instance object QueryValue(int32 lhKey, string szKeyName, string szValueName, bool bSupressErrorMessage) cil managed { .maxstack 1 .locals init ( [0] object obj2, [1] bool flag) L_0000: nop L_0001: ldarg.s bSupressErrorMessage L_0003: stloc.1 L_0004: ldloc.1 L_0005: brtrue.s L_0014 L_0007: nop L_0008: ldstr "by value: bSupressErrorMessage NOT true" L_000d: call void [mscorlib]System.Console::WriteLine(string) L_0012: nop L_0013: nop L_0014: ldnull L_0015: stloc.0 L_0016: br.s L_0018 L_0018: ldloc.0 L_0019: ret }

In both cases we can see a local variable is created

[1] bool flag

Then bSupressErrorMessage argument is loaded onto the stack:

ldarg.s bSupressErrorMessage

Now only the “by reference” version performs:

ldind.i1 (Loads a value of type int8 as an int32 onto the evaluation stack indirectly.)

Then both versions continue the same:

Pops the current value from the top of the evaluation stack and stores it in a the local variable list at index 1. (This saving our argument into the bool flag)

stloc.1

Loads the local variable at index 1 onto the evaluation stack.

ldloc.1

Transfers control to a target instruction (short form) if value is true, not null, or non-zero.

brtrue.s <target>

In our crashing code we found:

IL_012c:  ldarg.s    bSupressErrorMessage
    IL_012e:  ldind.i1
    IL_012f:  ldc.i4.0
    IL_0130:  ceq
    IL_0132:  stloc.s    V_10
    IL_0134:  ldloc.s    V_10
 

We removed the

ldind.i1

We can double check our patch with .NET reflector, we can see the [0] is gone…

image

And the app no longer crashes…

About chentiangemalc

specializes in end-user computing technologies. disclaimer 1) use at your own risk. test any solution in your environment. if you do not understand the impact/consequences of what you're doing please stop, and ask advice from somebody who does. 2) views are my own at the time of posting and do not necessarily represent my current view or the view of my employer and family members/relatives. 3) over the years Microsoft/Citrix/VMWare have given me a few free shirts, pens, paper notebooks/etc. despite these gifts i will try to remain unbiased.
This entry was posted in .NET, Hacking, MSIL, Patching and tagged . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s