Search code examples
ctypesvirtual-memoryreadprocessmemory

ReadProcessMemory() dont read pages with specific AllocationProtect values


I'm building a Memory Scanner and with some error handling I noticed that ReadProcessMemory() is reading 90% of process' pages, but the ones that have mbi.Protect value == 1 or 260 it fails and returns ERROR 299 (Partial Copy) and the output of BytesRead is 0. I run it as admin, set debug privileges and open process with VM_READ, but these exactly pages with mbi.Protect == 260 and 1 are unreadable. So, it's normal that it cant read all pages or am I doing something wrong ? Here is the code: (to be reproducable it also need this part of code that I import to main code and its where I setup all the ctypes background: https://pastebin.com/hMxLej5k, then you open python, import the code below and write "main(pid)" where pid is the pid of the process you want to read).

from ctypes import *
from ctypes import wintypes
import win32security
from setup_apis import *


def setDebugPriv():    

   token_handle = wintypes.HANDLE()

   if not OpenProcessToken(        

         GetCurrentProcess(),                     
         TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,   
         byref(token_handle),                     
   ):

      print("Error:",kernel32.GetLastError())
      return False    


   luidvalue = win32security.LookupPrivilegeValue ( None, win32security.SE_DEBUG_NAME )

 
   if not win32security.LookupPrivilegeValue(     

         None,                             
         win32security.SE_DEBUG_NAME     , 
        
         ):

      print("Error",kernel32.GetLastError())
      return False

   se_debug_name_value = LUID(luidvalue)   # Valor local do Privilégio de Debug 
   LAA                 = LUID_AND_ATTRIBUTES (

           se_debug_name_value,     
           SE_PRIVILEGE_ENABLED     

           )
 
   tkp = TOKEN_PRIVILEGES ( 

           1,       # DWORD PrivilegeCount    
           LAA,     # LUID_AND_ATTRIBUTES      

           )


   if not AdjustTokenPrivileges(

       token_handle,          
       False,                 
       byref(tkp),            
       sizeof(tkp),          
       None,                 
       None,                  

       ):

       print("Error:",GetLastError)
       CloseHandle(token_handle)       
       return False

   return True        

#################################

def main(pid=None):        

    setDebugPriv()           


    process = OpenProcess (
PROCESS_VM_READ|PROCESS_QUERY_INFORMATION, 
            False,                         
            pid,                          
          )

    
    system_info = SYSTEM_INFO()
    GetSystemInfo ( byref(system_info) )
    MaxAppAdress = system_info.lpMaximumApplicationAdress
    
    VirtualQueryEx = VirtualQueryEx64
    mbi            = MEMORY_BASIC_INFORMATION64()

    memset (                  
            byref(mbi),       
            0,                
            sizeof(mbi),      
            )
    Adress      = 0
    BytesRead   = c_size_t (0)
 
   
    while MaxAppAdress > Adress:


        VirtualQueryEx(         
                process,            
                Adress,             
                byref(mbi),         
                sizeof(mbi),        
                )
        
        if mbi.State == MEM_COMMIT:  
                
                
                 try:
                     ContentsBuffer = create_string_buffer(mbi.RegionSize)
                     
                 except:
                     pass


                 if not ReadProcessMemory (

                        process,                    
                        Adress,                     
                        ContentsBuffer,             
                        mbi.RegionSize,             
                        byref(BytesRead),           

                        ):
                

                    print("Cant Read, Error: %i, Protect State: %i" %(kernel32.GetLastError(), mbi.Protect) )
                    print("BytesRead:", BytesRead)
                    
                    Adress += mbi.RegionSize
                    continue

        Adress += mbi.RegionSize

'''


Solution

  • See Memory Protection Constants (260 = 0x104). No access and page guard regions cause exceptions. You can't access a no_access and you don't want to fire page_guard exceptions as they are meant to warn a process that a stack needs to grow and commit more pages. Don't attempt to read them.

    Constant Value Description
    PAGE_NOACCESS 0x01 Disables all access to the committed region of pages. An attempt to read from, write to, or execute the committed region results in an access violation.
    This flag is not supported by the CreateFileMapping function.
    PAGE_READWRITE 0x04 Enables read-only or read/write access to the committed region of pages. If Data Execution Prevention is enabled, attempting to execute code in the committed region results in an access violation.
    PAGE_GUARD 0x100 Pages in the region become guard pages. Any attempt to access a guard page causes the system to raise a STATUS_GUARD_PAGE_VIOLATION exception and turn off the guard page status. Guard pages thus act as a one-time access alarm. For more information, see Creating Guard Pages.
    When an access attempt leads the system to turn off guard page status, the underlying page protection takes over.
    If a guard page exception occurs during a system service, the service typically returns a failure status indicator.
    This value cannot be used with PAGE_NOACCESS.This flag is not supported by the CreateFileMapping function.