 |
Cheat Engine The Official Site of Cheat Engine
|
View previous topic :: View next topic |
Author |
Message |
dm0000 Newbie cheater
Reputation: 0
Joined: 09 Nov 2014 Posts: 16
|
Posted: Tue Dec 16, 2014 5:02 pm Post subject: C++ VirtualQueryEx |
|
|
Hello, here we go again,
I'm trying to scan a process memory using VirtualQueryEx for a value.
I'm only getting strange address's, well, it's not even a address.
Maybe something about conversions, idk.
The value have a address of 4 bytes type.
Code: | void ScanAndWrite(LPVOID value){
MyWnd.SetMyToken();
//1 - process ID
char buf[255];
GetDlgItemText(MyWnd.hwnd, 100, buf, 255); // get text from a combobox
//convert to string to parse (get number before a blank space)
string id = string(buf);
string strFinal = id.substr(0, id.find_first_of(" "));
DWORD pIDparsed = strtoul(strFinal.c_str(), 0, 0); // convert the parsed string to DWORD
//2 - Get process handle
HANDLE pHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pIDparsed);
if (pHandle == NULL) { MessageBox(NULL, "pHandle NULL", "OpenProcess", MB_OK | MB_ICONWARNING); TerminateThread(MyWnd.threadScan, 1); }
//3- Start memory scan
MEMORY_BASIC_INFORMATION mBI;
UINT memoryRegionStart = 0x1FFFFF;
UINT memoryRegionEnd = 0x7fffffffffffffff;
SendMessage(MyWnd.pB, PBM_SETRANGE, 0, MAKELPARAM(memoryRegionStart, memoryRegionEnd)); //progressbar
do{
SIZE_T vQe = VirtualQueryEx(pHandle, (void*)memoryRegionStart, &mBI, sizeof(MEMORY_BASIC_INFORMATION));
if (vQe == 0) { MessageBox(NULL, "VirtualQueryEx fails", "VirtualQueryEx", MB_OK | MB_ICONWARNING); TerminateThread(MyWnd.threadScan, 1); }
if ((mBI.State == MEM_COMMIT) && (mBI.Type == MEM_PRIVATE) && (mBI.Protect == PAGE_READWRITE)){
UINT start, end;
start = (UINT)mBI.BaseAddress;
end = (UINT)mBI.BaseAddress + mBI.RegionSize;
//ready memory
LPVOID valueFound;
SIZE_T byteToRead = 4;
for (start; start < end; start = start +4){
if (ReadProcessMemory(pHandle, (LPCVOID)start, &valueFound, sizeof(valueFound), &byteToRead)){
SendMessage(MyWnd.lB, LB_ADDSTRING, 0, (LPARAM)&start); // test to add all address
if (&valueFound == value){
SendMessage(MyWnd.lB, LB_ADDSTRING, 0, (LPARAM)&start); // add address to a listbox
}
}
}
}
memoryRegionStart += mBI.RegionSize;
SendMessage(MyWnd.pB, PBM_SETPOS, (WPARAM)memoryRegionStart, 0);
} while (memoryRegionStart < memoryRegionEnd);
CloseHandle(pHandle);
TerminateThread(MyWnd.threadScan, 0);
} |
I call it in a button like this:
Code: | LPVOID tP = "45000";
DWORD tId;
MyWnd.threadScan = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ScanAndWrite, tP, 0, &tId); |
Function is definide as:
Code: | void ScanAndWrite(LPVOID value); |
Result:
Gif with the result: http://s29.postimg.org/sbd98md8n/scan_gif.gif
*forum says that i can't post links =(
I need tips about what i need to change here to make it work. I'm out of ideas.
I should have something like 20~25 address only (tested with cheat engine).
Ty!
|
|
Back to top |
|
 |
atom0s Moderator
Reputation: 204
Joined: 25 Jan 2006 Posts: 8579 Location: 127.0.0.1
|
Posted: Tue Dec 16, 2014 5:28 pm Post subject: |
|
|
You need to format the string you are trying to show in the list. You can't just toss the listbox the value and assume it will do it for you.
So this line:
SendMessage(MyWnd.lB, LB_ADDSTRING, 0, (LPARAM)&start);
Should be using a formatted string instead of the raw searched result address. You could do something like this:
Code: |
char szBuffer[1024] = { 0 };
sprintf_s(szBuffer, 1024, "%d", start);
SendMessage(MyWnd.lB, LB_ADDSTRING, 0, (LPARAM)&szBuffer); |
You should also consider dumping the whole region at once and scanning within it instead of constantly calling ReadProcessMemory.
_________________
- Retired. |
|
Back to top |
|
 |
dm0000 Newbie cheater
Reputation: 0
Joined: 09 Nov 2014 Posts: 16
|
Posted: Tue Dec 16, 2014 10:44 pm Post subject: |
|
|
atom0s wrote: | You need to format the string you are trying to show in the list. You can't just toss the listbox the value and assume it will do it for you.
So this line:
SendMessage(MyWnd.lB, LB_ADDSTRING, 0, (LPARAM)&start);
Should be using a formatted string instead of the raw searched result address. You could do something like this:
Code: |
char szBuffer[1024] = { 0 };
sprintf_s(szBuffer, 1024, "%d", start);
SendMessage(MyWnd.lB, LB_ADDSTRING, 0, (LPARAM)&szBuffer); |
You should also consider dumping the whole region at once and scanning within it instead of constantly calling ReadProcessMemory. |
Yes.
start need to be %p to show the pointer.
The problem is to get the value now and make it scan faster as cheat engine. holly shit, to much time to finish.
This is the CE settings that i'm trying to reproduce in my code: s16.postimg.org/5upuj3aj9/ce_settings.png
|
|
Back to top |
|
 |
atom0s Moderator
Reputation: 204
Joined: 25 Jan 2006 Posts: 8579 Location: 127.0.0.1
|
Posted: Wed Dec 17, 2014 1:09 am Post subject: |
|
|
Like I said at the end of my post, you are constantly calling ReadProcessMemory, this is a very poor implementation of writing a scanner. You should dump the whole region of memory for the current block you iterated from VirtualQueryEx and scan locally within that block instead.
Here is a very basic thrown together example of searching for 4 byte values I threw together for you:
Code: |
/**
* Simple Memory Scanner Example
* (c) 2014 atom0s [[email protected]]
*/
#include <Windows.h>
#include <string>
#include <TlHelp32.h>
/**
* @brief The target process to scan within.
*/
#define TARGET_NAME "winmine.exe"
/**
* @brief Obtains the process id of the given target.
*
* @return The process id if found, 0 otherwise.
*/
unsigned int getTargetProcessId()
{
PROCESSENTRY32 pe32 = { sizeof(PROCESSENTRY32) };
// Obtain a snapshot of the current process list..
auto handle = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (handle == INVALID_HANDLE_VALUE)
return 0;
// Obtain the first process..
if (!::Process32First(handle, &pe32))
{
::CloseHandle(handle);
return 0;
}
// Loop each process looking for the target..
do
{
if (!_stricmp(pe32.szExeFile, TARGET_NAME))
{
::CloseHandle(handle);
return pe32.th32ProcessID;
}
} while (::Process32Next(handle, &pe32));
// Cleanup..
::CloseHandle(handle);
return 0;
}
/**
* @brief Entry point of this application.
*
* @param argc The count of arguments passed to this application.
* @param argv The array of arguments passed to this application.
*
* @return Non-important return.
*/
int __cdecl main(int argc, char* argv[])
{
// Obtain the target process id..
auto processId = getTargetProcessId();
if (processId == 0)
return 0;
// Open a handle to the target..
auto handle = ::OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, processId);
if (handle == INVALID_HANDLE_VALUE)
return 0;
// Obtain the current system information..
SYSTEM_INFO sysInfo = { 0 };
::GetSystemInfo(&sysInfo);
auto addr_min = (long)sysInfo.lpMinimumApplicationAddress;
auto addr_max = (long)sysInfo.lpMaximumApplicationAddress;
auto found = 0;
// Loop the pages of memory of the application..
while (addr_min < addr_max)
{
MEMORY_BASIC_INFORMATION mbi = { 0 };
if (!::VirtualQueryEx(handle, (LPCVOID)addr_min, &mbi, sizeof(mbi)))
{
printf_s("Failed to query memory.\n");
break;
}
// Determine if we have access to the page..
if (mbi.State == MEM_COMMIT && ((mbi.Protect & PAGE_GUARD) == 0) && ((mbi.Protect & PAGE_NOACCESS) == 0))
{
//
// Below are flags about the current region of memory. If you want to specifically scan for only
// certain things like if the area is writable, executable, etc. you can use these flags to prevent
// reading non-desired protection types.
//
auto isCopyOnWrite = ((mbi.Protect & PAGE_WRITECOPY) != 0 || (mbi.Protect & PAGE_EXECUTE_WRITECOPY) != 0);
auto isExecutable = ((mbi.Protect & PAGE_EXECUTE) != 0 || (mbi.Protect & PAGE_EXECUTE_READ) != 0 || (mbi.Protect & PAGE_EXECUTE_READWRITE) != 0 || (mbi.Protect & PAGE_EXECUTE_WRITECOPY) != 0);
auto isWritable = ((mbi.Protect & PAGE_READWRITE) != 0 || (mbi.Protect & PAGE_WRITECOPY) != 0 || (mbi.Protect & PAGE_EXECUTE_READWRITE) != 0 || (mbi.Protect & PAGE_EXECUTE_WRITECOPY) != 0);
// Dump the region into a memory block..
auto dump = new unsigned char[mbi.RegionSize + 1];
memset(dump, 0x00, mbi.RegionSize + 1);
if (!::ReadProcessMemory(handle, mbi.BaseAddress, dump, mbi.RegionSize, NULL))
{
printf_s("Failed to read memory of location: %08X\n", mbi.BaseAddress);
break;
}
// Scan for 4 byte value of 1337..
for (auto x = 0; x < mbi.RegionSize - 4; x += 4)
{
if (*(DWORD*)(dump + x) == 1337)
found++;
}
// Cleanup the memory dump..
delete[] dump;
}
// Step the current address by this regions size..
addr_min += mbi.RegionSize;
}
printf_s("Found %d results!\n", found);
// Cleanup..
::CloseHandle(handle);
return ERROR_SUCCESS;
} |
This will dump the region once, and locally scan within it. Which is much faster then constantly calling ReadProcessMemory.
_________________
- Retired. |
|
Back to top |
|
 |
dm0000 Newbie cheater
Reputation: 0
Joined: 09 Nov 2014 Posts: 16
|
Posted: Wed Dec 17, 2014 8:15 am Post subject: |
|
|
wow! Ty! I'm studying it. Since i don't like copy/paste.
Why are you not using PROCESS_ALL_ACCESS ?
I didn't know about "auto". I don't see why use "auto every time.
ow, you are using GetSystemInfo. nice.
VirtualQueryEx fails when return 0, no? it's not a bool.
My first try, and my code looks more complicated.
In my function to get the process i'm setting processentrry32 dwSize and not calling like you do.
Code: |
bool Window::ListProcess(){
PROCESSENTRY32 pEntry;
HANDLE pSnap;
int pos = 0;
pSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
pEntry.dwSize = sizeof(PROCESSENTRY32);
if (pSnap == INVALID_HANDLE_VALUE) return false;
if (!Process32First(pSnap, &pEntry)) {
CloseHandle(pSnap);
return false;
}
while (Process32Next(pSnap, &pEntry)){
//parse strings
string processExe = string(pEntry.szExeFile);
char procID[10];
sprintf_s(procID, "%lu", pEntry.th32ProcessID);
string finalText = string(procID) + " - " + processExe;
SendMessage(MyWnd.pB, PBM_SETRANGE, 0, MAKELPARAM(0, 100));
if (processExe.find("chrome") != string::npos || processExe.find("plugin") != string::npos
|| processExe.find("Flash") != string::npos || processExe.find("iex") != string::npos){
pos++;
SendMessage(MyWnd.pB, PBM_SETPOS, (WPARAM)pos, 0);
//get memory info
HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pEntry.th32ProcessID);
PROCESS_MEMORY_COUNTERS info = { 0 };
info.cb = sizeof(info);
GetProcessMemoryInfo(process, &info, sizeof(info));
SIZE_T memorySize = (info.WorkingSetSize / 1024) / 1024; // bytes to mega
char mU[255];
sprintf_s(mU, "%lu", memorySize);
finalText = finalText + " : " + string(mU);
SendMessage(cB, CB_ADDSTRING, 0, (LPARAM)(finalText.c_str()));
}
}
SendMessage(MyWnd.pB, PBM_SETPOS, (WPARAM)100, 0);
CloseHandle(pSnap);
return true;
}
|
BTW, the conversion of your "dump" is given wrong results:"
string test = to_string((int)dump);
string total = "Address: " + string(t1) + "- Value: " + test;
The address is missing characteris, i was trying to test your code to see how it work, using WPM to test the address.
And here:
for (auto x = 0; x < mbi.RegionSize - 4; x += 4)
i changed to
for (auto x = 0; x < (int)mbi.RegionSize - 4; x += 4)
To stop the warning: Warning 2 warning C4018: '<' : signed/unsigned mismatch
Ty! Very nice your code, i will rewrite my function i think. [/code]
EDIT:
I did mine using your + mine:
Code: | I just re-write it:
But it is stuck in second IF ( "if 2" ):
bool scan(){
//to write
LPCVOID valueToWrite = 0;
MyWnd.SetMyToken();
//1 - process ID
char buf[255];
GetDlgItemText(MyWnd.hwnd, 100, buf, 255); // get text from a combobox
//convert to string to parse (get number before a blank space)
string id = string(buf);
string strFinal = id.substr(0, id.find_first_of(" "));
DWORD pIDparsed = strtoul(strFinal.c_str(), 0, 0); // convert the parsed string to DWORD
HANDLE pHandle = OpenProcess(PROCESS_VM_OPERATION | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_VM_WRITE, FALSE, pIDparsed);
if (pHandle == INVALID_HANDLE_VALUE){
CloseHandle(pHandle); TerminateThread(MyWnd.threadScan, 1); return false; //not thread safe
}
SYSTEM_INFO sysInfo = { 0 };
GetSystemInfo(&sysInfo);
auto startAddress = (long)sysInfo.lpMinimumApplicationAddress;
auto endAddress = (long)sysInfo.lpMaximumApplicationAddress;
while (startAddress < endAddress){
MEMORY_BASIC_INFORMATION mbi = { 0 };
if (VirtualQueryEx(pHandle, (LPCVOID)startAddress, &mbi, sizeof(mbi)) != 0){ //if 1
if (mbi.State == MEM_COMMIT && ((mbi.Protect & PAGE_GUARD) == 0) && ((mbi.Protect & PAGE_NOACCESS) == 0)) { //if 2
bool isWritable = ( mbi.Protect == PAGE_READWRITE || mbi.Protect == PAGE_WRITECOPY || mbi.Protect == PAGE_EXECUTE_READWRITE || mbi.Protect == PAGE_EXECUTE_WRITECOPY);
if (isWritable){ //if 3
auto dump = new unsigned char[mbi.RegionSize + 1];
memset(dump, 0x00, mbi.RegionSize + 1);
if (!ReadProcessMemory(pHandle, mbi.BaseAddress, dump, mbi.RegionSize, NULL)){ MessageBox(NULL, "Can't read memory", "ReadProcessMemory", MB_OK); }
for (auto x = 0; x < mbi.RegionSize - 4; x += 4){ // 4 bytes
if (*(DWORD*)(dump + x) == (DWORD)"45000"){
char t1[255];
sprintf_s(t1, "%p", mbi.BaseAddress);
SendMessage(MyWnd.lB, LB_ADDSTRING, 0, (LPARAM)t1);
WriteProcessMemory(pHandle, mbi.BaseAddress, &valueToWrite, sizeof(valueToWrite), NULL);
}
}
delete[] dump;
}else{ MessageBox(NULL, "No Writable memory.", "mbi", MB_OK | MB_ICONERROR); return false; } //if 1
startAddress += mbi.RegionSize;
}else{ MessageBox(NULL, "No access to the page.", "mbi", MB_OK | MB_ICONERROR); return false; } //if 2
}else{ MessageBox(NULL, "VirtualQueryEx fails.", "VirtualQueryEx", MB_OK | MB_ICONERROR); return false; } // if 3
}// while
CloseHandle(pHandle);
TerminateThread(MyWnd.threadScan, 1);
return true;
}
|
I'm trying to understand why it is stuck in "if 2", never pass. NVM, it's working, but the results are so different from Cheat Engine.
s21.postimg.org/lc69ybyif/wrong_address.png
Description: |
Address differents / cheat engine |
|
Filesize: |
23.48 KB |
Viewed: |
26354 Time(s) |

|
|
|
Back to top |
|
 |
atom0s Moderator
Reputation: 204
Joined: 25 Jan 2006 Posts: 8579 Location: 127.0.0.1
|
Posted: Thu Dec 18, 2014 12:04 am Post subject: |
|
|
The return of VirtualQueryEx can be treated like a boolean. If it fails it returns 0. As a boolean, false is also equal to 0. So handling it the way I do is fine.
As for the auto keyword, it is used to automatically assume the variables type without having to manually define it. The compiler makes the assumption based on the right-hand value of the assignment.
Quote: | string test = to_string((int)dump);
string total = "Address: " + string(t1) + "- Value: " + test;
The address is missing characteris, i was trying to test your code to see how it work, using WPM to test the address. |
to_string just converts the raw number to a string. This is not using hexadecimal. You need to enforce hex if you want to see it that way.
Inside your new code you have:
Code: | if (*(DWORD*)(dump + x) == (DWORD)"45000"){ |
This is not valid in C++, you can't cast a string to a DWORD like that. You will not get the value you want, instead you are going to get the address that holds the value 45000.
As for PROCESS_ALL_ACCESS, I do not use it because it is poor practice to be greedy with flags like that. Along with that, on newer systems (Windows Vista and higher) using that flag can fail if the application does not have proper permissions.
It is better to specify only what you need.
_________________
- Retired. |
|
Back to top |
|
 |
dm0000 Newbie cheater
Reputation: 0
Joined: 09 Nov 2014 Posts: 16
|
Posted: Fri Dec 19, 2014 10:32 am Post subject: |
|
|
atom0s wrote: | The return of VirtualQueryEx can be treated like a boolean. If it fails it returns 0. As a boolean, false is also equal to 0. So handling it the way I do is fine.
As for the auto keyword, it is used to automatically assume the variables type without having to manually define it. The compiler makes the assumption based on the right-hand value of the assignment.
Quote: | string test = to_string((int)dump);
string total = "Address: " + string(t1) + "- Value: " + test;
The address is missing characteris, i was trying to test your code to see how it work, using WPM to test the address. |
to_string just converts the raw number to a string. This is not using hexadecimal. You need to enforce hex if you want to see it that way.
Inside your new code you have:
Code: | if (*(DWORD*)(dump + x) == (DWORD)"45000"){ |
This is not valid in C++, you can't cast a string to a DWORD like that. You will not get the value you want, instead you are going to get the address that holds the value 45000.
As for PROCESS_ALL_ACCESS, I do not use it because it is poor practice to be greedy with flags like that. Along with that, on newer systems (Windows Vista and higher) using that flag can fail if the application does not have proper permissions.
It is better to specify only what you need. |
Ty!
I'm studying your function do mine, and i was thinking, the address "dumped" is relative to my application no? beucase when i dump it, it will change the address. or i'm wrong?
I was trying to check it with cheat engine, and WPM with your code to see what happen, and the address is different. (added PROCESS_VM_WRITE).
I'm using this inside your code to check:
printf_s("Writed %#10.1x\n", mbi.BaseAddress);
or the real address in the target process would be mbi.baseAddress + mbi.RegionSize ?
baseAddress = where it start.
regionSize = bytes of the address that have the value.
So let's say i want to WPM, i would use the address: baseAddress + regionSize .
or even: process address + baseAddress + regionsize?
Maybe i need to use VirtualAllocEx ?
Sorry for ask, i'm just trying to understand.
*sometimes i take time to awser here because i need to work too.
|
|
Back to top |
|
 |
atom0s Moderator
Reputation: 204
Joined: 25 Jan 2006 Posts: 8579 Location: 127.0.0.1
|
Posted: Fri Dec 19, 2014 3:27 pm Post subject: |
|
|
dm0000 wrote: | Ty!
I'm studying your function do mine, and i was thinking, the address "dumped" is relative to my application no? beucase when i dump it, it will change the address. or i'm wrong?
I was trying to check it with cheat engine, and WPM with your code to see what happen, and the address is different. (added PROCESS_VM_WRITE).
I'm using this inside your code to check:
printf_s("Writed %#10.1x\n", mbi.BaseAddress);
or the real address in the target process would be mbi.baseAddress + mbi.RegionSize ?
baseAddress = where it start.
regionSize = bytes of the address that have the value.
So let's say i want to WPM, i would use the address: baseAddress + regionSize .
or even: process address + baseAddress + regionsize?
Maybe i need to use VirtualAllocEx ?
Sorry for ask, i'm just trying to understand.
*sometimes i take time to awser here because i need to work too.  |
Yes, when you dump the memory region you have to adjust it for your local memory. You would use the 'mbi.BaseAddress' and add the offset inside of the region the value was found out. While you are looping the region checking for the value, the value of say 'x' is the offset to where the value would be.
So taking my above code for example:
Code: |
// Scan for 4 byte value of 1337..
for (auto x = 0; x < mbi.RegionSize - 4; x += 4)
{
if (*(DWORD*)(dump + x) == 1337)
{
printf_s("Found 1337 at address: %08X", (DWORD)mbi.BaseAddress + x);
found++;
}
}
|
_________________
- Retired. |
|
Back to top |
|
 |
dm0000 Newbie cheater
Reputation: 0
Joined: 09 Nov 2014 Posts: 16
|
Posted: Sat Dec 20, 2014 1:19 pm Post subject: |
|
|
atom0s wrote: | dm0000 wrote: | Ty!
I'm studying your function do mine, and i was thinking, the address "dumped" is relative to my application no? beucase when i dump it, it will change the address. or i'm wrong?
I was trying to check it with cheat engine, and WPM with your code to see what happen, and the address is different. (added PROCESS_VM_WRITE).
I'm using this inside your code to check:
printf_s("Writed %#10.1x\n", mbi.BaseAddress);
or the real address in the target process would be mbi.baseAddress + mbi.RegionSize ?
baseAddress = where it start.
regionSize = bytes of the address that have the value.
So let's say i want to WPM, i would use the address: baseAddress + regionSize .
or even: process address + baseAddress + regionsize?
Maybe i need to use VirtualAllocEx ?
Sorry for ask, i'm just trying to understand.
*sometimes i take time to awser here because i need to work too.  |
Yes, when you dump the memory region you have to adjust it for your local memory. You would use the 'mbi.BaseAddress' and add the offset inside of the region the value was found out. While you are looping the region checking for the value, the value of say 'x' is the offset to where the value would be.
So taking my above code for example:
Code: |
// Scan for 4 byte value of 1337..
for (auto x = 0; x < mbi.RegionSize - 4; x += 4)
{
if (*(DWORD*)(dump + x) == 1337)
{
printf_s("Found 1337 at address: %08X", (DWORD)mbi.BaseAddress + x);
found++;
}
}
|
|
Ty for your attention! You are a very nice person for help me!
I just finished my program with your help.
Code: |
BYTE data[] = { 0x0, 0x0, 0x0, 0x0 };
DWORD wAddr = (DWORD)mbi.BaseAddress + x;
if (WriteProcessMemory(Funcs.pHandle, (BYTE*)wAddr, &data, sizeof(data), NULL)){
SendMessage(MyCtrl.lB, LB_ADDSTRING, 0, (LPARAM)"OK");
}
else{
SendMessage(MyCtrl.lB, LB_ADDSTRING, 0, (LPARAM)"NOT OK");
} |
Where i click to give rep for you? I don't have a button!
EDIT: i was searching in msdn about multiple access in memory with thread and i can't find this info, do you know if is safe to make 2 thread to read and write memory together? I will scan for more values and it is slow if i check for everything in the same thread i think.
EDIT2: i think i will use the same thread, do a vector list with the address for each value and write memory after.
EDIT3: Do you know if flash player pluging have a memory protection? With my code or CE it crash after sometime of WPM, or crash at start.
|
|
Back to top |
|
 |
eclessiastes How do I cheat?
Reputation: 0
Joined: 03 Sep 2016 Posts: 4
|
Posted: Sat Sep 03, 2016 4:19 am Post subject: |
|
|
Hello atom0s
I studied your readprocessmemory function and i have a confusion regarding it.
auto addr_min = (long)sysInfo.lpMinimumApplicationAddress;
auto addr_max = (long)sysInfo.lpMaximumApplicationAddress;
Why you use (long) ? I ask because it doesn't work.
If i change to char * it will work
auto addr_min = (char *)sysInfo.lpMinimumApplicationAddress;
auto addr_max = (char *)sysInfo.lpMaximumApplicationAddress;
Thank you for your time!
Best regards
|
|
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
|
|