You are dumping only the modules' code, but not the dynamic memory allocated by the process.
For getting the dynamically allocated memory, you need to enumerate heaps ( GetProcessHeaps() ) and then use HeapWalk() to enumerate memory blocks.
You are dumping only the modules' code, but not the dynamic memory allocated by the process.
For getting the dynamically allocated memory, you need to enumerate heaps ( GetProcessHeaps() ) and then use HeapWalk() to enumerate memory blocks.
Last edited by maxorator; 09-23-2008 at 02:12 AM.
Yeah, I don't get this HeapWalking crap. I also don't see an example on MSDN. I saw another thread somewhere suggesting VirtualQueryEx.
My Bad. I forgot call GlobalFree. Yes, there is definitely a reason why I used GlobalAlloc.GlobalAlloc and LocalAlloc functions provide two blocks of memory: fixed and moveable. Moveable memory could be allocated with the DISCARDABLE flag.When allocating a fixed memory block, the GlobalAlloc and LocalAlloc functions return a pointer to a memory block.I'm working on implementing that to see if it solves the problem. I've never used GlobalAlloc though. Is there some reason to choose that over malloc() or realloc()? You also didn't free (GlobalFree?) the memory when it finishes.
To work with a fixed memory block is same as working with malloc and the free function of CRT. The pointer returned for a fixed block can be used to free the memory allocated without retrieving a handle to that memory, by calling GlobalHandle function. In the case of moveable memory, instead of a pointer, the handle to the memory is returned. This is to provide an abstract layer over the allocated memory. Heap manager can move this memory block within its heap.
Moveable memory blocks are much preferable than fixed memory blocks. The system performance is better if we have minimum heap fragmentation. Fixed blocks get allocated at the bottom of the heap, and moveable blocks get locked down at the top. By marking your block moveable, once it has been freed, that free space will quickly be reused by other locked moveable blocks. If another fixed block is allocated, then when the first block is released, it will cause a hole to be created in the fixed memory area.
In brief, moveable means that a block may be moved in memory during a heap compaction to provide sufficient free space for other allocation requests. Discardable means that a block may be stored to disk and retrieved later, when needed again. Locked memory blocks are not moved or discarded. A fixed memory block will never be moved or discarded.
Also, check this post. It may be of some help to you. Finally, I also could not figure out how to use HeapWalk to traverse the dynamic memory of a remote process.
Well even Windows Mobile supports GlobalAlloc() so it isn't that unportable. For clipboard operations you actually have to use GlobalAlloc() exclusively. The HeapWalking stuff is soooo grossly underdocumented by microsoft that it isn't even funny (which is why I didn't even dignify them enough to capitalize their name).
I find it hard to believe that there aren't any decent examples of how to determine the size of a process and dump ALL of its memory like all these cheat search tools do.
The heap walking isn't hard if you just apply yourself a little. Here's a crude implementation that'll search the heap memory and main module of whatever process the code is in (obviously it's own if you just compile and run this).
Code:#define WIN32_LEAN_AND_MEAN #define _WIN32_WINNT 0x0500 #include <windows.h> #include <vector> #include <algorithm> #include <typeinfo> #include <iostream> #include <iterator> #include <psapi.h> template<class T> void FindTypeInBlock(LPVOID base, SIZE_T size, std::vector<LPCVOID>& result, const T& valToFind) { SIZE_T elements = size / sizeof(T); if(elements < 1) { return; } const T* const baseAsT = reinterpret_cast<T*>(base); std::vector<T> range(baseAsT, baseAsT + elements); typename std::vector<T>::iterator iter = range.begin(); while((iter = std::find(iter, range.end(), valToFind)) != range.end()) { const LPCVOID address = baseAsT + (iter - range.begin()); result.push_back(address); ++iter; } } int main() { std::vector<HANDLE> heaps(GetProcessHeaps(0, NULL)); GetProcessHeaps(heaps.size(), &heaps[0]); std::vector<LPCVOID> results; // change this to whatever type and value you want to search for int val = 28; for(DWORD i = 0; i < heaps.size(); ++i) { std::cout << "Heap handle:\t" << heaps[i] << '\n'; PROCESS_HEAP_ENTRY phi = {0}; while(HeapWalk(heaps[i], &phi)) { std::cout << "Block Start Address: " << phi.lpData << '\n'; std::cout << "\tSize: " << phi.cbData << " - Overhead: " << static_cast<DWORD>(phi.cbOverhead) << '\n'; std::cout << "Block is a"; if(phi.wFlags & PROCESS_HEAP_REGION) { std::cout << " VMem region\n"; std::cout << "\tCommitted size: " << phi.Region.dwCommittedSize << '\n'; std::cout << "\tUncomitted size: " << phi.Region.dwUnCommittedSize << '\n'; std::cout << "\tFirst block: " << phi.Region.lpFirstBlock << '\n'; std::cout << "\tLast block: " << phi.Region.lpLastBlock << '\n'; } else { if(phi.wFlags & PROCESS_HEAP_UNCOMMITTED_RANGE) { std::cout << "n uncommitted range\n"; } else if(phi.wFlags & PROCESS_HEAP_ENTRY_BUSY) { std::cout << "n Allocated range: Region index - " << static_cast<unsigned>(phi.iRegionIndex); if(phi.wFlags & PROCESS_HEAP_ENTRY_MOVEABLE) { std::cout << "\n\tMovable: Handle is 0x" << phi.Block.hMem << '\n'; } else if(phi.wFlags & PROCESS_HEAP_ENTRY_DDESHARE) { std::cout << "\n\tDDE Sharable\n"; } else std::cout << std::endl; // check this block for the data FindTypeInBlock(phi.lpData, phi.cbData, results, val); } } std::cout << std::endl; } } // now check the module, for static data MODULEINFO modInf = {0}; GetModuleInformation(GetCurrentProcess(), GetModuleHandle(NULL), &modInf, sizeof(MODULEINFO)); FindTypeInBlock(modInf.lpBaseOfDll, modInf.SizeOfImage, results, val); std::cout << "The " << typeid(val).name() << ' ' << val << " was found at the following locations:\n"; std::copy(results.begin(), results.end(), std::ostream_iterator<LPCVOID>(std::cout, "\n")); }
Last edited by adeyblue; 09-23-2008 at 08:44 PM.
Yes, but platform specific APIs are unportable, as we know.
If there's no need to use them (oh, they DO exist for a reason, and they are useful), then one shouldn't use them in favor of portability.
GlobalAlloc isn't a requirement in this code from what I see (not would I think, is there a sufficiently good reason to use it), so using malloc/free is better.
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 so sure this code is totally reliable. I've changed the value of the val int to 9999 and it did not find it. I've also tried accessing a remote process to find a global int variable with the value of 9999 and it did not find it.The heap walking isn't hard if you just apply yourself a little. Here's a crude implementation that'll search the heap memory and main module of whatever process the code is in (obviously it's own if you just compile and run this).
Huh? I just built the code and it finds the value I enter at least once (at 43BF28, but I expect that will vary depending on which compiler it is built with and other factors).
--
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.
I've attached an ASCII text file which captured the output of the executable for review.
Also, val = 28 works for me. I get the following:
I've also tried setting val to 12345 and it was not found.The int 28 was found at the following locations:
004287FC
00428A38
Last edited by BobS0327; 09-24-2008 at 07:19 AM.
Actually, you are right, as soon as you try to use it for a different process, there it doesn't work. GetProcessHeaps() only works on the current process. I couldn't find any other function doing something similar.
--
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.
I've been testing the code both ways. But even accessing the local process does not always produce a found memory location:
The following values used for val in a local process are not found in memory:Code:The int 101 was found at the following locations: 0042341C 00423448 00424A58 00424C7C 00425A08
98765
45678
12345
23456
34567
35471
I'll probably get in trouble with the mods for demonstrating cheat search tools 101. But anyway, the first listing is the target executable. The variable is initially set to "Bob". Start the target executable and then run the second executable which will change this variable from "Bob" to "Ted". Hitting any key in the target executable will display the new value which is "Ted".I find it hard to believe that there aren't any decent examples of how to determine the size of a process and dump ALL of its memory like all these cheat search tools do.
The second executable will need the ProcessId of the the target as command line input.
Code:#include <stdlib.h> #include <stdio.h> #include <string.h> #include <conio.h> char test[4] = {"Bob"}; int main(void) { printf("Before WriteProcessmemory update %s\n\n Hit any key to display new value\n\n", test); getch(); printf("After WriteProcessMemory update %s\n", test); getch(); return 0; }
Code:#pragma comment(lib, "advapi32.lib") #pragma comment(lib, "psapi.lib") #include <windows.h> #include <stdio.h> #include <psapi.h> // Binary equivalent of "Bob" which is used in search const BYTE bPattern[3]={0x42, 0x6f, 0x62}; 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; } BOOL SearchProcessMemory(int iProcessId) { DWORD cbRead, cbNeeded, iIndex; HMODULE hModule; MODULEINFO ModuleInfo; BYTE *bBuffer = 0; HANDLE hProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE, iProcessId); EnumProcessModules(hProcess, &hModule, sizeof(hModule), &cbNeeded); GetModuleInformation(hProcess, hModule, &ModuleInfo, sizeof(MODULEINFO)); bBuffer = (LPBYTE)GlobalAlloc(GMEM_FIXED, ModuleInfo.SizeOfImage+1); ReadProcessMemory(hProcess, ModuleInfo.lpBaseOfDll, bBuffer, ModuleInfo.SizeOfImage, &cbRead); //Search the entire memory of our target process for( iIndex = 0; iIndex < ModuleInfo.SizeOfImage; iIndex++) { if (!memcmp(&bBuffer[iIndex], bPattern, 3)) { // calculate of lpBaseOfDll (start of exe + i) DWORD dwFoundAtAddress = (DWORD)ModuleInfo.lpBaseOfDll + iIndex; printf("we found it at 0x%2X\n", dwFoundAtAddress); // Binary equivalent of "Ted" const BYTE bPatternOut[3]={0x54, 0x65, 0x64}; DWORD dwOrgProtect; // By default memory is read only, change it to read/write if(!VirtualProtectEx(hProcess,(LPVOID) dwFoundAtAddress , sizeof(bPatternOut), PAGE_READWRITE, &dwOrgProtect )) { printf("Virtual protect failed error %d\n", GetLastError()); return FALSE; } if (!WriteProcessMemory(hProcess, (LPVOID) dwFoundAtAddress, &bPatternOut, 3, &cbRead)) printf("WriteProcessMemory error %d\n", GetLastError()); else printf("WriteProcessMemory successful\n"); // Change memory back to read only VirtualProtectEx(hProcess, (LPVOID) dwFoundAtAddress ,sizeof(bPatternOut) , dwOrgProtect , &dwOrgProtect ); return TRUE; } } return FALSE; } int main(int argc, char **argv) { if ( !EnableTokenPrivilege (SE_DEBUG_NAME) ) { printf ( "Cannot get required privilege %lu\n", GetLastError () ); return 0; } if(SearchProcessMemory(atoi(argv[1])) == FALSE) printf("Binary signature NOT found\n"); return 0; }