2013-03-12 2 views
1

Okay все вы ctypes гуру там ...Python ReadProcessMemory не читает достаточно, байты

У меня есть скрипт, питона, который считывает адреса памяти в сотни раз в секунду. Значение, сохраненное на этом адресе памяти, представляет собой целое число без знака. Это значение увеличивается с течением времени. К сожалению, когда он проходит 2^8, 2^16 или 2^24, вызов ReadProcessMemory не считывает достаточное количество байтов, чтобы получить большее целое число. На самом деле, похоже, что после первого чтения не было прочитано правильное количество байтов.

Вот фрагмент кода:

from time import sleep 
from ctypes import * 
from struct import * 
pid = 0x0D50 
op = windll.kernel32.OpenProcess 
rpm = windll.kernel32.ReadProcessMemory 
ch = windll.kernel32.CloseHandle 
PAA = 0x1F0FFF 
addy = 0x38D53368 
ph = op(PAA,False,int(pid)) #program handle 
lastvalue = 0 
while True: 
    datadummy = b'.'*4 
    buff = c_char_p(datadummy) 
    bufferSize = (len(buff.value)) 
    bytesRead = c_ulong(0) 

    if rpm(ph,addy,buff,bufferSize,byref(bytesRead)): 
     value = unpack('I',datadummy)[0] 
     if lastvalue != value: 
      print value 
      print bytesRead 
      lastvalue = value 
    sleep(.01) 

И выход может быть что-то вроде:

191 
c_ulong(4L) ////Here it got 4 bytes like I expected 
211 
c_ulong(1L) ////But here it only got 1 byte.?? It should be reading 4 bytes everytime 
231 
c_ulong(1L) 
251 
c_ulong(1L) 
15   ////This value is incorrect, because it only reads 1 byte. (should be 271) 
c_ulong(1L) 

Что мне кажется, что это только чтение количество байт, что предыдущий звонок необходимо читать ...

Как это исправить?

ответ

3

Выражение b'.'*4 создал постоянную '....' в объекте кода для вашего модуля. Эта константа является объектом, подобным любому другому в Python, за исключением того, что предположил, что является неизменным. Вы нарушили это предположение, используя ctypes. Например:

>>> from ctypes import * 
>>> def f(): 
... s = b'.'*4 
... buf = c_char_p(s) 
... memset(buf, 0, 1) 
... 
>>> f.__code__.co_consts 
(None, '.', 4, 0, 1, '....') 
>>> c_char_p(f.__code__.co_consts[5]).value 
'....' 

>>> f() # set the first character to "\x00" 
>>> f.__code__.co_consts 
(None, '.', 4, 0, 1, '\x00...') 
>>> c_char_p(f.__code__.co_consts[5]).value 
'' 

value дескриптором c_char_p ожидает, что буфер, чтобы быть нулевым байтом строки. Когда первый байт мутирован до 0, value возвращает пустую строку. Теперь посмотрим на значение 191 упакованного как прямой порядок байтов, unsigned long:

>>> import struct 
>>> struct.pack("<L", 191) 
'\xbf\x00\x00\x00' 

Это должно объяснить, почему bufferSize стал 1 на 2-ом проходе.

Если строка была интернирована, вы можете либо свернуть интерпретатор, либо сделать его непригодным для использования. Большинство строк, используемых в CPython API, интернированы, а также имена модулей, классов, функций, атрибутов и переменных. Например:

>>> from ctypes import * 
>>> import numbers 
>>> s = 'numbers' 
>>> b = c_char_p(s) 
>>> r = memset(b, 100, 1) 
>>> s 
'dumbers' 

>>> numbers.Number 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
NameError: name 'numbers' is not defined 

>>> globals()[s].Number 
<class 'numbers.Number'> 

create_string_buffer является удобная функция для создания и установить char массив в один проход:

>>> b1 = create_string_buffer('test1') 
>>> type(b1) 
<class 'ctypes.c_char_Array_6'> 
>>> b1.value 
'test1' 

>>> b2 = (c_char * 6)() 
>>> b2.value = 'test2' 
>>> type(b2) 
<class 'ctypes.c_char_Array_6'> 
>>> b2.value 
'test2' 
>>> b2.raw 
'test2\x00' 

Вы также прошли ссылку на unsigned long вместо:

value = c_ulong() 
bytesRead = c_ulong() 
rpm(ph, addy, byref(value), sizeof(value), byref(bytesRead)) 

Другой пример, нарушающий предположение о неизменности. Целые числа менее 256 кэшируются, то есть код, который их использует, всегда относится к одному и тому же набору объектов.Поэтому, если вы меняете один из них, это влияет на всю систему:

>>> offset = sizeof(c_size_t)*2 
>>> addr = id(200) + offset 
>>> n = c_int.from_address(addr) 
>>> n 
c_long(200) 
>>> n.value = 2000000 
>>> 
>>> 200 + 1 
2000001 
+0

Очень хороший ответ! Спасибо за подробное объяснение того, что произошло. –

0

Хорошо, хорошо, я понял. Мне нужно было использовать ctypes.create_string_buffer(init_or_size[, size]) вместо c_char_p, который я пробовал.

Рабочий код:

from time import sleep 
from ctypes import * 
from struct import * 
pid = 0x0D50 
op = windll.kernel32.OpenProcess 
rpm = windll.kernel32.ReadProcessMemory 
ch = windll.kernel32.CloseHandle 
PAA = 0x1F0FFF 
addy = 0x543A88F0 
ph = op(PAA,False,int(pid)) #program handle 
lastvalue = 0 
while True: 

    buff = create_string_buffer(4) 
    bufferSize = (sizeof(buff)) 
    bytesRead = c_ulong(0) 

    if rpm(ph,addy,buff,bufferSize,byref(bytesRead)): 
     value = unpack('I',buff)[0] 
     if lastvalue != value: 
      print value 
      print bytesRead 
      lastvalue = value 
    sleep(.01) 
Смежные вопросы