Thread: Segmentation fault!?!?!?

  1. #46
    Registered User
    Join Date
    Mar 2005
    Location
    Mountaintop, Pa
    Posts
    1,058
    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
    They're found in the really old SDK's (pre 2000 SDK's). Bottomline, is that they're now considered obsolete. So, you won't find them in the current Windows Server 2003 SDK. It appears that MS now wants you to repeatedly call QueryWorkingSet to dynamically allocate the correct storage for a PVOID to hold your information. I'll have to write some POC code to verify this.
    Last edited by BobS0327; 09-27-2008 at 10:17 AM.

  2. #47
    Registered User
    Join Date
    Mar 2005
    Location
    Mountaintop, Pa
    Posts
    1,058
    Give this a try to see if it works...

    Code:
    #include <afx.h>
    #include <windows.h>  
    #include <psapi.h> 
    #include <tlhelp32.h> 
    #include <stdio.h>
    
    #define MAX_I86_MEMORY_LIMIT  0x4c4b40  // 5MEG 
    #define PAGE_SIZE 4096
    
    #pragma comment ( lib, "psapi.lib" ) 
    #pragma comment ( lib, "advapi32.lib" ) 
    
    FILE *outfile;
    
    typedef union _PSAPI_WORKING_SET_BLOCK {
        ULONG_PTR Flags;
        struct {
            ULONG_PTR Protection  :5;
            ULONG_PTR ShareCount  :3;
            ULONG_PTR Shared  :1;
            ULONG_PTR Reserved  :3;
            ULONG_PTR VirtualPage  :20;
        } ;
    } PSAPI_WORKING_SET_BLOCK, 
     *PPSAPI_WORKING_SET_BLOCK;
    
    typedef struct _PSAPI_WORKING_SET_INFORMATION {
        ULONG_PTR NumberOfEntries;
        PSAPI_WORKING_SET_BLOCK WorkingSetInfo[1];
    } PSAPI_WORKING_SET_INFORMATION, 
     *PPSAPI_WORKING_SET_INFORMATION;
    
    typedef struct MEMDIFF_SNAPSHOT
    {
        DWORD   m_signature;
        DWORD   m_dwAllocSize;
        DWORD   m_fCompared;    // 0 initially, 1 after filtering common pages
        DWORD   m_nPages;       // Must come last!!!
    } * PMEMDIFF_SNAPSHOT;
    
    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;
    }
    
    void ReadTheMemory(DWORD dwProcessId, DWORD dwInputAddress, DWORD dwInputBlockSize)
    {
        CString     strLine, strArr, strTemp;
        BYTE        byte;
        DWORD dwBlock = 0, dwOffset, dwBytesRead;
        BYTE * pBuffer = new BYTE[dwInputBlockSize];
        HANDLE hProcess = NULL;
        if (NULL != pBuffer)
        {    
            hProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE, dwProcessId);
            if(hProcess == NULL)
            {
                fprintf(outfile, "OpenProcess failed\n");
                return;
            }
            if (ReadProcessMemory( hProcess, (LPCVOID) dwInputAddress, pBuffer, dwInputBlockSize, &dwBytesRead) == TRUE)
            {
                for (dwBlock = 0; dwBlock < dwInputBlockSize; dwBlock += 16)
                {
                    strLine.Format("[%08x]:  ", dwInputAddress + 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 += '·';
                    }
                    strLine.Format ("%#x %#x (%lu) %s ", dwInputAddress, dwInputBlockSize, dwInputBlockSize, strArr);
                    fprintf(outfile, "%s\n", strLine);
                }    
            }else  fprintf(outfile, "%s\n",BuildErrorMessage("ReadProcessMemory"));
            delete [] pBuffer;
            CloseHandle( hProcess );
        }
    }
    
    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;
    }
    
    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;
    }
    
    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;
    }
    
    int main(int argc, char **argv )
    { 
        DWORD dwProcessId; 
        DWORD dwAlloc = 0;
        outfile = OpenLogFile();
    
        if ( !EnableTokenPrivilege (SE_DEBUG_NAME) )
        {
            printf("EnabletokenPrivilege issue\n");
            return 0;
        } 
        if(outfile == NULL)
        {
            printf("Log file open failed\n");
            return -1;
        }   
        dwProcessId = VerifyProcessId(argv[1]);
        if(dwProcessId == -1)
        {
            printf("Cannot determine Process id for %s, ABORTING!!\n",argv[1]);
            return -1;
        }   
    
        HANDLE hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, dwProcessId ); 
        PSAPI_WORKING_SET_INFORMATION *pv = NULL;
        start:
        dwAlloc += 0x2000;
        pv = (PSAPI_WORKING_SET_INFORMATION *)VirtualAlloc(0, dwAlloc, MEM_COMMIT,PAGE_READWRITE);            
        if ( QueryWorkingSet ( hProcess, pv, dwAlloc ) == FALSE ) 
        {  
            if ( dwAlloc < MAX_I86_MEMORY_LIMIT)   
            {
                VirtualFree( pv, 0, MEM_RELEASE );
                pv = NULL;
                fprintf(outfile, "Not enough memory allocated: %d\n", dwAlloc);
                goto start;
            }
            else
            {
                CloseHandle ( hProcess ) ;
                fprintf(outfile, "Aborting, Not enough memory allocated, cannot allocate more than %d\n", dwAlloc);
                return -1;
            }
        }  
        else 
        {
            fprintf(outfile, "Number of entries: %d\n", pv->NumberOfEntries ); 
            for(DWORD iIndex = 0; iIndex < pv->NumberOfEntries; iIndex++)
            {
    			DWORD dwDumpAddress = pv->WorkingSetInfo[iIndex].VirtualPage << 12;
                fprintf(outfile,"Virtual Page: 0x%06x (%d)\n",pv->WorkingSetInfo[iIndex].VirtualPage,pv->WorkingSetInfo[iIndex].VirtualPage );
    
                switch(pv->WorkingSetInfo[iIndex].Protection)
                {
                    case 0:
                        fprintf(outfile,"The page is not accessed.\n");
                        break; 
                    case 1:
                        fprintf(outfile,"Read-only.\n");
                        break;
                    case 2:
                        fprintf(outfile,"Executable.\n");
                        break;
                    case 3:
                        fprintf(outfile,"Executable and read-only.\n"); 
                        break;
                    case 4:
                        fprintf(outfile,"Read/write.\n"); 
                        break;
                    case 5:
                        fprintf(outfile,"Copy-on-write.\n"); 
                        break;
                    case 6:
                        fprintf(outfile,"Executable and read/write.\n");
                        break;
                    case 7:
                        fprintf(outfile,"Executable and copy-on-write.\n"); 
                        break;
                    case 8:
                        fprintf(outfile,"The page is not accessed.\n"); 
                        break;
                    case 9:
                        fprintf(outfile,"Non-cacheable and read-only.\n"); 
                        break;
                    case 10:
                        fprintf(outfile,"Non-cacheable and executable.\n"); 
                        break;
                    case 11:
                        fprintf(outfile,"Non-cacheable, executable, and read-only.\n");
                        break;
                    case 12:
                        fprintf(outfile,"Non-cacheable and read/write.\n");
                        break;
                    case 13:
                        fprintf(outfile,"Non-cacheable and copy-on-write.\n");
                        break;
                    case 14:
                        fprintf(outfile,"Non-cacheable, executable, and read/write.\n");
                        break;
                    case 15:
                        fprintf(outfile,"Non-cacheable, executable, and copy-on-write.\n");
                        break;
                    case 16:
                        fprintf(outfile,"The page is not accessed.\n");
                        break;
                    case 17:
                        fprintf(outfile,"Guard page and read-only.\n");
                        break;
                    case 18:
                        fprintf(outfile,"Guard page and executable.\n");
                        break;
                    case 19:
                        fprintf(outfile,"Guard page, executable, and read-only.\n");
                        break;
                    case 20:
                        fprintf(outfile,"Guard page and read/write.\n");
                        break;
                    case 21:
                        fprintf(outfile,"Guard page and copy-on-write.\n");
                        break;
                    case 22:
                        fprintf(outfile,"Guard page, executable, and read/write.\n");
                        break;
                    case 23:
                        fprintf(outfile,"Guard page, executable, and copy-on-write.\n");
                        break;
                    case 24:
                        fprintf(outfile,"The page is not accessed.\n");
                        break;
                    case 25:
                        fprintf(outfile,"Non-cacheable, guard page, and read-only.\n");
                        break;
                    case 26:
                        fprintf(outfile,"Non-cacheable, guard page, and executable.\n");
                        break;
                    case 27:
                        fprintf(outfile,"Non-cacheable, guard page, executable, and read-only.\n");
                        break;
                    case 28:
                        fprintf(outfile,"Non-cacheable, guard page, and read/write.\n");
                        break;
                    case 29:
                        fprintf(outfile,"Non-cacheable, guard page, and copy-on-write.\n");
                        break;
                    case 30:
                        fprintf(outfile,"Non-cacheable, guard page, executable, and read/write.\n");
                        break;
                    case 31:
                        fprintf(outfile,"Non-cacheable, guard page, executable, and copy-on-write.\n");
                    default:
                        fprintf(outfile,"unknown\n");
                        break; 
                }
                ReadTheMemory(dwProcessId,  dwDumpAddress, PAGE_SIZE);
            }
        }  
        CloseHandle ( hProcess ) ; 
        fclose(outfile);
        return 0 ; 
    }

  3. #48
    'Allo, 'Allo, Allo
    Join Date
    Apr 2008
    Posts
    639
    I think we've been going about this in the wrong way. The documented functions for querying and walking used memory only give back a subset of the total memory a process has access to. The working set doesn't necessarily contain all referenceable memory, likewise allocated memory doesn't have to come from a heap. Since there doesn't seem to be a public way to access the address and extents of just which vm regions are valid (short of VirtualQueryExing every page), the dump and inspect method isn't viable for total coverage.

    I thought of minidumping the process which can bring in all allocated memory, and querying the dump file for info but that obviously wouldn't scale very well, and there's a snag in that the dmp file doesn't seem to be (well) documented. Searching for the format lead me to this, and the idea of leveraging the same interfaces as WinDbg, which just happens to contain a method that can search for an arbitrary value in a specified virtual memory range of an attached process.

    I tested this with cmd.exe and it returned where the MZ dos header was for all loaded modules so it seems to work well for small values at least. I'm not saying this is the best method, but it seems a more complete and simpler method to use and build upon than the enumeration of pages and heap block approaches. With the immediate downside being that it won't work on Win2k as-is due to the use of DebugBreakProcess to trigger the initial debug event.

    I got the impression that the OP is simply looking for a certain value in memory and not specifically to dump it. If dumping is what's required, you can use the same framework, just dump the process with MiniDumpWriteDump and change
    pClient->AttachProcess
    to
    pClient->OpenDumpFile(dumpfile)

    Anyway, enough rambling.

    Code:
    #define _WIN32_WINNT 0x0501
    #include <windows.h>
    #include <objbase.h>
    #include <dbghelp.h>
    #include <dbgeng.h>
    #include <iostream>
    
    struct ThreadParams
    {
        HANDLE hProc;
        IDebugControl* control;
    };
    
    BOOL SetProcessPrivilege(HANDLE hProcess, LPCTSTR lpszPrivilege, BOOL bEnablePrivilege)
    {
        HANDLE hToken = NULL;
        BOOL retVal = FALSE;
    
        if(OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hToken))
        {
            LUID luid;
            if (LookupPrivilegeValue(NULL, lpszPrivilege, &luid))
            {
                TOKEN_PRIVILEGES tp;
                tp.PrivilegeCount = 1;
                tp.Privileges[0].Luid = luid;
                if (bEnablePrivilege)
                {
                    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
                }
                else
                {
                    tp.Privileges[0].Attributes = 0;
                }
                if (AdjustTokenPrivileges(
                    hToken, 
                    FALSE, 
                    &tp, 
                    sizeof(TOKEN_PRIVILEGES), 
                    (PTOKEN_PRIVILEGES) NULL, 
                    (PDWORD) NULL) && (GetLastError() != ERROR_NOT_ALL_ASSIGNED))
                {
                    retVal = TRUE;
                }
            }
            CloseHandle(hToken);
        }
        return retVal;
    }
    
    void ResumeBrokenProcess(IDebugControl* pCont)
    {
        // "g" is the windbg command for continuing from break
        pCont->Execute(DEBUG_OUTCTL_THIS_CLIENT, "g", DEBUG_EXECUTE_NOT_LOGGED);
    }
    
    // Thread function that will trigger a debug event in the attached process
    // A seperate thread is required since WaitForEvent blocks, and none
    // of the debugging functionality works until it has completed
    DWORD WINAPI CauseWaitForEventToComplete(LPVOID param)
    {
        Sleep(1000);
        ThreadParams* tp = (ThreadParams*)param;
        DebugBreakProcess(tp->hProc);
        ResumeBrokenProcess(tp->control);
        return 0;
    }
    
    int main()
    {
        SetProcessPrivilege(GetCurrentProcess(), SE_DEBUG_NAME, TRUE);
        CoInitialize(NULL);
        IDebugClient* pClient = NULL;
        if(SUCCEEDED(DebugCreate(IID_IDebugClient, reinterpret_cast<PVOID*>(&pClient))))
        {
            ULONG procId = 0;
            IDebugControl* pControl = NULL;
            // change exe name to desired process
            pClient->GetRunningProcessSystemIdByExecutableName(0, "cmd.exe", 0, &procId);
            HANDLE hProc = OpenProcess(GENERIC_WRITE, 0, procId);
            if(SUCCEEDED(DebugCreate(
                IID_IDebugControl,
                reinterpret_cast<PVOID*>(&pControl)))
                )
            {
                if(SUCCEEDED(pClient->AttachProcess(0, procId, DEBUG_ATTACH_DEFAULT)))
                {
                    ThreadParams tp = {0};
                    tp.hProc = hProc;
                    tp.control = pControl;
                    CloseHandle(CreateThread(NULL, 0, &CauseWaitForEventToComplete, &tp, 0, NULL));
                    // have to do this otherwise we won't be fully attached to the process
                    pControl->WaitForEvent(DEBUG_WAIT_DEFAULT, INFINITE);
                    IDebugDataSpaces* pDSpace = NULL;
    
                    if(SUCCEEDED(pClient->QueryInterface(
                        IID_IDebugDataSpaces, 
                        reinterpret_cast<PVOID*>(&pDSpace)))
                        )
                    {
                        // search for the "MZ" signature as an example
                        WORD imageHeader = IMAGE_DOS_SIGNATURE;
                        SYSTEM_INFO inf = {0};
                        GetSystemInfo(&inf);
                        // search the entire user address range
                        const ULONG64 maxAddress = reinterpret_cast<ULONG64>(inf.lpMaximumApplicationAddress);
                        ULONG64 startingOffset = reinterpret_cast<ULONG64>(inf.lpMinimumApplicationAddress);
                        ULONG64 bytesToSearch = maxAddress - startingOffset;
                        // suspending the process would probably be better for a "true" snapshot
                        // but that requires faffing with undocumented functions
                        DebugBreakProcess(hProc);
                        while(pDSpace->SearchVirtual(
                            startingOffset, bytesToSearch, &imageHeader,
                            sizeof(imageHeader), 2, &startingOffset)
                            == S_OK)
                        {
                            // LOBYTE and HIBYTE used just so cout outputs the MZ characters
                            std::cout << "Pattern " << LOBYTE(imageHeader) << HIBYTE(imageHeader) 
                                      << " found at 0x" << reinterpret_cast<PVOID>(startingOffset)
                                      << '\n';
                            startingOffset += 2;
                            bytesToSearch = maxAddress - startingOffset;
                        }
                        ResumeBrokenProcess(pControl);
                        pDSpace->Release();
                    }
                    pControl->Release();
                }
                pClient->DetachProcesses();
                pClient->Release();
            }
            CloseHandle(hProc);
        }
        CoUninitialize();
        return 0;
    }
    Last edited by adeyblue; 09-27-2008 at 03:09 PM.

  4. #49
    Registered User
    Join Date
    Jun 2008
    Posts
    161
    Quote Originally Posted by adeyblue View Post
    I think we've been going about this in the wrong way. The documented functions for querying and walking used memory only give back a subset of the total memory a process has access to. The working set doesn't necessarily contain all referenceable memory, likewise allocated memory doesn't have to come from a heap. Since there doesn't seem to be a public way to access the address and extents of just which vm regions are valid (short of VirtualQueryExing every page), the dump and inspect method isn't viable for total coverage.

    I thought of minidumping the process which can bring in all allocated memory, and querying the dump file for info but that obviously wouldn't scale very well, and there's a snag in that the dmp file doesn't seem to be (well) documented. Searching for the format lead me to this, and the idea of leveraging the same interfaces as WinDbg, which just happens to contain a method that can search for an arbitrary value in a specified virtual memory range of an attached process.

    I tested this with cmd.exe and it returned where the MZ dos header was for all loaded modules so it seems to work well for small values at least. I'm not saying this is the best method, but it seems a more complete and simpler method to use and build upon than the enumeration of pages and heap block approaches. With the immediate downside being that it won't work on Win2k as-is due to the use of DebugBreakProcess to trigger the initial debug event.

    I got the impression that the OP is simply looking for a certain value in memory and not specifically to dump it. If dumping is what's required, you can use the same framework, just dump the process with MiniDumpWriteDump and change
    pClient->AttachProcess
    to
    pClient->OpenDumpFile(dumpfile)

    Anyway, enough rambling.
    I keep saying it, but I'll say it again: I need a dump of the whole thing, not just a way to search a single value. Has nobody here ever used any type of cheat searching program? In most cases, I compare multiple dumps to find an unknown value controlling something (i.e. health bar). Anyway, I don't understand hardly any of what that's doing and it seems like a lot of odd stuff. I also don't entirely see how MiniDumpWriteDump would work, but it sounds promising.


    Bob - Most of that was in matsp's C++ example. I'm now sure what you were trying to show me. If I could sort that list of pages, it might be workable, but reading 4k chunks in random order without an easy way to keep track of what's where when loading a copy of the dump later doesn't help much. Reading pages just seems insane, IMO. I guess I could loop it that way if I have to. I'd just have to keep reallocating the dump array or write direct to a file as binary. Still seems like it'd take a lot longer than other search programs though.

    Edit: I posted on gamehacking.com as well. The only response I got so far was simply to "research VirtualQueryEx", and that was from the guy that wrote MHS. I guess I'll have to try to make that work. I was really hoping someone would know a better way. Whoever wrote the example here (matsp?) used QueryWorkingSet, but I don't remember seeing VirtualQueryEx. I"m not sure how it works or what I actually need from it though. It expects a hanlde and an address. I'm guessing the address comes from QueryWorkingSet? Do I use VirtualQueryEx to determine which pages are actually in use? MEM_COMMIT?
    Last edited by Viper187; 09-28-2008 at 07:12 AM.

  5. #50
    Registered User
    Join Date
    Mar 2005
    Location
    Mountaintop, Pa
    Posts
    1,058
    Edit: I posted on gamehacking.com as well. The only response I got so far was simply to "research VirtualQueryEx", and that was from the guy that wrote MHS. I guess I'll have to try to make that work. I was really hoping someone would know a better way. Whoever wrote the example here (matsp?) used QueryWorkingSet, but I don't remember seeing VirtualQueryEx. I"m not sure how it works or what I actually need from it though. It expects a hanlde and an address. I'm guessing the address comes from QueryWorkingSet? Do I use VirtualQueryEx to determine which pages are actually in use? MEM_COMMIT?

    I'm not sure QueryWorking set is the best solution after cruising a few news groups. My initial thoughts on the missing structures from psapi was that they were probably obsolete and MS had provided a more up to date approach to using QueryWorkingSet etc. But the news groups seem to think that MS is discouraging the use of QueryWorkingSet etc because of potential end user issues such as page faults etc. They prefer developers to use kernel methods such as the memory manager api. For example, in userland AFAIK there is no way to determine if a page is valid. On the kernel level you have mmIsAddressValid to validate a page. QueryWorkingSet just provides a snapshot of memory at a given point in time. So, if you put a Sleep call in the above code for let's say 10 seconds after calling QueryWorkingSet and then iterate the pages, there is a good possibility that you will generate a page fault.

    Also, it's obvious that the author of that memory scanner doesn't want competition since he doesn't want to provide any real details other than "research VirtualQueryEx". But anyway take a look at this old post. If it looks promising to you we can pursue it further.

  6. #51
    Registered User
    Join Date
    Jun 2008
    Posts
    161
    Quote Originally Posted by BobS0327 View Post
    But anyway take a look at this old post. If it looks promising to you we can pursue it further.
    Hmmmm. Looks like it's doing the same general thing, just using a different method. Instead of obtaining a list of pages, it appears to get the max address and work its way to there getting entire blocks of similar pages. This does indeed look promising. I don't entirely understand or see the need for VirtualAlloc or the extra struct info in my case though. Why the MEMORYOBJECT struct containing the MEMORY_BASIC_INFORMATION struct?

    Edit: Ok, I'm making some progress, hopefully. This seems to be dumping everything I would need from a process, but it also seems to be dumping some extra (useless) data (shared pages?). I noticed the file begins with a bunch of data about the user profile, computer name, etc. I'm wondering if I should be checking for only the memory that's not shared (MEM_PRIVATE) ? In theory, I could also knock off the region of the main module (and others?) so that I only get allocated data and not executable code, unless I want it.

    Code:
        FILE *DumpFile = fopen(DumpName, "wb");
        if (!(DumpFile)) {
            sprintf(ErrTxt, "Unable to open dump file (DumpRAM,1) -- Error &#37;u", GetLastError());
            MessageBox(NULL, ErrTxt, "Error", MB_OK);
            return 0;
        }
        fseek(DumpFile,0,SEEK_SET);
    
        MEMORY_BASIC_INFORMATION memInfo; memset(&memInfo,0,sizeof(MEMORY_BASIC_INFORMATION));
        SYSTEM_INFO sysInfo;
        GetSystemInfo(&sysInfo);
        VOID* address = 0;
        while (address < sysInfo.lpMaximumApplicationAddress)
        {
    	VirtualQueryEx(HookedProcess.hProcess, address, &memInfo, sizeof(MEMORY_BASIC_INFORMATION));
            address = memInfo.BaseAddress + memInfo.RegionSize;
            if (memInfo.State != 0x1000) { continue; }
    
            if (buffer) { free(buffer); buffer = NULL; }
            if (!(buffer = (unsigned char*)malloc((DWORD)memInfo.RegionSize+1))) {
                sprintf(ErrTxt, "Unable to allocate buffer memory (DumpRAM, 1) -- Error %u", GetLastError());
                MessageBox(NULL, ErrTxt, "Error", MB_OK);
                return 0;
            }
    
            if (ReadProcessMemory(HookedProcess.hProcess, memInfo.BaseAddress, buffer, memInfo.RegionSize, &bytesread) == 0) {
                    //fail
            }
            fwrite(buffer,1,memInfo.RegionSize,DumpFile);
        }
        free(buffer); buffer = NULL;
        fclose(DumpFile);
    Last edited by Viper187; 09-28-2008 at 01:44 PM.

  7. #52
    Registered User
    Join Date
    Mar 2005
    Location
    Mountaintop, Pa
    Posts
    1,058
    Edit: Ok, I'm making some progress, hopefully. This seems to be dumping everything I would need from a process, but it also seems to be dumping some extra (useless) data (shared pages?). I noticed the file begins with a bunch of data about the user profile, computer name, etc. I'm wondering if I should be checking for only the memory that's not shared (MEM_PRIVATE) ? In theory, I could also knock off the region of the main module (and others?) so that I only get allocated data and not executable code, unless I want it.
    You don't have to check memory whose STATE is either FREE or RESERVED.

    EDIT: I noticed a potential problem in my old code post. Try the following change:

    change this:
    Code:
    VOID *address = 0;
        while (address < sysInfo.lpMaximumApplicationAddress)
        {
    to this:


    Code:
    VOID *address = sysInfo.lpMinimumApplicationAddress
       while (address < sysInfo.lpMaximumApplicationAddress)
    Second EDIT:

    If the type of memory is MEM_PRIVATE and the state of memory is MEM_COMMIT, then this memory is worth checking out.
    Last edited by BobS0327; 09-29-2008 at 05:33 AM.

  8. #53
    Registered User
    Join Date
    Jun 2008
    Posts
    161
    Good stuff, but I still have 2 problems:

    1. It's not actually dumping memory. 299 error every time.
    2. Attempting to dump a process seems to instantly double its memory usage for some reason.

    Code:
        FILE *DumpFile = fopen(DumpName, "wb");
        if (!(DumpFile)) {
            sprintf(ErrTxt, "Unable to open dump file (DumpRAM,1) -- Error &#37;u", GetLastError());
            MessageBox(NULL, ErrTxt, "Error", MB_OK);
            return 0;
        }
        fseek(DumpFile,0,SEEK_SET);
    
        MEMORY_BASIC_INFORMATION memInfo; memset(&memInfo,0,sizeof(MEMORY_BASIC_INFORMATION));
        SYSTEM_INFO sysInfo;
        GetSystemInfo(&sysInfo);
        VOID* address = sysInfo.lpMinimumApplicationAddress;
        while (address < sysInfo.lpMaximumApplicationAddress)
    	{
    		VirtualQueryEx(HookedProcess.hProcess, address, &memInfo, sizeof(MEMORY_BASIC_INFORMATION));
            address = memInfo.BaseAddress + memInfo.RegionSize;
    		if (memInfo.State != MEM_COMMIT) { continue; }
    		if (memInfo.Type != MEM_PRIVATE) { continue; }
    
            if (buffer) { free(buffer); buffer = NULL; }
            if (!(buffer = (unsigned char*)malloc((DWORD)memInfo.RegionSize+1))) {
                sprintf(ErrTxt, "Unable to allocate buffer memory (DumpRAM, 1) -- Error %u", GetLastError());
                MessageBox(NULL, ErrTxt, "Error", MB_OK);
                return 0;
            }
    
            if (ReadProcessMemory(HookedProcess.hProcess, memInfo.BaseAddress, buffer, memInfo.RegionSize, &bytesread) == 0) {
                    //fail
                sprintf(ErrTxt, "Unable to dump RAM from process (DumpRAM, 1) -- Error %u", GetLastError());
                MessageBox(NULL, ErrTxt, "Error", MB_OK);
                free(buffer); buffer = NULL;
                fclose(DumpFile);
                return 0;
            }
            fwrite(buffer,1,memInfo.RegionSize,DumpFile);
            filesize += memInfo.RegionSize;
    	}
    	free(buffer); buffer = NULL;
        fclose(DumpFile);
    Last edited by Viper187; 09-29-2008 at 12:15 PM.

  9. #54
    Registered User
    Join Date
    Mar 2005
    Location
    Mountaintop, Pa
    Posts
    1,058
    Edit: I posted on gamehacking.com as well. The only response I got so far was simply to "research VirtualQueryEx", and that was from the guy that wrote MHS.
    Well, if your goal is to write an app equivalent to MHS, then you're going off on a tangent. I played with MHS for a while and realized how he's creating the hex editor. Listed below is the equivalent code to his hex editor. I can take this a few steps further and write the open source equivalent of MHS as a proof of concept. Reference this post to do a hex search.

    Code:
    #pragma comment(lib, "advapi32.lib")
    #pragma comment(lib, "psapi.lib")
    
    #include <windows.h>
    #include <stdio.h>
    #include <psapi.h>
    
    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 DumpProcessMemory(int iProcessId)
    {
        char szOut[128] = {0};
        DWORD dwAddress;
        int iIndex1 = 0;
        int iIndex2 = 0;
        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);
        for( iIndex = 0; iIndex < ModuleInfo.SizeOfImage; iIndex += 16)
        {
            printf("%08x ",   (DWORD)ModuleInfo.lpBaseOfDll + iIndex);
            iIndex2 = iIndex;
            for(iIndex1 =0; iIndex1 < 16; iIndex1++)
                printf("%02x ", bBuffer[iIndex2+iIndex1]);
            printf("   ");
            for(iIndex1 =0; iIndex1 < 16; iIndex1++)
            {
                if (32 <= bBuffer[iIndex2+iIndex1] && bBuffer[iIndex2+iIndex1] < 127)
                    printf("%c ", bBuffer[iIndex2+iIndex1]);
                else
                    printf(". ");
            } 
            printf("\n");
            dwAddress = (DWORD)ModuleInfo.lpBaseOfDll + iIndex;
        }
        return FALSE;
    }
    
    int main(int argc, char **argv)
    {
        if ( !EnableTokenPrivilege (SE_DEBUG_NAME) )
        {
            printf ( "Cannot get required privilege %lu\n", GetLastError () );
            return 0;
        }
        DumpProcessMemory(atoi(argv[1]));
    
        return 0;
    }

  10. #55
    Registered User
    Join Date
    Jun 2008
    Posts
    161
    I thought the modules were only the executable and that method didn't retrieve all the random memory allocated by the game/whatever. We tried that exact method earlier in this thread, didn't we? How bout telling me why our previous idea isn't working the way I wrote it.

    Edit: The guy on gamehacking.com shared a snippet. Looks like the same thing I was attempting except it checks a different element of the MBI.

    Code:
     HANDLE hProcess = GetCurrentProcess(); // or however you get your process handle.
    
        DWORD Address = 0x10000;  // The first user-land address.
    
        MEMORY_BASIC_INFORMATION mbi;
    
        VirtualQueryEx(hProcess, (PVOID)Address, &mbi, sizeof(mbi));
    
        do
    
        {
    
            if (mbi.State != MEM_FREE)
    
            {
    
                DWORD page_attr = PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY;
    
                if (mbi.Protect & page_attr)
    
                {
    
                    // Do something here.  Export to a file.
    
                    printf("ADDRESS -> 0x&#37;.8X\n\r", mbi.BaseAddress);
    
                };
    
            };
    
            Address += mbi.RegionSize;
    
        } while(VirtualQueryEx(hProcess, (PVOID)Address, &mbi, sizeof(mbi)));

    -----------------------------

    Edit 2: I can't figure out what I'm doing wrong here.
    Edit 3: Ok, I might be getting somewhere. Is it possible to bypass PAGE_GUARD protection on a page? I still have that token privilege thing being called, but it doesn't appear to solve this. Also, I'd still swear the process grows in size greatly the instant I dump with this.

    p.s. Why the hell is there a page in the process with windows user profile and environment variables. I don't think that block will be very useful for my purposes. lol

    Code:
        FILE *DumpFile = fopen(DumpName, "wb");
        if (!(DumpFile)) {
            sprintf(ErrTxt, "Unable to open dump file (DumpRAM,1) -- Error %u", GetLastError());
            MessageBox(NULL, ErrTxt, "Error", MB_OK);
            return 0;
        }
        fseek(DumpFile,0,SEEK_SET);
    
        MEMORY_BASIC_INFORMATION memInfo; memset(&memInfo,0,sizeof(MEMORY_BASIC_INFORMATION));
        SYSTEM_INFO sysInfo;
        GetSystemInfo(&sysInfo);
        VOID* address = sysInfo.lpMinimumApplicationAddress;
        while (address < sysInfo.lpMaximumApplicationAddress)
    	{
    		VirtualQueryEx(HookedProcess.hProcess, address, &memInfo, sizeof(MEMORY_BASIC_INFORMATION));
            address = memInfo.BaseAddress + memInfo.RegionSize;
    //		if (memInfo.State != MEM_COMMIT) { continue; }
    //		if (memInfo.Type != MEM_PRIVATE) { continue; }
    		if (memInfo.State == MEM_FREE) { continue; }
    		if ((memInfo.Protect & (PAGE_READWRITE|PAGE_WRITECOPY|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY)) == 0) { continue; }
    		if (memInfo.Protect & (PAGE_NOACCESS|PAGE_READONLY|PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_GUARD)) { continue; }
    
            if (buffer) { free(buffer); buffer = NULL; }
            if (!(buffer = (unsigned char*)malloc((DWORD)memInfo.RegionSize+1))) {
                sprintf(ErrTxt, "Unable to allocate buffer memory (DumpRAM, 1) -- Error %u", GetLastError());
                MessageBox(NULL, ErrTxt, "Error", MB_OK);
                return 0;
            }
    
            if (ReadProcessMemory(HookedProcess.hProcess, memInfo.BaseAddress, buffer, memInfo.RegionSize, &bytesread) == 0) {
                    //fail
                sprintf(ErrTxt, "Unable to dump RAM from process (DumpRAM, 2) -- Error %u\nAddress: %08X\nRegion Size: %X\nProtection: %X\n State: %X, Type: %X", GetLastError(), memInfo.BaseAddress, memInfo.RegionSize, memInfo.Protect, memInfo.State, memInfo.Type);
                MessageBox(NULL, ErrTxt, "Error", MB_OK);
                free(buffer); buffer = NULL;
                fclose(DumpFile);
                return 0;
            }
            fwrite(buffer,1,memInfo.RegionSize,DumpFile);
            filesize += memInfo.RegionSize;
    	}
    	free(buffer); buffer = NULL;
        fclose(DumpFile);
    Code:
    int InitHook()
    {
        if ( !EnableTokenPrivilege (SE_DEBUG_NAME) )
        {
            MessageBox(NULL, "Cannot get required privilege", "Error", MB_OK);
            return 1;
        }
        HookedProcess.hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, HookedProcess.dwProcessId);
        char txtTitle[100];
        strcpy(txtTitle,PROGRAM_NAME); strcat(txtTitle," - ");
        strcat(txtTitle,Settings.Hook.FileName);
        SetWindowText(hwndMain,txtTitle);
        return 0;
    }
    
    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;
    }
    Last edited by Viper187; 09-30-2008 at 02:44 PM.

  11. #56
    Registered User
    Join Date
    Mar 2005
    Location
    Mountaintop, Pa
    Posts
    1,058
    Is it possible to bypass PAGE_GUARD protection on a page?
    An excerpt from MSDN Guard Page

    If a program attempts to access an address within a guard page, the system raises a STATUS_GUARD_PAGE_VIOLATION (0x80000001) exception. The system also clears the PAGE_GUARD modifier, removing the memory page's guard page status. The system will not stop the next attempt to access the memory page with a STATUS_GUARD_PAGE_VIOLATION exception.
    So, on the second access you should be GTG.

  12. #57
    Registered User
    Join Date
    Mar 2005
    Location
    Mountaintop, Pa
    Posts
    1,058
    p.s. Why the hell is there a page in the process with windows user profile and environment variables. I don't think that block will be very useful for my purposes. lol
    You're probably picking this up from the virtual address range of 0x00000000 thru 0x0000FFFF. This area is just set aside in order to catch NULL pointer assignments. Also, the guard page which is considered to be inaccessible to the user is the range from 0x7FFF0000 thru 0x7FFFFFFF. This area is used by the OS to check on out of bounds pointer references. So the available user address space is 0x00010000 thru 0x7FFEFFFF. IMHO, this address range is what should be queried.

  13. #58
    Registered User
    Join Date
    Jun 2008
    Posts
    161
    Actually, the garbage info I'm getting appears at 0x10000. I checked with Tsearch.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Segmentation fault problem
    By odedbobi in forum Linux Programming
    Replies: 1
    Last Post: 11-19-2008, 03:36 AM
  2. Segmentation fault
    By bennyandthejets in forum C++ Programming
    Replies: 7
    Last Post: 09-07-2005, 05:04 PM
  3. Segmentation fault
    By NoUse in forum C Programming
    Replies: 4
    Last Post: 03-26-2005, 03:29 PM
  4. Locating A Segmentation Fault
    By Stack Overflow in forum C Programming
    Replies: 12
    Last Post: 12-14-2004, 01:33 PM
  5. Segmentation fault...
    By alvifarooq in forum C++ Programming
    Replies: 14
    Last Post: 09-26-2004, 12:53 PM