mirror of
https://github.com/hakril/PythonForWindows
synced 2026-06-08 14:31:45 +00:00
WinThread is not a THREADENTRY32 anymore + add Winprocess.peb.exe
This commit is contained in:
@@ -69,6 +69,14 @@ class WindowsTestCase(unittest.TestCase):
|
||||
def test_get_current_process_modules(self):
|
||||
self.assertIn("python", windows.current_process.peb.modules[0].name)
|
||||
|
||||
@check_for_gc_garbage
|
||||
def test_get_current_process_exe(self):
|
||||
exe = windows.current_process.peb.exe
|
||||
exe_by_module = windows.current_process.peb.modules[0].pe
|
||||
self.assertEqual(exe.baseaddr, exe_by_module.baseaddr)
|
||||
self.assertEqual(exe.bitness, exe_by_module.bitness)
|
||||
|
||||
|
||||
@check_for_gc_garbage
|
||||
def test_local_process_pe_imports(self):
|
||||
python_module = windows.current_process.peb.modules[0]
|
||||
@@ -312,6 +320,23 @@ class WindowsTestCase(unittest.TestCase):
|
||||
dword = struct.unpack("<Q", calc.read_memory(data, 8))[0]
|
||||
self.assertEqual(dword, get_current_proc_id)
|
||||
|
||||
@check_for_gc_garbage
|
||||
def test_remote_peb_exe_32(self):
|
||||
with Calc32() as calc:
|
||||
exe = calc.peb.exe
|
||||
exe_by_module = calc.peb.modules[0].pe
|
||||
self.assertEqual(exe.baseaddr, exe_by_module.baseaddr)
|
||||
self.assertEqual(exe.bitness, exe_by_module.bitness)
|
||||
|
||||
@windows_64bit_only
|
||||
@check_for_gc_garbage
|
||||
def test_remote_peb_exe_64(self):
|
||||
with Calc64() as calc:
|
||||
exe = calc.peb.exe
|
||||
exe_by_module = calc.peb.modules[0].pe
|
||||
self.assertEqual(exe.baseaddr, exe_by_module.baseaddr)
|
||||
self.assertEqual(exe.bitness, exe_by_module.bitness)
|
||||
|
||||
@check_for_gc_garbage
|
||||
def test_thread_exit_value_32(self):
|
||||
with Calc32() as calc:
|
||||
@@ -575,6 +600,23 @@ class WindowsTestCase(unittest.TestCase):
|
||||
t = calc.threads[0]
|
||||
self.assertNotEqual(t.teb_base, 0)
|
||||
|
||||
@check_for_gc_garbage
|
||||
def test_thread_owner_from_tid_32(self):
|
||||
with Calc32() as calc:
|
||||
thread = calc.threads[0]
|
||||
tst_thread = windows.winobject.process.WinThread(tid=thread.tid)
|
||||
self.assertEqual(thread.owner_pid, tst_thread.owner_pid)
|
||||
self.assertEqual(thread.owner.name, tst_thread.owner.name)
|
||||
|
||||
@windows_64bit_only
|
||||
@check_for_gc_garbage
|
||||
def test_thread_owner_from_tid_64(self):
|
||||
with Calc64() as calc:
|
||||
thread = calc.threads[0]
|
||||
tst_thread = windows.winobject.process.WinThread(tid=thread.tid)
|
||||
self.assertEqual(thread.owner_pid, tst_thread.owner_pid)
|
||||
self.assertEqual(thread.owner.name, tst_thread.owner.name)
|
||||
|
||||
|
||||
class WindowsAPITestCase(unittest.TestCase):
|
||||
def test_createfileA_fail(self):
|
||||
|
||||
@@ -104,12 +104,15 @@ class HookTestCase(unittest.TestCase):
|
||||
except WindowsError as e:
|
||||
pass
|
||||
"""
|
||||
calc.execute_python_unsafe(textwrap.dedent(code))
|
||||
calc.execute_python(textwrap.dedent(code))
|
||||
# Tricky part: we use an injected thread exit_value to ask stuff about the remote python
|
||||
def remote_ask(request):
|
||||
t = calc.execute_python_unsafe(request)
|
||||
t.wait()
|
||||
return t.exit_code
|
||||
result = t.exit_code
|
||||
if result > 100:
|
||||
import pdb;pdb.set_trace()
|
||||
return result
|
||||
|
||||
self.assertEqual(remote_ask("windows.current_thread.exit(len(calling_thread))"), 1)
|
||||
self.assertEqual(remote_ask("windows.current_thread.exit(calling_thread == set([hooking_thread]))"), 1)
|
||||
@@ -156,7 +159,7 @@ class HookTestCase(unittest.TestCase):
|
||||
except WindowsError as e:
|
||||
pass
|
||||
"""
|
||||
calc.execute_python_unsafe(textwrap.dedent(code))
|
||||
calc.execute_python(textwrap.dedent(code))
|
||||
# Tricky part: we use an injected thread exit_value to ask stuff about the remote python
|
||||
def remote_ask(request):
|
||||
t = calc.execute_python_unsafe(request)
|
||||
|
||||
@@ -64,14 +64,41 @@ class AutoHandle(object):
|
||||
self._close_function(self._handle)
|
||||
|
||||
|
||||
class WinThread(THREADENTRY32, AutoHandle):
|
||||
class WinThread(AutoHandle):
|
||||
"""Represent a thread """
|
||||
|
||||
def __init__(self, tid=None, handle=None, owner_pid=None, owner=None):
|
||||
if tid is None and handle is None:
|
||||
raise ValueError("Need at least <pid> or <handle> to create a {0}".format(type(self).__name__))
|
||||
|
||||
if tid is not None: self._tid = tid
|
||||
if handle is not None: self._handle = handle
|
||||
if owner is not None: self._owner = owner
|
||||
if owner_pid is not None: self._owner_pid = owner_pid
|
||||
if owner_pid is None and owner:
|
||||
self._owner_pid = owner.pid
|
||||
|
||||
@classmethod
|
||||
def _from_THREADENTRY32(cls, entry, owner=None):
|
||||
tid = entry.th32ThreadID
|
||||
owner_pid = entry.th32OwnerProcessID
|
||||
return cls(tid=tid, owner_pid=owner_pid, owner=owner)
|
||||
|
||||
@classmethod
|
||||
def _from_handle(cls, handle):
|
||||
# Create a DeadThread if thread is already dead ?
|
||||
return WinThread(handle=handle)
|
||||
|
||||
@utils.fixedpropety
|
||||
def tid(self):
|
||||
"""Thread ID
|
||||
|
||||
:type: :class:`int`"""
|
||||
return self.th32ThreadID
|
||||
return self._get_thread_id(self.handle)
|
||||
|
||||
@utils.fixedpropety
|
||||
def owner_pid(self):
|
||||
return self._get_thread_owner_pid(self.handle)
|
||||
|
||||
@utils.fixedpropety
|
||||
def owner(self):
|
||||
@@ -79,13 +106,7 @@ class WinThread(THREADENTRY32, AutoHandle):
|
||||
|
||||
:type: :class:`WinProcess`
|
||||
"""
|
||||
if hasattr(self, "_owner"):
|
||||
return self._owner
|
||||
try:
|
||||
self._owner = [process for process in windows.system.processes if process.pid == self.th32OwnerProcessID][0]
|
||||
except IndexError:
|
||||
return None
|
||||
return self._owner
|
||||
return WinProcess(pid=self.owner_pid)
|
||||
|
||||
@property
|
||||
def context(self):
|
||||
@@ -227,23 +248,12 @@ class WinThread(THREADENTRY32, AutoHandle):
|
||||
if owner is None:
|
||||
owner_name = "<Dead process with pid {0}>".format(hex(self.th32OwnerProcessID))
|
||||
else:
|
||||
owner_name = owner.name
|
||||
try:
|
||||
owner_name = owner.name
|
||||
except EnvironmentError:
|
||||
owner_name = "!cannot-retrieve-owner-name"
|
||||
return '<{0} {1} owner "{2}" at {3}>'.format(self.__class__.__name__, self.tid, owner_name, hex(id(self)))
|
||||
|
||||
@staticmethod
|
||||
def _from_handle(handle):
|
||||
tid = WinThread._get_thread_id(handle)
|
||||
try:
|
||||
# Really useful ?
|
||||
thread = [t for t in windows.winobject.system.System().threads if t.tid == tid][0]
|
||||
# set AutoHandle _handle
|
||||
thread._handle = handle
|
||||
dbgprint("Thread {0} from handle {1}".format(thread, hex(handle)), "HANDLE")
|
||||
return thread
|
||||
except IndexError:
|
||||
dbgprint("DeadThread from handle {0}".format(hex(handle)), "HANDLE")
|
||||
return DeadThread(handle, tid)
|
||||
|
||||
@staticmethod
|
||||
def _get_thread_id_by_api(handle):
|
||||
return winproxy.GetThreadId(handle)
|
||||
@@ -253,10 +263,17 @@ class WinThread(THREADENTRY32, AutoHandle):
|
||||
if windows.current_process.bitness == 32 and self.owner.bitness == 64:
|
||||
raise NotImplementedError("[_get_thread_id_manual] 32 -> 64 (XP64 bits + Syswow process ?)")
|
||||
res = THREAD_BASIC_INFORMATION()
|
||||
windows.winproxy.NtQueryInformationThread(hand, ThreadBasicInformation, byref(res), ctypes.sizeof(res))
|
||||
windows.winproxy.NtQueryInformationThread(handle, ThreadBasicInformation, byref(res), ctypes.sizeof(res))
|
||||
id2 = res.ClientId.UniqueThread
|
||||
return id2
|
||||
|
||||
def _get_thread_owner_pid(self, handle):
|
||||
res = THREAD_BASIC_INFORMATION()
|
||||
windows.winproxy.NtQueryInformationThread(handle, ThreadBasicInformation, byref(res), ctypes.sizeof(res))
|
||||
id2 = res.ClientId.UniqueProcess
|
||||
return id2
|
||||
|
||||
|
||||
if winproxy.is_implemented(winproxy.GetThreadId):
|
||||
_get_thread_id = _get_thread_id_by_api
|
||||
else:
|
||||
@@ -329,7 +346,8 @@ class Process(AutoHandle):
|
||||
|
||||
:type: [:class:`WinThread`] -- A list of Thread
|
||||
"""
|
||||
return [thread for thread in windows.system.threads if thread.th32OwnerProcessID == self.pid]
|
||||
owner_pid = self.pid
|
||||
return [WinThread._from_THREADENTRY32(th, owner=self) for th in windows.system.enumerate_threads_generator() if th.th32OwnerProcessID == owner_pid]
|
||||
|
||||
def virtual_alloc(self, size):
|
||||
raise NotImplementedError("virtual_alloc")
|
||||
@@ -821,7 +839,7 @@ class WinProcess(Process):
|
||||
"""A Process on the system"""
|
||||
def __init__(self, pid=None, handle=None, name=None, ppid=None):
|
||||
if pid is None and handle is None:
|
||||
raise ValueError("Need at least <pid> or <handle> to create a {0}".format(type(self).__name))
|
||||
raise ValueError("Need at least <pid> or <handle> to create a {0}".format(type(self).__name__))
|
||||
|
||||
if pid is not None: self._pid = pid
|
||||
if handle is not None: self._handle = handle
|
||||
@@ -843,7 +861,7 @@ class WinProcess(Process):
|
||||
name = entry.szExeFile.decode()
|
||||
pid = entry.th32ProcessID
|
||||
ppid = entry.th32ParentProcessID
|
||||
return WinProcess(pid=pid, name=name, ppid=ppid)
|
||||
return cls(pid=pid, name=name, ppid=ppid)
|
||||
|
||||
|
||||
@utils.fixedpropety
|
||||
@@ -879,12 +897,16 @@ class WinProcess(Process):
|
||||
return winproxy.OpenProcess(dwProcessId=self.pid)
|
||||
|
||||
def __repr__(self):
|
||||
try:
|
||||
exe_name = self.name
|
||||
except WindowsError as e:
|
||||
exe_name = "!cannot-retrieve-name"
|
||||
try:
|
||||
if self.is_exit:
|
||||
return '<{0} "{1}" pid {2} (DEAD) at {3}>'.format(self.__class__.__name__, self.name, self.pid, hex(id(self)))
|
||||
return '<{0} "{1}" pid {2} (DEAD) at {3}>'.format(self.__class__.__name__, exe_name, self.pid, hex(id(self)))
|
||||
except WindowsError: # Cannot open process
|
||||
pass
|
||||
return '<{0} "{1}" pid {2} at {3}>'.format(self.__class__.__name__, self.name, self.pid, hex(id(self)))
|
||||
return '<{0} "{1}" pid {2} at {3}>'.format(self.__class__.__name__, exe_name, self.pid, hex(id(self)))
|
||||
|
||||
def virtual_alloc(self, size, prot=PAGE_EXECUTE_READWRITE):
|
||||
"""Allocate memory in the process
|
||||
@@ -1231,6 +1253,14 @@ class PEB(Structure):
|
||||
{"ProcessParameters": POINTER(RTL_USER_PROCESS_PARAMETERS)}
|
||||
)
|
||||
|
||||
@property
|
||||
def exe(self):
|
||||
"""The executable of the process, as pointed by PEB.ImageBaseAddress
|
||||
|
||||
:type: :class:`windows.pe_parse.PEFile`
|
||||
"""
|
||||
return windows.pe_parse.GetPEFile(self.ImageBaseAddress)
|
||||
|
||||
@property
|
||||
def imagepath(self):
|
||||
"""The ImagePathName of the PEB
|
||||
@@ -1343,6 +1373,14 @@ class RemotePEB(rctypes.RemoteStructure.from_structure(PEB)):
|
||||
def ptr_flink_to_remote_module(self, ptr_value):
|
||||
return RemoteLoadedModule(ptr_value - ctypes.sizeof(ctypes.c_void_p) * 2, self._target)
|
||||
|
||||
@property
|
||||
def exe(self):
|
||||
"""The executable of the process, as pointed by PEB.ImageBaseAddress
|
||||
|
||||
:type: :class:`windows.pe_parse.PEFile`
|
||||
"""
|
||||
return pe_parse.GetPEFile(self.ImageBaseAddress, target=self._target)
|
||||
|
||||
@property
|
||||
def modules(self):
|
||||
"""The loaded modules present in the PEB
|
||||
@@ -1376,6 +1414,15 @@ if CurrentProcess().bitness == 32:
|
||||
def ptr_flink_to_remote_module(self, ptr_value):
|
||||
return RemoteLoadedModule64(ptr_value - ctypes.sizeof(rctypes.c_void_p64) * 2, self._target)
|
||||
|
||||
|
||||
@property
|
||||
def exe(self):
|
||||
"""The executable of the process, as pointed by PEB.ImageBaseAddress
|
||||
|
||||
:type: :class:`windows.pe_parse.PEFile`
|
||||
"""
|
||||
return pe_parse.GetPEFile(self.ImageBaseAddress, target=self._target)
|
||||
|
||||
@property
|
||||
def modules(self):
|
||||
"""The loaded modules present in the PEB
|
||||
@@ -1408,6 +1455,15 @@ if CurrentProcess().bitness == 64:
|
||||
def ptr_flink_to_remote_module(self, ptr_value):
|
||||
return RemoteLoadedModule32(ptr_value - ctypes.sizeof(rctypes.c_void_p32) * 2, self._target)
|
||||
|
||||
@property
|
||||
def exe(self):
|
||||
"""The executable of the process, as pointed by PEB.ImageBaseAddress
|
||||
|
||||
:type: :class:`windows.pe_parse.PEFile`
|
||||
"""
|
||||
return pe_parse.GetPEFile(self.ImageBaseAddress, target=self._target)
|
||||
|
||||
|
||||
@property
|
||||
def modules(self):
|
||||
"""The loaded modules present in the PEB
|
||||
|
||||
@@ -19,6 +19,7 @@ from windows.winobject import kernobj
|
||||
from windows.winobject import handle
|
||||
|
||||
from windows.generated_def.winstructs import *
|
||||
from windows.dbgprint import dbgprint
|
||||
|
||||
class System(object):
|
||||
"""The state of the current ``Windows`` system ``Python`` is running on"""
|
||||
@@ -42,7 +43,7 @@ class System(object):
|
||||
|
||||
:type: [:class:`process.WinThread`] -- A list of Thread
|
||||
"""
|
||||
return self.enumerate_threads()
|
||||
return self.enumerate_threads_setup_owners()
|
||||
|
||||
@property
|
||||
def logicaldrives(self):
|
||||
@@ -202,6 +203,7 @@ class System(object):
|
||||
|
||||
@staticmethod
|
||||
def enumerate_processes():
|
||||
dbgprint("Enumerating processes with CreateToolhelp32Snapshot", "SLOW")
|
||||
process_entry = PROCESSENTRY32()
|
||||
process_entry.dwSize = ctypes.sizeof(process_entry)
|
||||
snap = winproxy.CreateToolhelp32Snapshot(windef.TH32CS_SNAPPROCESS, 0)
|
||||
@@ -214,14 +216,57 @@ class System(object):
|
||||
return res
|
||||
|
||||
@staticmethod
|
||||
def enumerate_threads():
|
||||
thread_entry = process.WinThread()
|
||||
def enumerate_threads_generator():
|
||||
# Ptet dangereux, parce que on yield la meme THREADENTRY32 a chaque fois
|
||||
dbgprint("Enumerating threads with CreateToolhelp32Snapshot <generator>", "SLOW")
|
||||
thread_entry = THREADENTRY32()
|
||||
thread_entry.dwSize = ctypes.sizeof(thread_entry)
|
||||
snap = winproxy.CreateToolhelp32Snapshot(windef.TH32CS_SNAPTHREAD, 0)
|
||||
dbgprint("New handle CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD) <generator> | {0:#x}".format(snap), "HANDLE")
|
||||
try:
|
||||
winproxy.Thread32First(snap, thread_entry)
|
||||
yield thread_entry
|
||||
while winproxy.Thread32Next(snap, thread_entry):
|
||||
yield thread_entry
|
||||
finally:
|
||||
winproxy.CloseHandle(snap)
|
||||
dbgprint("CLOSE CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD) <generator> | {0:#x}".format(snap), "HANDLE")
|
||||
|
||||
|
||||
@staticmethod
|
||||
def enumerate_threads():
|
||||
return [WinThread._from_THREADENTRY32(th) for th in System.enumerate_threads_generator()]
|
||||
|
||||
|
||||
def enumerate_threads_setup_owners(self):
|
||||
# Enumerating threads is a special operation concerning the owner process.
|
||||
# We may not be able to retrieve the name of the owning process by normal way
|
||||
# (as we need to get a handle on the process)
|
||||
# So, this implementation of enumerate_thread also setup the owner with the result of enumerate_processes
|
||||
dbgprint("Enumerating threads with CreateToolhelp32Snapshot and setup owner", "SLOW")
|
||||
|
||||
# One snap for both enum to be prevent race
|
||||
snap = winproxy.CreateToolhelp32Snapshot(windef.TH32CS_SNAPTHREAD | windef.TH32CS_SNAPPROCESS, 0)
|
||||
|
||||
process_entry = PROCESSENTRY32()
|
||||
process_entry.dwSize = ctypes.sizeof(process_entry)
|
||||
winproxy.Process32First(snap, process_entry)
|
||||
processes = []
|
||||
processes.append(process.WinProcess._from_PROCESSENTRY32(process_entry))
|
||||
while winproxy.Process32Next(snap, process_entry):
|
||||
processes.append(process.WinProcess._from_PROCESSENTRY32(process_entry))
|
||||
|
||||
# Forge a dict pid -> process
|
||||
proc_dict = {proc.pid: proc for proc in processes}
|
||||
|
||||
thread_entry = THREADENTRY32()
|
||||
thread_entry.dwSize = ctypes.sizeof(thread_entry)
|
||||
threads = []
|
||||
winproxy.Thread32First(snap, thread_entry)
|
||||
threads.append(copy.copy(thread_entry))
|
||||
parent = proc_dict[thread_entry.th32OwnerProcessID]
|
||||
threads.append(process.WinThread._from_THREADENTRY32(thread_entry, owner=parent))
|
||||
while winproxy.Thread32Next(snap, thread_entry):
|
||||
threads.append(copy.copy(thread_entry))
|
||||
parent = proc_dict[thread_entry.th32OwnerProcessID]
|
||||
threads.append(process.WinThread._from_THREADENTRY32(thread_entry, owner=parent))
|
||||
winproxy.CloseHandle(snap)
|
||||
return threads
|
||||
return threads
|
||||
Reference in New Issue
Block a user