三、获取寄存器信息

一个调试器应该可以提供任意某个时刻 CPU 上的寄存器状态信息;
从本质上来说, 进程是线程的集合, 线程才是CPU调度和分派的基本单位, 所以通常所说的 CPU 状态信息是针对线程而言的.
所以在获取寄存器信息之前, 首先要明确我们的获取目标 – 某个运行在 debugee 进程下的执行线程, 这一步通过 OpenThread() 函数来处理.

HANDLE WINAPI OpenThread(
    _In_ DWORD dwDesiredAccess, // 渴望得到的访问权限(标志)应该为 PROCESS_ALL_ACCESS
    _In_ BOOL  bInheritHandle, // 是否继承句柄, 总为 FALSE
    _In_ DWORD dwThreadId 
);

为了得到 dwThreadId 这个参数, 我们就需要枚举目标进程中的线程列表.

1. 线程枚举

借助 CreateToolhelp32Snapshot() 函数, 通过获取进程信息, 为指定的进程、进程使用的堆[HEAP]、模块[MODULE]、线程建立一个快照, 说到底,就是可以获取系统中正在运行的进程信息、线程信息等.

HANDLE WINAPI CreateToolhelp32Snapshot(
    DWORD dwFlags, // 用来指定“快照”中需要返回的对象
    DWORD th32ProcessID // 一个进程 ID 号,用来指定要获取哪一个进程的快照,当获取系统进程列表或获取当前进程快照时可以设为0
);

dwFlags 参数用于告知这个函数我们想要获取的信息的确切类型(进程列表、线程列表、模块列表还是堆列表).

  • TH32CS_INHERIT 声明快照句柄是可继承的
  • TH32CS_SNAPALL 在快照中包含系统中所有的进程和线程
  • TH32CS_SNAPHEAPLIST 在快照中包含在 th32ProcessID 中指定的进程的所有的堆
  • TH32CS_SNAPMODULE 在快照中包含在 th32ProcessID 中指定的进程的所有的模块
  • TH32CS_SNAPPROCESS 在快照中包含系统中所有的进程
  • TH32CS_SNAPTHREAD 在快照中包含系统中所有的线程

th32ProcessID 这个参数只有在 TH32CS_SNAPHEAPLIST、TH32CS_SNAPMODULE 和 TH32CS_SNAPALL 下才有意义, 这表示函数在提取线程信息时并不会理会这个参数, 它依然提取的是系统中的所有线程, 因此我们需要自行完成对线程的筛选.

CreateToolhelp32Snapshot 执行成功后, 我们将得到一个快照的句柄, 现在开始筛选这个快照:

BOOL WINAPI Thread32First(
      _In_ HANDLE hSnapshot, // CreateToolhelp32Snapshot 的返回值
      _Inout_ LPTHREADENTRY32 lpte // 指向 THREADENTRY32 的指针
);

lpte 参数将在 Thread32First() 返回时更新, 该结构体中包含着枚举到的首个线程的相关信息, 主要关注的结构体属性是 th32ThreadID 和 th32OwnerProcessID (具体见后面的代码).
一旦从系统快照中成功地提取首个线程信息, 接下来就可以通过另一个 api 来遍历下一个线程的相关信息.

BOOL WINAPI Thread32Next(
      _In_ HANDLE hSnapshot, // CreateToolhelp32Snapshot 的返回值
      _Inout_ LPTHREADENTRY32 lpte // 指向 THREADENTRY32 的指针
);

我们现在只需要循环地调用 Thread32Next() 直到遍历完成.

2. 获取寄存器信息

一旦我们取得目标线程的句柄后, 就可以通过 GetThreadContext() 来获取目标线程的上下文信息:

BOOL GetThreadContext(
    HANDLE hThread,
    LPCONTEXT lpContext
);

lpContext 是输出函数, 保存的就是目标线程的上下文信息, 具体内容见后面的代码.
同时, 我们还可以通过 SetThreadContext() 来设置目标线程的上下文.

数据结构定义:

class FLOATING_SAVE_AREA(Structure):
   _fields_ = [
        ('ControlWord', DWORD),
        ('StatusWord', DWORD),
        ('TagWord', DWORD),
        ('ErrorOffset', DWORD),
        ('ErrorSelector', DWORD),
        ('DataOffset', DWORD),
        ('DataSelector', DWORD),
        ('RegisterArea', BYTE * 80),
        ('Cr0NpxState', DWORD),
]

class CONTEXT(Structure):
    _fields_ = [
        ('ContextFlags', DWORD),
        ('Dr0', DWORD),
        ('Dr1', DWORD),
        ('Dr2', DWORD),
        ('Dr3', DWORD),
        ('Dr6', DWORD),
        ('Dr7', DWORD),
        ('FloatSave', FLOATING_SAVE_AREA),
        ('SegGs', DWORD),
        ('SegFs', DWORD),
        ('SegEs', DWORD),
        ('SegDs', DWORD),
        ('Edi', DWORD),
        ('Esi', DWORD),
        ('Ebx', DWORD),
        ('Edx', DWORD),
        ('Ecx', DWORD),
        ('Eax', DWORD),
        ('Ebp', DWORD),
        ('Eip', DWORD),
        ('SegCs', DWORD),
        ('EFlags', DWORD),
        ('Esp', DWORD),
        ('SegSs', DWORD),
        ('ExtendedRegisters', BYTE * 512),
]

class THREADENTRY32(Structure):
    _fields_ = [
        ('dwSize', DWORD),
        ('cntUsage', DWORD),
        ('th32ThreadID', DWORD),
        ('th32OwnerProcessID', DWORD),
        ('tpBasePri', DWORD),
        ('tpDeltaPri', DWORD),
        ('dwFlags', DWORD),
    ]

核心代码:

# -*- coding: utf-8 -*-
from ctypes import *
from my_debugger_defines import *

kernel32 = windll.kernel32
advapi32 = windll.advapi32

class debugger():
    def __init__(self):
        self.h_process = None
        self.pid = None
        self.debugger_active = False # 用来控制循环的开关

    def load(self, path_to_exe):
        //...

    def open_process(self, pid):
        h_process = kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, pid)
        return h_process

    def attach(self, pid):
        self.h_process = self.open_process(pid)
        if kernel32.DebugActiveProcess(pid):
            self.pid = int(pid)
            //...
        else:
            print 'Unable to attach to the process.'

    def run(self):
        //...

    def get_debug_event(self):
        //...

    def detach(self):
        if kernel32.DebugActiveProcessStop(self.pid):
            print 'Finished debugging. Exiting ...'
            return True
        else:
            print 'There was an error'
            return False

    def open_thread(self, tid):
        h_thread = kernel32.OpenThread(THREAD_ALL_ACCESS, None, tid)
        if h_thread is not None:
            return h_thread
        else:
            print 'Could not obtain a valid thread handle.'
            return False

    def enumerate_threads(self):
        thread_entry = THREADENTRY32()
        thread_list = []
        snapshot = kernel32.CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, self.pid)

        if snapshot is not None:
            thread_entry.dwSize = sizeof(thread_entry)
            success = kernel32.Thread32First(snapshot, byref(thread_entry))

            while success:
                if thread_entry.th32OwnerProcessID == self.pid:
                    thread_list.append(thread_entry.th32ThreadID)
                success = kernel32.Thread32Next(snapshot, byref(thread_entry))

            kernel32.CloseHandle(snapshot)
            return thread_list
        else:
            return False

    def get_thread_context(self, thread_id = None, h_thread = None):
        context = CONTEXT()
        context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS

        h_thread = self.open_thread(thread_id)
        if kernel32.GetThreadContext(h_thread, byref(context)):
            kernel32.CloseHandle(h_thread)
            return context
        else:
            return False

测试代码:

# -*- coding: utf-8 -*-
import my_debugger

debugger = my_debugger.debugger()

pid = raw_input('Enter the pid: ')

debugger.attach(int(pid))

list_threads = debugger.enumerate_threads()

for thread in list_threads:
    thread_context = debugger.get_thread_context(thread)
    print thread
    print 'eax: 0x%08x' %  thread_context.Eax
    print 'ebx: 0x%08x' %  thread_context.Ebx
    print 'ecx: 0x%08x' %  thread_context.Ecx
    print 'edx: 0x%08x' % thread_context.Edx
    print 'esp: 0x%08x' %  thread_context.Esp

debugger.detach()

你可能感兴趣的:(★Python,灰帽★)