ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
## 4.1扩展断点处理 在前面的章节中我们讲解了用事件处理函数处理调试事件的方法。用 PyDbg 可以很容 易的扩展这种功能,只需要构建一个用户模式的回调函数。当收到一个调试事件的时候,回 调函数执行我们定义的操作。比如读取特定地址的数据,设置更更多的断点,操作内存。操 作完成后,再将权限交还给调试器,恢复被调试的进程。 PyDbg 设置函数的断点原型如下: ``` bp_set(address, description="",restore=True,handler=None) ``` address 是要设置的断点的地址,description 参数可选,用来给每个断点设置唯一的名字。 restore 决定了是否要在断点被触发以后重新设置, handler 指向断点触发时候调用的回调函 数。断点回调函数只接收一个参数,就是 pydbg()类的实例化对象。所有的上下文数据,线 程,进程信息都在回调函数被调用的时候,装填在这个类中。 以 printf_loop.py 为测试目标,让我们实现一个自定义的回调函数。这次我们在 printf() 函数上下断点,以便读取 printf()输出时用到的参数 counter 变量,之后用一个 1 到 100 的随 机数替换这个变量的值,最后再打印出来。记住,我们是在目标进程内处理,拷贝,操作这 些实时的断点信息。这非常的强大!新建一个 printf_random.py 文件,键入下面的代码。 ``` #printf_random.py from pydbg import * from pydbg.defines import * import struct import random # This is our user defined callback function def printf_randomizer(dbg): # Read in the value of the counter at ESP + 0x8 as a DWORD parameter_addr = dbg.context.Esp + 0x8 counter = dbg.read_process_memory(parameter_addr,4) # When we use read_process_memory, it returns a packed binary # string. We must first unpack it before we can use it further. counter = struct.unpack("L",counter)[0] print "Counter: %d" % int(counter) # Generate a random number and pack it into binary format # so that it is written correctly back into the process random_counter = random.randint(1,100) random_counter = struct.pack("L",random_counter)[0] # Now swap in our random number and resume the process dbg.write_process_memory(parameter_addr,random_counter) return DBG_CONTINUE # Instantiate the pydbg class dbg = pydbg() # Now enter the PID of the printf_loop.py process pid = raw_input("Enter the printf_loop.py PID: ") # Attach the debugger to that process dbg.attach(int(pid)) # Set the breakpoint with the printf_randomizer function # defined as a callback printf_address = dbg.func_resolve("msvcrt","printf") dbg.bp_set(printf_address,description="printf_address",handler=printf_randomizer) # Resume the process dbg.run() ``` 现在运行 printf_loop.py 和 printf_random.py 两个文件。输出结果将和表 4-1 相似。 Table 4-1:调试器和进程的输出 | Output from Debugger | Output from Debugged Process | | --- | --- | | Enter the printf_loop.py PID: 3466 | Loop iteration 0! | | … | Loop iteration 1! | | … | Loop iteration 2! | | … | Loop iteration 3! | | Counter: 4 | Loop iteration 32! | | Counter: 5 | Loop iteration 39! | | Counter: 6 | Loop iteration 86! | | Counter: 7 | Loop iteration 22! | | Counter: 8 | Loop iteration 70! | | Counter: 9 | Loop iteration 95! | | Counter: 10 | Loop iteration 60! | 为了不把你搞混,让我们看看 printf_loop.py 代码。 ``` from ctypes import * import time msvcrt = cdll.msvcrt counter = 0 while 1: msvcrt.printf("Loop iteration %d!\n" % counter) time.sleep(2) counter += 1 ``` 先搞明白一点,printf()接受的这个 counter 是主函数里 counter 的拷贝,就是说在 printf 函数内部,无论怎么修改都不会影响到外面的这个 counter(C 语言所说的只有传递指针才能真 正的改变值)。 你应该看到,调试器在 printf 循环到第 counter 变量为 4 的时候才设置了断点。这是 因 为被 counter 被捕捉到的时候已经为 4 了(这是为了让大家看到对比结果,不要认为调试器 傻了)。同样你会看到 printf_loop.py 的输出结果一直到 3 都是正常的。到 4 的时候,printf() 被中断,内部的 counter 被随即修改为 32!这个例子很简单且强大,它告诉了你在调试事件 发生的时候如何构建回调函数完成自定义的操作。现在让我们看一看 PyDbg 是如何处理应 用程序崩溃的。