That's the same example you already showed me. It only reads the main module. I want ALL the memory the process is using.
That's the same example you already showed me. It only reads the main module. I want ALL the memory the process is using.
That was much appreciated. It reminds me of a question way back when where a person was asking how to read the contents of a file and dump it into an edit control. I pointed out that they may wish to consider using fread() instead of ReadFile() since it is more portable, and I believe it was Sabastiani who pointed out that it was pointless to try and remain portable considering the nature of the question to begin with. Plus ReadFile() is more native to the OS and is probably eventually called when using the fread() function. Sometimes its just better to just say touche, nod and move on.
---I digress. In responce to the previous post: EnumProcesses()? Have you looked at that at all. And EnumProcesseModules().
Try looking up QueryWorkingSet().
I'm about to go to be now, but I did a very quick hack to see what it gives, and it looks soemwhat sensible. Make sure you give a big enough table.
--
Mats
Compilers can produce warnings - make the compiler programmers happy: Use them!
Please don't PM me for help - and no, I don't do help over instant messengers.
Nifty. QueryWorkingSetEx() looks like it can pick up a little more info than QueryWorkingSet().
Well, the only thing I can figure to do at this point is use GetProcessMemoryInfo to get the size of the process, which gives me the exact amount shown by Task Manager. Then loop starting at 0 (or the entrypoint?) and skip over anything unreadable. Not exactly the most reliable or efficient thing to do, but I'm not seeing any other way that I can actually understand. All I want is ALL the memory allocated to the process--not the base module, not 1 page, ALL of it--dumped into an array in the fastest, most simplistic way possible.
I'm pretty sure QueryWorkingSet (and perhaps QueryWorkingSetEx as well), assuming you give it a large enough table, is the right way to go.
GetProcessMemoryInfo only shows HOW much memory the process uses, not where the memory is. QueryWorkingSet(Ex) gives you the actual memory used by the process.
I'll see if I can hack up a little example in a bit.
--
Mats
Compilers can produce warnings - make the compiler programmers happy: Use them!
Please don't PM me for help - and no, I don't do help over instant messengers.
Give the following a try. It dumps ALL the process memory to a log file that will be found in the same subdirectory from which the executable was started. Start the executable from the command line with the name of the target process as the first parm on the command line. Be forewarned, dumping something like iexplore.exe will create a HUGE file.
Code:#include <afx.h> #include <windows.h> #include <TLHelp32.h> #include <stdio.h> #include <psapi.h> FILE *outfile; int VerifyProcessId(char *pProcessName) { HANDLE hSnap = INVALID_HANDLE_VALUE; HANDLE hProcess = INVALID_HANDLE_VALUE; PROCESSENTRY32 ProcessStruct; ProcessStruct.dwSize = sizeof(PROCESSENTRY32); hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if(hSnap == INVALID_HANDLE_VALUE) return -1; if(Process32First(hSnap, &ProcessStruct) == FALSE) return -1; do { if(stricmp(strupr(ProcessStruct.szExeFile), pProcessName)==0) { CloseHandle( hSnap ); return ProcessStruct.th32ProcessID; break; } } while( Process32Next( hSnap, &ProcessStruct ) ); CloseHandle( hSnap ); return -1; } static FILE *OpenLogFile(void) { int iIndex; for (iIndex = 0; iIndex < 0x10000; ++iIndex) { char fname[4096]; sprintf(fname, "DUMP%04x.log", iIndex); FILE *f = fopen(fname, "r"); if (f == NULL) { fprintf(stderr, "Log file being used: %s\n",fname); return fopen(fname, "wt"); } else { fclose(f); } } return NULL; } char * BuildErrorMessage( char* pMessage) { DWORD eNum; CHAR sysMsg[256] = {0}; CHAR* p; static CHAR *szReturn[512]; eNum = GetLastError( ); FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, eNum, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language sysMsg, 256, NULL ); // Trim the end of the line and terminate it with a null p = sysMsg; while( ( *p > 31 ) || ( *p == 9 ) ) ++p; do { *p-- = 0; } while( ( p >= sysMsg ) && ( ( *p == '.' ) || ( *p < 33 ) ) ); memset(szReturn, 0, sizeof szReturn); sprintf((char *)szReturn,"WARNING: %s failed with error %d (%s)\n",pMessage, eNum, sysMsg ); return (char *)szReturn; } BOOL EnableTokenPrivilege (LPTSTR privilege) { HANDLE hToken; TOKEN_PRIVILEGES token_privileges; DWORD dwSize; ZeroMemory (&token_privileges, sizeof (token_privileges)); token_privileges.PrivilegeCount = 1; if ( !OpenProcessToken (GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken)) return FALSE; if (!LookupPrivilegeValue ( NULL, privilege, &token_privileges.Privileges[0].Luid)) { CloseHandle (hToken); return FALSE; } token_privileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if (!AdjustTokenPrivileges ( hToken, FALSE, &token_privileges, 0, NULL, &dwSize)) { CloseHandle (hToken); return FALSE; } CloseHandle (hToken); return TRUE; } void ReadTheMemory(DWORD dwProcessId, HEAPENTRY32 heapentry) { CString strLine, strArr, strTemp; BYTE byte; DWORD dwBlock = 0, dwOffset, dwBytesRead; BYTE * pBuffer = new BYTE[heapentry.dwBlockSize]; HANDLE hProcess = NULL; if (NULL != pBuffer) { hProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE, dwProcessId); if(hProcess == NULL) { fprintf(outfile, "%s", BuildErrorMessage("OpenProcess")); return; } if (ReadProcessMemory( hProcess, (LPCVOID) heapentry.dwAddress, pBuffer, heapentry.dwBlockSize, &dwBytesRead) == TRUE) { for (dwBlock = 0; dwBlock < heapentry.dwBlockSize; dwBlock += 16) { strLine.Format("[%08x]: ", heapentry.dwAddress + dwBlock); strArr.Empty(); // we'll use 16 bytes per line for (dwOffset = 0; dwOffset < 16; dwOffset++) { byte = *(pBuffer + dwBlock + dwOffset); strLine.Format("%02x ", byte); // account for non-printable characters if (32 <= byte && byte < 127) strArr += byte; else strArr += '·'; } if(LF32_FIXED == heapentry.dwFlags) strTemp.Format("%s", "Fixed "); else if(LF32_FREE == heapentry.dwFlags) strTemp.Format("%s", "Free "); else if(LF32_MOVEABLE == heapentry.dwFlags) strTemp.Format("%s", "Movable"); else strTemp.Format("%s", "Unknown"); strLine.Format ("%#x %#x %s %#x (%lu) %s ", heapentry.th32HeapID, heapentry.dwAddress, strTemp,heapentry.dwBlockSize, heapentry.dwBlockSize, strArr); fprintf(outfile, "%s\n", strLine); } } else { strLine.Format ("%#x %#x %s %#x (%lu) %s ", heapentry.th32HeapID, heapentry.dwAddress, strTemp,heapentry.dwBlockSize, heapentry.dwBlockSize, strArr);if(LF32_FIXED == heapentry.dwFlags) strTemp.Format("%s", "Fixed "); else if(LF32_FREE == heapentry.dwFlags) strTemp.Format("%s", "Free "); else if(LF32_MOVEABLE == heapentry.dwFlags) strTemp.Format("%s", "Movable"); else strTemp.Format("%s", "Unknown"); strLine.Format ("%#x %#x %s %#x (%lu)", heapentry.th32HeapID, heapentry.dwAddress,strTemp ,heapentry.dwBlockSize, heapentry.dwBlockSize); fprintf(outfile, " %s ", strLine); fprintf(outfile, "%s", BuildErrorMessage("ReadProcessMemory")); } delete [] pBuffer; CloseHandle( hProcess ); } } int main(int argc, char **argv) { PROCESSENTRY32 ProcessEntry = {0}; HANDLE hProcessSnapshot; HANDLE hProcess = NULL; int nIndex = 0, nItem; CString strText, strTemp; DWORD dwProcessId ; HEAPENTRY32 HeapEntry; HEAPLIST32 HeapList; if ( !EnableTokenPrivilege (SE_DEBUG_NAME) ) { fprintf(outfile, "%s", BuildErrorMessage("EnabletokenPrivilege issue")); return 0; } dwProcessId = VerifyProcessId(argv[1]); if(dwProcessId == -1) { printf("Cannot determine Process id for %s, ABORTING!!\n",argv[1]); return -1; } outfile = OpenLogFile(); if(outfile == NULL) { printf("Log file open failed\n"); return -1; } HANDLE hHeapSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPHEAPLIST, dwProcessId); if (INVALID_HANDLE_VALUE != hHeapSnapshot) { HeapList.dwSize = sizeof(HEAPLIST32); if (Heap32ListFirst(hHeapSnapshot, &HeapList) != FALSE) { do { HeapEntry.dwSize = sizeof(HEAPENTRY32); if (Heap32First(&HeapEntry, HeapList.th32ProcessID, HeapList.th32HeapID) != FALSE) { do { ReadTheMemory( dwProcessId, HeapEntry); } while (Heap32Next(&HeapEntry) != FALSE); } else { fprintf(outfile, "%s", BuildErrorMessage("Iterating heap block error")); break; } } while (Heap32ListNext(hHeapSnapshot, &HeapList) != FALSE); } else fprintf(outfile, "%s", BuildErrorMessage("Iterating heap list error")); CloseHandle(hHeapSnapshot); } else fprintf(outfile, "%s", BuildErrorMessage("Can't acquire snapshot")); return 0; }
I've hacked up something that uses QueryWorkingSet here. It appears to work OK on Win2K - you may need to use some of that "set privilige" code that Bob has shown.
I'm afraid I'm not a very good with GUI programming, so I have been using MFC for this, hence a large zipfile rather than a few hundred lines of code. The main parts is in
which gathers and stores the memory information in a TreeView. Currently it's pretty stupid - I probably should use a more dynamic model of expanding the application when it's selected, rather than storing ALL page info for ALL processes (at the moment nobbled to stop at the 10th process to avoid it taking too long to start up - in the min(..., 10), either change 10 to a much larger number or remove the min(..., 10) except for cProcesses).Code:LeftView.cpp void CLeftView::OnInitialUpdate()
Connects the click on the memory block with the rigthPane drawing.Code:void CLeftView::OnTvnSelchanged(NMHDR *pNMHDR, LRESULT *pResult)
Draws the content of the memory block selected.Code:showmemView.cpp: void CshowmemView::OnPaint()
The thing that is needed here is a scrollbar to scroll to the second half of memory (and perhaps the next block of memory as well).
The attachment is a .zip file with modified extension to get past the "you can only put these types of files up" filter.
--
Mats
Compilers can produce warnings - make the compiler programmers happy: Use them!
Please don't PM me for help - and no, I don't do help over instant messengers.
Uh, sorry Bob. That seems to fail too. I did a quick test just outputting the HeapEntry.dwAddress and HeapEntry.dwBlockSize from the loop exactly as you had it. The highest it goes is 0x5F30008, despite the fact the process in question is reported (by task manager) to be using 70 megs--and I've personally dumped from 0x3B0A0000-3B0F0000 before. It also completely ignores the 0x400000 (entrypoint/main module) region. I don't get it.
Last edited by Viper187; 09-26-2008 at 09:14 AM.
Whilst I don't think looking for "heap" is the correct solution (I believe my suggestion of QueryWorkingSet is the correct solution), I would say that it's likely that an applicatin that uses addresses up to 95MB uses has approximately 70MB of space used. 5f30008 is 95.18 MB according to Calculator.
--
Mats
Compilers can produce warnings - make the compiler programmers happy: Use them!
Please don't PM me for help - and no, I don't do help over instant messengers.
Compilers can produce warnings - make the compiler programmers happy: Use them!
Please don't PM me for help - and no, I don't do help over instant messengers.
I'm not sure what you're going to do with all that memory stored in an array. The task manager working set consists of thread stacks, environment memory, system data structures, shared memory and much more. If you plan on searching all this memory, you may end up in pages of shared memory loaded from NTDLL.DLL. IMHO, trying to sort out all of this in the working set would probably require a lot of "rocket science" coding in addition to just dumping all this memory in a array. So, my question is, can you make any practical use of this memory once you acquire it?
Ok, so back to the original problem. I looked at the source you provided (I absolutely despise C++ project sand source, btw). in any case, I haven't figured out everything I need form it, assuming it even gets the right data. I get that looping wsInfo->WorkingSetInfo[j].VirtualPage gives me memory locations; however, I don't get how to determine the size of these "pages." They also don't output in order. I don't suppose there's an easy way to find the max address to malloc() my array so I can have everything padded the way I want. Sort the WorkingSetInfo array before looping, maybe? I dunno.
p.s. Why the hell aren't PSAPI_WORKING_SET_INFORMATION and PSAPI_WORKING_SET_BLOCK declared in psapi.h like they should've been?
I don't know the right way to do it. That's why I'm asking you guys. I need to dump the entire process for comparison (i.e. game hacking). As I said... this 70 meg process stores data (actually, vital game data) at that higher address range (0x3B0A0000+), which your method didn't touch. you have no idea how useful it can be to run an N64 game on the PC to find gameshark codes for it. I've even done some PS2 codes, despite my system being slow. I want my program to branch out and do PC games as well though. Think Tsearch/Art Money on steroids. My previous program was called Renegade64, though I doubt anyone here would've heard of it. It was mainly written in VB6 with a C DLL to speed dumping/searching (only dumping the console RAM part of the process memory after finding it with tsearch). Now that I actually got off my ass and learned enough win32 to write C programs with a GUI, I'm trying to write a better version of my old program. Naturally, I'm running into issues I don't know how to handle. I never expected to be hung up for a week trying to figure out how to dump a process though.
Last edited by Viper187; 09-26-2008 at 08:54 PM.
I have no idea why psapi.h doesn't declare the types for QueryWorkingSet... I copied and pasted from the MSDN pages, and it appears to work.
As to finding the total size of the project, I would actually just count the number of pages that came back and multiply by 4096 bytes. You will probably not sensibly be able to malloc a region big enough for the entire memory to fit in as one. Instead, store all of the memory as one big lump, and use an array with addresses to store the location of each block.
It is possible that the system uses "large pages", in which case you would have to use QueryWorkingSetEx for each location to find the size of the page (the data returned by this function has a flag to say "LargePage", which means that it is 2 or 4MB depending on the mode of the processor: PAE -> 2MB, non-PAE->4MB). But I doubt it is likely that you will have any large pages. You could pre-check by this method:
--Code:if (!(proc->va & ((1 << 21)-1))) { // Need to check if it's a large page. }
Mats
Compilers can produce warnings - make the compiler programmers happy: Use them!
Please don't PM me for help - and no, I don't do help over instant messengers.