 |
Cheat Engine The Official Site of Cheat Engine
|
View previous topic :: View next topic |
Author |
Message |
sullx Cheater
Reputation: 0
Joined: 03 Jan 2013 Posts: 37
|
Posted: Sun Jul 27, 2014 6:24 pm Post subject: Calling client function from c++ or CE |
|
|
The client of the application I am reversing has a function known as GetObjectFromID which returns a pointer to an object and takes as input parameters an integer ID. I have found this function in ollydbg, and what I am trying to understand is how I can call this function from c++ (or CE). This is how the code gets called in the actual client
Code: | .text:00714288 mov ecx, [ebx+8]
.text:0071428B push eax //object ID
.text:0071428C mov eax, [ecx]
.text:0071428E mov edx, [eax+8]
.text:00714291 call edx //Vfunc call to retrieve some pointer needed for GetObjectByID
.text:00714293 mov esi, eax // pass said pointer as param in esi register
.text:00714295 call GetObjectByID |
Someone I talked to said that this should be easily callable if:
Pointer in ecx : ecx = [[[["Client.exe"+0x6BF530]+0x4]+0x7C]+0x94]+0xB4
But I don't understand where this nested chain of pointers is coming from, nor how that actually calls the function as I don't see how any arguments could be pushed on to the stack with this method.
In hope of simplifying this I have written my own Crackme app with a function that I want to be able to call from a separate program that I attach.
For CE my thought would be to auto-assemble a jmp to my own code where I would back up the state of the registers, then set eax to my own argument and push it to the stack; then call the function, get the return value, then reset the registers back to what they were and then call the function again with the original values. Is there a better way to do this in CE?
And how about in c++?
|
|
Back to top |
|
 |
661089799107 Expert Cheater
Reputation: 3
Joined: 25 Jan 2009 Posts: 186
|
Posted: Sun Jul 27, 2014 8:58 pm Post subject: |
|
|
sullx wrote: | I don't see how any arguments could be pushed on to the stack with this method. |
Keep in mind that call itself does not pop the stack arguments afterwards. It has to either be done by the caller (example: add esp, 4), or by the called function (ret 4, or restore from saved value - mov esp, ebp).
This all depends on the calling convention that is used by the function.
So if the virtual function doesn't clean the stack then the arguments go straight to the next function.
sullx wrote: |
[[[["Client.exe"+0x6BF530]+0x4]+0x7C]+0x94]+0xB4
Pointer in ecx : ecx = But I don't understand where this nested chain of pointers is coming from
|
The result of the nested pointers is the 'this' pointer. It should be the same value that ECX is after the following instruction:
If you didn't have the pointer path then from here you would find out where 'EBX' is coming from, and where the value that writes to that is coming from, and so on. Eventually you would track it down to a static address: "Client.exe"+0x6BF530.
There are many different pointer paths that are valid so it may of been tracked down from a different function as well. Or even pointer scanned.
As for calling it in c++ it may look something like this:
Code: |
// Make sure you replace __stdcall with the proper calling convention, you didn't show enough code for me to see what it actually was.
typedef void*(__stdcall *t_GetObjectByID)(int id);
// Replace 0x00000000 with the address of the game function
t_GetObjectByID GetObjectByID = 0x00000000;
int objectId = 0; // your id
void* objectPtr = GetObjectByID(objectId);
|
|
|
Back to top |
|
 |
sullx Cheater
Reputation: 0
Joined: 03 Jan 2013 Posts: 37
|
Posted: Mon Jul 28, 2014 9:27 pm Post subject: |
|
|
Thanks for this.
So when I try to naively typedef the function pointer and then create a function of this type equal to an address I keep getting an error. I wrote a small crackme app with a simple global scope function to test this on. In the actual crackme app the function is called in this way:
Code: |
011218B9 B8 01000000 MOV EAX,1
011218BE 85C0 TEST EAX,EAX
011218C0 74 53 JE SHORT function.01121915
011218C2 6A 00 PUSH 0 <<< single int argument
011218C4 E8 2CF8FFFF CALL function.011210F5 <<< function call
011218C9 83C4 04 ADD ESP,4
|
I then try to write a dll to inject with the following, as per your suggestion:
Code: |
typedef int(__stdcall* tFunction)(int param1);
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch(ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
int base;
base = (ULONG)GetModuleHandleW(0);
tFunction nFunction = base+0x110F5;
// call function next
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
|
But I get this compiler error:
Code: | error C2440: 'initializing' : cannot convert from 'int' to 'tFunction'
1> Conversion from integral type to pointer type requires reinterpret_cast, C-style cast or function-style cast
1>main.cpp(36): error C2360: initialization of 'nFunction' is skipped by 'case' label |
What obvious mistake am I making here? Am I not setting the address but trying to assign a value?
|
|
Back to top |
|
 |
atom0s Moderator
Reputation: 205
Joined: 25 Jan 2006 Posts: 8587 Location: 127.0.0.1
|
Posted: Mon Jul 28, 2014 11:18 pm Post subject: |
|
|
You need to cast your pointer to the function type manually. Also you cannot initialize within a switch case like that unless you force a scope onto the given case.
So you would need something like this instead:
Code: | case DLL_PROCESS_ATTACH:
{
auto funcAddr = (ULONG)GetModuleHandleW(0) + 0x110F5;
((tFunction*)funcAddr)(0x1234);
break;
} |
Also, your typedef should be:
Code: | typedef int(__stdcall tFunction)(int param1); |
_________________
- Retired. |
|
Back to top |
|
 |
661089799107 Expert Cheater
Reputation: 3
Joined: 25 Jan 2009 Posts: 186
|
Posted: Tue Jul 29, 2014 2:18 am Post subject: |
|
|
Code: |
011218C4 E8 2CF8FFFF CALL function.011210F5 <<< function call
011218C9 83C4 04 ADD ESP,4
|
Note the ADD ESP, 4 after the function call (c/c++ default convention).
Quote: |
The main characteristics of __cdecl calling convention are:
Arguments are passed from right to left, and placed on the stack.
Stack cleanup is performed by the caller.
|
Code: |
typedef int(__cdecl *t_GetObjectByID)(int id);
t_GetObjectByID pGetObjectByID = reinterpret_cast<t_GetObjectByID>(base + 0x00000000);
int retVal = pGetObjectByID(0);
|
With the asterisks is fine.
Also you shouldn't call non kernel32 win32 functions inside DllMain. So make sure even your target function doesn't do that.
Create a new thread if you have to.
Quote: |
Calling functions that require DLLs other than Kernel32.dll may result in problems that are difficult to diagnose. For example, calling User, Shell, and COM functions can cause access violation errors, because some functions load other system components. Conversely, calling functions such as these during termination can cause access violation errors because the corresponding component may already have been unloaded or uninitialized.
|
|
|
Back to top |
|
 |
sullx Cheater
Reputation: 0
Joined: 03 Jan 2013 Posts: 37
|
Posted: Tue Jul 29, 2014 8:57 pm Post subject: |
|
|
Wonderful! This works great, thank you! So just a few updates so the whole story is here and a question about inline asm..
I updated the client program I was injecting into so that the function called in main was a method of myClass. In asm the caller looks like this:
Code: |
00C91056 . 8B35 0020C900 MOV ESI,DWORD PTR DS:[<&KERNEL32.Sleep>]
00C9105C . 8D6424 00 LEA ESP,DWORD PTR SS:[ESP]
00C91060 > 6A 00 PUSH 0
00C91062 . 8D4D FF LEA ECX,DWORD PTR SS:[EBP-1]
00C91065 . E8 16020000 CALL function.myClass::GetObjectFromID
00C9106A . 8B0D 5020C900 MOV ECX,DWORD PTR DS:[<&MSVCP100....$basic_ostream...>]
00C91070 . 51 PUSH ECX
|
The dll I inject looks like this (note that the calling convention is now __stdcall as opposed to before when it was cdecl, because it appears in the asm the called function is doing the clean up):
Code: |
typedef int(__stdcall* tFunction)(int param1);
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
tFunction nFunction;
int retval;
int base;
switch(ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
base = (ULONG)GetModuleHandleW(0);
cout << "base = "<< hex << base <<dec<<endl;
cout << "function address = " << hex << base+0x1280 << dec << endl;
nFunction = reinterpret_cast<tFunction>(base+0x1280);
retval = nFunction(2112);
cout << "I have a return value = " << retval << endl;
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
|
Now, I should be able to achieve the same thing by using inline asm, but unfortunately when I make a similar dll, it never makes it passed the call statement:
Code: |
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
int base;
int retval;
switch(ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
base = (ULONG)GetModuleHandleW(0);
cout << "base = "<< hex << base <<dec<<endl;
cout << "function address = " << hex << base+0x1280 << dec << endl;
__asm {
push 8;
call base+0x1280;
mov [retval],eax;
}
cout << "I have a return value = " << retval << endl;
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
|
Any idea why it's hanging here?
|
|
Back to top |
|
 |
661089799107 Expert Cheater
Reputation: 3
Joined: 25 Jan 2009 Posts: 186
|
Posted: Wed Jul 30, 2014 1:41 am Post subject: |
|
|
The stack cleanup is indeed done by the called function, but since it is a class function it actually uses the __thiscall convention.
Code: |
Thiscall is the default calling convention for calling member functions of C++ classes (except for those with a variable number of arguments).
The main characteristics of thiscall calling convention are:
Arguments are passed from right to left, and placed on the stack. this is placed in ECX.
Stack cleanup is performed by the called function.
|
With class functions there is a hidden paramter. The this pointer.
So your typedef should actually look like:
Code: |
// Can change MyClass* to void* or DWORD if the class isn't yours
typedef int(__thiscall* tFunction)(MyClass* This, int param1);
|
If the real GetObjectByID uses that ECX register then it is probably a __thiscall.
Code: |
typedef int(__thiscall *t_GetObjectByID)(void* This, int id);
|
|
|
Back to top |
|
 |
sullx Cheater
Reputation: 0
Joined: 03 Jan 2013 Posts: 37
|
Posted: Wed Jul 30, 2014 9:03 am Post subject: |
|
|
Ah, I see. And I assume that the reason that the function call still worked with my other typedef (with __stdcall and without the object pointer) was because the function was simple and didn't depend on any of the class members or methods.
Any idea on how I can do this with inline assembler? Still having issues with it.
|
|
Back to top |
|
 |
atom0s Moderator
Reputation: 205
Joined: 25 Jan 2006 Posts: 8587 Location: 127.0.0.1
|
Posted: Wed Jul 30, 2014 12:04 pm Post subject: |
|
|
sullx wrote: | Ah, I see. And I assume that the reason that the function call still worked with my other typedef (with __stdcall and without the object pointer) was because the function was simple and didn't depend on any of the class members or methods.
Any idea on how I can do this with inline assembler? Still having issues with it. |
In some cases you may run into issues casting directly as __thiscall. Instead you can just use __fastcall and be sure to pass the 'this' pointer as the first param.
For example, I get this issue from time to time when hooking onto class functions inside of various MMORPGs.
Code: | extern "C"
{
char (__fastcall* Real_SomeClassFunction)(LPVOID pThisPtr, LPVOID lpUnknown, char* pszText, int nUnknown); = 0;
} |
Then later in the code Real_SomeClassFunction is set via detouring or setting it via a cast to a raw pointer.
And is there any reason you need to do this with inline asm vs. just doing it with C style casting? If it works already with the C casting, why bother writing it in inline asm?
But if you still feel the need for it, don't calculate the address during the call, that is where its failing. Instead, put the address of the function into a variable and use the variable as the address like this:
Code: | auto address = base+0x1280;
__asm
{
push 8
call [address]
mov retval, eax
} |
_________________
- Retired. |
|
Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum You cannot attach files in this forum You can download files in this forum
|
|