mirror of
https://github.com/hakril/PythonForWindows
synced 2026-06-08 14:31:45 +00:00
181 lines
7.8 KiB
Python
181 lines
7.8 KiB
Python
import ctypes
|
|
import textwrap
|
|
|
|
from test_utils import *
|
|
from windows.generated_def.winstructs import *
|
|
|
|
|
|
class HookTestCase(unittest.TestCase):
|
|
@check_for_gc_garbage
|
|
def test_self_iat_hook_success(self):
|
|
"""Test hook success in single(self) thread"""
|
|
pythondll_mod = [m for m in windows.current_process.peb.modules if m.name.startswith("python") and m.name.endswith(".dll")][0]
|
|
RegOpenKeyExA = [n for n in pythondll_mod.pe.imports['advapi32.dll'] if n.name == "RegOpenKeyExA"][0]
|
|
|
|
hook_value = []
|
|
|
|
@windows.hooks.RegOpenKeyExACallback
|
|
def open_reg_hook(hKey, lpSubKey, ulOptions, samDesired, phkResult, real_function):
|
|
hook_value.append((hKey, lpSubKey.value))
|
|
phkResult[0] = 12345678
|
|
return 0
|
|
|
|
x = RegOpenKeyExA.set_hook(open_reg_hook)
|
|
import _winreg
|
|
open_args = (0x12345678, "MY_KEY_VALUE")
|
|
k = _winreg.OpenKey(*open_args)
|
|
self.assertEqual(k.handle, 12345678)
|
|
self.assertEqual(hook_value[0], open_args)
|
|
# Remove the hook
|
|
x.disable()
|
|
|
|
@check_for_gc_garbage
|
|
def test_self_iat_hook_fail_return(self):
|
|
"""Test hook fail in single(self) thread"""
|
|
pythondll_mod = [m for m in windows.current_process.peb.modules if m.name.startswith("python") and m.name.endswith(".dll")][0]
|
|
RegOpenKeyExA = [n for n in pythondll_mod.pe.imports['advapi32.dll'] if n.name == "RegOpenKeyExA"][0]
|
|
|
|
@windows.hooks.RegOpenKeyExACallback
|
|
def open_reg_hook_fail(hKey, lpSubKey, ulOptions, samDesired, phkResult, real_function):
|
|
return 0x11223344
|
|
|
|
x = RegOpenKeyExA.set_hook(open_reg_hook_fail)
|
|
import _winreg
|
|
open_args = (0x12345678, "MY_KEY_VALUE")
|
|
with self.assertRaises(WindowsError) as ar:
|
|
_winreg.OpenKey(*open_args)
|
|
self.assertEqual(ar.exception.winerror, 0x11223344)
|
|
x.disable()
|
|
|
|
@check_for_gc_garbage
|
|
def test_self_iat_hook_multithread(self):
|
|
"""Test IAT hook in current process with multi thread trigger"""
|
|
cp = windows.current_process
|
|
# Might change this to XP compat ?
|
|
kernelbase_mod = [m for m in cp.peb.modules if m.name == "kernelbase.dll"][0]
|
|
LdrLoadDll = [n for n in kernelbase_mod.pe.imports['ntdll.dll'] if n.name == "LdrLoadDll"][0]
|
|
|
|
calling_thread = set([])
|
|
@windows.hooks.LdrLoadDllCallback
|
|
def MyHook(*args, **kwargs):
|
|
calling_thread.add(windows.current_thread.tid)
|
|
return kwargs["real_function"]()
|
|
|
|
x = LdrLoadDll.set_hook(MyHook)
|
|
# Trigger from local thread
|
|
ctypes.WinDLL("kernel32.dll")
|
|
self.assertEqual(calling_thread, set([windows.current_thread.tid]))
|
|
# Trigger from another thread
|
|
k32 = [m for m in cp.peb.modules if m.name == "kernel32.dll"][0]
|
|
load_libraryA = k32.pe.exports["LoadLibraryA"]
|
|
with cp.allocated_memory(0x1000) as addr:
|
|
cp.write_memory(addr, "DLLNOTFOUND.NOT_A_REAL_DLL" + "\x00")
|
|
t = cp.create_thread(load_libraryA, addr)
|
|
t.wait()
|
|
self.assertEqual(len(calling_thread), 2)
|
|
x.disable()
|
|
|
|
@check_for_gc_garbage
|
|
def test_remote_iat_hook_32(self):
|
|
with Calc32() as calc:
|
|
calc.execute_python("import windows")
|
|
calc.execute_python("windows.utils.create_console()")
|
|
|
|
code = """
|
|
import windows.generated_def as gdef
|
|
|
|
cp = windows.current_process
|
|
kernelbase_mod = [m for m in cp.peb.modules if m.name == "kernelbase.dll"][0]
|
|
LdrLoadDll = [n for n in kernelbase_mod.pe.imports['ntdll.dll'] if n.name == "LdrLoadDll"][0]
|
|
|
|
calling_thread = set([])
|
|
hooking_thread = windows.current_thread.tid
|
|
@windows.hooks.LdrLoadDllCallback
|
|
def MyHook(*args, **kwargs):
|
|
calling_thread.add(windows.current_thread.tid)
|
|
print(windows.current_thread.tid)
|
|
return kwargs["real_function"]()
|
|
|
|
x = LdrLoadDll.set_hook(MyHook)
|
|
print("Hooker = " + str(windows.current_thread.tid))
|
|
import ctypes
|
|
try:
|
|
ctypes.WinDLL("NOT_A_REAL_DLL")
|
|
except WindowsError as e:
|
|
pass
|
|
"""
|
|
calc.execute_python_unsafe(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
|
|
|
|
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)
|
|
|
|
# Trigger hook from another Python thread
|
|
calc.execute_python_unsafe("ctypes.WinDLL('ANOTHER_FAKE_DLL')").wait()
|
|
self.assertEqual(remote_ask("windows.current_thread.exit(len(calling_thread))"), 2)
|
|
|
|
# Trigger hook from a NONPython thread
|
|
k32 = [m for m in calc.peb.modules if m.name == "kernel32.dll"][0]
|
|
load_libraryA = k32.pe.exports["LoadLibraryA"]
|
|
with calc.allocated_memory(0x1000) as addr:
|
|
calc.write_memory(addr, "DLLNOTFOUND.NOT_A_REAL_DLL" + "\x00")
|
|
t = calc.create_thread(load_libraryA, addr)
|
|
t.wait()
|
|
self.assertEqual(remote_ask("windows.current_thread.exit(len(calling_thread))"), 3)
|
|
|
|
@check_for_gc_garbage
|
|
def test_remote_iat_hook_64(self):
|
|
with Calc64() as calc:
|
|
calc.execute_python("import windows")
|
|
calc.execute_python("windows.utils.create_console()")
|
|
|
|
code = """
|
|
import windows.generated_def as gdef
|
|
|
|
cp = windows.current_process
|
|
kernelbase_mod = [m for m in cp.peb.modules if m.name == "kernelbase.dll"][0]
|
|
LdrLoadDll = [n for n in kernelbase_mod.pe.imports['ntdll.dll'] if n.name == "LdrLoadDll"][0]
|
|
|
|
calling_thread = set([])
|
|
hooking_thread = windows.current_thread.tid
|
|
@windows.hooks.Callback(*[gdef.PVOID] * 5)
|
|
def MyHook(*args, **kwargs):
|
|
calling_thread.add(windows.current_thread.tid)
|
|
print(windows.current_thread.tid)
|
|
return kwargs["real_function"]()
|
|
|
|
x = LdrLoadDll.set_hook(MyHook)
|
|
print("Hooker = " + str(windows.current_thread.tid))
|
|
import ctypes
|
|
try:
|
|
ctypes.WinDLL("NOT_A_REAL_DLL")
|
|
except WindowsError as e:
|
|
pass
|
|
"""
|
|
calc.execute_python_unsafe(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
|
|
|
|
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)
|
|
|
|
# Trigger hook from another Python thread
|
|
calc.execute_python_unsafe("ctypes.WinDLL('ANOTHER_FAKE_DLL')").wait()
|
|
self.assertEqual(remote_ask("windows.current_thread.exit(len(calling_thread))"), 2)
|
|
|
|
# Trigger hook from a NONPython thread
|
|
k32 = [m for m in calc.peb.modules if m.name == "kernel32.dll"][0]
|
|
load_libraryA = k32.pe.exports["LoadLibraryA"]
|
|
with calc.allocated_memory(0x1000) as addr:
|
|
calc.write_memory(addr, "DLLNOTFOUND.NOT_A_REAL_DLL" + "\x00")
|
|
t = calc.create_thread(load_libraryA, addr)
|
|
t.wait()
|
|
self.assertEqual(remote_ask("windows.current_thread.exit(len(calling_thread))"), 3)
|