Endscene Detour

04/03/2014 16:33 kurrbis#1
Hi,
i successfully hooked the directx endscene method in heathstone with easyhook, but when i try to call game functions like GameState.Get().GetLocalPlayer() it throws an exception. Someone knows why its not working? I mean, i am calling the functions from the hooked endscene, which is running in the main thread, so in my opinion it should work fine, but it doesnt....
04/03/2014 17:58 Bl@ze!#2
Would you share your hook ? Maybe we can find the issue. Check whether your really in the main thread.

Also calling GameState.Get() etc. shouldn't be called outside of Unity Engine Tasks, try to call it in a custom MonoBehaviour within Update()
04/03/2014 19:31 kurrbis#3
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using EasyHook;
using System.Windows.Forms;
using SlimDX;
using SlimDX.Direct3D9;
using System.Diagnostics;

namespace hsBot
{
    public class Main : EasyHook.IEntryPoint
    {
        LocalHook eHook;

        public Main(RemoteHooking.IContext InContext, String InChannelName)
        {
        }

        public void Run(RemoteHooking.IContext InContext, String InChannelName)
        {          
            Device dev;
            dev = new Device(new Direct3D(), 0, DeviceType.Hardware, IntPtr.Zero, CreateFlags.HardwareVertexProcessing, new PresentParameters() { BackBufferWidth = 1, BackBufferHeight = 1 });

            IntPtr address = dev.ComPointer;
            address = (IntPtr)Marshal.ReadInt32(address);
            address = (IntPtr)((int)address + 0xA8);
            address = (IntPtr)Marshal.ReadInt32(address);
            

            eHook = LocalHook.Create((IntPtr)address, new DEndScene(EndSceneHook), this);
            eHook.ThreadACL.SetExclusiveACL(new Int32[] { 0 });   
        }


        [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = true)]
        delegate int DEndScene(IntPtr Direct3dDevice);

        public int EndSceneHook(IntPtr Direct3dDevice)
        {
            using (Device d3d = Device.FromPointer(Direct3dDevice))
            {
                MessageBox.Show("Called from EndScene");
                return d3d.EndScene().Code;
            }
        }

    }
}
This is the code from the injected dll. If i replace the MessageBox with a game function call its throws an exception...
04/03/2014 21:34 dready#4
You are in the Context of the Nativ Heartstone client, you would need to invoke the function to load an assembly from mono.dll and call the functions from there.

Edit:

Oh and make sure you are calling from the Mainthread, otherwise TLS will bite you
04/03/2014 22:24 Bl@ze!#5
You could hook : mono_exception_from_name_msg and throw an exception for example. Or use any other mono.dll function see dependency walker.
04/04/2014 20:06 kurrbis#6
Thanks for your answers!

What i am trying now, is to inject a native dll which will load my managed dll with the mono functions, reffering to [Only registered and activated users can see links. Click Here To Register...] :

Code:
#include <windows.h>
#include <cstdio>

#include <mono/jit/jit.h>
#include <mono/metadata/assembly.h>


typedef MonoDomain* (__stdcall * mono_jit_initPointer)(const char* file);
typedef MonoAssembly* (__stdcall * mono_domain_assembly_openPointer)(MonoDomain*, const char* name);



void init()
{
	
	mono_jit_initPointer mono_jit_initRemote;
	mono_domain_assembly_openPointer mono_domain_assembly_openRemote;

	HINSTANCE hDLL = LoadLibraryA("mono");
	mono_jit_initRemote = (mono_jit_initPointer)GetProcAddress(hDLL,"mono_jit_init");
	mono_domain_assembly_openRemote = (mono_domain_assembly_openPointer)GetProcAddress(hDLL,"mono_domain_assembly_open");


	MonoDomain *domain;
	domain= mono_jit_initRemote("Hearthstone");
	
	MonoAssembly *assembly;
	assembly = mono_domain_assembly_openRemote(domain, "test.dll");

	if (!assembly)
		MessageBoxA(NULL,"ERROR", "ERROR", MB_OK);


	//int retval = mono_jit_exec (domain, assembly, 0, NULL);
	

}

int __stdcall DllMain(HINSTANCE hInst,DWORD reason,LPVOID reserved)
{
 switch(reason)
 {
 case DLL_PROCESS_ATTACH:
	 init();
 break;
 }
 return true;
}
If i inject that code, Hearthstone just closes.

I am not sure what the domain_name is for use in mono_jit_init (domain_name)?
I tried to detour that function and grab the domain_name parameter, but i am not able to inject the dll before the first mono_jit_init call is made...

Some suggestions?
04/04/2014 22:18 dready#7
Look for process creation, suspend it, add your hook, resume.

EDIT: Fetch the right domain name from the initialization of the monovm for the heartstone logic code.
Guess you can read it from memory, but hooking it seems like a legit solution.

Another approach, hook the assembly loading and manipulate the bytecode onthefly to load your dll.
04/04/2014 23:29 kurrbis#8
Code:
			MonoDomain *domain = mono_domain_get();
			MonoAssembly *monoAssembly = mono_domain_assembly_open(domain, "D:\\loadDll.dll");

			MonoImage *image = mono_assembly_get_image (monoAssembly);
		    MonoClass *my_class = mono_class_from_name (image, "MyNamespace", "Loader");

			MonoObject* obj = mono_object_new (domain, my_class);

			MonoMethod *method = NULL, *m = NULL;
			void* iter;
			iter = NULL;
			while ((m = mono_class_get_methods (my_class, &iter))) {
				if (strcmp (mono_method_get_name (m), "testMethod") == 0) {
					method = m;
				} 
			}
			void *args[1];
			args[0] = mono_string_new(mono_domain_get(), "blup");
			mono_runtime_invoke(method, obj, args, NULL);
Someone got an idead why mono_runtime_invoke is not working(no error, but the method does not get called)?
04/06/2014 11:19 Bl@ze!#9
Did you find a solution ? I am interested in this. :)
04/06/2014 12:20 kurrbis#10
Still having trouble with invoking the method...
The above codes loads the assembly, gets the image, gets the class and finally gets the the right method. All these steps are working, but executing the method does not and i have no clue why its not working...
04/06/2014 13:36 Bl@ze!#11
Would you zip your solution? I can help you better then. Invoking your plugin method shouldn't be that hard.
04/06/2014 15:52 kurrbis#12
Code:
#include <Windows.h>
#include "Detours\detours.h"
#include <mono/jit/jit.h>
#include <mono/metadata/assembly.h>
#include <mono/metadata/debug-helpers.h>
#pragma comment(lib,"mono/mono.lib")
#include <d3dx9.h>
#include <d3d9.h>

bool loaded = false;


typedef HRESULT(__stdcall* EndScene_t)(LPDIRECT3DDEVICE9);
EndScene_t pEndScene;


HRESULT __stdcall hkEndScene(LPDIRECT3DDEVICE9 pDevice) 
{
	if(!loaded)
	{
		loaded = true;
		MessageBoxA(NULL,"Loading stuff from EndScene", "asd", MB_OK);
		
		MonoDomain *domain = mono_domain_get();
		MonoAssembly *monoAssembly = mono_domain_assembly_open(domain, "D:\\loadDll.dll");

		MonoImage *image = mono_assembly_get_image (monoAssembly);
		MonoClass *my_class = mono_class_from_name (image, "MyNamespace", "Loader");

		MonoObject* obj = mono_object_new (domain, my_class);

		MonoMethod *method = NULL, *m = NULL;
		void* iter;
		iter = NULL;
		while ((m = mono_class_get_methods (my_class, &iter))) {
			if (strcmp (mono_method_get_name (m), "testMethod") == 0) {
				method = m;
			} 
		}
		//void *args[1];
		//args[0] = mono_string_new(mono_domain_get(), "D:\\loadDll.dll");
		MessageBoxA(NULL,"executing","",MB_OK);
		mono_runtime_invoke(method, NULL, NULL, NULL);
		MessageBoxA(NULL,"executed","",MB_OK);
	}

	
	return pEndScene(pDevice);
}

bool bCompare(const BYTE* pData, const BYTE* bMask, const char* szMask)
{
    for(;*szMask;++szMask,++pData,++bMask)
        if(*szMask=='x' && *pData!=*bMask ) 
            return false;

    return (*szMask) == NULL;
}
DWORD FindPattern(DWORD dwAddress,DWORD dwLen,BYTE *bMask,char * szMask)
{
    for(DWORD i=0; i < dwLen; i++)
        if( bCompare( (BYTE*)( dwAddress+i ),bMask,szMask) )
            return (DWORD)(dwAddress+i);

    return 0;
}



void init()
{
	DWORD* vtbl = 0;     
    DWORD table = FindPattern((DWORD)GetModuleHandleA("d3d9.dll"), 0x128000,     (PBYTE)"\xC7\x06\x00\x00\x00\x00\x89\x86\x00\x00\x00\x00\x89\x86", "xx????xx????xx");
    memcpy(&vtbl, (void*)(table+2), 4);
    DWORD EndSceneaddy = vtbl[42];
	pEndScene = ( EndScene_t )DetourFunction((PBYTE) EndSceneaddy,(PBYTE)hkEndScene);
}

int __stdcall DllMain(HINSTANCE hInst,DWORD reason,LPVOID reserved)
{
 switch(reason)
 {
 case DLL_PROCESS_ATTACH:
	 init();
 break;
 }
 return true;
}
Okay this is the code for the dll you have to inject.

First you need to include Microsoft Detours which i put in the attachment.

You need to get the DirectX Sdk from here: [Only registered and activated users can see links. Click Here To Register...] and include the source and libs in your projects.

These steps are necessary for the EndScene Hook. We need that Hook, because we need to load the assemblys from the MainThread of Hearthstone.

Also you need to get the mono source files from: [Only registered and activated users can see links. Click Here To Register...] and include them in your project.
The last step is to include the mono.lib which i put in the attachment.

Now you should be able to compile the dll and inject it.

The net dll which should be loaded and executed looks like this:
Code:
using System;
using System.Collections.Generic;
using System.Text;

namespace MyNamespace
{
    public class Loader
    {
        public Loader()
        {
            System.Windows.Forms.MessageBox.Show("aus dll");
        }

        public static void testMethod()
        {
            System.Windows.Forms.MessageBox.Show("aus testMethod");
        }
    }
04/06/2014 19:22 Bl@ze!#13
ok, trying it out - i am familar with detours so let me try something
04/06/2014 20:11 kurrbis#14
If you need some other detours for the mono functions
Code:
typedef MonoDomain* (__cdecl * mono_jit_init_versionPointer)(const char* file, const char* version);
typedef MonoAssembly* (__cdecl * mono_domain_assembly_openPointer)(MonoDomain* domain, const char* name);
typedef MonoAssembly* (__cdecl * mono_assembly_loadPointer) (MonoAssemblyName *aname, const char *basedir, MonoImageOpenStatus *status);
typedef int (__cdecl * mono_jit_execPointer)(MonoDomain* domain, MonoAssembly* assembly, int argc, char** argv);
typedef MonoClass* (__cdecl * mono_class_from_namePointer)(MonoImage* image, const char* name_space, const char* name);
typedef MonoObject* (__cdecl * mono_object_newPointer)(MonoDomain* domain, MonoClass* klass);
typedef void (__cdecl * mono_runtime_object_initPointer)(MonoObject* thisObject);
typedef MonoMethod* (__cdecl * mono_method_desc_search_in_classPointer)(MonoMethodDesc *desc, MonoClass *klass);
typedef MonoObject* (__cdecl * mono_runtime_invokePointer)(MonoMethod *method, void *obj, void **params, MonoObject **exc);

mono_jit_init_versionPointer pInit;
mono_assembly_loadPointer pAssemblyLoad;
mono_jit_execPointer pMono_jit_exec;
mono_class_from_namePointer pMono_class_from_name;
mono_object_newPointer pMono_object_new;
mono_domain_assembly_openPointer pMono_domain_assembly_open;
mono_runtime_object_initPointer pMono_runtime_object_init;
mono_method_desc_search_in_classPointer pMono_method_desc_search_in_class;
mono_runtime_invokePointer pMono_runtime_invoke;

MonoObject* __cdecl hkmono_runtime_invoke(MonoMethod *method, void *obj, void **params, MonoObject **exc)
{
	MessageBoxA(NULL,"a","a",MB_OK);
	return pMono_runtime_invoke(method,obj,params,exc);
}

MonoMethod* __cdecl hkmono_method_desc_search_in_class(MonoMethodDesc *desc, MonoClass *klass)
{
	MessageBoxA(NULL,"a","a",MB_OK);
	return pMono_method_desc_search_in_class(desc, klass);
}

void __cdecl hkmono_runtime_object_init(MonoObject* thisObject)
{
	MessageBoxA(NULL,"a","a",MB_OK);
	pMono_runtime_object_init(thisObject);
}

MonoAssembly* __cdecl hkmono_domain_assembly_open(MonoDomain* domain, const char* name)
{
	MessageBoxA(NULL,name,name,MB_OK);
	return pMono_domain_assembly_open(domain, name);
}

MonoObject* __cdecl hkmono_object_new(MonoDomain* domain, MonoClass* klass)
{
	MessageBoxA(NULL,"a","a",MB_OK);
	return pMono_object_new(domain, klass);
}

MonoClass* __cdecl hkmono_class_from_name(MonoImage* image, const char* name_space, const char* name)
{
	MessageBoxA(NULL,name_space,name,MB_OK);
	return pMono_class_from_name(image, name_space, name);
}

int __cdecl hkmono_jit_execPointer(MonoDomain* domain, MonoAssembly* assembly, int argc, char** argv)
{
	MessageBoxA(NULL,"a","A",MB_OK);
	return pMono_jit_exec(domain,assembly,argc,argv);
}

MonoAssembly* __cdecl hkAssemblyLoad(MonoAssemblyName *aname, const char *basedir, MonoImageOpenStatus *status)
{
	return pAssemblyLoad(aname,basedir,status);
}

MonoDomain* __cdecl hkInit(const char* file, const char* version) 
{
	MessageBoxA(NULL,file,file,MB_OK);
return pInit(file, version);
}



HINSTANCE hDLL = GetModuleHandleA("mono.dll");
	pInit = ( mono_jit_init_versionPointer )DetourFunction((PBYTE) GetProcAddress(hDLL,"mono_jit_init_version"),(PBYTE)hkInit);
	pAssemblyLoad = ( mono_assembly_loadPointer )DetourFunction((PBYTE) GetProcAddress(hDLL,"mono_assembly_load"),(PBYTE)hkAssemblyLoad);
	pMono_jit_exec = ( mono_jit_execPointer )DetourFunction((PBYTE) GetProcAddress(hDLL,"mono_jit_exec"),(PBYTE)hkmono_jit_execPointer);
	pMono_class_from_name = (mono_class_from_namePointer)DetourFunction((PBYTE) GetProcAddress(hDLL,"mono_class_from_name"),(PBYTE)hkmono_class_from_name);
	//pMono_object_new = (mono_object_newPointer)DetourFunction((PBYTE) GetProcAddress(hDLL,"mono_object_new"),(PBYTE)hkmono_object_new);
	pMono_domain_assembly_open = (mono_domain_assembly_openPointer)DetourFunction((PBYTE) GetProcAddress(hDLL,"mono_domain_assembly_open"),(PBYTE)hkmono_domain_assembly_open);
	pMono_runtime_object_init = (mono_runtime_object_initPointer)DetourFunction((PBYTE) GetProcAddress(hDLL,"mono_runtime_object_init"),(PBYTE)hkmono_runtime_object_init);
	//pMono_method_desc_search_in_class = (mono_method_desc_search_in_classPointer)DetourFunction((PBYTE) GetProcAddress(hDLL,"mono_method_desc_search_in_class"),(PBYTE)hkmono_method_desc_search_in_class);
	pMono_runtime_invoke = (mono_runtime_invokePointer)DetourFunction((PBYTE) GetProcAddress(hDLL,"mono_runtime_invoke"),(PBYTE)hkmono_runtime_invoke);
04/06/2014 22:23 Bl@ze!#15
I got the EndScene Hook running now. Trying mono now :p