How to get process's base address with MODULEENTRY32?

1

I'm looking to do something in this example: Python - How to get the start/base address of a process?. I'm having the same issue as the person in that topic, in that the pointers cheat engine provides is in reference to the base address of the process itself.

I've looked around and it looks like the best solution is to use ctypes and the MODULEENTRY32 to store snapshots of processes and analyze their modBaseAddr.

Here is my current code

import os.path, ctypes, ctypes.wintypes
from ctypes import *
from ctypes.wintypes import *

PROCESS_QUERY_INFORMATION = (0x0400)
PROCESS_VM_OPERATION = (0x0008)
PROCESS_VM_READ = (0x0010)
PROCESS_VM_WRITE = (0x0020)
TH32CS_SNAPMODULE = (0x00000008)

CreateToolhelp32Snapshot= ctypes.windll.kernel32.CreateToolhelp32Snapshot
Process32First = ctypes.windll.kernel32.Process32First
Process32Next = ctypes.windll.kernel32.Process32Next
Module32First = ctypes.windll.kernel32.Module32First
Module32Next = ctypes.windll.kernel32.Module32Next
GetLastError = ctypes.windll.kernel32.GetLastError
OpenProcess = ctypes.windll.kernel32.OpenProcess
GetPriorityClass = ctypes.windll.kernel32.GetPriorityClass
CloseHandle = ctypes.windll.kernel32.CloseHandle

class MODULEENTRY32(Structure):
       _fields_ = [ ( 'dwSize' , DWORD ) , 
                ( 'th32ModuleID' , DWORD ),
                ( 'th32ProcessID' , DWORD ),
                ( 'GlblcntUsage' , DWORD ),
                ( 'ProccntUsage' , DWORD ) ,
                ( 'modBaseAddr' , POINTER(BYTE)) ,
                ( 'modBaseSize' , DWORD ) , 
                ( 'hModule' , HMODULE ) ,
                ( 'szModule' , c_char * 256 ),
                ( 'szExePath' , c_char * 260 ) ]



def GetBaseAddr(ProcId, ProcName):
       me32 = MODULEENTRY32()
       me32.dwSize = sizeof(me32)
       hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, ProcId)
       if GetLastError() != 0:
              CloseHandle(hSnapshot)
              print 'Handle Error %s' % WinError()
              return 'Error'

       else:
              if Module32First(hSnapshot, byref(me32)):
                     if me32.szModule == ProcName:
                            CloseHandle(hSnapshot)
                            return id(me32.modBaseAddr)

                     else:
                            Module32Next(hSnapshot, byref(me32))
                            while int(GetLastError())!= 18:
                                   if me32.szModule == ProcName:
                                          CloseHandle(hSnapshot)
                                          return id(me32.modBaseAddr)

                                   else:
                                          Module32Next(hSnapshot, byref(me32))

                            CloseHandle(hSnapshot)
                            print 'Couldn\'t find Process with name %s' % ProcName

              else:
                     print 'Module32First is False %s' % WinError()
                     CloseHandle(hSnapshot)

def GetProcessIdByName( pName):
       if pName.endswith('.exe'):
              pass
       else:
              pName = pName+'.exe'

       ProcessIds, BytesReturned = EnumProcesses()

       for index in range(BytesReturned / ctypes.sizeof(ctypes.wintypes.DWORD)):
              ProcessId = ProcessIds[index]
              hProcess = ctypes.windll.kernel32.OpenProcess(PROCESS_QUERY_INFORMATION, False, ProcessId)
              if hProcess:
                     ImageFileName = (ctypes.c_char*MAX_PATH)()
                     if ctypes.windll.psapi.GetProcessImageFileNameA(hProcess, ImageFileName, MAX_PATH)>0:
                            filename = os.path.basename(ImageFileName.value)
                            if filename == pName:
                                   return ProcessId
                     CloseHandle(hProcess)

def EnumProcesses():
       count = 32
       while True:
              ProcessIds = (ctypes.wintypes.DWORD*count)()
              cb = ctypes.sizeof(ProcessIds)
              BytesReturned = ctypes.wintypes.DWORD()
              if ctypes.windll.Psapi.EnumProcesses(ctypes.byref(ProcessIds), cb, ctypes.byref(BytesReturned)):
                     if BytesReturned.value<cb:
                            return ProcessIds, BytesReturned.value
                            break
                     else:
                            count *= 2
              else:
                     return None



if __name__ == '__main__':
       ProcId = GetProcessIdByName('RocketLeague.exe')
       #print ProcId
       print hex(GetBaseAddr(ProcId, 'RocketLeague.exe'))
       #print hex(GetBaseAddr(8252,'RocketLeague.exe'))

Now my understanding of memory isn't the greatest, but I'd figure that the base address should be static while a program is running. When I do get this code to run, the ModBaseAddr I get back changes every time I run it. Another weird Issue I'm having is that without that print ProcId statement, running the program returns an ERROR_ACCESS_DENIED (error 5) from line 41 (This has something to do with the CreateToolhelp32Snapshot function I assume as I have admin rights on the computer). With the print statement, however, the program runs through giving me a different ModBaseAddr every time. If I feed the GetBaseAddr function the ProcessId manually it also works without the print statement, again however, it's giving me a random address every time. If anyone could provide me any help or point me in the right direction I'd really appreciate it!

python
memory-address
base
cheat-engine
asked on Stack Overflow Oct 13, 2016 by Garrett Jones • edited Oct 14, 2016 by martineau

1 Answer

0

Clarification: MODULEENTRY32 stores information about modules, not processes. when you call CreateToolhelp32Snapshot using TH32CS_SNAPMODULE you are getting modules loaded by the process, not processes themselves.

Instead of getting the MODULEENTRY32 in combination with EnumProcesses you can instead use CreateToolHelp32Snapshot with TH32CS_SNAPPROCESS to get a list of processes in the form of PROCESSENRTY32 structs, which also contains the process identifier.

Despite being a user with administrator privileges, you must also run the process as an administrator.

You should also ensure you're initializing your MODULEENTRY32 to {0} for proper error handling and not running into an issue of the returned value being subject to undefined behavior of uninitialized memory.

I do not know the specific cause of your issue but I have used a source code for this purpose that is very robust that may be a plug and play alternative to what you're currently using, the important snippet will follow, but the full source is available here.

def ListProcessModules( ProcessID ):
    hModuleSnap = c_void_p(0)
    me32 = MODULEENTRY32()
    me32.dwSize = sizeof( MODULEENTRY32 )
    hModuleSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, ProcessID )

    ret = Module32First( hModuleSnap, pointer(me32) )
    if ret == 0 :
        print 'ListProcessModules() Error on Module32First[%d]' % GetLastError()
        CloseHandle( hModuleSnap )
        return False 

    while ret :
        print "   MODULE NAME:     %s"%             me32.szModule 
        print "   executable     = %s"%             me32.szExePath 
        print "   process ID     = 0x%08X"%         me32.th32ProcessID 
        print "   ref count (g)  =     0x%04X"%     me32.GlblcntUsage 
        print "   ref count (p)  =     0x%04X"%     me32.ProccntUsage 
        print "   base address   = 0x%08X"%         me32.modBaseAddr 
        print "   base size      = %d"%             me32.modBaseSize 

        ret = Module32Next( hModuleSnap , pointer(me32) )

    CloseHandle( hModuleSnap )
    return True
answered on Stack Overflow Jul 23, 2019 by GuidedHacking

User contributions licensed under CC BY-SA 3.0