4.1、拓展断点处理
使用PyDbg只需要构建一个用户模式的回调函数,当收到一个调试事件的时候,回调函数执行我们定义的操作。操作完成后,再将权限交还给调试器,回复被调试的进程。‘
PyDbg设置函数的断点原型如下:bp_set(address,description="",restore=True,handler=None)表示:设置的断点的地址,可选,是否要在断点被触发后重置,指向断点触发时调用的回调函数。
我们在printf()函数上下断点,以便读取printf()输出时用到的参数counter变量,之后用要给1到100的随机数替换这个变量的值最后打印出来。
from pydbg import *
from pydbg.defines import *
import struct
import random
#这是我们自定义的回掉函数
def printf_randomizer(dbg):
#从ESP+0x8读取计数器的值
parameter_addr = dbg.context.Esp + 0x8
counter = dbg.read_process_memory(parameter_addr,4)
#当我们使用read_process_memory时,它返回的是一个二进制的串,我们使用它之前要解压他。
counter = struct.unpack("L",counter)[0]
print "Counter:%d"% int(counter)
#随机产生要给数字并打包成二进制格式,为了嗯能够正确地写入进程。
random_counter = random.randint(1,100)
random_counter = struct.pack("L",random_counter)[0]
#交换我们的随机数然后继续进程
dbg.write_process_memory(parameter_addr,random_counter)
return DBG_CONTINUE
#初始化pydbg类
dbg = pydbg()
pid = raw_input("Enter the PID of the printf_loop.py PID:")
dbg.attach(int(pid))
#设置断点进行调用
printf_address = dbg.func_resolve("msvcrt","printf")
dbg.bp_set(printf_address,description="printf_address",handler=printf_randomizer)
dbg.run()
运行不出正确结果,随机数一直不能插入,,找不出原因,,http://blog.csdn.net/u012763794/article/details/52174275
当程序尝试访问它没有权限访问的页面或以一种不合法的方式访问内存的时候就会产生访问违例。
PyDbg能很方便的实现一个为例访问处理函数并输出相关的崩溃信息。本次测试的目标为危险的c函数strcpy(),我们用它创建一个会被溢出的程序,接下来我们再写一个简短的PyDbg脚本附加到进程并处理违例。
溢出脚本buffer_overflow.py:
from ctypes import * msvcrt = cdll.msvcrt raw_input("Once the debugger is attached,press any key.") buffer = c_char_p("AAAAA") overflow = "A"*10 msvcrt.strcpy(buffer,overflow)
处理程序access_violation_handler.py:
from pydbg import *
from pydbg.defines import *
import distutils
def check_accessv(dbg):
#我们跳过首次异常
if dbg.dbg.u.Exception.dwFirstChance:
return DBG_EXCEPTION_NOT_HANDLED
crash_bin = distutils.crash_binning.crash_binning()
crash_bin.record_crash(dbg)
print crash_bin.crash_synopsis()
dbg.terminate_process()
return DBG_EXCEPTION_NOT_HANDLED
pid = raw_input("Enter the ProcessID:")
dbg = pydbg()
dbg.attach(int(pid))
dbg.set_callback(EXCEPTION_ACCESS_VIOLATION,check_accessv)
dbg.run()
运行没有任何结果。。。未知原因。。。
应该输出的信息:
1)指出了那个指令引发了访问异常以及指令在哪个块里。
2)转储了所有寄存器的值
3)故障指令附近代码的反汇编指令。
4)崩溃发生时候注册的结构化异常处理程序的列表。
4.3、进程快照
使用进程快照,我们能够冰冻进程,获取进程的内存数据。以后我们想要让进程回到这个时刻的状态,只要使用这个时刻的快照就行了。
4.3.1、获得进程快照
首先将进程挂起用suspend_all_threads()完成,之后用process_snapshot()获取快照。快照完成后,用resume_all_threads()恢复挂起的进程,让程序继续执行。当我们需要将进程恢复到从前的状态时,简单的process_restore()就行。
#-*-coding:utf-8-*-
from pydbg import *
from pydbg.defines import *
import threading
import time
import sys
class snapshot(object):
def __init__(self,exe_path):
self.exe_path = exe_path
self.pid = None
self.dbg = None
self.running = True
#开启一个调试器线程,然后循环直到设置了目标进程的PID
pydbg_thread = threading.Thread(target=self.start_debugger)
pydbg_thread.setDaemon(0)#守护进程就是主线程执行完成后不管子线程有没有执行完成都停止
pydbg_thread.start()
while self.pid == None:
time.sleep(1)
#我们现在有了一个PID且目标进程正在运行。再开启第二个线程运行快照。为什么?
monitor_thread = threading.Thread(target=self.monitor_debugger)
monitor_thread.setDaemon(0)
monitor_thread.start()
def monitor_debugger(self):
while self.running == True:
input = raw_input("Enter:'snap','restore'or 'quit'")
input = input.lower().strip()
if input == 'quit':
print "[*]Exiting the snapshotter."
self.running = False
self.dbg.terminate_process()
elif input == 'snap':
print "[*]Suspending all threads."
self.dbg.suspend_all_threads()
print "[*]Obtaining snapshot."
self.dbg.process_snapshot()
print "[*]Resuming operation."
self.dbg.resume_all_threads()
elif input == 'restore':
print "[*]Suspending all threads."
self.dbg.suspend_all_threads()
print "[*]Restoring snapshots."
self.dbg.process_restore()
print "[*]Resuming operation."
self.dbg.resume_all_threads()
def start_debugger(self):
self.dbg = pydbg()
pid = self.dbg.load(self.exe_path)
self.pid = self.dbg.pid
self.dbg.run()
exe_path = "C:\\Windows\\System32\\calc.exe"
snapshot(exe_path)
运行结果如下:
那么第一步就是在调试器内部创建一个新线程,并用此启动目标进程。通过使用分开的线程,就能将被调试的进程和调试器的操作分开,这样我们输入不同的快照命令进行操作的时候,就不用强迫被调试进程暂停。当创建新线程的代码返回了有效的 PID,我们就创建另一个线程,接受我们输入的调试命令。之后这个线程根据我们输入的命令决定不同的操作(快照,恢复快照,结束程序)。
接下来构造一个工具来根除应用程序中出现的可利用的漏洞。工具将定位于危险函数并跟踪他们的调用。
当我们认为函数被危险调用了,就将4堆栈中的4个参数接触引用,弹出栈,并且在函数产生溢出之前对进程快照。
如果这次访问违例了,我们的脚本将把进程恢复到函数被调用之前的快照。并从这开始,单步执行,同时反汇编每个执行的代码直到我们也抛出了访问违例或者执行完了代码。
#-*-coding:utf-8-*-
#用python编写软件漏洞挖掘脚本
from pydbg import *
from pydbg.defines import *
import utils
#在一个访问违例后,我们记录的最大的指令数
MAX_INSTRUCTIONS = 10
#这不仅仅是一个详细的清单,添加了更多的加分
dangerous_functions = {
"strcpy":"msvcrt.dll",
"strncpy":"msvcrt.dll",
"sprintf":"msvcrt.dll",
"vsprintf":"msvcrt.dll"
}
dangerous_functions_resloved = {}
crash_encountered = False
instruction_count = 0
def danger_handler(dbg):
#我们想要打印出栈的内容,这一般仅有一个参数,因此我们会获得ESP到ESP+20,这会有足够的信息决定我们是否有很多的数据
esp_offset = 0
print "[*] Hit %s"%dangerous_functions_resloved[dbg.context.Eip]
print "====================================================="
while esp_offset <= 20:
parameter = dbg.smart_dereference(dbg.context.Esp+esp_offset)#啥意思?
print "[ESP+%d] => %s"%(esp_offset,parameter)
esp_offset += 4
print "============================================"
dbg.suspend_all_threads()
dbg.process_snapshot()
dbg.resume_all_threads()
return DBG_CONTINUE
def access_violation_handler(dbg):
global crash_encountered
#让我们处理访问违例然后存储进程到上次危险函数调用的时候
if dbg.dbg.u.Exception.deFirstChance:
return DBG_EXCEPTION_NOT_HANDLED
crash_bin = utils.crash_binning.crash_binning()#这三行是啥意思?
crash_bin.recored_crash(dbg)
print crash_bin.crash_synopsis()
if crash_encountered == False:
dbg.suspend_all_threads()
dbg.process_restore()
crash_encountered = True
#我们标志每一个线程
for thread_id in dbg.enumerate_threads():
print "[*] Setting single step for thread:0x%08x"%thread_id
h_thread = dbg.open_thread(thread_id)
dbg.single_step(True,h_thread)
dbg.close_handle(h_thread)
#现在继续运行,将控制权传递给我们的单步处理程序
dbg.resume_all_threads()
return DBG_CONTINUE
else:
dbg.terminate_process()
return DBG_EXCEPTION_NOT_HANDLED
def single_step_handler(dbg):
global instruction_count
global crash_encountered
if crash_encountered:
if instruction_count == MAX_INSTRUCTIONS:
dbg.single_step(False)
return DBG_CONTINUE
else:
#拆卸这个指令
instruction = dbg.disasm(dbg.context.Eip)
print "#%d\t0x%08x:%s"%(instruction_count,dbg.context.Eip,instruction)
instruction_count +=1
dbg.single_step(True)
return DBG_CONTINUE
dbg = pydbg()
pid = int(raw_input("Enter the PID you wish to monitor:"))
dbg.attach(pid)
#跟踪所有的危险函数并设置断点
for func in dangerous_functions.keys():
func_address = dbg.func_resolve(dangerous_functions[func],func)
print "[*]Resolved breakpoint:%s->0x%08x"%(func,func_address)
dbg.bp_set(func_address,handler=danger_handler)
dangerous_functions_resloved[func_address] = func
dbg.set_callback(EXCEPTION_ACCESS_VIOLATION,access_violation_handler)
dbg.set_callback(EXCEPTION_SINGLE_STEP,single_step_handler)
dbg.run()
运行一个有漏洞的程序,然后将脚本附加到进程,和程序进行交互,尝试crash程序