Webinar: Hacking C# with Adam Furmanek

by Andrea Brezinova on 16 Feb 2022

Adam Furmanek shows how to hack the C# language and the .NET runtime: calling a specific override of a virtual method, calling assembly instructions provided as a byte array, replacing a method by another one, or dynamically changing the type of an object.

We’re publishing the recording and the transcript of the webinar that went live on January 20th.


Gael: Hello again, everyone. And thank you for joining us today for this webinar. This is Gael Fraiteur, and today I’m joined by Adam Furmanek.

Adam: Hey everyone.

Gael: Adam is a software engineer at Amazon. Adam is the author of Applied Integer Programming and .NET Internals Cookbook. Adam is also a speaker. He Spoke at [00:00:30] NDC, .NET [inaudible 00:00:32] and other international conferences. And today Adam will be talking about hacking C#.

Just a quick note, before we begin. Please use the Q&A feature to ask any questions you may have during today’s live webinar, and we’ll be sure to address them at the end of the presentation. We’ll follow up offline to answer all the questions we won’t be able to [00:01:00] answer during this live webinar. We are also recording the webinar and everyone will receive a link to the recording via email. Okay, let’s talk about hacking C# from the inside. Adam, welcome.

Adam: Okay. Thank you for this introduction, Gael. Let me just share my screen and hopefully it works so we can now begin. Hello everyone. As mentioned, [00:01:30] I’m Adam Furmanek and I talk about .NET internals quite a lot. And if you go to YouTube, you can find quite multiple talks about how to hack memory, how to control garbage collector, how to do all the nasty stuff, move machine code directly in C#. However, what I typically do is I just show the business purposes or how to use, how to do those things for some other reason, for some other goal. However, Kevin Gosse, which you probably [00:02:00] know always asks me, “Hey, how do we actually do all those things?” And this is how this talk started. He pointed me that even though I do use plenty of those nice techniques, I do not necessarily explain them. I just use them to do some other things.

So today it’s going to be a little different. What we are going to do today is we’ll learn how to do, how to implement those building blocks for hacking things under the hood, in .NET platform, in C# language directly. [00:02:30] So we’ll see how to generate machine code, how to abuse the type system. We’ll see how to wrap windows APIs to do some fancy stuff. All of that, we’ll just learn how to implement those things. I won’t be focusing necessarily on some business use cases. So this talk is not necessarily that you take all the things here and start applying them in your day to day production code tomorrow. However, what we’ll learn is how those things work internally, how they are implemented and how [00:03:00] we can use them and abuse them. So the agenda for today’s talk is, we will see quite a multiple examples, not necessarily related to each other.

Some of them are like just pieces of code you write directly in C# which are working on the C# level. However, some of them will be actually interrupting to some low level scenarios like operating system, or machine code or CPU, et cetera, et cetera. [00:03:30] So we will go through those multiple areas. We’ll start with something very high level like how to avoid dynamic dispatch, or how to catch async, exceptions from async void method or how to actually await asynch void methods. And then we’ll move on to some other things playing with memory directly. We’ll see how to generate a machine code and execute it from a byte… Array of bytes. Or how to handle StackOverflowException or do other stuff, which we do not necessarily need to do just [00:04:00] for the sake of doing it. But for some other purpose, which we will not necessarily be covered in today.

So let’s begin. Let’s begin. The first thing we’ll actually see is how to avoid dynamic dispatch. Just to get like a quick warm up before we do some more low levelish thingies. So the dynamic dispatch like the pillar of object oriented programming is that whenever you do have a base class and some other classes inheriting from the base class, and then when you create the instance of [00:04:30] the… Sorry. Of the… My PowerPoint is not necessarily too fast today. Okay, go back.

When you create an instance of the derived class, but assign it to the base type, and when you call a method, which is a overloaded method, which is like a polymorphic one, you expect this method to be executed against the actual instance, which is stored in the variable, right? So even though this new derived is assigned to the variable [00:05:00] of type of base, whenever we call foo we actually expect it to call the foo from the derived2, because that’s the instance we are talking about here.

So we expect to see the derived2 value printed to the screen. However, if we did not have the polymorphic invocation, what we would end up with is the base method being code. So we would just call the base for implementation and see base printed out to the screen. This is because that the new derived is [00:05:30] on static time in compilation time assigned to a variable of type base. Right?

However, the question now arises, can we control this mechanism to some extent? Can we actually call b.foo and decide whether we would like to have foo executed from the base class, from derived2 class, or maybe from somewhere in between, like the derived class. And the answer is yes, we can do that. The thing is that the way we do it is we need to understand how are those things compiled under the hood.

[00:06:00] So generally whenever you get down to the intermediate language level, what you end up with is you have… You do have these two different instructions of how you can actually execute the code. So you do have the call instruction and you do have callvirt instruction. The callvirt instruction is used to do the dynamic polymorphic invocation, right? So callvirt instruction calls this late-bound method on the object.

So we just take the object [00:06:30] time at the run time and decide which method to call. On the other hand, there is this call instruction. What it does, is it calls the method like in a static manner. Meaning that whatever we do provide to the call instruction to be executed is directly executed no matter whether it’s overloaded or not. Because we do not consider the run time inheritance hierarchy in this part. So the way we can actually achieve this dynamic invocation or [00:07:00] avoid dynamic invocation is we can control which instruction we use, whether it’s call or callvirt.

And the way we’ll do is, we’ll actually go to the first example of the day. So we do have this class hierarchies, which we seen in the slides. So there is derived2 which inherits from derived, which in turn inherits from base. We do have virtual foo, which is overridden twice in both of these classes. And now what we do is we are creating a helper method called invoke method, [00:07:30] which accepts one generic parameter of which class we would like to use to actually execute this line of code. See that invoke method accepts an expression of action, not the action itself. So this lambda is not being executed at all. It’s merely like as a recipe of what code we would like to execute, of what we are trying to run. So we take this lambda. And the first thing we are doing is we check whether this lambda is an expression call.

If it’s not, then we actually [00:08:00] throw the exception, right? That’s the only place in this code where we do not have the compile-time safety, right? However, whatever else we do is compile-time safe, meaning that we cannot provide here invalid arguments, we cannot provide strings here. All we do is captured and checked by the compiler, even though we are trying to abuse those pieces a little. So we do take this lambda [00:08:30] out after we check whether it’s expression code, what we do next is we cast it and then we get all the arguments and finally get the method which we would like to call, okay? Ultimately we get the method from the type of type of T, which we provide as the… Sorry. Sorry of that. Which we provide as the generic parameter here.

What we do next is we get the lambda, which calls the method to be executed. [00:09:00] So what we get is we get ilgenerator, we start emitting the code, we get the arguments one by one. We put them to the stack. And ultimately what we do at the very end is we emit the instruction, which is call not callvirt. So when we get this thingy the call instruction is being executed. And this way we can avoid the whole dynamic dispatch. We’ll get to the code, which is executed statically, meaning that we provide which class to execute. [00:09:30] So this is the way how we can do this. And it’s all directly written in C# as you can see. There is no magic happening under the hood. Moving on to another example. So this is how we do this dispatch. Now we would like to do something else still in C# level.

We won’t be dwelling into the machine level code yet. What we would like to do is we would like to await the async void methods. Okay? So what we have with async void, the problem is we [00:10:00] do not have a thing which we can await, right? Whenever we do have async methods, they typically return task or return whatever else, so we can have something which is awaitable.

On the other hand, async void methods, they do not return things like this. So it’s much harder actually to see what we could await here, right? However, instead of awaiting them directly, what we can do is we can implement a custom synchronization context, which will [00:10:30] do the magic and await the method for us. In order to do that, if you would like to understand all the things which are happening here, I refer you to the talk I gave, which is called Internals of Async. Where you can see all the bits and bytes of the synchronization context.

However, what we will do today is we’ll just see how to implement this thing. Okay? So we will close this demo. We’ll move on to [inaudible 00:10:53]. To another one. Which is catch async void. And what we are going to do in this demo is we would like to start [00:11:00] with a piece of code like this method, which is an async void method. And it just prints something out, waits for a second and then throws the exception. Okay? If you do know how async works, you probably remember that throwing the exception from an async void method is not a very good idea because this most likely will crash your application. So it’ll get terminated. So we would like to not only to await for this method, but also we would like to catch the exception. So it doesn’t [00:11:30] break the application. So what can we do?

So first thing we need to do is we implement our custom task scheduler. So we’ll have a piece of logic, which takes tasks one by one, and manages how they are scheduled, how they are executed. So we’ll have a task scheduler, which will keep just this one little collection of all the tasks, all the continuations we would like to execute one by one. Okay? Having this task scheduler, what we do next is we implement our custom synchronization context. [00:12:00] And this synchronization context will first use… Sorry, create the task factory, which will use our custom task scheduler instead of the default one. So it’ll hide the scheduler, which is provided by the .NET platform and use our custom to manage all the tasks. Next, what we do is we have two different methods called post, post and send to handle all the things which are scheduled via the synchronization context.

And finally, what we do is we just create [00:12:30] a helper function, which triggers the whole code. So what we will do is instead of calling this async void method directly, we’ll actually run it within our custom synchronization context called, MyContext in here. So what this method does is it takes the lambda which we would like to execute. It first captures the current synchronization context and creates the custom one, our own synchronization context to handle all [00:13:00] those things.

Then what we do is we replace the synchronization context on the thread and we iterate through all the tasks, one by one to execute all of them in order. So we get all the continuations. Whenever you have task, whenever you have continue with whatever, you do execute it here, one by one, and you await for the results. Finally, at the very end, you just restore the synchronization context on a thread. On the thread you are executing [00:13:30] on.

And the important remark here is that we just run it within task.run method, which gives you and returns you the task you can actually use. So now when you have this task returned from here, you can actually get it and do whatever you wish. You can await it. You can call .wait. You can do all the stuff you would do with your regular tasks, like continue with, et cetera, et cetera.

So having all those things, what we can actually [00:14:00] see is that this… Sorry, I did not set the correct project to run. So this [inaudible 00:14:07]. So whenever we do run this node, you can actually see that it is being run in the try-catch thingy. So we can see that not only it does not crush at the very end, because it just swallows the exception as you can see here, but also you can get all the things, [00:14:30] all the benefits of awaiting the task.

So you can clearly see that yes, we managed to await, we managed to handle the exception and our application still works. So this is how you can actually await all the async void methods and continue safely with your application.

Okay. So moving on. Those two things were the examples of how we can do fine stuff just in C#. Let’s return to the slides. And what we are going to do next is we’ll [00:15:00] just move on to running any machine code from an array bytes in C# sharp application. So the important thing before we get to the machine code is we need to understand how those things work in C#, in .NET platform in general. So when it comes to functions, they are JIT-compiled typically, however they can be pregenerated, meaning compiled in ahead of time manner. Which you typically do using ngen or ready to run [00:15:30] mechanism in .NET Core.

If you ever wondered, why is your do .NET Framework, after you install your .NET Framework or do .NET Core or whatever .NET runtime, why is your CPU eating like 100% of its power for couple of minutes? That is because right after you install it, it starts compiling all the framework functions to the machine code capable of being executed directly on your CPU. That is because once you install the .NET, [00:16:00] you do know what CPU you have, what machine you are running on. So you know how to optimize things, what CPU instructions you have, et cetera, et cetera. So you can compile the code and have it right pregenerated in the machine form. However, for most of our applications and most of the code we write in C#, we cannot do that.

So it is delivered to our customers just as a simple intermediate language function, which was compiled from a C# code with the C# compile, right? [00:16:30] So once we start the application, we need to compile this intermediate language code to the actual machine code running on the machine. And this is what just-in-time compiler does. Now, when we are talking about compilation from intermediate language to some machine code, we need to deal with multiple other low level things, which we typically do not think of when we are just writing C#, right? Those things include calling convention. Those things include how parameters are passed, [00:17:00] whether it’s via registers, whether it’s via stack, whether it’s via well known location, global variables or whatever else. You need to deal with how those things return values, who cleans the stack, who serializes those values.

If we are talking about any marshaling or whatever else. So generally there is quite a lot, quite many things we need to deal with, we need to handle in order to get those things rolling. And this is what just-in-time compiler does [00:17:30] for you. The other thing which is very important here is that every single function has a thing which is called a method descriptor or a method handle. There are actually multiple names for the same thing here. So the method descriptor is a piece of metadata, which the .NET runtime uses to actually describe functions for you whenever it’s needed.

Like whenever you use reflection, when you ask for the function parameters, function generic type, function return [00:18:00] type, whether it’s overloaded, overridden, final whatever, it’s all stored in the method descriptor, in the metadata. So this is something which is held and managed by the .NET platform.

And we can get an access to that. We can read those things and actually start playing with them, doing some fancy stuff as we will see in the moment. However, this is about .NET functions. Now the thing is, the machine code, which is at the very end is not [00:18:30] an assembly code. We won’t be generating like the assembly language here. So we won’t be using nice up codes like move, like push, like pop, whatever else. Because we are just a one level below that. Assembly language is a bit, a piece of mnemonics and all the other instructions which then need to be translated to actual numbers. To actual numbers of the instructions we would like to execute. And we are talking about the [00:19:00] architecture x86, actually 32 and 64-bit on this machine. We’ll be mostly running with 32-bit examples, but they do work in 64-bit the same way.

However, all those examples, conceptually and technically will work on other architectures as well. If we are talking ARM here, PowerPC, whatever you wish. Those examples conceptually can be translated to other architectures, but here we’ll be generating the machine code, which is directly and strictly for x86 architecture. [00:19:30] So when it comes to machine code, it’s generally a bunch of data. You cannot discern one from another because in our architecture, the way we implement our computers, data and code are stored in the same memory space.

So we can consider the same bytes as being data or code, whatever you wish. You can actually go onto an internet and look for like bitmap graphics, like BMP file, which is both an image, like [00:20:00] a nice image, and an executable application. So we can find a file, which you can see in Microsoft Paint or just execute because it’s all the same.

It’s just a bunch of bytes, which we can interpret and use in different multiple ways. However, when talking about machine code, we also need to think about the operating system. So we need to handle the security, whether page is executable, whether the OS has an access to it, et cetera, et cetera. Many other things which we’ll need to deal with. [00:20:30] Now, the question is, how do we actually generate a bit of machine code? And the answer is you can just compile it. So you can write some C++ or whatever language or assembly language, compile it, and then you get the machine code. Or you can go to some webpage like this one mentioned on the screen and see how it compiles your mov eax, 123 instruction to some actual array of bytes. And this [00:21:00] is what we are going to use.

So we will take those things and we’ll start generating the machine code. And there are actually two ways we’ll do that. We’ll see two examples for doing so. The first example is once we have the array of bytes, we need to have a physical handle, which we can use to call that array, right? We need to have some pointers, some delegates, some managed thingy, which we can use to call the method from C#. And there are two ways to get such a thing. [00:21:30] One of them is method called GetDelegateForFunctionPointer, which is in your marshal class. What it does is, it’s meant to be used with interrupt scenarios. So whenever we do have the interrupt, like we would like to pass things from .NET to C++ or the other way around, you can use this method to get the pointer to some actual code to be executed.

And marshal does that. It converts the method to some specific delegate of some specific calling convention. So you [00:22:00] need to adhere to all those things. However, this is something we’ll see in a sec to understand how it works internally. The other technique we can use is we will get the C# coded directly, and then we’ll modify it in place to modify the machine code of the method, to jump from here to there and to modify the execution. How we are going between methods.

So let’s see all those things in action. So we’ll have the [00:22:30] two examples for today. The first one will be using the marshal, get function pointer. And the other thing is going to be the jump instruction. Okay. So let’s now switch to Visual Studio. And I’m sorry for that, because apparently Zoom is capturing my mouse, so I cannot use it freely, which is a bit funny and surprising.

Okay. So here we are. So the first thing [00:23:00] we are going to use is we will use the ByteToFunc_Marshal thingy. So what we’ll do is we need to first set it as a startup project, and then we’ll see what we are doing, what we are going to do with this trick. So, okay. There we go. And what we are going to do is, let’s skip on over those things. What we first do is we would like to have actually two examples. First of them is the ActionTest. [00:23:30] And the other example is the FuncTest. So we’ll test actually two different delegates. So we need to have two delegate helpers, one of them being action of integer. So this is just a generic action of int thingy, only written this way. So we have a better compile time safety and the compiler takes care of all the stuff around.

And the other is just function of int returning int. So what we want to do is, let’s start with FuncTest first. What we want to do is we want to [00:24:00] implement a very simple function, which accepts just one integer parameter, increases it by four and then returns the value. So you can see the machine code for the method being actually here. So this is the machine code, not the assembly code, not the assembly language, but the actual machine code, which we execute. And we would like to code this method in this way, right? So we would like to get the delegate, which we can just use in C#. Now, how do we do that? We need to have this [00:24:30] generate function, which helps us doing that. So the first thing we do is we get the array of bytes which we would like to execute.

Then we need to get the address of bytes in that array. So what we do first is within this array in memory. So it’s not being used by the garbage collector, because as you probably recall, the garbage collector can take your objects and shuffle them around in memory. Can make sure that objects are [00:25:00] compact. That there is no fragmentation. So no holes in between the objects and all that stuff happens directly by garbage collector. So we can ask it to not do that anymore.

And in order to do that, we can team those objects. So we need to first create the GC handle. We can ask it to be pinned. We can then use it. And finally, we can get the Marshal.ReadIntPtr function to get the actual pointer pointing to that first [00:25:30] byte of the structure of this object.

And if we take a look how array of bytes is implemented under the hood before the actual data, it stores two integers, specifying first, the type of the objects. So that this is an array of bytes and also the length of this array. So that’s why we skip by eight. We increase this address by eight. What we need to do next is… What we need to do is we need to unlock the page. [00:26:00] So it’s being executable. So the CPU does not complain that, “Hey, those things cannot be executed.” And finally, what we do is we use, in line 65, we GetDelegateForFunctionPointer and just returns it. So this is what we do. The same actually happens for the other example we have. This time we’ll be running an action, which not only does something, but also calls some other method.

So we will have the MyWriteLine method, which we would like [00:26:30] to call and execute from within that machine code. First thing we need to do is we take the method handle of the, MyWriteLine method as you can see, then we JIT compile this method, get the pointer pointing to that method. And ultimately we generate a bit of a machine code, which pushes the address of the method we would like to execute. And then just returns to this address. So we push it to the stack [00:27:00] and then do the return thingy, which ultimately, what it does is it jumps to the method we would like to execute. So this is the idea. This is the plan for what we would like to do. So let’s run those thingies.

And as we can see that we were executing those two methods. So you can see that, hey, we are actually in MyWriteLine. As you can see, and we do have the parameter five being printed out. But also when we do call the function [00:27:30] method, which wants to take an integer parameter and increase it by four, you can see that, hey, we do get 27 printed out as expected. So this is the first trick of how we can generate some piece of machine code in .NET. The other trick is actually very similar.

What it does on the other hand is instead of using the get function, get function pointer using marshal, what we are going to do is we will jump around the code base. [00:28:00] So this time our action integer, the same delegate we had last time is going to inherit from some base stub class. And the base stub class is a very simple, very straightforward type, which holds just one integer. One integer named target, which is, and we’ll use it to point to the actual method we want to execute.

So having this field, we can actually inherit from base stub and create an action [00:28:30] int which has the method called stub, which accepts the parameter we wish. So it just accepts the integer and returns void. That’s how ActionInt is going to work. And the other class, the other delegate we have is the FuncInt which is just a func of and integer and integer. So it accepts one integer and then returns the value. The other code of the example stays almost the same. So we do have the same machine code calling my MyWriteLine and the same machine code actually [00:29:00] adding four to the number we pass to it. What changes now is the way we get the pointer, the delegate to execute it. So we start in a very similar manner. We get the array of bytes we would like to run.

We pin it, we get the pointer, we move by eight bytes to skip the first two integers. We unlock the page. And then what we do next is we create a new instance of something inheriting from the base stub. And we provide the target. We set it to the [00:29:30] address pointing to the bytes from within the array of bites we would like to run. What we do next is we create the delegate as you can see. We create the delegate in line 75. And once we have this delegate, what we do is we get the method stub to be executed. So we get the stub method from the delegate we would like to use. So you can see, we will be getting this stub method from the ActionInt. And once we have [00:30:00] this method, we modify it. So we get the function pointer of this method in line 79.

So we get the pointer here and we would like to get the machine code of that method and hack it. So we modify it in here to replace first bytes of this code, which is generated and JIT compiled to actually do this very nice machine code trick. So we get the first field of the instance on which we are executing, and [00:30:30] then we push it to the stack and return. So we effectively jump to this address. So we get the first field of this class, which is the target, and then we jump to it. So instead of getting the pointer directly pointing to the bytes we’d like to use. We first call the stub method. And then from the stub method, we jump somewhere else. So as you can see, this is the trick we would like to do, and we can try it out. So we can start it and to observe one more super [00:31:00] interesting thing here.

So first notice that, hey, we do print the value five. We do print value 27. So it seems to be working okay, but also in this MyWriteLine, what we do apart from printing where we are… Or sorry, or printing the parameter. The other thing which we are doing is in line 116, we are printing our type of what have been executing. And you can see that even though MyWriteLine is in class program, the GetType returns [00:31:30] as the ByteToFunc.ActionInt. Why is that?

That is because when we get the handle here, the function which we would like to call, I told you that first thing, what we do is we call the stub method. So we called this method. And then because we modified the Console.WriteLine here, what we are doing is we jump from this place, from line 96, we jump to the custom, MyWriteLine, which is in line 115.

So because [00:32:00] we are jumping on a very low level, on a machine code level, the .NET platform actually does not recognize that, “Hey, we did switch gears and we are in different class now.” Because it’s just a bunch of bytes. And it’s just a piece of methods, piece of bytes we can execute. So that’s why, whenever we call this GetType, we are getting ByteToFunc.ActionInt instead of the program, MyWriteLine. So that would be it for the tricks [00:32:30] with jumping and generating machine code. What we can do next is we can start hijacking methods. So what we do now is first we generated a piece of machine code. Now what we would like to do is we would like to get some existing method in .NET and ask it to do something else.

And the trick for doing that is very similar to what we have just seen. So what we are going to do is we’ll use two different tricks of how we [00:33:00] can do that. The first trick to call different methods. So let’s see what we are going to do is. We do have a test class and we do have couple of methods here like ReturnString, ReturnStringHijacked, some properties, whatever, whatever. So we do create… Sorry, we do call a return string static method from the test class. So you can see that return string is static string returning just the original string. And then first after we call it, we then try to hijack it. [00:33:30] So then whenever we call return string, it’s not return string what is being called, but return string hijacked instead. So this is what we are going to do. And the way we do it is we can actually modify the metadata.

That’s one trick. And the other trick is we can jump. So let’s see first that it works. So let’s actually start this application. And what we should see is that, hey, first when we call the [00:34:00] return string, we get the original string. But then when we call the return string, after hacking it, we get the modified string. So how does it work? In this example, what we do is we get the method handle, method descriptor of the method we are executing. So we are calling HijackMethod with the ReturnString method, and we get the metadata for it. The same metadata used for reflection, the same metadata you use when you call, GetType, GetMethod, GetConstruct or [00:34:30] whatever. And we do take those handles. We compile them. And then what we do is we understand, or actually we use the fact that the actual pointer pointing to the physical machine code of the method is eight bytes from the beginning of the method handle.

So we get the source address of the method handle. We move by eight bytes, and then we modify the pointer to point to some other method. This way, what [00:35:00] we actually do is when we call the return string next time we still call this method, but it points to the machine code of this method. So now actually two different methods, both ReturnString and ReturnStringHijacked, they do point to the exact same machine code, which brings or returns just the modified string. So that’s the first trick we can use.

And the other trick is very similar to what we have already seen. [00:35:30] Instead of modifying the metadata. What we can do is we can modify the machine code itself. So we get the address of the method handle of the source method. We unlock the page and then we get the address of the target method.

And then we just jump from the original method to the target method, right? So what we do is we can jump from the original method, by [00:36:00] using this long jump instruction, which you can see again, how we generate a piece of machine code in here. So we generate this machine code, we put it in the method and this is how it works. So this time when we first call the return string, we call the method non modified, the way it was compiled by the C# compiler and then just-in time-compiler. However, the next thing we call this method, we do have not this return original string thingy here, but we do have [00:36:30] a jump instruction from here to this place. So we execute this method and we jump from one place to another. So this is how this example works. Now the question comes, okay, do I ever need those things?

And now I’m going to show you a couple of things. So when do we want to use a thing like this? So the first thing I’m going to show you, or actually I cannot show it to you because I’m using Zoom and Zoom will not [00:37:00] allow me to change desktops here so we would lose the stream. So I would just explain it in theory, how it works. So whenever you do have a multiple desktops in your application or sorry, in your operating system, you’d like to run an application. And by default it runs on the desktop you are currently on. So the question comes, can we run this application on some other desktop? And believe [00:37:30] it or not multiple desktops were in… Sorry, were in operating system, in Windows operating system for 30 years now, has been since the Windows 3.11 or whatever.

So very, very old thingy. The thing is, in order to run the process, we would like to specify on which desktop we run the application. So we would like to run the Notepad on one desktop this way. But also we would like to be able to specify [00:38:00] on which desktop it is being executed. Now comes the think, can we do this in .NET? And if we go to the source code of .NET libraries, you can see the process API, which creates this startup infrastructure, which you use to provide parameters of how you run the process. You can see that it does have the lpDesktop name. So the pointer to the desktop name you would like to use for the application. However, it’s always [00:38:30] being set to zero. It’s always nullified. So you can never actually override this thingy and you cannot provide the desktop name you would like to use to start your application.

The question comes, how can we modify that? And there are multiple things which can come to your mind. First idea is, instead of just running this process API, the .NET API, why not copy it on the side because we have the access to the source code. [00:39:00] We have access to both do .NET Core and .NET Framework. We can get this code, copy it, and then modify the way we wish. It would work. The problem is with this approach, the problem is that, hey, if they now change the process API, process classes, for whatever reason, we need to apply those changes to our source code, right? So we need to keep those two repositories basically in sync. The other approach could be, “Okay, let’s not use the process API [00:39:30] at all. Let’s go directly to the Windows API libraries and code, create process X or whatever the method from the operating system.”

It would work again, but the problem is now we lose the whole power of .NET wrappers, of .NET APIs. So we cannot now use C# classes for managing processes, getting command line, changing priorities, whatever. We just can’t do that. So what can we do in instead? Well, what we can do is we can modify the [00:40:00] source code. If we go to the startup infrastructure, which is… Sorry, the startup info class, which is very internal thinking, you can see that it does have the constructor here and this construct or this startup info is being created when you call the method, start with create process. So more or less when you do process.start. So we do process.start Do start and then .NET creates the instance of the startup inform, calls its constructor. [00:40:30] And here we can see lpDesktop is always zeroed out. What can we do about that? The trick is we can modify this constructor.

We can hijack it and modify the machine code of this method. How do we do that? Well, first thing is we prepare the desktop name. We prepare the string of the desktop name, which will be able to be consumed by the Win API. Then we scan all the assemblies with reflection and we get the [00:41:00] type which is called startup info. We get its constructor and ultimately we hijack this constructor with our new constructor thingy. So now what happens is when we call, when .NET calls this constructor when creating the startup info, it’s not this line of code, which is being executed, but instead this code is being triggered.

And what we do here is first we set the cb variable of the class, the same [00:41:30] way the constructor does it. And then we just set the lpDesktop to the value we would like to have.

So we can see that just by modifying the machine code under the hood, we can modify the behavior of our application. The other example of what we could do is we could modify the way threats are created. So let’s actually see this in action. Let’s see what we are doing here. So if you do know the threading in . [00:42:00] NET, you probably do understand that whenever you create a thread, which just throws the unhandled exception, when this exception is being propagated, it cues your application. You cannot deal with that at all. You cannot stop your process from terminating.

So what is happening here is this exception is going to kill you. The question is, can we help that? Can we, in some way, avoid this process from being terminated? And the answer is, yes. We can [00:42:30] get the constructor of the thread and drop it with some nice helper method, which will just wrap the original lambda with the try-catch block.

So how do we do that? Again, we use the magic of the low level code. So we get the thread, we get the constructor which accepts just one thread start parameter. We compile those methods. And then we start modifying them on a very low level. So instead of calling this original [00:43:00] threat constructor block, what happens is we just call our modified block and you can see that the exception was being thrown, but it was handled. And the application did not crash. So this is what we can do. This is how we can deal with all those things.

And we can modify the machine code under the hood to actually do some very, very nice things. Okay. So that would be it when it comes to my examples. [00:43:30] And now over to you Gael, to provide some other thing, which is worth of listening.

Gael: Yes. Thank you, Adam. This is absolutely fascinating. And when I’ve seen this demo with hijacking methods, I really wanted to share that to our audience. I would like you to say, well, we’ve with been doing not hijacking, but we’ve been doing [00:44:00] interceptions before with PostSharp. Actually, you mentioned that we have… So we can do interceptions at three levels.

Actually what you are doing is you’ve shown how to do that at the level of machine code from inside the CLR, actually a little known fact is that in PostSharp we’ve been using the… Well, we are still using the library mhook [00:44:30] to disable the whole validation of strong name keys for the PostSharp process. So how do we do that? We hook the regulatory API. We simulate that there is a setting that sets up that removes strong name signing.

So we have a real use case for that. So, PostSharp itself is the technology [00:45:00] that allows you to replace a method body by another method body. So we’ve built a complete product based on that. And today I would lik to introduce a product we’ve been working on for a year and a half, and it is called metalama. And instead of doing this interception or replacing the method body at the level of is [inaudible 00:45:28] code as in PostSharp, we actually do [00:45:30] that at the level of source code by hacking Roslyn.

So I will just play a very short demo, two minutes. To show what metalama is about. Metalama used to be named Project Caravela and is going to be released under the name metalama in a couple of weeks. Let’s play the demo.

With metalama, you can encapsulate repetitive logic into a special class, named an aspect. [00:46:00] You could do that with PostSharp, but metalama is different. In metalama, an aspect is like a code template that is applied to your source code during compilation and generates other source that is then executed. Look at the log attribute class. The code that is grayed out executes at compile time and the rest of the code executes at trend time.

[00:46:30] The call to meta.proceed means that the original method code should be invoked here. To apply the template to a method at the aspect as a custom attribute. To preview the code that will be executed enter the diff [inaudible 00:46:57] feature. The [inaudible 00:46:57] view compares the source code with the transform [00:47:00] code, where the template has been applied to your source code.

We can now execute the program. As expected, this program includes instructions that come from the template and instructions that come from the source code. If you want to debug the transformer code instead of the source code, select the LamaDebug configuration and step into. [00:47:30] As you can see, we are stepping into the transform code. That’s all I have for today. You will hear more about metalama in a couple of weeks. With… Thank you. Thank you. Let’s continue with the webinar, Adam.

Adam: Okay. Thank you for that. Let me [00:48:00] take over the screen. Hopefully it works again. Yes. And we are back. So we have seen quite multiple examples of what we can do with methods, whether we can run them or modify them from C# or from the low level machine code. What we are going to do now is we are going to play with types just a bit. So we are going to modify the type system. We are going to abuse it a bit.

So [00:48:30] let’s see what we have. So the first thing about the type system in .NET or in the platform form in general is that it is being verified during the compilation time. And also when we load the types, but once they are loaded, the platform doesn’t care anymore. Does not verify things, does not check whether the assumptions are still held, meaning that the assumptions [00:49:00] that the method accepts, these parameters return, that parameter those values, et cetera. Those things are not later verified, meaning that as we have already seen, we can modify the machine code of a method, even though after we compile it, even though it’s being tested or checked by the compiler before. But afterwards, no one cares.

Compiler does not reverify those things. Again, the same happens with the type system. Type system or the classes, inheritance and all that stuff. [00:49:30] They are checked. They are tested during the compilation time. Meaning if you try to do something, which should be not allowed by the language itself, the compiler will stop you. And not emit the code doing that. However, what we can do instead is we can modify the things internally to abuse the type system, because at the very end instance of the type, it is just a bunch of bytes. It doesn’t have anything to it which would make it magically type [00:50:00] safe.

It’s only that we make sure or try to make sure that the code we run against those instances is valid and does not abuse the type system. As we’ll see, we can modify that. Also, the important thing about the types or the instances is that all the types are generally like bags of data.

The instance itself has no methods associated to it, right? It’s not like in JavaScript that that type or the instance of a type [00:50:30] has pointers to the methods it’s being executed. No. In .NET, all the instances of given type they do share their methods. We can see as we have already seen with the method handles that the pointer is stored in one place in the metadata, which is accessible via reflection or used by the .NET internally. So the methods are not carried with the instance of the type. What is carried though is the data, all the variables, all the fields, which are [00:51:00] stored with the object.

Okay, let’s see what we can do. So the first example which we are going to see is we’ll just play with two unrelated types to see if we can hug them a bit. So we have a class A, which has this virtual void method print, and it prints that, “Hey, this is A.Print and I am this.GetType”

So we would expect A being printed out here. And also we have class B, which has exactly the same [00:51:30] method, virtual void print with no parameters. However, this method is not related to this print A, right? Those classes are not inheriting from each other. They do not have common parent apart from system.object. They have nothing else in common. So this is just a pure coincidence that those methods have exactly the same parameters and exactly the same name. So what we would like to do is we would like to get an instance of class A and call [00:52:00] method B.Print for that instance. How do we do that? Well, again, we can hijack methods. So we create an instance of class A and then call print with A and PrintWithA accepts just one parameter A, calls printing with A, and then calls a.Print.

So the method from the very top here, but we also have a very similar method print with B, which also prints PrintWithB and calls two methods from the B type print and [00:52:30] Print2. And what we do is we just hijack those methods. Sorry, we modify the print with A two code PrintWithB. Okay. So what we expect to see, and let’s actually see this in action and this application should crash. And we’ll understand in a sec why it does. However, the first thing we see is that even though we have an instance of type A and we call PrintWithA what we actually see is that [00:53:00] it’s printing with B, which has been executed in here, right? You can see Printing with B being executed, and then we try calling print b.Print. However, what happened is we executed a.Print and executed abusing type system.A.

Why is that? That is because even though we did call this method with instance of B as a parameter, actually under the hood, there was [00:53:30] this instance of class A being passed. So we now try calling method print on class B, which happens to be in exactly the same location as print in class A. So that is why we get different method being executed. Because what we try to do next is we try calling Print2, and you can see that when we tried calling b.Print2, we get a very nasty access violation exception.

[00:54:00] Why is that? That is because we tried calling the method, which is virtual. So the virtual method needs to be executed in runtime, need to be executed in a polymorphic manner, meaning that this Print2 was actually checked against the instance of the type we are executing. But hey, there is no Print2 in instance of class A so .NET just thrown a terrible exception that everything just crashed. Because we tried calling God knows what. However, if we do change this Print2, [00:54:30] if we do change it to being a non-virtual… Sorry, non-virtual method, but just the regular one.

And if we try calling this thing now, what we should observe is that Print2 is being executed properly. That is because Print2 now does not need to go through the callvirt instruction. So you can see that even though we are calling B.Print2 against instance of type [00:55:00] A, it still works just because we switched from using a virtual method to non-virtual one, we were able to call some code of completely unrelated type of a method of a completely unrelated type on the instance of a different thingy. And how can this be useful for us at all? We can actually start toying with it and do two fancy examples.

First thing is we will try serializing [00:55:30] a non serializable type. Okay? So let’s see what we have here. We have a class root, which is marked as serialize, right?

And it has some nice field, but also has non serializable child. Okay? So what is non serializable child is, is just another class with yet another field, but it does not have the attribute. Right? There is no serializable in here. So what happens is if we try serializing [00:56:00] instance of the root class, we’ll get an exception that, “Hey, child is not serializable.” What can we do instead? What we can try with is we can get the instance of this class and replace it with similar class, or actually a very similar thingy, which has exactly the same field, exactly the same method. Meaning the same structure, the same scheme and the memory. It’s actually reserved and stored [00:56:30] in the same manner when it comes to the actual order of bytes. However, this thingy has a serializable attribute on it, meaning that we can serialize the instance of serializable child.

What we do next is we create a hierarchy. We create the root object with one non serializable child. And now if we try to serialize this, we would get the exception. “Hey, root object is not serializable because the child cannot be serialized.” So we [00:57:00] do create an instance of non serializable child on the side. Ideally we copy exactly all the fields, one by one with reflection or whatever else. And then what we do is we would like to modify the instance. So we create a pointer to the root object here, and we would like to get a pointer to this non serializable child. So we get the pointer to the non serializable child… Sorry. To the serializable child. And [00:57:30] then we modify the pointer in place. So we get the handle to the object. We move by a specific amount of bytes, and then the size where the field is being stored.

And then we place the new child over there. And now when we try running this thingy and we are actually… When we try, sorry. When we try executing this thing, what we’ll see and actually printing into the stream, what we will see is that this thingy should work correctly. [00:58:00] Should not fail at all. Why is that? That is because when we execute this part, the objects should… Oh, sorry. I run it with debugger, which is not what I wanted. I want to run it without the debugger. What we should get here is you can see that, hey, before changing the children, it was non serializable that we hacked it. And we do have serializable child. And this time it printed exactly the same values, but when we tried serializing it, it did not crash, it [00:58:30] worked properly. So you can see just by toying on this level, we can replace the type, replace the instance and everything works correctly.

Moving on. We can actually do even more magical thingies. We can implement a multiple inheritance in C# or actually something which is going to pretend like we are inheriting from multiple classes. Let’s say that we do have multiple bases. So we would like to have a true multiple [00:59:00] inheritance in C#. We do have base one with one integer field and print integer method. Okay. Then we have base two with float field and PrintFloat. Then we have base three with two short fields and print field method. Then we have base four with one string field and PrintString. What we would like to do is we would like to create a class which inherits from base one and base two and base three and base four. How do we do that? We’ll actually be [00:59:30] swapping the contents of our object and morph it, depending on the use case we have. Because we cannot, on the .NET intermediate language, we cannot implement a true multiple inheritance in that place.

However, when we are dealing with the object, we actually need it to be an instance of one single class, not four of them, right? We’ll be dealing with just an instance of one single class, but we’ll be changing, switching [01:00:00] what the parent is for that instance. So we’ll have an interface called multiple base, which will have a very nice dictionary for which we’ll be holding the state. So we’ll have a dictionary holding all the possible fields for this type, meaning that there will be this integer field, float field, two short fields, and one string field, which are in all those base classes together. And we also store the type which was the type which we are currently… [01:00:30] Like the instance is pretending to be. So we have this CurrentState thingy and what we need to do now is we need to create a new class, which inherits from the specific base and implements this interface.

Okay? So that we do have those things wrapped together. Carrying on, what we do is we create an instance of FakeChild1 which inherits from base one, but is also an instance of this multiple base. Where we do specify that we would like to be [01:01:00] instances of base one, base two, base three, and base four. What we do next is we morph this child from FakeChild1 to Base1. Meaning that now this thingy becomes an instance of Base1, and you can see that we can assign to the integer field and call the print integer method, right? This is the regular variable of the Base1 type. Next, we modify the same child to represent [01:01:30] Base2. So you can see that this time we do have a float field and print float method. With base three, we have two short fields. With base four, we actually have the integer, right?

And then we can go back. We can morph to base three, base two, base one, starts printing those things. So we can see that all those things are executed properly, correctly as we would like them to be. So this is the option of the application. Now, how does this work? Okay. You can see that we assign 123 here, [01:02:00] 456, some other integer, some abracadabra, all those things. How does it work? The crucial part is in this morph method. So what we do is we get the state of the object. And if there is a need, we change the type. We store the values of the fields in the dictionary. So they can be later reused, so they can be preserved. But ultimately what we do is we change the type. How do we do that? Because each type, each instance has a [01:02:30] so-called… Just like method do have method handle, the same way objects, they have a thing which is called type handle.

And this type handle is a bit of metadata, which is used with reflection and all the stuff, which specifies that, “Hey, I am an instance of string. I am an instance of object. I am an instance of whatever.” So we get this type handle. And what we do is we modify this. So we get the actual, the original type handle [01:03:00] and replace it with the new type we would like it to be. So we go to the object and when we are calling this morph thingy, we go to child, which is instance of fake child. And then we, directly, straight into its metadata. We write an in integer or write attack saying that, “Hey, you are not FakeChild1. You are not FakeChild2. You are Base1, Base2, Base3.” Whatever needed.

So this is how we can do it. And as you can see, it works. It has compiler [01:03:30] support and allows us to implement the multiple inheritance thingy. So to sum up, as you can see, as you have seen, by understanding all the things under the hood and all the mechanisms with machine code, with objects, with all the stuff, we can actually play with it a lot. We can modify objects, we can generate machine code, modify methods, hack things. Obviously you need to understand quite a lot. You need to understand calling conventions, [01:04:00] garbage collector. You need to understand the CPU, your architecture, how to generate machine code.

You need to understand Win API, permissions, all the stuff which happens under the hood, but ultimately once you get a grasp of all of that, it’s just a bunch of bytes which you can modify, which you can toy with. And this is what you can do once you learn all the things which are just below the C# language, but you can implement those examples without leaving the C# language, directly. This QR code points to [01:04:30] the slide deck. If you would like to download the materials there, you can find them over there and now let’s move on the Q&A session or over to you, Gael.

Gael: Yes. Thank you. That was so fascinating. And it really gives an insight about the runtime. Like the internals of the CLR. Before we go to Q&A, I would like to introduce [01:05:00] the next webinar. So sharing this slide. Yes. So the next webinar, next month will be about Roslyn source generators. Stefan Polz will explain you and how and why never send a human to do a machine’s job. Stephan Polz is a clean C# sharp coder. He’s a speaker and an open source contributor. So see you the same [01:05:30] time on February 23rd.

We are continuing with questions. And actually we have just one question from Abraham. And that was a question about hijacking methods and executing byte arrays. The question is, if you can set an arbitrary address in memory and [01:06:00] run whatever there is there, then what is stopping us from jumping to memory that is not allocated from all application and run that? Isn’t that a security risk?

Adam: Yeah, that’s a very good question. And the answer is, or should be pretty straightforward. Yes. Nothing stops us from jumping to whatever others we have. We can do whatever we wish. We can execute any code, [01:06:30] jump to any place in our application, do whatever we want. I could even make a very bad joke here. Like the code execution results in code execution, you can do whatever you want. Is that a security risk? Not necessarily. Not in this way I’ve been doing that. That is because it is me who controls the code you are running. However, if you are loading some external code, like from a plugin or from the Internet or whatever else, then the answer [01:07:00] is yes. You do have a security risk. You cannot control what the application will do, because once you give it permission to just trigger any code, any C# code, well, it can execute as we’ve seen any machine code you like. Is this the security risk for the operating system or other applications? No.

But security risk for the user? Yes, it is. You can for instance start calling Win API, implement keyloggers or whatever else. Yes. That’s the thingy. [01:07:30] However, if you do not load code from the external sources, then it’s the same risk as with just your application doing something nasty. The other question, actually, which is very interesting and we could ask it. Hey, so if the plugin we load can do whatever it likes, can we stop it somehow?

And the answer to that is only partially. Meaning that there is this code access security, or have been [01:08:00] in .NET Framework 2.0, was then abandoned. So there were some mechanisms to stop you, for instance, to not allow you to call the marshal functions or whatever. But generally, if you can execute any code, which is marked as unsafe, and by default, you should be able to, then in unsafe, you can get pointers and do whatever you wish. So there is not a single reliable way to stop the external [01:08:30] code from doing anything it wants. So generally… Well, it can do whatever magic you imagine.

Gael: Good. But Adam, I think there are two conditions. If I didn’t miss anything in your presentation. There are two conditions to execute the code. First, regarding this question, the code must be in the current process. And the second, the page must have the execute flag, right?

Adam: [01:09:00] That is correct. And those things do not stop us because you can inject code from some external process if you wish. There is actually my talk, which is called DLL injection. You can find it on YouTube where I show you how to do it. And also to make the page executable, you just call the Win API method, VirtualProtect. And this is what I was doing in those demos. You may have recalled, there was the line unlock page.

Gael: Yes.

Adam: Which was actually doing that. So [01:09:30] you do have permissions to do that and nothing will stop you from doing so.

Gael: Okay. Maybe a second question from myself since we have no question from the audience, is any of the tricks you’ve been showing used in production?

Adam: Yeah, I do use those tricks in production. For instance, the trick with desktops I’ve shown you, and this is actually I’m using in production. And [01:10:00] that is because… Well, I was running some interactive application like Puppeteer, like automated Chrome, or just automated browser, which was stealing focus a lot. Meaning that this just pops up on your screen, takes control over your keyboard, et cetera. Pretty annoying, I’m telling you. So what I wanted to do is I wanted to run it on some other desktop. And .NET did not allow me to do it cleanly. So I was running it with the hack I’ve shown you. So this was [01:10:30] in production, actually still is. The example with hijacking the thread constructor. So when there was some exception being thrown and we were wrapping this with try-catch. This is also something I was using in production.

You may ask why? “Hey, why can’t you just change this thread to handle the exceptions with try-catch by itself?” Right? And the answer is I was actually using some Java code at that point, because there is a thing which is called IKVM, which is Java to .NET compiler, [01:11:00] which allows you to run any Java or JVM code inside your .NET application with direct Java code, not translated to C# and compiled.

And in Java, when you do have a new thread and you throw an exception, it does not kill your application. So Java programmers do not necessarily care to handle all the unhandled exceptions because it’s safe or it’s harmless in their applications. So I was running some code, which was creating a thread in Java, [01:11:30] not handling the exception and then crushing my .NET app. So I had to interject those things this way. I was doing couple other things like catching stack overflow in my custom test runner, right?

When you do have a stack overflow exception, it kills your application. You can handle those things a bit. You can deal with them. So there were other examples I was playing with it. So generally the answer is, yes, those things are in production, but I generally do not recommend you to use them if there is [01:12:00] any other way. You need to understand quite a lot. And they tend to break over time when you update .NET and change major versions, those codes, they may just start throwing exceptions or something. Just because you rely on the internal representation of your methods. So you need to be very careful whether that pointer is actually eight bytes from the beginning of the structure, or maybe 12 bytes or whatever. So this is nasty when doing upgrades, but technically it [01:12:30] works for many years now in my production code.

Gael: Excellent. Thank you very much. This is the end of this webinar. It has been an amazing, fascinating webinar, Adam. Thank you very much for being with us today. On behalf of the team, this is Gael Fraiteur, and I’m saying goodbye for now and see you on the next PostSharp live webinar on February 23rd. Thank you very much.



Subscribe to our

Stay up to date on all the latest releases, news and discounts.
Follow us on social media:

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.