diff --git a/TODO b/TODO index ac7dfe3..389b9e6 100644 --- a/TODO +++ b/TODO @@ -24,6 +24,10 @@ TODO: - code generated by generate_python_exec_shellcode_64[32] may be reused Just need to passe the address of the python string as argument + - GetProcesAddress + - CRASH if API is not found.. + - fail when resolving xxxW function + FIXME: - WMI - COM initialisation when injected in another process @@ -31,4 +35,6 @@ FIXME: - Fix that - setup.py build seems to raise an error - - winutils.create_process : use WinProcess._from_handle \ No newline at end of file + - winutils.create_process : use WinProcess._from_handle + + - Push("[ECX]") in simple_x64 as a "H" rex and i think it should not.. \ No newline at end of file diff --git a/windows/injection.py b/windows/injection.py index 3bbf19b..7ec2bd3 100644 --- a/windows/injection.py +++ b/windows/injection.py @@ -8,42 +8,47 @@ import windows.utils as utils from .native_exec import simple_x86 as x86 from .native_exec import simple_x64 as x64 -from windows.native_exec.nativeutils import GetProcAddress64 +from windows.native_exec.nativeutils import GetProcAddress64, GetProcAddress32 from windows.dbgprint import dbgprint -def load_dll_in_remote_process(target, dll_name): - rpeb = target.peb - if rpeb.Ldr: - # LDR est parcourable, ca va etre deja plus simple.. - modules = rpeb.modules - if any(mod.name == dll_name for mod in modules): - # DLL already loaded - dbgprint("DLL already present in DLL", "DLLINJECT") - return True - k32 = [mod for mod in modules if mod.name.lower() == "kernel32.dll"] - if k32: - # We have kernel32 \o/ - k32 = k32[0] - try: - load_libraryA = k32.pe.exports["LoadLibraryA"] - except KeyError: - raise ValueError("Kernel32 have no export (wtf)") +def perform_manual_getproc_loadlib_32(target, dll_name): + dll = "KERNEL32.DLL\x00".encode("utf-16-le") + api = "LoadLibraryA\x00" + dll_to_load = dll_name + "\x00" - addr = target.virtual_alloc(0x1000) - target.write_memory(addr, dll_name + "\x00") - t = target.create_thread(load_libraryA, addr) - t.wait() - windows.winproxy.VirtualFreeEx(target.handle, addr) - dbgprint("DLL Injected via (LoadLibray)", "DLLINJECT") - return True - # Hardcore mode - # We don't have k32 or PEB->Ldr - # Go inject a GetProcAddress(LoadLib) + LoadLib shellcode :D - if target.bitness == 32: - raise NotImplementedError("Manuel GetProcAddress 32bits") + RemoteManualLoadLibray = x86.MultipleInstr() + code = RemoteManualLoadLibray + code += x86.Mov("ECX", x86.mem("[ESP + 4]")) + code += x86.Push(x86.mem("[ECX + 4]")) + code += x86.Push(x86.mem("[ECX]")) + code += x86.Call(":FUNC_GETPROCADDRESS32") + code += x86.Push(x86.mem("[ECX + 8]")) + code += x86.Call("EAX") # LoadLibrary + code += x86.Pop("ECX") + code += x86.Pop("ECX") + code += x86.Ret() + RemoteManualLoadLibray += GetProcAddress32 + + addr = target.virtual_alloc(0x1000) + addr2 = addr + len(dll) + addr3 = addr2 + len(api) + addr4 = addr3 + len(dll_to_load) + + target.write_memory(addr, dll) + target.write_memory(addr2, api) + target.write_memory(addr3, dll_to_load) + target.write_qword(addr4, addr) + target.write_qword(addr4 + 4, addr2) + target.write_qword(addr4 + 0x8, addr3) + + t = target.execute(RemoteManualLoadLibray.get_code(), addr4) + t.wait() + return True + +def perform_manual_getproc_loadlib_64(target, dll_name): dll = "KERNEL32.DLL\x00".encode("utf-16-le") api = "LoadLibraryA\x00" dll_to_load = dll_name + "\x00" @@ -80,9 +85,41 @@ def load_dll_in_remote_process(target, dll_name): t = target.execute(RemoteManualLoadLibray.get_code(), addr4) t.wait() - dbgprint("DLL Injected via manual GetProc(LoadLibray)", "DLLINJECT") return True + +def load_dll_in_remote_process(target, dll_name): + rpeb = target.peb + if rpeb.Ldr: + # LDR est parcourable, ca va etre deja plus simple.. + modules = rpeb.modules + if any(mod.name == dll_name for mod in modules): + # DLL already loaded + dbgprint("DLL already present in target", "DLLINJECT") + return True + k32 = [mod for mod in modules if mod.name.lower() == "kernel32.dll"] + if k32: + # We have kernel32 \o/ + k32 = k32[0] + try: + load_libraryA = k32.pe.exports["LoadLibraryA"] + except KeyError: + raise ValueError("Kernel32 have no export (wtf)") + + addr = target.virtual_alloc(0x1000) + target.write_memory(addr, dll_name + "\x00") + t = target.create_thread(load_libraryA, addr) + t.wait() + windows.winproxy.VirtualFreeEx(target.handle, addr) + dbgprint("DLL Injected via LoadLibray", "DLLINJECT") + return True + # Hardcore mode + # We don't have k32 or PEB->Ldr + # Go inject a GetProcAddress(LoadLib) + LoadLib shellcode :D + if target.bitness == 32: + return perform_manual_getproc_loadlib_32(target, dll_name) + return perform_manual_getproc_loadlib_64(target, dll_name) + python_function_32_bits = {} # 32 to 32 injection def generate_python_exec_shellcode_32(target, PYCODE_ADDR, PyDll): @@ -163,10 +200,8 @@ def generate_python_exec_shellcode_64(target, PYCODE_ADDR, PyDll): Py_Initialize = Py_exports["Py_Initialize"] PyRun_SimpleString = Py_exports["PyRun_SimpleString"] - Reserve_space_for_call = x64.MultipleInstr([x64.Push('RDI')] * 4) Clean_space_for_call = x64.MultipleInstr([x64.Pop('RDI')] * 4) - code = x64.MultipleInstr() # Do stack alignement code += x64.Push('RCX') @@ -189,7 +224,6 @@ def generate_python_exec_shellcode_64(target, PYCODE_ADDR, PyDll): code += x64.Call('RAX') code += x64.Mov('RCX', 'R15') code += x64.Mov('R15', 'RAX') - code += x64.Mov('RAX', PyGILState_Release) code += x64.Call('RAX') code += x64.Cmp("RDI", 0) diff --git a/windows/native_exec/nativeutils.py b/windows/native_exec/nativeutils.py index 7930d83..406a181 100644 --- a/windows/native_exec/nativeutils.py +++ b/windows/native_exec/nativeutils.py @@ -1,6 +1,7 @@ import windows import windows.native_exec.simple_x64 as x64 +import windows.native_exec.simple_x86 as x86 from windows.generated_def.winstructs import * @@ -26,7 +27,7 @@ StrlenA64 += x64.Label(":FUNC_STRLENA64") StrlenA64 += x64.Push("RCX") StrlenA64 += x64.Push("RDI") StrlenA64 += x64.Mov("RDI", "RCX") -StrlenA64 += x64.Xor("RAX", "rax") +StrlenA64 += x64.Xor("RAX", "RAX") StrlenA64 += x64.Xor("RCX", "RCX") StrlenA64 += x64.Dec("RCX") StrlenA64 += x64.Repne + x64.ScasB() @@ -85,7 +86,7 @@ GetProcAddress64 += x64.Add("RCX", "RBX") # ;rcx = export_dir GetProcAddress64 += x64.Mov("RAX", "RCX") # ;RAX = export_dir GetProcAddress64 += x64.Push("RAX") # ;Save it for after function search # ; EBX = BASE | EAX = EXPORT DIR -GetProcAddress64 += x64.Mov("ECX", x64.mem("[RAX + 24] ")) # rax = PEBASE RVA +GetProcAddress64 += x64.Mov("ECX", x64.mem("[RAX + 24] ")) GetProcAddress64 += x64.Mov("R13", "RCX") # ;r13 = NB names GetProcAddress64 += x64.Mov("EDX", x64.mem("[RAX + 32] ")) # EDX = names array RVA GetProcAddress64 += x64.Add("RDX", "RBX") # RDX = names array @@ -98,6 +99,7 @@ GetProcAddress64 += x64.Mov("RCX", "R12") GetProcAddress64 += x64.Call(":FUNC_STRLENA64") # TODO: mov outside the loop :D GetProcAddress64 += x64.Mov("RCX", "RAX") GetProcAddress64 += x64.Mov("RDI", "R12") +GetProcAddress64 += x64.Inc("RCX") GetProcAddress64 += x64.Rep + x64.CmpsB() GetProcAddress64 += x64.Mov("EAX", "ECX") GetProcAddress64 += x64.Pop("RCX") @@ -136,3 +138,131 @@ GetProcAddress64 += StrlenW64 GetProcAddress64 += StrlenA64 + +###### 32 bits ####### + + +StrlenW32 = x86.MultipleInstr() +StrlenW32 += x86.Label(":FUNC_STRLENW32") +StrlenW32 += x86.Push("EDI") +StrlenW32 += x86.Mov("EDI", x86.mem("[ESP + 8]")) +StrlenW32 += x86.Push("ECX") +StrlenW32 += x86.Xor("EAX", "EAX") +StrlenW32 += x86.Xor("ECX", "ECX") +StrlenW32 += x86.Dec("ECX") +StrlenW32 += x86.Repne + x86.ScasW() +StrlenW32 += x86.Not("ECX") +StrlenW32 += x86.Dec("ECX") +StrlenW32 += x86.Mov("EAX", "ECX") +StrlenW32 += x86.Pop("ECX") +StrlenW32 += x86.Pop("EDI") +StrlenW32 += x86.Ret() + + +StrlenA32 = x86.MultipleInstr() +StrlenA32 += x86.Label(":FUNC_STRLENA32") +StrlenA32 += x86.Push("EDI") +StrlenA32 += x86.Mov("EDI", x86.mem("[ESP + 8]")) +StrlenA32 += x86.Push("ECX") +StrlenA32 += x86.Xor("EAX", "EAX") +StrlenA32 += x86.Xor("ECX", "ECX") +StrlenA32 += x86.Dec("ECX") +StrlenA32 += x86.Repne + x86.ScasB() +StrlenA32 += x86.Not("ECX") +StrlenA32 += x86.Dec("ECX") +StrlenA32 += x86.Mov("EAX", "ECX") +StrlenA32 += x86.Pop("ECX") +StrlenA32 += x86.Pop("EDI") +StrlenA32 += x86.Ret() + + +GetProcAddress32 = x86.MultipleInstr() +GetProcAddress32 += x86.Label(":FUNC_GETPROCADDRESS32") +GetProcAddress32 += x86.Push("EBX") +GetProcAddress32 += x86.Push("ECX") +GetProcAddress32 += x86.Push("EDI") +GetProcAddress32 += x86.Push("ESI") +GetProcAddress32 += x86.Push("EBP") +GetProcAddress32 += x86.Mov("EAX", x86.mem("FS:[0x30]")) +GetProcAddress32 += x86.Mov("EAX", x86.mem("[EAX + 0xC]")) +GetProcAddress32 += x86.Mov("EAX", x86.mem("[EAX + 0xC]")) # ; RAX on the first elt of the list (first module) +GetProcAddress32 += x86.Mov("EDX", "EAX") +GetProcAddress32 += x86.Label(":a_dest") +GetProcAddress32 += x86.Mov("EAX", "EDX") +GetProcAddress32 += x86.Mov("EBX", x86.mem("[EAX + 0x18]")) # EBX : first base ! (base of current module) +GetProcAddress32 += x86.Cmp("EBX", 0) +GetProcAddress32 += x86.Jz(":NOT_FOUND") +GetProcAddress32 += x86.Mov("ECX", x86.mem("[EAX + 0x30]")) # RCX = NAME (UNICODE_STRING.Buffer) +GetProcAddress32 += x86.Push("ECX") +GetProcAddress32 += x86.Call(":FUNC_STRLENW32") +GetProcAddress32 += x86.Pop("EDI") # Current name +GetProcAddress32 += x86.Mov("ECX", "EAX") +GetProcAddress32 += x86.Mov("ESI", x86.mem("[ESP + 0x18]")) +GetProcAddress32 += x86.Rep + x86.CmpsW() +GetProcAddress32 += x86.Test("ECX", "ECX") +GetProcAddress32 += x86.Jz(":DLL_FOUND") +GetProcAddress32 += x86.Mov("EDX", x86.mem("[EDX]")) +GetProcAddress32 += x86.Jmp(":a_dest") +GetProcAddress32 += x86.Label(":DLL_FOUND") +GetProcAddress32 += x86.Mov("EAX", x86.mem("[EBX + 0x3c]")) # rax = PEBASE RVA +GetProcAddress32 += x86.Add("EAX", "EBX") # RAX = PEBASE +GetProcAddress32 += x86.Add("EAX", 0x18) # ;OPTIONAL HEADER +GetProcAddress32 += x86.Mov("ECX", x86.mem("[EAX + 0x60]")) # ;ecx = RVA export dir +GetProcAddress32 += x86.Add("ECX", "EBX") # ;ecx = export_dir +GetProcAddress32 += x86.Mov("EAX", "ECX") +GetProcAddress32 += x86.Push("EAX") # Save it +# ; EBX = BASE | EAX = EXPORT DIR +GetProcAddress32 += x86.Mov("ECX", x86.mem("[EAX + 24] ")) +GetProcAddress32 += x86.Mov("EBP", "ECX") # ;EBP = NB names +GetProcAddress32 += x86.Mov("EDX", x86.mem("[EAX + 32] ")) # EDX = names array RVA +GetProcAddress32 += x86.Add("EDX", "EBX") # RDX = names array +GetProcAddress32 += x86.Xor("ECX", "ECX") +GetProcAddress32 += x86.Mov("ESI", x86.mem("[ESP + 0x20]")) +GetProcAddress32 += x86.Label(":SEARCH_LOOP") +GetProcAddress32 += x86.Mov("EDI", x86.mem("[EDX + ECX * 4]")) # ;Get function name RVA +GetProcAddress32 += x86.Add("EDI", "EBX") # ;Get name addr +GetProcAddress32 += x86.Push("ECX") # Save current index +GetProcAddress32 += x86.Push("ESI") +GetProcAddress32 += x86.Call(":FUNC_STRLENA32") +GetProcAddress32 += x86.Mov("ECX", "EAX") +GetProcAddress32 += x86.Push("EDI") +GetProcAddress32 += x86.Call(":FUNC_STRLENA32") +GetProcAddress32 += x86.Pop("EDI") +GetProcAddress32 += x86.Cmp("EAX", "ECX") +GetProcAddress32 += x86.Jnz(":ABORT_STRCMP") +GetProcAddress32 += x86.Inc("ECX") +GetProcAddress32 += x86.Rep + x86.CmpsB() +GetProcAddress32 += x86.Label(":ABORT_STRCMP") +GetProcAddress32 += x86.Pop("ESI") +GetProcAddress32 += x86.Mov("EAX", "ECX") +GetProcAddress32 += x86.Pop("ECX") +GetProcAddress32 += x86.Inc("ECX") +GetProcAddress32 += x86.Test("EAX", "EAX") +GetProcAddress32 += x86.Jnz(":SEARCH_LOOP") + +GetProcAddress32 += x86.Dec("ECX") +#GetProcAddress32 += x86.Int3() +#GetProcAddress32 += x86.Int3() # da poi(edx + (ecx * 4)) + ebx; da esi +GetProcAddress32 += x86.Pop("EAX") # ;Restore export_dir addr +GetProcAddress32 += x86.Mov("EDX", x86.mem("[EAX + 36]")) # ;EDX = AddressOfNameOrdinals RVX +GetProcAddress32 += x86.Add("EDX", "EBX") +#GetProcAddress32 += x86.Mov("ECX", x86.mem("[EDX + ECX * 2]")) +GetProcAddress32 += x86.OperandSizeOverride + x86.Mov("ECX", x86.mem("[EDX + ECX * 2]")) +# ; ecx = Ieme ordinal (short array) +GetProcAddress32 += x86.And('ECX', 0xffff) +GetProcAddress32 += x86.Mov("EDX", x86.mem("[EAX + 28]")) # ; AddressOfFunctions RVA +GetProcAddress32 += x86.Add("EDX", "EBX") +GetProcAddress32 += x86.Mov("EDX", x86.mem("[EDX + ECX * 4]")) +GetProcAddress32 += x86.Add("EDX", "EBX") +GetProcAddress32 += x86.Mov("EAX", "EDX") +GetProcAddress32 += x86.Pop("EBP") +GetProcAddress32 += x86.Pop("ESI") +GetProcAddress32 += x86.Pop("EDI") +GetProcAddress32 += x86.Pop("ECX") +GetProcAddress32 += x86.Pop("EBX") +GetProcAddress32 += x86.Ret() +GetProcAddress32 += x86.Label(":NOT_FOUND") +GetProcAddress32 += x86.Xor("EAX", "EAX") +GetProcAddress32 += x86.Ret() +GetProcAddress32 += StrlenW32 +GetProcAddress32 += StrlenA32 \ No newline at end of file diff --git a/windows/native_exec/simple_x64.py b/windows/native_exec/simple_x64.py index 75a9d62..a1b8b37 100644 --- a/windows/native_exec/simple_x64.py +++ b/windows/native_exec/simple_x64.py @@ -715,7 +715,8 @@ class JmpType(Instruction): class Push(Instruction): encoding = [(RawBits.from_int(5, 0x50 >> 3), X64RegisterSelector()), - (RawBits.from_int(8, 0x68), Imm32())] + (RawBits.from_int(8, 0x68), Imm32()), + (RawBits.from_int(8, 0xff), Slash(6))] class Pop(Instruction): diff --git a/windows/native_exec/simple_x86.py b/windows/native_exec/simple_x86.py index 90ba2f6..301633a 100644 --- a/windows/native_exec/simple_x86.py +++ b/windows/native_exec/simple_x86.py @@ -612,7 +612,8 @@ class Jnb(JmpType): class Push(Instruction): encoding = [(RawBits.from_int(5, 0x50 >> 3), X86RegisterSelector()), - (RawBits.from_int(8, 0x68), Imm32())] + (RawBits.from_int(8, 0x68), Imm32()), + (RawBits.from_int(8, 0xff), Slash(6))] class Pop(Instruction): diff --git a/windows/native_exec/test_simple_x64.py b/windows/native_exec/test_simple_x64.py index 181d569..3592548 100644 --- a/windows/native_exec/test_simple_x64.py +++ b/windows/native_exec/test_simple_x64.py @@ -164,6 +164,10 @@ assert Test(mem('[RDI + 0x100]'), 'RCX').get_code() == Test('RCX', mem('[RDI + 0 TestInstr(Push)('R15') TestInstr(Push)(0x42) TestInstr(Push)(-1) +TestInstr(Push)(mem("[ECX]")) +TestInstr(Push)(mem("[RCX]")) + + TestInstr(Call)('RAX') TestInstr(Call)(mem('[RAX + RCX * 8]')) TestInstr(Cpuid)() diff --git a/windows/native_exec/test_simple_x86.py b/windows/native_exec/test_simple_x86.py index f180cf6..8ecf67e 100644 --- a/windows/native_exec/test_simple_x86.py +++ b/windows/native_exec/test_simple_x86.py @@ -115,6 +115,8 @@ TestInstr(Add)(mem('[EAX]'), 10) TestInstr(Mov)('EAX', mem('fs:[0xfffc]')) TestInstr(Mov)(mem('fs:[0xfffc]'), 0) +TestInstr(Push)('ECX') +TestInstr(Push)(mem('[ECX + 8]')) TestInstr(Sub)('ECX', 'ESP') TestInstr(Sub)('ECX', mem('[ESP]')) @@ -153,7 +155,7 @@ TestInstr(CmpsD, expected_result="cmpsd dword ptr [esi], dword ptr es:[edi]")() TestInstr(Test)('EAX', 'EAX') -TestInstr(Test, expected_result="test edi, ecx ")('ECX', 'EDI') +TestInstr(Test, expected_result="test edi, ecx")('ECX', 'EDI') TestInstr(Test)(mem('[ECX + 0x100]'), 'ECX') diff --git a/windows/test/__init__.py b/windows/test/__init__.py index 8b6e9ab..8937c54 100644 --- a/windows/test/__init__.py +++ b/windows/test/__init__.py @@ -1,3 +1,3 @@ -from mytest import WindowsTestCase, WindowsAPITestCase, DebuggerTestCase, pop_calc_32, pop_calc_64, Calc32, Calc64 +from mytest import WindowsTestCase, WindowsAPITestCase, DebuggerTestCase, NativeUtilsTestCase, pop_calc_32, pop_calc_64, Calc32, Calc64 -__all__ = ["WindowsTestCase", "WindowsAPITestCase", "DebuggerTestCase"] +__all__ = ["WindowsTestCase", "WindowsAPITestCase", "DebuggerTestCase", "NativeUtilsTestCase"] diff --git a/windows/test/mytest.py b/windows/test/mytest.py index d3d1b26..a06f08c 100644 --- a/windows/test/mytest.py +++ b/windows/test/mytest.py @@ -3,6 +3,7 @@ import struct import time import os import textwrap +import random from contextlib import contextmanager sys.path.append(".") @@ -11,9 +12,12 @@ import windows import windows.debug import windows.native_exec.simple_x86 as x86 import windows.native_exec.simple_x64 as x64 +import windows.native_exec.nativeutils as nativeutils from windows.generated_def.winstructs import * + + is_process_32_bits = windows.current_process.bitness == 32 is_process_64_bits = windows.current_process.bitness == 64 @@ -299,6 +303,59 @@ class WindowsAPITestCase(unittest.TestCase): with self.assertRaises(WindowsError) as ar: windows.winproxy.CreateFileA("NONEXISTFILE.FILE") +class NativeUtilsTestCase(unittest.TestCase): + + @process_64bit_only + def test_strlenw64(self): + strlenw64 = windows.native_exec.create_function(nativeutils.StrlenW64.get_code(), [UINT, LPCWSTR]) + self.assertEqual(strlenw64("YOLO"), 4) + self.assertEqual(strlenw64(""), 0) + + @process_64bit_only + def test_strlena64(self): + strlena64 = windows.native_exec.create_function(nativeutils.StrlenA64.get_code(), [UINT, LPCSTR]) + self.assertEqual(strlena64("YOLO"), 4) + self.assertEqual(strlena64(""), 0) + + @process_64bit_only + def test_getprocaddr64(self): + getprocaddr64 = windows.native_exec.create_function(nativeutils.GetProcAddress64.get_code(), [ULONG64, LPCWSTR, LPCSTR]) + k32 = [mod for mod in windows.current_process.peb.modules if mod.name == "kernel32.dll"][0] + exports = [(x,y) for x,y in k32.pe.exports.items() if isinstance(x, basestring)] + + for i in range(15): + name, addr = random.choice(exports) + name = name.encode() + compute_addr = getprocaddr64("KERNEL32.DLL", name) + # Put name in test to know which function caused the assert fails + self.assertEqual((name, hex(addr)), (name, hex(compute_addr))) + + @process_32bit_only + def test_strlenw32(self): + strlenw32 = windows.native_exec.create_function(nativeutils.StrlenW32.get_code(), [UINT, LPCWSTR]) + self.assertEqual(strlenw32("YOLO"), 4) + self.assertEqual(strlenw32(""), 0) + + @process_32bit_only + def test_strlena32(self): + strlena32 = windows.native_exec.create_function(nativeutils.StrlenA32.get_code(), [UINT, LPCSTR]) + self.assertEqual(strlena32("YOLO"), 4) + self.assertEqual(strlena32(""), 0) + + @process_32bit_only + def test_getprocaddr32(self): + getprocaddr32 = windows.native_exec.create_function(nativeutils.GetProcAddress32.get_code(), [UINT, LPCWSTR, LPCSTR]) + k32 = [mod for mod in windows.current_process.peb.modules if mod.name == "kernel32.dll"][0] + exports = [(x,y) for x,y in k32.pe.exports.items() if isinstance(x, basestring)] + + + for i in range(1500): + name, addr = random.choice(exports) + name = name.encode() + compute_addr = getprocaddr32("KERNEL32.DLL", name) + # Put name in test to know which function caused the assert fails + self.assertEqual((name, hex(addr)), (name, hex(compute_addr))) + class DebuggerTestCase(unittest.TestCase): @@ -447,6 +504,7 @@ if __name__ == '__main__': alltests.addTest(unittest.makeSuite(WindowsTestCase)) alltests.addTest(unittest.makeSuite(WindowsAPITestCase)) alltests.addTest(unittest.makeSuite(DebuggerTestCase)) + alltests.addTest(unittest.makeSuite(NativeUtilsTestCase)) alltests.debug() tester = unittest.TextTestRunner(verbosity=2) tester.run(alltests)