最近在看Gray Hat Python这本讲debugger的书,按着上面的说明走,发现printf_random.py这个执行不正确,努力了一把,终于找到了原因,趁着还有些印象,写下来。
printf_random.py这段代码的作用是,在执行后,attach到另一个已运行的进程中,在那个进程的某一个地方设置一个soft breakpoint,然后通过读取并修改该进程的某些变量来完成debugger的工作,先看一下我们要监控的进程
from ctypes import * import time import os print os.getpid() msvcrt = cdll.msvcrt counter = 0 while 1: msvcrt.printf("Loop iteration %d!\n" % counter) time.sleep(2) counter += 1
这个很简单,就是python通过调用Windows的msvcrt库中的printf函数,每隔2秒钟打印一行字符而已。
现在我们来看看气到debugger作用的监控代码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 + 0x08 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. #print "Before unpack, Count = %0x08x" % int(counter)[0] 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") print "printf_address: 0x%08x" % printf_address dbg.bp_set(printf_address, description="printf_address", handler=printf_randomizer) # Resume the process dbg.run()
代码中大体的步骤是,找到printf的地址,在这里设置一个soft breakpoint,每当执行到这个地址的时候,通过ESP + 0×8来获取printf函数的第二个参数(ESP + 0×0是Return address, ESP + 0×4是第一个参数,也就是printf_loop.py中printf所接受的一个string,ESP + 0×8就是printf的第二个参数,counter);当获取到这个printf的参数counter后,通过random生成一个1到100间的随机数来替换printf所接收counter的实际值,然后打印出来,但是当我运行时,出现了这样的问题
printf_loop.py的输出
C:\Windows\System32\config\systemprofile\workspace\Gray Hat Python\src>python pr intf_loop.py 4884 Loop iteration 0! Loop iteration 1! Loop iteration 2! Loop iteration 3! Loop iteration 4! Loop iteration 5! Loop iteration 6! Loop iteration 7! Loop iteration 8! Loop iteration 9! Loop iteration 10! Loop iteration 11! Traceback (most recent call last): File "printf_loop.py", line 16, inmsvcrt.printf("Loop iteration %d!\n" % counter) WindowsError: exception: breakpoint encountered
printf_random.py的输出
Enter the printf_loop.py PID: 4884 printf_address: 0x752cc5b9 Counter: 2227068 Counter: 2227068 Counter: 2227068 Counter: 2227068 Counter: 2227068 Counter: 2227068
发现counter根本没有抓到,看了一个pydbg的文档,发现pydbg提供了一个比较有用的函数,dump_context(),如下加进代码后再看一下输出
parameter_addr = dbg.context.Esp + 0x08 print dbg.dump_context() counter = dbg.read_process_memory(parameter_addr, 4)
结果如下
Enter the printf_loop.py PID: 9296 printf_address: 0x752cc5b9 CONTEXT DUMP EIP: 752cc5b9 push byte 0xc EAX: 0021fb7c ( 2227068) -> ..........!...............!.....P-...... .!...!.x.!.............\.!..x........,u..!...!.......!..................x....!..........w...w..h........w......h...|................v..0...HF............!.......!.....0... .!...........!..E....,u................`&.. (stack) EBX: 0021fb01 ( 2226945) -> .!...,u|.!...!...!...!.br....,u..!...!...!.x...d.....!.v.....!.B.......d.!..I0q..!.p.!..I0q..!.x.....!.......!...!.$*....!...........!...............!.....P-...... .!...!.x.!.............\.!..x........,u..!...!.......!..................x....!..........w... (stack) ECX: 00000001 ( 1) -> N/A EDX: 0021fae0 ( 2226912) -> |...|.!...!...........!...........!...,u|.!...!...!...!.br....,u..!...!...!.x...d.....!.v.....!.B.......d.!..I0q..!.p.!..I0q..!.x.....!.......!...!.$*....!...........!...............!.....P-...... .!...!.x.!.............\.!..x........,u..!...!.......!..... (stack) EDI: 01b08ad8 ( 28347096) -> ........................K'......................0............... *.......................................[....P.@.......L..8@........L{O ....................r0. .......=...@....................!LZx...`&...................................................... (heap) ESI: 0021fae4 ( 2226916) -> |! (stack) EBP: 0021fae8 ( 2226920) -> ..!...........!...........!...,u|.!...!...!...!.br....,u..!...!...!.x...d.....!.v.....!.B.......d.!..I0q..!.p.!..I0q..!.x.....!.......!...!.$*....!...........!...............!.....P-...... .!...!.x.!.............\.!..x........,u..!...!.......!............. (stack) ESP: 0021fadc ( 2226908) -> ....|...|.!...!...........!...........!...,u|.!...!...!...!.br....,u..!...!...!.x...d.....!.v.....!.B.......d.!..I0q..!.p.!..I0q..!.x.....!.......!...!.$*....!...........!...............!.....P-...... .!...!.x.!.............\.!..x........,u..!...!.......!. (stack) +00: 1d1aadb8 ( 488287672) -> N/A +04: 01fe187c ( 33429628) -> Loop iteration 5! (heap) +08: 0021fb7c ( 2227068) -> ..........!...............!.....P-...... .!...!.x.!.............\.!..x........,u..!...!.......!..................x....!..........w...w..h........w......h...|................v..0...HF............!.......!.....0... .!...........!..E....,u................`&.. (stack) +0c: 0021fb14 ( 2226964) -> ..!.br....,u..!...!...!.x...d.....!.v.....!.B.......d.!..I0q..!.p.!..I0q..!.x.....!.......!...!.$*....!...........!...............!.....P-...... .!...!.x.!.............\.!..x........,u..!...!.......!..................x....!..........w...w..h........w...... (stack) +10: 1d1a9ef1 ( 488283889) -> N/A +14: 1d1a9db6 ( 488283574) -> N/A Counter: 2227068 CONTEXT DUMP EIP: 752cc5b9 push byte 0xc EAX: 0021fb7c ( 2227068) -> ..........!...............!.....P-...... .!...!.x.!.............\.!..x........,u..!...!.......!..................x....!..........w...w..h........w......h...|................v..0...HF............!.......!.....0... .!...........!..E....,u................`&.. (stack) EBX: 0021fb01 ( 2226945) -> .!...,u|.!...!...!...!.br....,u..!...!...!.x...d.....!.v.....!.B.......d.!..I0q..!.p.!..I0q..!.x.....!.......!...!.$*....!...........!...............!.....P-...... .!...!.x.!.............\.!..x........,u..!...!.......!..................x....!..........w... (stack) ECX: 00000001 ( 1) -> N/A EDX: 0021fae0 ( 2226912) -> |...|.!...!...........!...........!...,u|.!...!...!...!.br....,u..!...!...!.x...d.....!.v.....!.B.......d.!..I0q..!.p.!..I0q..!.x.....!.......!...!.$*....!...........!...............!.....P-...... .!...!.x.!.............\.!..x........,u..!...!.......!..... (stack) EDI: 01b08ad8 ( 28347096) -> ........................K'......................0............... *.......................................[....P.@.......L..8@........L{O ....................r0. .......=...@....................!LZx...`&...................................................... (heap) ESI: 0021fae4 ( 2226916) -> |! (stack) EBP: 0021fae8 ( 2226920) -> ..!...........!...........!...,u|.!...!...!...!.br....,u..!...!...!.x...d.....!.v.....!.B.......d.!..I0q..!.p.!..I0q..!.x.....!.......!...!.$*....!...........!...............!.....P-...... .!...!.x.!.............\.!..x........,u..!...!.......!............. (stack) ESP: 0021fadc ( 2226908) -> ....|...|.!...!...........!...........!...,u|.!...!...!...!.br....,u..!...!...!.x...d.....!.v.....!.B.......d.!..I0q..!.p.!..I0q..!.x.....!.......!...!.$*....!...........!...............!.....P-...... .!...!.x.!.............\.!..x........,u..!...!.......!. (stack) +00: 1d1aadb8 ( 488287672) -> N/A +04: 01fe187c ( 33429628) -> Loop iteration 6! (heap) +08: 0021fb7c ( 2227068) -> ..........!...............!.....P-...... .!...!.x.!.............\.!..x........,u..!...!.......!..................x....!..........w...w..h........w......h...|................v..0...HF............!.......!.....0... .!...........!..E....,u................`&.. (stack) +0c: 0021fb14 ( 2226964) -> ..!.br....,u..!...!...!.x...d.....!.v.....!.B.......d.!..I0q..!.p.!..I0q..!.x.....!.......!...!.$*....!...........!...............!.....P-...... .!...!.x.!.............\.!..x........,u..!...!.......!..................x....!..........w...w..h........w...... (stack) +10: 1d1a9ef1 ( 488283889) -> N/A +14: 1d1a9db6 ( 488283574) -> N/A Counter: 2227068
可以清晰的看到ESP + 0x8的内容是+08: 0021fb7c ( 2227068),也就是代码中抓到的值,但这个根本不是printf中counter的值。
仔细看一下输出内容可以发现,
+04: 01fe187c ( 33429628) -> Loop iteration 6! (heap)
输出的内容已经在ESP + 0x4的位置,但奇怪的是counter的值在这里已经被替换为6,而不是通过printf自己去解析的,所以转回去看看printf_loop.py的代码,发现
msvcrt.printf("Loop iteration %d!\n" % counter)
这个是python的语法,不是C的,这样写,在调用C的printf之前参数就已经预先被python处理了一遍,所以printf在这种情况下只有一个参数,因此通过ESP + 0x8的方式获取第二个参数肯定是失败的,在改为C的格式后
msvcrt.printf("Loop iteration %d!\n", counter)
结果就OK了
C:\Windows\System32\config\systemprofile\workspace\Gray Hat Python\src>python pr intf_loop.py 4897 Loop iteration 0! Loop iteration 1! Loop iteration 2! Loop iteration 3! Loop iteration 36! Loop iteration 28! Loop iteration 54! Loop iteration 77! Loop iteration 89! Traceback (most recent call last): File "printf_loop.py", line 16, inmsvcrt.printf("Loop iteration %d!\n" % counter) WindowsError: exception: breakpoint encountered
Enter the printf_loop.py PID: 4897 printf_address: 0x752cc5b9 Counter: 4 Counter: 5 Counter: 6 Counter: 7 Counter: 8
后记:
后来想了想,在python语法格式的情况下,可以通过分析字符串内容的方式将counter的值获取出来,代码如下
def new_printf_randomizer(dbg): # 只读取printf的第一个参数,为一个字符串地址,DWORD格式,分配在heap上 parameter_addr = dbg.context.Esp + 0x4 counter = dbg.read_process_memory(parameter_addr, 4) # When using read_process_memory, it returns a packed binary # string, we must first unpack it before we can use it further string_addr = struct.unpack("L", counter)[0] # "Loop iteration "的长度是15,printf_loop中短时间内不会超过3位数 # printf_loop中字符串已"!\n"结尾,为2个bytes,所以最终长度为 str_len = 15 + 3 + 2 counter_string = dbg.read_process_memory(string_addr, int(str_len)) counter_string = struct.unpack(str(str_len) + "s", counter_string)[0] counter_string = counter_string.split("!\n")[0] # 获取contern值 counter = counter_string[15:] print "Counter: %d" % int(counter) return DBG_CONTINUE
这里没有对printf的counter值做修改或替换。修改的方法也很简单,因为printf只有一个字符串参数,所以把counter当作字符串处理就可以了,不需要使用”L”来pack一下,唯一需要注意的是要修改counter后面的”!\n”,并且注意不要越界就可以了