Add sample / test for memory breakpoint and single step

This commit is contained in:
Clement Rouault
2016-07-13 10:10:12 +02:00
parent 8bf3c0312c
commit 125888cd60
4 changed files with 312 additions and 39 deletions
+100 -2
View File
@@ -156,14 +156,112 @@ KeyValue(name='MYQWORD', value=123456789987654321L, type=11)
# Explore Values
>>> tstkey.values
[KeyValue(name='MYQWORD', value=123456789987654321L, type=11), KeyValue(name='VALUE', value=u'a_value_for_my_key', type=1)]
```
### Debugger
PythonForWindows provides a standard debugger to debug other processes.
```python
import windows
import windows.debug
import windows.test
import windows.native_exec.simple_x86 as x86
from windows.test import pop_calc_32
from windows.generated_def import EXCEPTION_ACCESS_VIOLATION
class MyDebugger(windows.debug.Debugger):
def on_exception(self, exception):
code = exception.ExceptionRecord.ExceptionCode
addr = exception.ExceptionRecord.ExceptionAddress
print("Got exception {0} at 0x{1:x}".format(code, addr))
if code == EXCEPTION_ACCESS_VIOLATION:
print("Access Violation: kill target process")
self.current_process.exit()
calc = windows.test.pop_calc_32(dwCreationFlags=DEBUG_PROCESS)
d = MyDebugger(calc)
calc.execute(x86.assemble("int3; mov [0x42424242], EAX; ret"))
d.loop()
## Ouput ##
Got exception EXCEPTION_BREAKPOINT(0x80000003L) at 0x77e13c7d
Got exception EXCEPTION_BREAKPOINT(0x80000003L) at 0x230000
Got exception EXCEPTION_ACCESS_VIOLATION(0xc0000005L) at 0x230001
Access Violation: kill target process
```
The debugger handles
* Standard breakpoint ``int3``
* Hardware Execution breakpoint ``DrX``
* Memory breakpoint ``virtual protect``
#### LocalDebugger
You can also debug your own process (or debug a process by injection) via the LocalDebugger.
The LocalDebugger is an abstraction around Vectored Exception Handler (VEH)
```python
import windows
from windows.generated_def.winstructs import *
import windows.native_exec.simple_x86 as x86
class SingleSteppingDebugger(windows.debug.LocalDebugger):
SINGLE_STEP_COUNT = 4
def on_exception(self, exc):
code = self.get_exception_code()
context = self.get_exception_context()
print("EXCEPTION !!!! Got a {0} at 0x{1:x}".format(code, context.pc))
self.SINGLE_STEP_COUNT -= 1
if self.SINGLE_STEP_COUNT:
return self.single_step()
return EXCEPTION_CONTINUE_EXECUTION
class RewriteBreakpoint(windows.debug.HXBreakpoint):
def trigger(self, dbg, exc):
context = dbg.get_exception_context()
print("GOT AN HXBP at 0x{0:x}".format(context.pc))
# Rewrite the infinite loop with 2 nop
windows.current_process.write_memory(self.addr, "\x90\x90")
# Ask for a single stepping
return dbg.single_step()
d = SingleSteppingDebugger()
# Infinite loop + nop + ret
code = x86.assemble("label :begin; jmp :begin; nop; ret")
func = windows.native_exec.create_function(code, [PVOID])
print("Code addr = 0x{0:x}".format(func.code_addr))
# Create a thread that will infinite loop
t = windows.current_process.create_thread(func.code_addr, 0)
# Add a breakpoint on the infitine loop
d.add_bp(RewriteBreakpoint(func.code_addr))
t.wait()
print("Done!")
## Output ##
Code addr = 0x6a0002
GOT AN HXBP at 0x6a0002
EXCEPTION !!!! Got a EXCEPTION_SINGLE_STEP(0x80000004L) at 0x6a0003
EXCEPTION !!!! Got a EXCEPTION_SINGLE_STEP(0x80000004L) at 0x6a0004
EXCEPTION !!!! Got a EXCEPTION_SINGLE_STEP(0x80000004L) at 0x6a0005
EXCEPTION !!!! Got a EXCEPTION_SINGLE_STEP(0x80000004L) at 0x770c7c04
Done!
```
The local debugger handles
* Standard breakpoint ``int3``
* Hardware Execution breakpoint ``DrX``
### Other stuff (see doc / samples)
- Debugger
- LocalDebugger (VEH based)
- Network
- Services
- COM
+7 -1
View File
@@ -38,6 +38,10 @@ class SingleStepOnWrite(windows.debug.MemoryBreakpoint):
eip = dbg.current_thread.context.pc
print("Instruction at <{0:#x}> wrote at <{1:#x}>".format(eip, fault_addr))
dbg.single_step_counter = 4
#import pdb;pdb.set_trace()
#if fault_addr == self.addr + 4:
# print("Delete self BP")
# dbg.del_bp(self)
return dbg.single_step()
@@ -52,12 +56,14 @@ injected += x86.Mov("EAX", 0)
injected += x86.Mov(x86.deref(data), "EAX")
injected += x86.Add("EAX", 4)
injected += x86.Mov(x86.deref(data + 4), "EAX")
injected += x86.Add("EAX", 8)
injected += x86.Mov(x86.deref(data + 8), "EAX")
injected += x86.Nop()
injected += x86.Nop()
injected += x86.Ret()
calc.write_memory(code, injected.get_code())
d.add_bp(SingleStepOnWrite(data, size=0x1000))
d.add_bp(SingleStepOnWrite(data + 1, size=5))
calc.create_thread(code, 0)
d.loop()
+18 -31
View File
@@ -3,54 +3,41 @@ import os.path
import pprint
sys.path.append(os.path.abspath(__file__ + "\..\.."))
import ctypes
import windows
import windows.debug
from windows.generated_def.winstructs import *
import windows.native_exec.simple_x86 as x86
ct = windows.current_thread
t = [t for t in windows.current_process.threads if t.tid == ct.tid][0]
class YoloDebugger(windows.debug.LocalDebugger):
def __init__(self, single_step_count):
super(YoloDebugger, self).__init__()
self.single_step_count = single_step_count
class SingleSteppingDebugger(windows.debug.LocalDebugger):
SINGLE_STEP_COUNT = 4
def on_exception(self, exc):
code = self.get_exception_code()
context = self.get_exception_context()
print("EXCEPTION !!!! Got a {0} at 0x{1:x}".format(code, context.pc))
if self.single_step_count:
self.single_step_count -= 1
self.SINGLE_STEP_COUNT -= 1
if self.SINGLE_STEP_COUNT:
return self.single_step()
return EXCEPTION_CONTINUE_EXECUTION
class YoloHXBP(windows.debug.HXBreakpoint):
class RewriteBreakpoint(windows.debug.HXBreakpoint):
def trigger(self, dbg, exc):
context = dbg.get_exception_context()
print("GOT AN HXBP <3 at 0x{0:x}".format(context.pc))
print("GOT AN HXBP at 0x{0:x}".format(context.pc))
# Rewrite the infinite loop with 2 nop
windows.current_process.write_memory(self.addr, "\x90\x90")
# Ask for a single stepping
return dbg.single_step()
print("Your main thread is {0}".format(windows.current_thread.tid))
d = YoloDebugger(5)
d = SingleSteppingDebugger()
# Infinite loop + nop + ret
addr = windows.native_exec.native_function.allocator.write_code("\xeb\xfe\x90\x90\x90\x90\xc3")
func_type = ctypes.CFUNCTYPE(PVOID)
func = func_type(addr)
print("Code addr = 0x{0:x}".format(addr))
t = windows.current_process.create_thread(addr, 0)
d.add_bp(YoloHXBP(addr))
code = x86.assemble("label :begin; jmp :begin; nop; ret")
func = windows.native_exec.create_function(code, [PVOID])
print("Code addr = 0x{0:x}".format(func.code_addr))
# Create a thread that will infinite loop
t = windows.current_process.create_thread(func.code_addr, 0)
# Add a breakpoint on the infitine loop
d.add_bp(RewriteBreakpoint(func.code_addr))
t.wait()
print("Done!")
+187 -5
View File
@@ -1,6 +1,8 @@
from test_utils import *
from windows.generated_def.winstructs import *
import threading
class DebuggerTestCase(unittest.TestCase):
def debuggable_calc_32(self):
return windows.utils.create_process(r"C:\python27\python.exe", dwCreationFlags=DEBUG_PROCESS | CREATE_NEW_CONSOLE, show_windows=True)
@@ -380,10 +382,11 @@ class DebuggerTestCase(unittest.TestCase):
def trigger(self, dbg, exc):
fault_addr = exc.ExceptionRecord.ExceptionInformation[1]
eax = dbg.current_thread.context.Eax
if eax == 42:
dbg.current_process.exit()
return
TEST_CASE.assertEqual(fault_addr, data + eax)
store_data[0] += 1
if store_data[0] == 2:
dbg.current_process.exit()
return
calc = pop_calc_32(dwCreationFlags=DEBUG_PROCESS)
@@ -396,17 +399,22 @@ class DebuggerTestCase(unittest.TestCase):
injected += x86.Mov(x86.deref(data), "EAX")
injected += x86.Add("EAX", 4)
injected += x86.Mov(x86.deref(data + 4), "EAX")
injected += x86.Add("EAX", 4)
# This one should NOT trigger the MemBP of size 8
injected += x86.Mov(x86.deref(data + 8), "EAX")
injected += x86.Mov("EAX", 42)
injected += x86.Mov(x86.deref(data), "EAX")
injected += x86.Ret()
calc.write_memory(addr, injected.get_code())
d.add_bp(TSTBP(data, size=0x1000))
d.add_bp(TSTBP(data, size=0x8))
calc.create_thread(addr, 0)
d.loop()
# Used to verif we actually called the Breakpoints
# Used to verif we actually called the Breakpoints for the good addresses
TEST_CASE.assertEqual(store_data[0], 2)
def test_memory_breakpoint_exec(self):
"""Check that HXBPBP/dbg can trigger single step"""
"""Check MemoryBP EXEC"""
TEST_CASE = self
NB_NOP_IN_PAGE = 3
data = []
@@ -432,6 +440,180 @@ class DebuggerTestCase(unittest.TestCase):
for i in range(NB_NOP_IN_PAGE + 1):
TEST_CASE.assertEqual(data[i], addr + i)
def test_standard_breakpoint_self_remove(self):
TEST_CASE = self
data = []
def do_check():
calc.execute_python_unsafe("open(u'FILENAME1')").wait()
calc.execute_python_unsafe("open(u'FILENAME2')").wait()
calc.execute_python_unsafe("open(u'FILENAME3')").wait()
calc.exit()
class TSTBP(windows.debug.Breakpoint):
def trigger(self, dbg, exc):
addr = exc.ExceptionRecord.ExceptionAddress
ctx = dbg.current_thread.context
filename = dbg.current_process.read_wstring(dbg.current_process.read_ptr(ctx.sp + 0x4))
data.append(filename)
if filename == u"FILENAME2":
dbg.del_bp(self)
calc = pop_calc_32(dwCreationFlags=DEBUG_PROCESS)
d = windows.debug.Debugger(calc)
d.add_bp(TSTBP("kernel32.dll!CreateFileW"))
threading.Thread(target=do_check).start()
d.loop()
TEST_CASE.assertEqual(data, [u"FILENAME1", u"FILENAME2"])
def test_standard_breakpoint_remove(self):
TEST_CASE = self
data = []
def do_check():
calc.execute_python_unsafe("open(u'FILENAME1')").wait()
calc.execute_python_unsafe("open(u'FILENAME2')").wait()
d.del_bp(the_bp)
calc.execute_python_unsafe("open(u'FILENAME3')").wait()
calc.exit()
class TSTBP(windows.debug.Breakpoint):
def trigger(self, dbg, exc):
addr = exc.ExceptionRecord.ExceptionAddress
ctx = dbg.current_thread.context
filename = dbg.current_process.read_wstring(dbg.current_process.read_ptr(ctx.sp + 0x4))
data.append(filename)
calc = pop_calc_32(dwCreationFlags=DEBUG_PROCESS)
d = windows.debug.Debugger(calc)
the_bp = TSTBP("kernel32.dll!CreateFileW")
d.add_bp(the_bp)
threading.Thread(target=do_check).start()
d.loop()
TEST_CASE.assertEqual(data, [u"FILENAME1", u"FILENAME2"])
def test_hxbp_breakpoint_remove(self):
TEST_CASE = self
data = []
def do_check():
calc.execute_python_unsafe("open(u'FILENAME1')").wait()
calc.execute_python_unsafe("open(u'FILENAME2')").wait()
d.del_bp(the_bp)
calc.execute_python_unsafe("open(u'FILENAME3')").wait()
calc.exit()
class TSTBP(windows.debug.HXBreakpoint):
def trigger(self, dbg, exc):
addr = exc.ExceptionRecord.ExceptionAddress
ctx = dbg.current_thread.context
filename = dbg.current_process.read_wstring(dbg.current_process.read_ptr(ctx.sp + 0x4))
data.append(filename)
calc = pop_calc_32(dwCreationFlags=DEBUG_PROCESS)
d = windows.debug.Debugger(calc)
the_bp = TSTBP("kernel32.dll!CreateFileW")
d.add_bp(the_bp)
threading.Thread(target=do_check).start()
d.loop()
TEST_CASE.assertEqual(data, [u"FILENAME1", u"FILENAME2"])
def test_hxbp_breakpoint_self_remove(self):
TEST_CASE = self
data = []
def do_check():
calc.execute_python_unsafe("open(u'FILENAME1')").wait()
calc.execute_python_unsafe("open(u'FILENAME2')").wait()
calc.execute_python_unsafe("open(u'FILENAME3')").wait()
calc.exit()
class TSTBP(windows.debug.HXBreakpoint):
def trigger(self, dbg, exc):
addr = exc.ExceptionRecord.ExceptionAddress
ctx = dbg.current_thread.context
filename = dbg.current_process.read_wstring(dbg.current_process.read_ptr(ctx.sp + 0x4))
data.append(filename)
if filename == u"FILENAME2":
#import pdb;pdb.set_trace()
dbg.del_bp(self)
calc = pop_calc_32(dwCreationFlags=DEBUG_PROCESS)
d = windows.debug.Debugger(calc)
d.add_bp(TSTBP("kernel32.dll!CreateFileW"))
threading.Thread(target=do_check).start()
d.loop()
TEST_CASE.assertEqual(data, [u"FILENAME1", u"FILENAME2"])
def test_mem_breakpoint_remove(self):
TEST_CASE = self
data = []
def generate_read_at(addr):
res = x86.MultipleInstr()
res += x86.Mov("EAX", x86.deref(addr))
res += x86.Ret()
return res.get_code()
def do_check():
calc.execute(generate_read_at(data_addr)).wait()
calc.execute(generate_read_at(data_addr + 4)).wait()
d.del_bp(the_bp)
calc.execute(generate_read_at(data_addr + 8)).wait()
calc.exit()
class TSTBP(windows.debug.MemoryBreakpoint):
DEFAULT_PROTECT = PAGE_NOACCESS
def trigger(self, dbg, exc):
addr = exc.ExceptionRecord.ExceptionAddress
fault_addr = exc.ExceptionRecord.ExceptionInformation[1]
data.append(fault_addr)
calc = pop_calc_32(dwCreationFlags=DEBUG_PROCESS)
d = windows.debug.Debugger(calc)
data_addr = calc.virtual_alloc(0x1000)
the_bp = TSTBP(data_addr, size=0x1000)
d.add_bp(the_bp)
threading.Thread(target=do_check).start()
d.loop()
TEST_CASE.assertEqual(data, [data_addr, data_addr + 4])
def test_mem_breakpoint_self_remove(self):
TEST_CASE = self
data = []
def generate_read_at(addr):
res = x86.MultipleInstr()
res += x86.Mov("EAX", x86.deref(addr))
res += x86.Ret()
return res.get_code()
def do_check():
calc.execute(generate_read_at(data_addr)).wait()
calc.execute(generate_read_at(data_addr + 4)).wait()
calc.execute(generate_read_at(data_addr + 8)).wait()
calc.exit()
class TSTBP(windows.debug.MemoryBreakpoint):
DEFAULT_PROTECT = PAGE_NOACCESS
def trigger(self, dbg, exc):
addr = exc.ExceptionRecord.ExceptionAddress
fault_addr = exc.ExceptionRecord.ExceptionInformation[1]
data.append(fault_addr)
if fault_addr == data_addr + 4:
dbg.del_bp(self)
calc = pop_calc_32(dwCreationFlags=DEBUG_PROCESS)
d = windows.debug.Debugger(calc)
data_addr = calc.virtual_alloc(0x1000)
the_bp = TSTBP(data_addr, size=0x1000)
d.add_bp(the_bp)
threading.Thread(target=do_check).start()
d.loop()
TEST_CASE.assertEqual(data, [data_addr, data_addr + 4])
if __name__ == '__main__':
alltests = unittest.TestSuite()
alltests.addTest(unittest.makeSuite(DebuggerTestCase))