mirror of
https://github.com/hakril/PythonForWindows
synced 2026-06-08 14:31:45 +00:00
more pytest POC + fixed debugger code + test FunctionHXBP
This commit is contained in:
+23
-42
@@ -27,23 +27,19 @@ else:
|
||||
|
||||
|
||||
import sys
|
||||
import weakref
|
||||
|
||||
def generate_pop_and_exit_fixtures(proc_popers, ids=[], dwCreationFlags=DEFAULT_CREATION_FLAGS):
|
||||
@pytest.fixture(params=proc_popers, ids=ids)
|
||||
def pop_and_exit_process(request):
|
||||
proc_poper = request.param
|
||||
proc = proc_poper(dwCreationFlags=dwCreationFlags)
|
||||
yield proc # provide the fixture value
|
||||
yield weakref.proxy(proc) # provide the fixture value
|
||||
try:
|
||||
print("EXIT PROC <{0}>".format(sys.getrefcount(proc)))
|
||||
# if sys.getrefcount(proc) > 5:
|
||||
# import pdb;pdb.set_trace()
|
||||
proc.exit(0)
|
||||
except WindowsError as e:
|
||||
if not proc.is_exit:
|
||||
raise
|
||||
# import pdb;pdb.set_trace()
|
||||
# proc.__del__()
|
||||
del proc
|
||||
return pop_and_exit_process
|
||||
|
||||
@@ -59,13 +55,7 @@ else:
|
||||
dwCreationFlags=gdef.CREATE_SUSPENDED)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def check_for_gc_garbage(request):
|
||||
garbage_before = set(gc.garbage)
|
||||
yield
|
||||
gc.collect()
|
||||
new_garbage = set(gc.garbage) - garbage_before
|
||||
assert not new_garbage, "Test generated uncollectable object ({0})".format(new_garbage)
|
||||
|
||||
|
||||
class HandleDebugger(object):
|
||||
def __init__(self, pid):
|
||||
@@ -96,38 +86,29 @@ class HandleDebugger(object):
|
||||
current_process_hdebugger = HandleDebugger(windows.current_process.pid)
|
||||
current_process_hdebugger.refresh_handles()
|
||||
|
||||
# TST = current_process_hdebugger.refresh_handles()
|
||||
|
||||
RESULT = {}
|
||||
|
||||
@pytest.fixture()
|
||||
def check_for_handle_leak(request):
|
||||
# current_process_hdebugger.refresh_handles()
|
||||
yield
|
||||
# leaked_handles = current_process_hdebugger.get_new_handle()
|
||||
# for lh in leaked_handles:
|
||||
# RESULT[lh.wValue] = request.function.__name__
|
||||
print("HANDLE LEAK SAVE")
|
||||
# assert not leaked_handles, "Test Leaked <{0}> handles of types ({1})".format(len(leaked_handles), set(h.type for h in leaked_handles))
|
||||
x = current_process_hdebugger.refresh_handles()
|
||||
yield None
|
||||
leaked_handles = current_process_hdebugger.get_new_handle(x)
|
||||
try:
|
||||
leaked_handles_types = set(h.type for h in leaked_handles)
|
||||
except Exception as e:
|
||||
leaked_handles = current_process_hdebugger.get_new_handle(x)
|
||||
leaked_handles_types = set(h.type for h in leaked_handles)
|
||||
# print("ERROR FOR TYPE, newleaked = {0}".format(current_process_hdebugger.get_new_handle(x)))
|
||||
leaked_handles_types -= set(['EtwRegistration', 'Key', 'DebugObject', 'Event'])
|
||||
assert not leaked_handles_types, "Test Leaked <{0}> handles of types ({1})".format(len(leaked_handles), leaked_handles_types)
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def check_for_handle_leak_final(request):
|
||||
# x = current_process_hdebugger.get_handles()
|
||||
print("CHECK HANDLE FINAL :D")
|
||||
# current_process_hdebugger.refresh_handles()
|
||||
yield
|
||||
# leaked_handles = current_process_hdebugger.get_new_handle(x)
|
||||
# import pdb;pdb.set_trace()
|
||||
# print(leaked_handles)
|
||||
|
||||
# leaked_handles = current_process_hdebugger.get_new_handle()
|
||||
# import pdb;pdb.set_trace()
|
||||
# assert not leaked_handles, "Test Leaked <{0}> handles of types ({1})".format(len(leaked_handles), set(h.type for h in leaked_handles))
|
||||
|
||||
|
||||
def pytest_unconfigure(*args, **kwargs):
|
||||
import pdb;pdb.set_trace()
|
||||
print(TST)
|
||||
|
||||
# pytestmark = pytest.mark.usefixtures('check_for_handle_leak_final')
|
||||
@pytest.fixture()
|
||||
def check_for_gc_garbage(request):
|
||||
# print("GC CHECK")
|
||||
garbage_before = set(gc.garbage)
|
||||
yield None
|
||||
gc.collect()
|
||||
new_garbage = set(gc.garbage) - garbage_before
|
||||
assert not new_garbage, "Test generated uncollectable object ({0})".format(new_garbage)
|
||||
# print("GC CHECK END")
|
||||
@@ -11,7 +11,7 @@ import windows.native_exec.simple_x64 as x64
|
||||
from conftest import generate_pop_and_exit_fixtures, pop_proc_32, pop_proc_64
|
||||
from pfwtest import *
|
||||
|
||||
pytestmark = pytest.mark.usefixtures('check_for_gc_garbage')
|
||||
# pytestmark = pytest.mark.usefixtures('check_for_gc_garbage', "check_for_handle_leak")
|
||||
|
||||
proc32_debug = generate_pop_and_exit_fixtures([pop_proc_32], ids=["proc32dbg"], dwCreationFlags=gdef.DEBUG_PROCESS)
|
||||
proc64_debug = generate_pop_and_exit_fixtures([pop_proc_64], ids=["proc64dbg"], dwCreationFlags=gdef.DEBUG_PROCESS)
|
||||
@@ -106,9 +106,11 @@ def test_multiple_hwx_breakpoint(proc32_64_debug):
|
||||
# Used to verif we actually called the Breakpoints
|
||||
assert TSTBP.COUNTER == 4
|
||||
|
||||
|
||||
# @check_for_gc_garbage
|
||||
@check_for_handle_leak
|
||||
def test_four_hwx_breakpoint_fail(proc32_64_debug):
|
||||
"""Check that setting 4HXBP in the same thread fails"""
|
||||
# print("test_four_hwx_breakpoint_fail {0}".format(proc32_64_debug))
|
||||
class TSTBP(windows.debug.HXBreakpoint):
|
||||
def __init__(self, addr, expec_before):
|
||||
self.addr = addr
|
||||
@@ -165,7 +167,8 @@ def test_hwx_breakpoint_are_on_all_thread(proc32_64_debug):
|
||||
# Used to verif we actually called the Breakpoints
|
||||
assert TSTBP.COUNTER == 2
|
||||
|
||||
@check_for_handle_leak
|
||||
# @check_for_handle_leak
|
||||
# @check_for_gc_garbage
|
||||
@pytest.mark.parametrize("bptype", [windows.debug.Breakpoint, windows.debug.HXBreakpoint])
|
||||
def test_simple_breakpoint_name_addr(proc32_64_debug, bptype):
|
||||
"""Check breakpoint address resolution for format dll!api"""
|
||||
|
||||
@@ -63,8 +63,13 @@ class TestCurrentProcessWithCheckGarbage(object):
|
||||
assert isinstance(token.integrity, (int, long))
|
||||
assert isinstance(token.is_elevated, (bool))
|
||||
|
||||
@check_for_handle_leak
|
||||
def test_yolo(proc32_64):
|
||||
print(proc32_64)
|
||||
print(proc32_64.handle)
|
||||
|
||||
|
||||
@check_for_handle_leak
|
||||
@check_for_gc_garbage
|
||||
class TestProcessWithCheckGarbage(object):
|
||||
def test_pop_proc_32(self, proc32):
|
||||
|
||||
@@ -73,8 +73,9 @@ class X64ArgumentRetriever(object):
|
||||
return proc.read_qword(thread.context.sp + 8 + (8 * nb))
|
||||
|
||||
## Behaviour breakpoint !
|
||||
class FunctionParamDumpBP(Breakpoint):
|
||||
def __init__(self, target=None, addr=None):
|
||||
# class FunctionParamDumpBP(Breakpoint):
|
||||
class FunctionParamDumpBPAbstract(object):
|
||||
def __init__(self, addr=None, target=None):
|
||||
if target is None:
|
||||
try:
|
||||
target = self.TARGET
|
||||
@@ -82,7 +83,7 @@ class FunctionParamDumpBP(Breakpoint):
|
||||
raise ValueError("{0} bp without a <target> must have a <TARGET> class attribute")
|
||||
if addr is None:
|
||||
addr = "{0}!{1}".format(target.target_dll, target.target_func)
|
||||
super(FunctionParamDumpBP, self).__init__(addr)
|
||||
super(FunctionParamDumpBPAbstract, self).__init__(addr)
|
||||
self.target = target
|
||||
self.target_args = target.prototype._argtypes_
|
||||
self.target_params = target.params
|
||||
@@ -134,6 +135,11 @@ class FunctionParamDumpBP(Breakpoint):
|
||||
return self.extract_arguments_32bits(cproc, cthread)
|
||||
return self.extract_arguments_64bits(cproc, cthread)
|
||||
|
||||
class FunctionParamDumpBP(FunctionParamDumpBPAbstract, Breakpoint):
|
||||
pass
|
||||
|
||||
class FunctionParamDumpHXBP(FunctionParamDumpBPAbstract, HXBreakpoint):
|
||||
pass
|
||||
|
||||
class FunctionRetBP(Breakpoint):
|
||||
def __init__(self, addr, initial_breakpoint):
|
||||
|
||||
@@ -295,8 +295,10 @@ class Debugger(object):
|
||||
raise ValueError("Cannot put {0} in {1} (DRx full)".format(bp, target))
|
||||
empty_drx = str([pos for pos in range(4) if pos not in x][0])
|
||||
ctx = target.context
|
||||
ctx.EDr7.GE = 1
|
||||
ctx.EDr7.LE = 1
|
||||
# Windows DebugCtl aliasing in DR7
|
||||
# See https://www.codeproject.com/Articles/517466/Last-branch-records-and-branch-tracing
|
||||
ctx.EDr7.LE = 0 # bit 8 of DR7 represents bit 0 of DebugCtl. This is the LBR bit. (last branch record, will explain)
|
||||
ctx.EDr7.GE = 0 # bit 9 of DR7 represents bit 1 of DebugCtl. This is the BTF bit. (single-step on branches)
|
||||
setattr(ctx.EDr7, "L" + empty_drx, 1)
|
||||
setattr(ctx, "Dr" + empty_drx, addr)
|
||||
x[int(empty_drx)] = bp
|
||||
@@ -332,13 +334,15 @@ class Debugger(object):
|
||||
return PAGE_EXECUTE_READ
|
||||
if events == set("X"):
|
||||
# Might have problem if DEP is not enabled
|
||||
if windows.winproxy.is_implemented(windows.winproxy.GetProcessDEPPolicy):
|
||||
if target.bitness == 64:
|
||||
has_DEP = True
|
||||
elif windows.winproxy.is_implemented(windows.winproxy.GetProcessDEPPolicy):
|
||||
has_DEP = DWORD()
|
||||
permaned = LONG()
|
||||
windows.winproxy.GetProcessDEPPolicy(target.handle, has_DEP, permaned)
|
||||
has_DEP = has_DEP.value
|
||||
else:
|
||||
has_DEP = 0
|
||||
has_DEP = False
|
||||
return PAGE_READWRITE if has_DEP else PAGE_NOACCESS
|
||||
raise ValueError("Unexpected set of event for Membp: {0}".format(events))
|
||||
|
||||
|
||||
@@ -94,6 +94,11 @@ def perform_manual_getproc_loadlib_64(target, dll_name):
|
||||
raise InjectionFailedError("Injection of <{0}> failed".format(dll_name))
|
||||
return True
|
||||
|
||||
def perform_manual_getproc_loadlib(target, *args, **kwargs):
|
||||
if target.bitness == 32:
|
||||
return perform_manual_getproc_loadlib_32(target, *args, **kwargs)
|
||||
return perform_manual_getproc_loadlib_64(target, *args, **kwargs)
|
||||
|
||||
|
||||
def load_dll_in_remote_process(target, dll_name):
|
||||
rpeb = target.peb
|
||||
|
||||
@@ -56,7 +56,7 @@ class Handle(SYSTEM_HANDLE):
|
||||
winproxy.NtQueryObject(lh, ObjectTypeInformation, ctypes.byref(xxx), ctypes.sizeof(xxx), ctypes.byref(size_needed))
|
||||
except WindowsError as e:
|
||||
if e.code != STATUS_INFO_LENGTH_MISMATCH:
|
||||
print("ERROR WITH {0:x}".format(lh))
|
||||
# print("ERROR WITH {0:x}".format(lh))
|
||||
raise
|
||||
size = size_needed.value
|
||||
buffer = ctypes.c_buffer(size)
|
||||
|
||||
Reference in New Issue
Block a user