最近,在工作之余学习python逆向工程,找到了一本翻译过来的中文参考书:Python灰帽子——黑客与逆向工程师的Python编程之道。按照书上的顺序,从基础开始构建基于python语言的调试器,目前已经推进到第四章。期间也遇到了一些问题,经过不断地百度和查阅相关书籍,最终都成功解决,同时有了很多收获。这里,想要将这些问题和解决办法记录下来,供以后回头学习。同时期望能够帮助到刚刚入门python,同样在学习的Python灰帽子的同学。

在运行4.1节扩展断点处理例程时,总是得不到预期的效果。
中文版被中断的目标代码如下:

#print_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

调试器代码如下:

#print_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)

    counter = struct.unpack("L",counter)[0]
    print "Counter: %d" % int(counter)

    # When using read_process_memory, it returns a packed binary
    # string, we must first unpack it before we can use it further


    # 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)
    print "random_counter: %d" %random_counter
    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()

运行上述代码,出现如下结果:

python黑帽子第二版pdf密码 python灰帽子pdf百度网盘_python灰帽子

发现通过中断获取的counter结果并不对,通过搜索发现有人在运行这段代码时出现一样的错误,并给出了解决的办法和思路,链接如下:https://wenku.baidu.com/view/c70de996a0116c175f0e48f7

本着求知精神,并没有采用这种办法,继续探索简洁的解决办法。通过深入学习栈帧知识,最终发现问题出在目标代码这一句上:

msvcrt.printf("Loop iteration %d:\n" %counter)

这句调用printf函数输入,实际上只传递了一个实参:”Loop iteration x:\n”,这是一个字符串,其中x代表当前counter值。因此在渗透代码实现printf函数调用前中断时,只有一个实参,且保存在ESP+0x4,这里保存一个指向字符串”Loop iteration x:\n”的指针(如下图所示)。上面链接里的方法就是通过读取ESP+0x4位置处的地址,并进一步读取字符串”Loop iteration x:\n”,然后通过字符串操作实现获取x值,但是这又涉及到一个问题:x的位数?因此,采用这种方法需要进一步考虑x(也即counter)的位数。写入随机数也是同样道理,替换x值即可,但同样有数字位数问题。

python黑帽子第二版pdf密码 python灰帽子pdf百度网盘_字符串_02

但是改成如下形式就不存在这个问题。

msvcrt.printf("Loop iteration %d:\n", counter)

这里实际上传入了两个实参(以下图时刻为例),”Loop iteration %d:\n”字符串和4,其中字符串指针保存在ESP+0x4位置,4保存在ESP+0x8位置(实参从右到左入栈原则)。

python黑帽子第二版pdf密码 python灰帽子pdf百度网盘_python_03

至此,问题圆满解决。