native_function.CustomAllocator now has the ability to be closed, freeing its pages.

This commit is contained in:
clement rouault
2021-06-15 15:35:46 +02:00
parent c68dc4e29a
commit 1d128f43ab
+19 -49
View File
@@ -5,59 +5,12 @@ import sys
import windows
import windows.winproxy
import windows.generated_def as gdef
from . import simple_x86 as x86
from . import simple_x64 as x64
class PyObj(ctypes.Structure):
_fields_ = [("ob_refcnt", ctypes.c_size_t),
("ob_type", ctypes.c_void_p)] # must be cast
class PyMmap(PyObj):
_fields_ = [("ob_addr", ctypes.c_size_t), ("ob_size", ctypes.c_size_t)]
# Specific mmap class for code injection
class MyMap(mmap.mmap):
""" A mmap that is never unmapped and that contains the page address """
def __init__(self, *args, **kwarg):
# Get the page address by 'introspection' of the C struct
m = PyMmap.from_address(id(self))
self.addr = m.ob_addr
# Prevent garbage collection (so unmaping) of the page
m.ob_refcnt += 1
@classmethod
def get_map(cls, size):
""" Dispatch to the good mmap implem depending on the current system """
systems = {'windows': Win32MyMap,
'linux': UnixMyMap}
x = platform.system().lower()
if x not in systems:
raise ValueError("Unknow system {0}".format(x))
return systems[x].get_map(size)
class Win32MyMap(MyMap):
@classmethod
def get_map(cls, size):
addr = windows.winproxy.VirtualAlloc(0, size, 0x1000, 0x40)
new_map = (ctypes.c_char * size).from_address(addr)
new_map.addr = addr
if new_map.addr == 0:
raise ctypes.WinError()
return new_map
class UnixMyMap(MyMap):
@classmethod
def get_map(cls, size):
prot = mmap.PROT_EXEC | mmap.PROT_WRITE | mmap.PROT_READ
return cls(-1, size, prot=prot)
class CustomAllocator(object):
int_size = {'32bit': 4, '64bit': 8}
@@ -75,7 +28,10 @@ class CustomAllocator(object):
return cls.int_size[bits]
def get_new_page(self, size):
self.maps.append(MyMap.get_map(size))
addr = windows.winproxy.VirtualAlloc(0, size, 0x1000, gdef.PAGE_EXECUTE_READWRITE)
mymap = (ctypes.c_char * size).from_address(addr)
mymap.addr = addr
self.maps.append(mymap)
self.cur_offset = 0
self.cur_page_size = size
@@ -99,6 +55,20 @@ class CustomAllocator(object):
self.cur_offset += size
return addr
def close(self):
maps = self.maps
self.maps = []
self.cur_offset = 0
self.cur_page_size = 0
if getattr(sys, "path", None) is None:
# Path is None -> Python shutdown
return
for mymap in maps:
windows.winproxy.VirtualFree(mymap.addr, dwFreeType=gdef.MEM_RELEASE)
def __del__(self):
self.close()
allocator = CustomAllocator()