Disclaimer

Some initial information

public class Helper { public virtual void Foo(int param) { } } public class Program { public void Main() { Helper helper = new Helper(); var param = 5; helper.Foo(param); } }

1: mov dword [ebp-0x8], 0x5 2: mov ecx, [ebp-0xc] 3: mov edx, [ebp-0x8] 4: mov eax, [ecx] 5: mov eax, [eax+0x28] 6: call dword [eax+0x10]

Goto Practice;

[StructLayout(LayoutKind.Explicit)] public class CustomStructWithLayout { [FieldOffset(0)] public Test1 Test1; [FieldOffset(0)] public Test2 Test2; } public class Test1 { public virtual int Useless(int param) { Console.WriteLine(param); return param; } } public class Test2 { public virtual int Useless() { return 888; } } public class Stub { public void Foo(int stub) { } }

class Program { static void Main(string[] args) { Test2 fake = new CustomStructWithLayout { Test2 = new Test2(), Test1 = new Test1() }.Test2; Stub bar = new Stub(); int param = 55555; bar.Foo(param); fake.Useless(); Console.Read(); } }

mov ecx, [ebp-0x20] mov edx, [ebp-0x10] cmp [ecx], ecx call Stub.Foo(Int32) mov ecx, [ebp-0x1c] mov eax, [ecx] mov eax, [eax+0x28] call dword [eax+0x10]

Total

Hello. This time we continue to laugh at the normal method call. I propose to get acquainted with the method call with parameters without passing parameters. We will also try to convert the reference type to a number — its address, without using pointers andBefore proceeding with the story, I strongly recommend that you read the previous post about StructLayout . Here I will use some features, that were described there.Also I would like to warn that this article does not contain material that should be used in real projects.Before we start practicing, let's remember how the C# code is converted into assembler code.Let us examine a simple example.This code does not contain anything difficult, but the instructions generated by JiT contain several key points. I propose to look only on a small fragment of the generated code. in my examples I will use assembler code for 32 bit machines.In this small example, you can observe fastcall — calling convention that uses registers to pass parameters (the first two parameters from left to right in the ecx and edx registers), and the remaining parameters are passed through the stack from right to left. The first (implicit) parameter is the address of the instance of the class on which the method is called (for non-static methods).In our case first parameter is the address of the instance, second one is ourvalue.So int theline we see the local variable 5, there is nothing interesting here.In theline, we copy the address of the Helper instance into the ecx register. This is the address of the pointer to method table.In theline there is copying of local variable 5 into the edx registerIn theline we can see copying of the method table address into the eax registerline contains loading of the value from memory at the address 40 bytes larger than the address of the method table: the start of the methods addresses in the method table. (The method table contains various information that is stored before. For example address of the base class method table, the EEClass address, various flags, including the garbage collector flag, and so on). Thus, the address of the first method from the method table is now stored in the eax register.In theline, the method is called at offset 16 from the beginning, that is, the fifth one in the method table. Why is our only method is fifth? I remind you thathas 4 virtual methods (), which all classes will have, respectively.Practive:It's time to start a small demonstration. I suggest such small blank (very similar to the blank from the previous article).And let's use that stuff in such way:As you might guess, from the experience of the previous article, themethod of typewill be called.But what will be displayed? The attentive reader, I believe, has already answered this question. «55555» is displayed on the console.But let's still look at the generated code fragments.I think you recognize the virtual method call pattern, it starts after. As we can see, as expected ecx is filled with the address of the instance on which the method is called. But since the compiler think that we call a method of type Test2, which has no parameters, nothing is written to edx. However, we have another method call before. And there we have used edx to pass parameter. And of course we don't have instructions, that clear edx. So, as you can see in console output, previous edx value was used.There is another interesting nuance. I specifically used the meaningful type. I suggest trying to replace the parameter type of the Foo method of the Stub type with any reference type, for example, a string. But the parameter type of the methoddoes not change. Below you can see the result on my machine with some clarifying information: WinDBG and Calculator :)The output window displays the address of the reference type in decimal notation.We refreshed the knowledge of calling methods using the fastcall convention and immediately used the wonderful edx register to pass a parameter in 2 methods at a time. We also spat on all types and with the knowledge that everything is only bytes easily obtained the address of the object without using pointers and unsafe code. Further I plan to use the received address for even more inapplicable purposes!Thanks for attention!P.S. C# code can be found here