Files
hakril-PythonForWindows/windows/test/test_hooks.py
T
2017-04-26 11:07:52 +02:00

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)