 |
Cheat Engine The Official Site of Cheat Engine
|
| View previous topic :: View next topic |
| Author |
Message |
iLogic Newbie cheater
Reputation: 0
Joined: 28 Oct 2012 Posts: 13
|
Posted: Sun Oct 28, 2012 2:22 am Post subject: [C++ dll-injection] Building and reading pointers |
|
|
I'm currently attempting to code a dll-trainer however I'm having some problem with the pointers.
The pointer that i have looks like this:
DWORD PointerBase = 0x0296F2CC;
DWORD PointerOffset[5] = { 0x10, 0x2C, 0x2C, 0x24, 0x94 };
If I were to use that pointer in C#, it works perfectly.
Same with in CE.
But if I inject it with a dll, it just refuses to work.
One of the ways I tested is this
| Code: | ULONG_PTR build = ReadPointer((ULONG_PTR*)0x0296F2CC,0x10);
if(build!=0)
{
build = ReadPointer((ULONG_PTR*)build,0x2C);
if(build!=0)
{
build = ReadPointer((ULONG_PTR*)build,0x2C);
if(build!=0)
{
build = ReadPointer((ULONG_PTR*)build,0x24);
if(build!=0)
{
build = ReadPointer((ULONG_PTR*)build,0x94);
}
}
}
}
|
Where the "ReadPointer" function looks like this
| Code: | __inline ULONG_PTR ReadPointer(ULONG_PTR* ulBase, INT nOffset)
{
if ( !IsBadReadPtr((VOID*)ulBase, sizeof(ULONG_PTR)) )
if ( !IsBadReadPtr((VOID*)((*(ULONG_PTR*)ulBase)+nOffset), sizeof(ULONG_PTR)) )
return *(ULONG_PTR*)((*(ULONG_PTR*)ulBase)+nOffset);
return 0;
} |
OBS: The pointer is not for an ASM script, what I want is the actual value on that address and I wish to modify it.
If anyone could give me any tips on what I might be doing wrong I'd be eternally grateful.
|
|
| Back to top |
|
 |
Dark Byte Site Admin
Reputation: 471
Joined: 09 May 2003 Posts: 25837 Location: The netherlands
|
Posted: Sun Oct 28, 2012 2:39 am Post subject: |
|
|
let's see,
First you check if the 4/8 byte value at the location of ulBase is readable
Then you typecast the ulBase which is a ULONG_PTR* to a ULONG_PTR* and dereference that. So you get a ULONG_PTR that holds the 4/8 byte value ulBase was pointing at
Then to that integer type you add the value provided in nOffset to get an integer that holds the final address
And then convert that integer to a typeless pointer and pass it on to IsBadReadPtr
so far so good (Quite a bit of typecasting that wasn't necessary though, and really, there's nothing wrong with using local variables)
Then the same as above, but when you get to the integer that represents the address you convert it to a pointer of the ULONG_PTR type (pointer to a 4/8 byte integer)
And then you dereference that (so you get the value the pointer was pointing to, e.g 100 for health)
I suggest you don't dereference that and just return the address
e.g:
return *ulBase+nOffset;
_________________
Do not ask me about online cheats. I don't know any and wont help finding them.
Like my help? Join me on Patreon so i can keep helping |
|
| Back to top |
|
 |
iLogic Newbie cheater
Reputation: 0
Joined: 28 Oct 2012 Posts: 13
|
Posted: Sun Oct 28, 2012 3:57 am Post subject: |
|
|
Thank you for your quick answer, I really appreciate that
So I changed the ReadPointer function into this now:
| Code: | __inline ULONG_PTR ReadPointer(ULONG_PTR* ulBase, INT nOffset)
{
if ( !IsBadReadPtr((VOID*)ulBase, sizeof(ULONG_PTR)) )
if ( !IsBadReadPtr((VOID*)(*ulBase+nOffset), sizeof(ULONG_PTR)) )
return *ulBase+nOffset;
return 0;
} |
Cleaned it up a little after your suggestions.
However, my problem is that it always return 0 after I add my first offset to the base.
So I took another look at my "BasePointer" address and the value that is stored there is 0 :/ (I'm guessing that it's really null..)
(PS: What I mean by that is that I tried to just output the value from my trainer, not that it's 0 in CE etc since it works there)
Is there something else I need to do with it before I start adding my offsets to it?
I shouldn't need to add it with the base address for the process right, since I'm doing this within the process itself?
|
|
| Back to top |
|
 |
Dark Byte Site Admin
Reputation: 471
Joined: 09 May 2003 Posts: 25837 Location: The netherlands
|
Posted: Sun Oct 28, 2012 4:03 am Post subject: |
|
|
That depends. is the original pointer a modulename+offset notation, or just the hexadecimal address?
Anyhow, use OutputDebugString and output the results of every call to ReadPointer and see where it goes wrong
_________________
Do not ask me about online cheats. I don't know any and wont help finding them.
Like my help? Join me on Patreon so i can keep helping |
|
| Back to top |
|
 |
iLogic Newbie cheater
Reputation: 0
Joined: 28 Oct 2012 Posts: 13
|
Posted: Sun Oct 28, 2012 4:29 am Post subject: |
|
|
What I did to narrow it down was this:
| Code: | ULONG_PTR build = ReadPointer((ULONG_PTR*)0x0296F2CC ,0x10);
SetDlgItemText(hWnd, IDC_CHARX, "Added address and first offset" );
if(build!=0)
{
SetDlgItemText(hWnd, IDC_CHARX, "Added second offset" );
build = ReadPointer((ULONG_PTR*)build,0x2C);
if(build!=0)
{
SetDlgItemText(hWnd, IDC_CHARX, "Added address and third offset" );
build = ReadPointer((ULONG_PTR*)build,0x2C);
if(build!=0)
{
SetDlgItemText(hWnd, IDC_CHARX, "Added address and fourth offset" );
build = ReadPointer((ULONG_PTR*)build,0x24);
if(build!=0)
{
SetDlgItemText(hWnd, IDC_CHARX, "Added fourth offset and we seem to still have an working address" );
}
}
}
} |
IDC_CHARX is where I'm displaying my output for the ReadPointer results.
After that I did this:
| Code: | char buf [33];
build = *(ULONG_PTR*)0x0296F2CC;
SetDlgItemText(hWnd, IDC_CHARY, _itoa(build, buf, 10) ); |
IDC_CHARY is where I'm displaying the result of my PointerBase read.
The dialog shows the following:
| Code: | Added address and first offset
0 |
Which means it became 0 after the first ReadPointer call. :/
Also, yes the original pointer did look like "Module.exe + 0x0296F2CC".
So I'm guessing that means that I'll need the base address then :/
If so, then how exactly would I go about getting the main modules base address from inside the module?
There would be an easier way than this right?
| Code: | | HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId); |
Edit: Alright, so I've confirmed that I do need the base address (the real one and not the 0x00400000 that you get from GetModuleHandle(NULL)..).
What I tried was this
| Code: | | while(build == 0 && itter < 0x02FE0000) |
And attempted to build the pointer in each itteration, if I found the value I wanted then it would jump out (since build would no longer be 0).
While this works, it takes forever to find it (like 10-20 minutes..).
So is there some function like "GetModuleHandle" that actually returns the true baseaddress?
Edit 2: Ok scratch what I said above about "0x00400000" not being the real base address.. I decided to display the address directly from my C# version (Process.MainModule.BaseAddress) which showed me the 0x00400000 address as well ^^
What's weird though is that even if I try to just add my Pointer address with that, the pointer wont work, am I missing something?
I'm building the pointer in the exact same way as I do in C#..
PointerAddress + BaseAddress then read the address it has stored.
Add the offsets one after one (read in between)..
Yet it wont work :/
Last edited by iLogic on Sun Oct 28, 2012 5:51 pm; edited 1 time in total |
|
| Back to top |
|
 |
SteveAndrew Master Cheater
Reputation: 30
Joined: 02 Sep 2012 Posts: 323
|
Posted: Sun Oct 28, 2012 5:50 pm Post subject: |
|
|
| iLogic wrote: | What I did to narrow it down was this:
| Code: | ULONG_PTR build = ReadPointer((ULONG_PTR*)0x0296F2CC ,0x10);
SetDlgItemText(hWnd, IDC_CHARX, "Added address and first offset" );
if(build!=0)
{
SetDlgItemText(hWnd, IDC_CHARX, "Added second offset" );
build = ReadPointer((ULONG_PTR*)build,0x2C);
if(build!=0)
{
SetDlgItemText(hWnd, IDC_CHARX, "Added address and third offset" );
build = ReadPointer((ULONG_PTR*)build,0x2C);
if(build!=0)
{
SetDlgItemText(hWnd, IDC_CHARX, "Added address and fourth offset" );
build = ReadPointer((ULONG_PTR*)build,0x24);
if(build!=0)
{
SetDlgItemText(hWnd, IDC_CHARX, "Added fourth offset and we seem to still have an working address" );
}
}
}
} |
IDC_CHARX is where I'm displaying my output for the ReadPointer results.
After that I did this:
| Code: | char buf [33];
build = *(ULONG_PTR*)0x0296F2CC;
SetDlgItemText(hWnd, IDC_CHARY, _itoa(build, buf, 10) ); |
IDC_CHARY is where I'm displaying the result of my PointerBase read.
The dialog shows the following:
| Code: | Added address and first offset
0 |
Which means it became 0 after the first ReadPointer call. :/
Also, yes the original pointer did look like "Module.exe + 0x0296F2CC".
So I'm guessing that means that I'll need the base address then :/
If so, then how exactly would I go about getting the main modules base address from inside the module?
There would be an easier way than this right?
| Code: | | HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId); |
Edit: Alright, so I've confirmed that I do need the base address (the real one and not the 0x00400000 that you get from GetModuleHandle(NULL)..).
What I tried was this
| Code: | | while(build == 0 && itter < 0x02FE0000) |
And attempted to build the pointer in each itteration, if I found the value I wanted then it would jump out (since build would no longer be 0).
While this works, it takes forever to find it (like 10-20 minutes..).
So is there some function like "GetModuleHandle" that actually returns the true baseaddress? |
hmm... Something else must not be quite right then, because GetModuleHandle(0); Does return the true executable base address (of whatever executable you are inside of)
Browse to this address and you'll see an MZ which marks the start of the executable (Well with Windows portable executables that is)
I built a C++ class which I posted in another thread which simplifies reading/writing of pointers (although now that I think about it I should make it use IsBadReadPtr rather then just making sure its not 0 at any point)
It's really meant for working with many pointers to make them easier to manage/ less repetitive code to write but you could use it for only just 1 pointer if you wanted...
In the following example of how it's used you can see how convenient it is to work with pointers by name...
| Code: |
//Allocate new Pointer Helper class object
PointerHelper *PtrHelper = new PointerHelper();
//Get base address of executable we are within
HANDLE ExeBaseAddress = GetModuleHandleA(0);
//example: Add "Score" pointer for x64 Solitaire to pointer helper object //[[[Solitaire.exe+BAFA8]+50]+14]
PtrHelper->AddPointer("Score", ExeBaseAddress, 0xBAFA8, 0x50, 0x14, -1);
//example: Write 32-bit value to "Score" pointer
PtrHelper->WriteValue32("Score", 123456789);
//example: Read 32-bit value from "Score" pointer
Score = PtrHelper->ReadValue32("Score");
//If you wanted to grab the dynamic address from the last time it was read/written
void *DynamicAddress = PtrHelper->GetPointerByName("Score")->CurrentDynamicAddress;
|
Full code and example project found here: http://forum.cheatengine.org/viewtopic.php?p=5422282&highlight=#5422282
Try it and see how it compares to your code, you've got the basic idea down, and this code does the same as yours it just abstracts it into a class so you don't have to worry about these details every time you want to read or write a pointer in c++ but its still important to know how it works!
Though I made it work in a 64-bit DLL (for a 64-bit exe) I'm pretty sure the same code would compile and work for 32-bit pointers (in a 32-bit executable) But I'm gonna make sure of that right now
_________________
|
|
| Back to top |
|
 |
iLogic Newbie cheater
Reputation: 0
Joined: 28 Oct 2012 Posts: 13
|
Posted: Sun Oct 28, 2012 5:56 pm Post subject: |
|
|
Ah, you posted while I were still in the "edit post" window ^^
I were confirming that it really didn't work before I pressed submit
But yeah, as I added to my last post
| Quote: | Edit 2: Ok scratch what I said above about "0x00400000" not being the real base address.. I decided to display the address directly from my C# version (Process.MainModule.BaseAddress) which showed me the 0x00400000 address as well ^^
What's weird though is that even if I try to just add my Pointer address with that, the pointer wont work, am I missing something?
I'm building the pointer in the exact same way as I do in C#..
PointerAddress + BaseAddress then read the address it has stored.
Add the offsets one after one (read in between)..
Yet it wont work :/ |
I'll go ahead and take a look at your class
Edit: Ok, tested it now. I'm getting the same result as before (it ends up giving me a 0/NULL value back). It got me thinking though that the 64bit thing might be what caused this problem to begin with for me.
From what I had gathered "ULONG_PTR" would work on both 64bit and 32bit, however in the documentation it says this:
| Quote: | #if defined(_WIN64)
typedef unsigned __int64 ULONG_PTR;
#else
typedef unsigned long ULONG_PTR;
#endif |
So, if I compile my dll on a 64bit system/use the dll on a 64 bit system, it would act as 64bit software, right? Does that still apply if I inject it into a 32bit client?
If it does, then that's probably why it wouldn't work..
Also, I really like this API of yours
Really cleaned up the code and simplified things for me.
I did plan on using more than 1 pointer, however I wanted to at least get 1 pointer working before I started to add more ^^
I looked through your class and I can't really see why it wouldn't be supported in a 32 bit application. Using "long long" should be supported..
I really don't see what's wrong this time..
|
|
| Back to top |
|
 |
SteveAndrew Master Cheater
Reputation: 30
Joined: 02 Sep 2012 Posts: 323
|
Posted: Sun Oct 28, 2012 7:47 pm Post subject: |
|
|
| iLogic wrote: | Ah, you posted while I were still in the "edit post" window ^^
I were confirming that it really didn't work before I pressed submit
But yeah, as I added to my last post
| Quote: | Edit 2: Ok scratch what I said above about "0x00400000" not being the real base address.. I decided to display the address directly from my C# version (Process.MainModule.BaseAddress) which showed me the 0x00400000 address as well ^^
What's weird though is that even if I try to just add my Pointer address with that, the pointer wont work, am I missing something?
I'm building the pointer in the exact same way as I do in C#..
PointerAddress + BaseAddress then read the address it has stored.
Add the offsets one after one (read in between)..
Yet it wont work :/ |
I'll go ahead and take a look at your class
Edit: Ok, tested it now. I'm getting the same result as before (it ends up giving me a 0/NULL value back). It got me thinking though that the 64bit thing might be what caused this problem to begin with for me.
From what I had gathered "ULONG_PTR" would work on both 64bit and 32bit, however in the documentation it says this:
| Quote: | #if defined(_WIN64)
typedef unsigned __int64 ULONG_PTR;
#else
typedef unsigned long ULONG_PTR;
#endif |
So, if I compile my dll on a 64bit system/use the dll on a 64 bit system, it would act as 64bit software, right? Does that still apply if I inject it into a 32bit client?
If it does, then that's probably why it wouldn't work..
Also, I really like this API of yours
Really cleaned up the code and simplified things for me.
I did plan on using more than 1 pointer, however I wanted to at least get 1 pointer working before I started to add more ^^
I looked through your class and I can't really see why it wouldn't be supported in a 32 bit application. Using "long long" should be supported..
I really don't see what's wrong this time.. |
Okay I figured out where my class is going wrong, where It for some reason did not when testing with 64bit solitaire...
I added many output debug strings into it, to see where the problem was... It's with the pointer's name string... For some reason it's not getting the size properly, and its copying a bunch of garbage into the string, which is making the compare fail... I even converted the whole thing to use a unicode string for the pointer name rather then ANSI, and still (as shown in the image I've attached, the pointer name's contain garbage at the end... They were supposed to just be 'Player X', 'Y', and 'Zval' but instead you get what you see... (Just a little variation in string names to illustrate it happens whether its long or short or contains spaces or whatever)
Sometimes the garbage isn't there, but that isn't good enough it needs to always not be there...
Do we have to specify the string length in the AddPointer call? That seems silly to have to... I thought I could just make the AddPointer method get the length with strlen or now im using wcslen with unicode instead... hmmm...
It is adding the pointers to the list properly though, and if you create a pointer class object directly instead of using the pointer helper class that will work...
Like this:
| Code: |
Pointer *ScorePointer = new Pointer();
ScorePointer->BasePtr = GetModuleHandleA(0);
ScorePointer->AddOffset(0xBAFA8);
ScorePointer->AddOffset(0x50);
ScorePointer->AddOffset(0x14);
unsigned int Score = ScorePointer->ReadValue32();
ScorePointer->WriteValue32(0x539);
|
Try that way ^^
Oh yeah and I'm also adding Read/WriteFloat and Read/WriteDouble as well as I forgot those... but this string thing is starting to annoy me! Why did it work before and now I'm getting garbage at the end of my strings... Besides this issue everything else is working...
Oh and to answer your question, no you can't inject a 64-bit dll into a 32-bit application, or vice versa... you can't inject a 32-bit dll into a 64-bit application... they just aren't compatible... but yeah since pointers are 32-bit in 32-bit and 64-bit in 64-bit it seems the code doesn't need much modification if any to work on both... Still gotta work out this string issue though or if worst comes to worst we'll just have to pass the string length along into AddPointer method along with the other base address and offset parameters which will kind of suck to have to but oh well... I'm trying avoid that if possible...
| Description: |
|
| Filesize: |
81.43 KB |
| Viewed: |
15482 Time(s) |

|
_________________
|
|
| Back to top |
|
 |
iLogic Newbie cheater
Reputation: 0
Joined: 28 Oct 2012 Posts: 13
|
Posted: Sun Oct 28, 2012 8:40 pm Post subject: |
|
|
Awesome! It worked!
Well, at least as far as reading the pointers
I tried to only create "Pointer" pointers and now it's reading my pointer correctly
Though it wouldn't write to it but that might be because of something else..
The address that it points to is a float value, I figured I could perhaps write to it in hex format but that might not work?
The value of it is:
in int: 1065353216
in hex: 0x3F800000
in float: 1
What I tried to do was:
TestPtr->WriteValue32(0x3F991444);
Edit:
Yep, that was it. I added this function:
| Code: | unsigned int Pointer::WriteValueFloat( float ValueToWrite)
{
unsigned long long PtrValue = GetDynamicAddress();
if(PtrValue)
{
*(float*)PtrValue = ValueToWrite;
return 1;
}
return 0;
} |
Worked like charm
Thank you so much for all the help and this class!
|
|
| Back to top |
|
 |
SteveAndrew Master Cheater
Reputation: 30
Joined: 02 Sep 2012 Posts: 323
|
Posted: Sun Oct 28, 2012 9:12 pm Post subject: |
|
|
Well glad you got it working using the pointer class directly! And no problem! And yep I added the float and double methods the same exact way, so you did it right!
I still haven't solved the string problem yet though...
Here's one thing though, I'm not 100% sure it will work all the time though...
Think about this for a second, this line in the 'GetDynamicAddress()' private method:
| Code: |
PtrValue = *(unsigned long long*)(PtrValue + CurrentOffset->Offset);
|
Okay so we know in 32-bit code pointers are 32-bits in size (4 bytes) and in 64-bit code they are 64-bits in size... So in either one a pointer to an 'unsigned long long' is going to be either 32-bits or 64-bits respectively... So that will be fine...
But the size of an actual 'unsigned long long' is 64-bits whether you're on 32-bit or 64-bit yes? So is it reading 64-bits from the 32-bit pointer on 32-bit, and its reading 64-bits from the 64-bit pointer on 64-bit? I think so...
If it is reading 64-bits of data on 32-bit, couldn't that screw up your next pointer value? Or wait.... ...... I think I see whats going on here...
Okay even though its reading 64-bits in any case, if your on 32-bit the second 32-bits of the 64-bit 'PtrValue' unsigned long long variable is being shaved off when you typecast it as a pointer via the line:
| Code: |
PtrValue = *(unsigned long long*)(PtrValue + CurrentOffset->Offset);
|
So even though it's reading 32-bits more then the pointer we need in each iteration of adding the offset, getting the new value, it's not screwing up the next pointer to read because that extra 32-bits we dont need is being truncated and lost...
And on 64-bit, since pointers are now 64-bits that extra 32-bits isn't extra anymore and now it's part of the pointer we need, so when you then typecast that as a pointer to an unsigned long long everything is still fine and great!
So I think I understand it now! Without even realizing it I wrote code that's 32-bit and 64-bit compatible! Very Cool!
_________________
|
|
| Back to top |
|
 |
iLogic Newbie cheater
Reputation: 0
Joined: 28 Oct 2012 Posts: 13
|
Posted: Sun Oct 28, 2012 9:22 pm Post subject: |
|
|
Haha, that's great
Also, I don't really mind only using the "Pointer" class, even though PtrHelper was nice and all. It is still making my life a lot easier than it was earlier
|
|
| Back to top |
|
 |
SteveAndrew Master Cheater
Reputation: 30
Joined: 02 Sep 2012 Posts: 323
|
Posted: Fri Nov 09, 2012 5:15 pm Post subject: |
|
|
Alright I must've been tired or something as it was a tiny simple mistake...
I checked and strlen inside the class method is actually getting the right length of the string in question... I just forgot that strlen doesn't count the null byte at the end of the c style string... so you change 4 lines, where strlen is used, and then it wont copy any extra garbage characters into the string...
Change:
| Code: |
NewPointer->Name = new char[strlen(Name)];
strncpy(NewPointer->Name, Name, strlen(Name));
|
Into:
| Code: |
NewPointer->Name = new char[strlen(Name)+1]; //strlen +1 as it doesn't account for the null byte ;)
strncpy(NewPointer->Name, Name, strlen(Name)+1);
|
In the two places in the pointer helper 'AddPointer' method...
Also I changed the name of 'Pointer' class into 'GamePointer' as 'Pointer' was already defined when coding in Embarcadero RAD Studio C++ so I realized that it's maybe too general of a name and changed it...
So now everything works like it should!
_________________
|
|
| 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
|
|