3
votes

As a side-project for learning more about c#'s native side, i wanted to hook a virtual method in a virtual method table.

I successfully can call the function, but changing the pointer to the virtual method to my own method crashs when the function is called.

I made a small c++ application for this learning purpose and here is it

#include <iostream>
#include <conio.h>
using namespace std;

class Class
{
	public:
	virtual void Function ( ) = 0;
	virtual void Function2 ( ) = 0;
	virtual void Function3 ( ) = 0;
};


class ClassI : Class
{
	public:
	void Function ( )
	{
		cout << "Function1" << endl;
	}

	void Function2 ( )
	{
		cout << "Function2" << endl;
	}

	void Function3 ( )
	{
		cout << "Function3" << endl;
	}


};



int main ( )
{
	ClassI* a = new ClassI ( );
	int aaaa = 10;

	int* aaa = &aaaa;

	cout << "AddressOfClass: " << &a << endl;
	getch ( );

	a->Function ( );

	getch ( );
	delete a;
	return 0;
}

I get a instance of the Class pointer and output it and wait for input.

After input i run the function i want to hook.

Now in my c# side, i created a dll and the dll is in the process'es memory space through clr injection.

I verified the injection works with a bunch of ways.

 public class EntryPoint
    {
        #region Delegates
        private delegate void orgFunction();
        private static orgFunction oFunction;
        #endregion




        public static void Hooked()
        {
            Console.WriteLine("HookedFunction");
            oFunction();
        }



        [DllExport("DllMain",CallingConvention.Cdecl)]
        public static void DllMain()
        {

            unsafe
            {
                Delegate Hook = new Action(Hooked);
                IntPtr* vtable = (IntPtr*)*(IntPtr*)0x00F3FE10;
                oFunction = Marshal.GetDelegateForFunctionPointer<orgFunction>(*(IntPtr*)vtable[0]);
                uint OldProtection;
                MUtil.MEMORY_BASIC_INFORMATION mbi;
                MUtil.VirtualQuery((IntPtr)vtable, out mbi, (IntPtr)sizeof(MUtil.MEMORY_BASIC_INFORMATION));
                MUtil.VirtualProtect(mbi.BaseAddress, (uint)mbi.RegionSize, 0x04, out OldProtection);
                vtable[0] = Marshal.GetFunctionPointerForDelegate(Hook);
                MUtil.VirtualProtect(mbi.BaseAddress, (uint)mbi.RegionSize, OldProtection, out OldProtection);
            }
        }
    }

That is what i am doing at the c# side.

after getting the oFunction, for test i did call it and removed the other code and it worked.

but hooking it everything is fine but when in the c++ program i send a input and it runs the function we hooked, the app crashes.

I also update the address each time i run the c++ program so ye.

        [StructLayout(LayoutKind.Sequential)]
        public struct MEMORY_BASIC_INFORMATION
        {
            public IntPtr BaseAddress;
            public IntPtr AllocationBase;
            public uint AllocationProtect;
            public IntPtr RegionSize;
            public uint State;
            public uint Protect;
            public uint Type;
        }

        [DllImport("kernel32.dll")]
        public static extern UIntPtr VirtualQuery(IntPtr lpAddress, out MEMORY_BASIC_INFORMATION lpBuffer, IntPtr dwLength);


        [DllImport("kernel32.dll", SetLastError = true)]
        internal static extern bool VirtualProtect(IntPtr address, uint size, uint newProtect, out uint oldProtect);

Edit:

I decided to make a simple little test, i changed oFunction to vmtable[2]

oFunction = Marshal.GetDelegateForFunctionPointer<orgFunction>(*(IntPtr*) vtable[1]  );

That code above crashed me and i was quite surpired, this brings me the idea that getting the address of ClassI instance might give as the address of the first function instead of the vmtable which i don't think is the case.


-

Thanks for reading this, have a good day/night.

Best regards.

1

1 Answers

0
votes

To do a vmtable hook all you do is overwrite the function pointer inside the vtable with a new one, to do that you just use VirtualProtect() to get write permissions if necessary and then use WriteProcessMemory() to overwrite it.

You need to manually reverse the vtable and manually overwrite it using WriteProcessMemory().

  1. Get the address of the vtable
  2. Add vTableIndex*4 or vTableIndex*8 (depending on x86 vs x64)
  3. Overwrite this address with WriteProcessMemory