Quote:
Originally Posted by 0bl1ky
So currently i can get read access to the process memory, does anyone have any experience grabbing things from an active processes memory? Ill keep snooping around but if anyone has any tips that would be great.
Here is the code that was used for FPHG to grab hands from Party's memory, it might be helpful:
Code:
void PartyMemoryScanner(const char* LobbyTitle)
{ // This function will scan all of the party memory looking for new Hand Histories,
// and do all the required work when found.
// Returns false if something wrong, else true.
// v0.04b: Can now handle multiple copies of Party in the same user area.
// v0.05: Can now take different lobby titles, so will work on Empire also.
// Process ID of the window.
DWORD PID;
// Used to find the allowable range of memory for the process.
SYSTEM_INFO SI;
// Used to check each mem segment's properties.
MEMORY_BASIC_INFORMATION MBI;
// This is used so we don't keep reallocating this.
static char* Buffer = NULL;
static DWORD BufferLen = 0;
// Window handle for party processes.
HWND WH=NULL; // Init to NULL for FindNextWithPrefix() loop.
// 0.04b: Loop through all of the party poker processes.
while ((WH=FindNextWithPrefix(WH,LobbyTitle))!=NULL)
{
// Get the PID of the window.
GetWindowThreadProcessId(WH,&PID);
// Open the process.
HANDLE hProcess = OpenProcess(PROCESS_VM_READ|PROCESS_QUERY_INFORMATION|PROCESS_VM_OPERATION,false,PID);
// Determine applications memory addresses range.
GetSystemInfo(&SI);
// Set to the start/base address.
void* CurrentAddress = SI.lpMinimumApplicationAddress;
// Keep searching until we got to the end of memory chunks.
while (CurrentAddress <= SI.lpMaximumApplicationAddress) {
// Set to 0, just in case this fails.
MBI.RegionSize = 0;
// Try to query the memory region.
DWORD Ret = VirtualQueryEx(hProcess,CurrentAddress,&MBI,sizeof(MEMORY_BASIC_INFORMATION));
if (Ret == sizeof(MEMORY_BASIC_INFORMATION)) {
// Is this the type of memory region where we hand hands?
if (MBI.Type == MEM_PRIVATE && MBI.State == MEM_COMMIT
&& MBI.AllocationProtect == PAGE_READWRITE
&& MBI.Protect == PAGE_READWRITE
&& MBI.RegionSize >= MIN_PAGE_SIZE) {
// None read yet.
DWORD NumRead=0;
// Do we need a bigger buffer?
if (MBI.RegionSize>BufferLen) {
if (Buffer!=NULL)
delete [] Buffer;
Buffer = new char[MBI.RegionSize];
BufferLen=MBI.RegionSize;
}
// v0.03: Read the memory - Try many times in case locked, then give in...
int FailCount=0;
while (!ReadProcessMemory(hProcess,MBI.BaseAddress,Buffer,MBI.RegionSize,&NumRead)) {
// Failed again.
FailCount++;
// Just incase we get stuck in a loop forever...
if (FailCount==MAX_FAIL_RPM_COUNT) {
//Error(WARNING,"FreePHG: ReadProcessMemory() failed, even with retries.");
NumRead=0; // So we will have to skip this.
break; // No more trying.
}
// Sleep a little to give party a chance to unlock the mem.
Sleep(FAIL_RPM_SLEEP);
};
// v0.06c: Remove all EVEN bytes (remember that now string allignment needs to be set to 4).
for (unsigned int I=0;I<BufferLen;I+=2)
Buffer[I/2]=Buffer[I];
NumRead/=2;
// Have we at least got a big enough region to be useful
if (NumRead>=strlen("#Game No : ##########"))
{
// Check if region contain search string.
// v0.03: Optimized (Crappy VC6++ compilier don't like strlen in for)...
// - Note: Using Boyer-Moore etc, not gonna help much here...
unsigned int MaxLen=NumRead-strlen("#Game No : ##########");
for (unsigned int I=0;I<MaxLen;I++) {
// Look for "#Game No : " - Don't use stncmp - slow as ****...
// v0.04: Use the fact always alligned to just check via a 32bit comp.
// NOTE: Will even cast to non-aligned int, but will be slower.
if ((*((unsigned int*)(&Buffer[I]))) == ((((unsigned int)'m')<<24)
|(((unsigned int)'a')<<16)
|(((unsigned int)'G')<<8)
|(((unsigned int)'#'))) // Remember: big-endian...
&& Buffer[I+4]=='e'
&& Buffer[I+5]==' '
&& Buffer[I+6]=='N'
&& Buffer[I+7]=='o'
&& Buffer[I+8]==' '
&& Buffer[I+9]==':'
&& Buffer[I+10]==' '
&& Buffer[I+11]>='0'
&& Buffer[I+11]<='9') {
// Does it have the stars?
if (!(Buffer[I+23]=='\n' && Buffer[I+24]=='*')
&& !(Buffer[I+24]=='\n' && Buffer[I+25]=='*')) {
continue;
}
// Find the end of the hand.
for (unsigned int J=I+1+strlen("#Game No : ##########");J<NumRead;J++)
{
// Check we have no garbage characters (ie: only allow ASCII char, CR/NL or accented) before terminator.
if (Buffer[J]!=10 && Buffer[J]!=13 // NL or CR.
&& Buffer[J]!=128 && Buffer[J]!=174 // v3.04: '€' or '®'.
&& !(Buffer[J]>=32 && Buffer[J]<=127) // Normal ASCII characters.
&& !((unsigned char)Buffer[J]>=0xC0 && (unsigned char)Buffer[J]<=0xFF)) { // Accented characters.
break;
}
// Terminate on "\r\n\0" if in realtime mode.
if (Buffer[J-1]=='\r' && Buffer[J]=='\n'&& Buffer[J+1]==0) {
HandleTableData(&Buffer[I],(J-I)); // @@@BUG: Note we don't count the null terminator here!
break;
}
}
} // End match "#Game No : ".
} // End Find HH loop.
} // End if (NumRead>=strlen("#Game No : ").
// ------------------------------------------------------------------#
}
}
// Increase base address for next searching cicle.
// NOTE: We were using a DWORD here rather than a SIZE_T which might explain why it didn't work on 64bit systems.
CurrentAddress = (void*)((SIZE_T)MBI.BaseAddress+(SIZE_T)MBI.RegionSize);
} // End while more chunks.
CloseHandle(hProcess);
} // While loop to scan multiple party processes (if they exist).
} // End PartyMemoryScanner.
Helpful things I remember from doing this:
* Start off writing something to dump all the process memory to a file or files and then you can work through it using a hex editor to try to figure out what you are looking for.
* Search for both ASCII and UNICODE type strings.
* Once you find what you are looking for, use the MEMORY_BASIC_INFORMATION values to try to narrow down what type of segments you need to search (see above "if (MBI.Type == MEM_PRIVATE ..." part - MIN_PAGE_SIZE was 64k IIRC). This will speed up the amount of time you spend on each memory pass significantly.
* Similar to the above point, try to work out if the values are aligned to always start on a 2, 4, 8, 16, etc byte boundary... This will let you iterate much quicker (IIRC Party started off aligned to 8-byte boundaries but then they changed something and it stopped being aligned).
* Don't bother using a complex string search algorithm (eg: Boyer–Moore, memmem, etc) - just cast the first 4 (ASCII) or 2 (UNICODE) to a single 32bit value (remember to reverse the endianess) and use that to check like I did in the code above. I tried several string search algorithms and they were all 2-3x slower than this simple method for this task...
* Sometimes ReadProcessMemory() fails (I think when the process itself locks a page or something) so make sure you try several times with a small 20-50ms sleep between calls (see above code).
* Don't convert all the characters to ASCII from UNICODE like I did above lol - that was just a crappy hack to unbreak the memory searching code work after Party changed from using ASCII to UNICODE characters (FPHG actually was changed to work by hooking memcpy() shortly after this so it was never really needed).
If you still can't figure out what you need to grab then you can look into hooking windows' GDI textout functions (or whatever stars is using). It's not very easy to work out which window/table goes with which HDC though (backbuffering, etc) so best to try scanning memory first.
Hope this helps
Juk
EDIT: This might not work well on 64bit OSes - it was written way before anybody used 64bit OSes and I see I've hacked some bit of it to use SIZE_T to make it work in 64bit OS scanning a 32bit process, but you'll prolly have to do some more reading to grab from a 64bit process, etc.
Last edited by jukofyork; 05-14-2013 at 01:17 PM.