We push parameters into unsafe operations in a safe code

    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. Also we will try to convert the reference type to a number - its address, without using pointers and unsafe code.

    Disclaimer


    Before proceeding with the story, I strongly recommend that you familiarize yourself with the previous post about StructLayout , since things specified there will not be repeated here.

    I would also like to warn that this article does not contain material that should be used in real projects.

    Some background information.


    Before we start practicing, let's remember how C # code is converted.
    Let us examine a simple example. Let me remind you that in order to have fun with StructLayout, I use only virtual methods.

    publicclassHelper 
    {
            publicvirtualvoidFoo(int param)
            {
            }
    }
    publicclassProgram 
    {
        publicvoidMain() 
        {
            Helper helper = new Helper();
            var param = 5;
            helper.Foo(param);
        }
    }
    

    This code does not contain anything complicated, but the instructions generated by JiT contain several key points. I propose to parse only a small fragment of the generated code.

        1: movdword[ebp-0x8], 0x5
        2: movecx, [ebp-0xc]
        3: movedx, [ebp-0x8]
        4: moveax, [ecx]
        5: moveax, [eax+0x28]
        6: calldword[eax+0x10]

    In this small example, you can observe fastcall - an agreement on the transfer of parameters through registers (the first two parameters from left to right in the ecx and edx registers), and the remaining parameters are passed from right to left in the stack. The first (implicit) parameter is the address of the instance of the class on which the method is called. It is passed as the first implicit parameter for each instance method. The second parameter is a local variable of type int (in our case).

    So, in the first line we see the local variable 5, there is nothing interesting here.
    In the second line, we copy the address of the Helper instance into the ecx register. This is the address of the method table itself.
    The third line contains a copy of local variable 5 to the register edx
    Fourththe string copies the address of the method table to the eax register
    The fifth line contains the shift of the eax register by 40 bytes; loading the value from the memory at an address that is 40 bytes larger than the address of the method table: to the start address of the methods in the method table. (The method table contains various information that is stored before. Such information, for example, includes the address of the base class's method table, the EEClass address, various flags, including the garbage collector flag, and so on). Accordingly, the address of the first method from the method table is now stored in the eax register.
    In the sixththe line calls the method at offset 16 from the beginning, that is, the fifth in the method table. Why is our only method fifth? I remind you that object has 4 virtual methods (ToString, Equals, GetHashCode and Finalize), which, accordingly, will be in all classes.

    Go to practice


    It's time to start a small demonstration. I offer just such a blank (very similar to the blank from the previous article).

    [StructLayout(LayoutKind.Explicit)]
        publicclassCustomStructWithLayout
        {
            [FieldOffset(0)]
            public Test1 Test1;
            [FieldOffset(0)]
            public Test2 Test2;
        }
        publicclassTest1
        {
            publicvirtualintUseless(int param)
            {
                Console.WriteLine(param);
                return param;
            }
        }
        publicclassTest2
        {
            publicvirtualintUseless()
            {
                return888;
            }
        }
        publicclassStub
        {
            publicvoidFoo(int stub) { }
        }
    

    And the following filling of the Main method:

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

    As you might guess, from the experience of the previous article, the Useless (int j) method of type Test1 will 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.

    movecx, [ebp-0x20]movedx, [ebp-0x10]cmp[ecx], ecxcallStub.Foo(Int32)
         nopmovecx, [ebp-0x1c]moveax, [ecx]moveax, [eax+0x28]calldword[eax+0x10]

    I think you know the virtual method call pattern, it starts after L00cc: nop. As we can see, the address of the instance on which the method is called is written to ecx. But since we supposedly call a method of the type Test2, which has no parameters, then nothing is written to edx. However, before that, the method was called to which the parameter was passed just through the edx register, respectively, the value in it remained. and we can see it in the output window.

    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 method Useless does not change. Below you can see the result on my machine with some clarifying elements: WinDBG and Calculator :) The picture is clickable




    The output window displays the address of the reference type in decimal notation.

    Total


    They refreshed the knowledge about calling methods using the fastcall convention and immediately used the wonderful edx register to pass a parameter 2 methods at a time. They also spat on all types and remembering 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!

    PS C # code can be found at the link

    Also popular now: