Python Forum

Full Version: Windows Python Memory Scanner
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Hi all, I trying to write a Python Windows Memory Scanner, that finds the addresses of a specific value. The complete code is at the bottom, and I'll explain how it works.

First, System info is called, and the values of

sysinfo.lpMinimumApplicationAddress
sysinfo.lpMaximumApplicationAddress

are obtained, so I can feed it to my loop which has VirtualQueryEx()
Kernel32.OpenProcess is used to be fed to ReadMemoryProcess.

Then finally, A loop is used to get VirtualQueryEx() to return base address to scan and it's
mbi.RegionSize.

And then:

I have the following code, and somehow I must have fed the read process
Memory incorrectly. what the code does is to check a region of memory to see
whether or not it can be scanned.

mbi.Protect == PAGE_READWRITE and mbi.State == MEM_COMMIT

If this is true,then it proceeds to scan the memory fro current_address to
current_address + mbi.RegionSize.

However, a strange thing happens: The loop runs twice successfully, and then it
pops:

raise ctypes.WinError(ctypes.get_last_error())
OSError: [WinError 299] Only part of a ReadProcessMemory or WriteProcessMemory request was completed.

Now, I know the problem is not with VirtualQueryEx, because if I comment out
the red part and just run VirtualQueryEx, it would actually skim through all regions
without a single error.

Somehow, if I use this:

index = current_address
end = current_address + mbi.RegionSize - 7

Where the end is less by 7, the loop would not pop any error and it would finish
the loop

What did I do wrong?

thanks!















This is the full code.

import ctypes
from ctypes.wintypes import WORD, DWORD, LPVOID

PVOID = LPVOID
SIZE_T = ctypes.c_size_t

# https://msdn.microsoft.com/en-us/library/aa383751#DWORD_PTR
if ctypes.sizeof(ctypes.c_void_p) == ctypes.sizeof(ctypes.c_ulonglong):
    DWORD_PTR = ctypes.c_ulonglong
elif ctypes.sizeof(ctypes.c_void_p) == ctypes.sizeof(ctypes.c_ulong):
    DWORD_PTR = ctypes.c_ulong

class SYSTEM_INFO(ctypes.Structure):
    """https://msdn.microsoft.com/en-us/library/ms724958"""
    class _U(ctypes.Union):
        class _S(ctypes.Structure):
            _fields_ = (('wProcessorArchitecture', WORD),
                        ('wReserved', WORD))
        _fields_ = (('dwOemId', DWORD), # obsolete
                    ('_s', _S))
        _anonymous_ = ('_s',)
    _fields_ = (('_u', _U),
                ('dwPageSize', DWORD),
                ('lpMinimumApplicationAddress', LPVOID),
                ('lpMaximumApplicationAddress', LPVOID),
                ('dwActiveProcessorMask',   DWORD_PTR),
                ('dwNumberOfProcessors',    DWORD),
                ('dwProcessorType',         DWORD),
                ('dwAllocationGranularity', DWORD),
                ('wProcessorLevel',    WORD),
                ('wProcessorRevision', WORD))
    _anonymous_ = ('_u',)

LPSYSTEM_INFO = ctypes.POINTER(SYSTEM_INFO)



Kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
Kernel32.GetSystemInfo.restype = None
Kernel32.GetSystemInfo.argtypes = (LPSYSTEM_INFO,)

sysinfo = SYSTEM_INFO()
Kernel32.GetSystemInfo(ctypes.byref(sysinfo))

print(sysinfo.lpMinimumApplicationAddress)
print(sysinfo.lpMaximumApplicationAddress)


# maybe it will change, maybe it won't. Assuming it won't.

# 2nd, get Open process.
import psutil
import sys

PID = 1234
        
PROCESS_QUERY_INFORMATION = 0x0400
PROCESS_VM_READ = 0x0010

Process = Kernel32.OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ, False, PID)
print('process:', Process)



# 3rd

class MEMORY_BASIC_INFORMATION(ctypes.Structure):
    """https://msdn.microsoft.com/en-us/library/aa366775"""
    _fields_ = (('BaseAddress', PVOID),
                ('AllocationBase',    PVOID),
                ('AllocationProtect', DWORD),
                ('RegionSize', SIZE_T),
                ('State',   DWORD),
                ('Protect', DWORD),
                ('Type',    DWORD))

##PMEMORY_BASIC_INFORMATION = ctypes.POINTER(MEMORY_BASIC_INFORMATION)

mbi = MEMORY_BASIC_INFORMATION()
##sysinfo.lpMinimumApplicationAddress

print('VirtualQueryEx ran properly?',Kernel32.VirtualQueryEx(Process, \
    sysinfo.lpMinimumApplicationAddress, ctypes.byref(mbi),ctypes.sizeof(mbi)))


ReadProcessMemory = Kernel32.ReadProcessMemory

##
MEM_COMMIT = 0x00001000;
PAGE_READWRITE = 0x04;

##buffer = ctypes.c_uint()
buffer = ctypes.c_double()

nread = SIZE_T()

start = ctypes.c_void_p(mbi.BaseAddress)

current_address = sysinfo.lpMinimumApplicationAddress
end_address = sysinfo.lpMaximumApplicationAddress

while current_address < end_address:
    Kernel32.VirtualQueryEx(Process, \
    current_address, ctypes.byref(mbi),ctypes.sizeof(mbi))

    if mbi.Protect == PAGE_READWRITE and mbi.State == MEM_COMMIT :
        print('This region can be scanned!')
        index = current_address
        end = current_address + mbi.RegionSize

        while index < end:
            if ReadProcessMemory(Process, index, ctypes.byref(buffer), \
                                 ctypes.sizeof(buffer), ctypes.byref(nread)):
                ## value comparison to be implemented.
                pass   
            else:
                print('else happend.')
##                pass
##                raise ctypes.WinError(ctypes.get_last_error())

            index += 1
            
    current_address += mbi.RegionSize
(Oct-08-2017, 10:39 PM)Awesometech Wrote: [ -> ]Hi all, I trying to write a Python Windows Memory Scanner, that finds the addresses....
You read ctypes.sizeof(buffer) bytes, but your increment and check (index < end, inndex += 1) about 1 byte.
So fix is simple:
index += ctypes.sizeof(buffer)

p.s. thx for code :)