Gray Hat Python灰帽子学习+python3+win10笔记二


前言

创建自己的调试器的两种方法:创建一个新的进程(CreateProcess)和附加到现有线程(OpenProcess)


方法一:开启一个全新的进程

1.原文代码

my_debugger_defines结构体和常量参数定义:

from ctypes import *

# 为ctype变量创建符合匈牙利命名风格的匿名,这样可以使代码更贴近win32的风格
WORD = c_ushort
DWORD = c_ulong
LPBYTE = POINTER(c_ubyte)
LPTSTR = POINTER(c_char)
HANDLE = c_void_p

# 常值定义
DEBUG_PROCESS = 0x00000001
CREATE_NEW_CONSOLE = 0x00000010

# 定义函数CreateProcessA()所需的结构体
class STARTUPINFO(Structure):
    _fields_ = [
        ("cb",DWORD),
        ("lpReserved",LPTSTR),
        ("lpDesktop",LPTSTR),
        ("lpTitle",LPTSTR),
        ("dwX",DWORD),
        ("dwY",DWORD),
        ("dwXSize",DWORD),
        ("dwYSize",DWORD),
        ("dwXCountChars",DWORD),
        ("dwYCountChars", DWORD),
        ("dwFillAttribute", DWORD),
        ("dwFlage", DWORD),
        ("wShowWindow", DWORD),
        ("cbReserved2", DWORD),
        ("lpReserved2", LPBYTE),
        ("hStdInput", HANDLE),
        ("hStdOutput", HANDLE),
        ("cbReserverd2", HANDLE),
    ]

class PROCESS_INFORMATION(Structure):
    _fields_ = [
        ("hProcess",HANDLE),
        ("hThread", HANDLE),
        ("dwProcessId",HANDLE),
        ("dwThreadId", HANDLE),
    ]

my_debugger.py主函数部分:

from ctypes import *
from my_debugger_defines import *

kernel32 = windll.kernel32

class debugger():
    def __init__(self):
        self.h_process = None  # 进程句柄
        self.pid = None  # 进程pid
        self.debugger_active = False  # 进程活跃状态
        pass

    def load(self,path_to_exe):

        # 参数dwCreationFlage中的标志位控制着进程的创建方式,你若希望
        # 新创建的进程独占一个新的控制台窗口,而不是与父进程共用
        # 同一个控制台,你可以加上标志位CREATE_NEW_CONSOLE
        creation_flags = DEBUG_PROCESS

        # 实例化之间定义的结构体
        startupinfo = STARTUPINFO()
        process_information = PROCESS_INFORMATION()

        # 在以下两个成员变量的共同作用下,新建进程将在一个单独
        # 的窗体中被显示,你可以通过改变结构体STARTUPINFO中的各
        # 成员变量的值来控制debugee进程的行为
        startupinfo.dwFlags = 0x1
        startupinfo.wShowWindow = 0x0

        # 设置结构体STARTUPINFO中的成员变量
        # cb的值,用以表示结构体本身的大小
        startupinfo.cb = sizeof(startupinfo)

        if kernel32.CreateProcessA(path_to_exe,
                                   None,                                   
                                   creation_flags,
                                   None,
                                   None,
                                   byref(startupinfo),
                                   byref(process_information)):

            print("[*] We have successfully launched the process!")
            print("[*] PID:%d" % process_information.dwProcessId)


        else:
            print("[*] Error:0x%08x." % kernel32.GetLastError())

my_test.py主函数部分:

import my_debugger
debugger = my_debugger.debugger()
debugger.load(r"C:\Windows\System32\calc.exe")

2.执行结果

在这里插入图片描述

3.原因分析

1、python3代码(因python2用asci,python3使用Unicode编码):所以需要将CreateProcessA更改为CreateProcessW
2、另外文中CreateProcess参数为提供7个参数,实际参数为10个:
(1) LPCTSTR lpApplicationName:想运行的可执行文件的名字的字符串(应含扩展名)。如果找不到该文件,CreateProcess运行失败。应该设为NULL。
(2) LPTSTR lpCommandLine:传递给新进程的命令行字符串,应当为非常量字符串的地址。可以设定一个完整的命令行,如果第一个标记没有扩展名,CreateProcess将其假设为.exe。如果找不到该文件,CreateProcess按环境设置目录搜索运行。
(3) LPSECURITY_ATTRIBUTES:设定进程对象的安全性。可以为这些参数传递NULL,在这种情况下,系统为这些对象赋予默认安全性描述符。
(4) LPSECURITY_ATTRIBUTES lpThreadAttributes
设定线程对象的安全性。可以为这些参数传递NULL,在这种情况下,系统为这些对象赋予默认安全性描述符。
(5) BOOL bInheritHandles:决定子进程对父进程继承性,一般设为FALSE。
(6) DWORD dwCreationFlags:用于标识标志,以便用于规定如何来创建新进程。

(7) LPVOID lpEnvironment:指向包含新进程将要使用的环境字符串的内存块。在大多数情况下,为该参数传递NULL,使子进程能够继承它的父进程正在使用的一组环境字符串。也可以使用GetEnvironmentStrings函数当不再需要该内存块时,应该调用FreeEnvironmentStrings函数将内存块释放。
(8) LPCTSTR lpCurrentDirectory:设置子进程的当前驱动器和目录。如果本参数是NULL,则新进程的工作目录将与生成新进程的应用程序的目录相同。如果本参数不是NULL,那么必须指向包含需要的工作驱动器和工作目录的以0 结尾的字符串。注意,必须设定路径中的驱动器名。
(9) LPSTARTUPINFO lpStartupInfo:使用时应首先进行初始化。

(10) LPPROCESS_INFORMATION lpProcessInformation: 新进程的返回信息。
各参数详细描述请参考CSDN博主「lhy2199」的原创文章

4.代码修改

仅需要修改创建新进程函数CreateProcess处的代码:

        if kernel32.CreateProcessW(path_to_exe,
                                   None,
                                   None,
                                   None,
                                   None,
                                   creation_flags,
                                   None,
                                   None,
                                   byref(startupinfo),
                                   byref(process_information)):

            print("[*] We have successfully launched the process!")
            print("[*] PID:%d" % process_information.dwProcessId)


        else:
            print("[*] Error:0x%08x." % kernel32.GetLastError())

4.运行结果

...Python/codes/debuggerC/my_test.py"
[*] We have successfully launched the process!
[*] PID:41678362649384

方法二:附加到目标程

1.流程图

流程图

2.代码

my_debugger_defines结构体和常量参数定义:

from ctypes import *

# 为ctype变量创建符合匈牙利命名风格的匿名,这样可以使代码更贴近win32的风格
BYTE = c_ubyte
WORD = c_ushort
DWORD = c_ulong
LPBYTE = POINTER(c_ubyte)
LPTSTR = POINTER(c_char)
HANDLE = c_void_p
PVOID = c_void_p
LPVOID = c_void_p
UINT_PTR = c_ulong
SIZE_T = c_ulong

# 定义常量
DEBUG_PROCESS = 0x00000001
PROCESS_ALL_ACCESS = 0x001F0FFF
INFINITE = 0xFFFFFFFF
DBG_CONTINUE = 0x00010002

class STARTUPINFO(Structure):
    _fields_ = [
        ("cb",            DWORD),
        ("lpReserved",    LPTSTR),
        ("lpDesktop",     LPTSTR),
        ("lpTitle",       LPTSTR),
        ("dwX",           DWORD),
        ("dwY",           DWORD),
        ("dwXSize",       DWORD),
        ("dwYSize",       DWORD),
        ("dwXCountChars", DWORD),
        ("dwYCountChars", DWORD),
        ("dwFillAttribute",DWORD),
        ("dwFlags",       DWORD),
        ("wShowWindow",   WORD),
        ("cbReserved2",   WORD),
        ("lpReserved2",   LPBYTE),
        ("hStdInput",     HANDLE),
        ("hStdOutput",    HANDLE),
        ("hStdError",     HANDLE),
        ]


class EXCEPTION_RECORD(Structure):
    pass


EXCEPTION_RECORD._fields_ = [
    ("ExceptionCode", DWORD),
    ("ExceptionFlags", DWORD),
    ("ExceptionRecord", POINTER(EXCEPTION_RECORD)),
    ("ExceptionAddress", PVOID),
    ("NumberParameters", DWORD),
    ("ExceptionInformation", UINT_PTR * 15),
]


# Exceptions
class EXCEPTION_DEBUG_INFO(Structure):
    _fields_ = [
        ("ExceptionRecord",    EXCEPTION_RECORD),
        ("dwFirstChance",      DWORD),
        ]

class DEBUG_EVENT_UNION(Union):
    _fields_ = [
        ("Exception",         EXCEPTION_DEBUG_INFO),
        # ("CreateThread",      CREATE_THREAD_DEBUG_INFO),
        # ("CreateProcessInfo", CREATE_PROCESS_DEBUG_INFO),
        # ("ExitThread",        EXIT_THREAD_DEBUG_INFO),
        # ("ExitProcess",       EXIT_PROCESS_DEBUG_INFO),
        # ("LoadDll",           LOAD_DLL_DEBUG_INFO),
        # ("UnloadDll",         UNLOAD_DLL_DEBUG_INFO),
        # ("DebugString",       OUTPUT_DEBUG_STRING_INFO),
        # ("RipInfo",           RIP_INFO),
        ]

class PROCESS_INFORMATION(Structure):
    _fields_ = [
        ("hProcess",HANDLE),
        ("hThread", HANDLE),
        ("dwProcessId",HANDLE),
        ("dwThreadId", HANDLE),
    ]

class DEBUG_EVENT(Structure):
    _fields_ = [
        ("dwDebugEventCode", DWORD),
        ("dwProcessId",      DWORD),
        ("dwThreadId",       DWORD),
        ("u",                DEBUG_EVENT_UNION),
        ]

my_debugger.py主函数部分:

from ctypes import *
from my_debugger_defines import *

kernel32 = windll.kernel32

class debugger():

    def __init__(self):
        self.h_process = None
        self.pid = None
        self.debugger_active = False

    def load(self,path_to_exe):
        process_information = PROCESS_INFORMATION
        print("[*] We have successfully launched the process!")
        print("[*] PID: %d" % process_information.dwProcessId)

        # 保存一个指向新建进程的句柄,以供
        # 后续的进程访问所使用
        self.h_process = self.open_process(process_information.dwPorcessId)

    def open_process(self,pid):
        # OpenProcess获取进程句柄
        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.debugger_active = True
            self.pid = int(pid)
            self.run()
        else:
            print("[*] Unable to attach to the process.")

    def run(self):
        # 现在我们等待发生在debugee进程中
        # 的调试事件

        while self.debugger_active == True:
            self.get_debug_event()

    def get_debug_event(self):

        debug_event = DEBUG_EVENT()
        continue_status = DBG_CONTINUE

        if kernel32.WaitForDebugEvent(byref(debug_event),INFINITE):
            # 目前我们还未构建任何与事件处理相关功能逻辑,
            # 这里我们只是简单地恢复执行目标进程

            input("Press a key to continue...")
            self.debugger_active = False
            kernel32.ContinueDebugEvent(debug_event.dwProcessId,debug_event.dwThreadId,continue_status)

    def detach(self):

        if kernel32.DebugActiveProcessStop(self.pid):
            print("[*] Finished debugging. Exiting...")
            return True
        else:
            print("There are an error")
            return False

my_test.py主函数部分:

import my_debugger

debugger = my_debugger.debugger()

pid = input("Enter the PID of the process to attach to:")

debugger.attach(int(pid))

debugger.detach()

3.运行结果

运行结果

Python/codes/debuggerO/my_test.py"
Enter the PID of the process to attach to:11540
Press a key to continue...
[*] Finished debugging. Exiting...

Process finished with exit code 0

完成
资源来源于网络,如有侵权请联系作者删除!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值