├── .gitignore ├── README.md ├── demo.gif ├── deps └── README.md ├── docs ├── event-handle.md ├── index.html ├── pywdbg-api.md ├── quick-start.md ├── rpc-api.md └── write-plugin.md ├── pywdbg ├── K │ ├── ATTACH.py │ ├── CP.py │ ├── DUMP.py │ ├── END.py │ ├── ENGOPT.py │ ├── ERROR.py │ ├── EVENT.py │ ├── EXCEPTION.py │ ├── FILTER.py │ ├── FORMAT.py │ ├── OUTCTL.py │ ├── OUTPUT.py │ ├── STATUS.py │ ├── SYMBOL.py │ ├── VALUE.py │ └── __init__.py ├── __init__.py ├── pywdbg.py ├── srpc.py ├── tstream.py └── wdbg.py ├── setup.py ├── src ├── callback.cpp ├── callback.h ├── config.h ├── main.cpp ├── wdbg.cpp ├── wdbg.h ├── wdbg_client.cpp ├── wdbg_ctrl.cpp ├── wdbg_ext.cpp ├── wdbg_regs.cpp ├── wdbg_spaces.cpp ├── wdbg_syms.cpp ├── wdbg_sysobj.cpp └── xmake.lua ├── wdbg-dist └── README.md └── xmake.lua /.gitignore: -------------------------------------------------------------------------------- 1 | .*/ 2 | .* 3 | vs20*/ 4 | build/ 5 | **/__pycache__/ 6 | *dist/ 7 | *.zip 8 | *.exe 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # WDBG 3 | 4 | * 是基于微软发布的调试引擎`dbgeng`(WinDbg的调试引擎)开发的调试器 5 | * 并不直接给用户提供UI,而是以`RPC`的形式提供API接口 6 | 7 | 此项目包含两大模块,一个是用C++编写提供RPC调试服务的,称为WDBG调试器后台;另一个是使用Python封装的调试器前台pywdbg 8 | 9 | 用户既可以基于RPC接口使用其它编程语言开发扩展,也可以使用C++编写WDBG调试器后台插件并为前台调试器程序提供服务 10 | 11 | 此外,用户可以使用pywdbg提供的接口在python控制台交互调试,或者根据自己的需求编写python脚本自动化调试 12 | 13 | ## pywdbg 14 | 15 | ![](./demo.gif) 16 | 17 | 文档:https://luzhlon.github.io/wdbg/index.html 18 | 19 | ## Features 20 | 21 | * 支持32位和**64位**程序调试 22 | * 支持双机**内核调试** 23 | * 支持**插件开发** 24 | * 可通过网络**远程调试** 25 | 26 | ## Related project 27 | 28 | * srpc: https://github.com/luzhlon/srpc 29 | * xval: https://github.com/luzhlon/xval 30 | 31 | ## Thanks 32 | 33 | * xmake: https://github.com/tboox/xmake/ 34 | 35 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luzhlon/wdbg/a53c73425de989e9e31b94ee9af569050dfb5e34/demo.gif -------------------------------------------------------------------------------- /deps/README.md: -------------------------------------------------------------------------------- 1 | 2 | This directory should contain these projects: 3 | 4 | * websocket-cpp : https://github.com/luzhlon/websocket-cpp 5 | * xval : https://github.com/luzhlon/xval 6 | * xrpc : https://github.com/luzhlon/xrpc 7 | -------------------------------------------------------------------------------- /docs/event-handle.md: -------------------------------------------------------------------------------- 1 | 2 | 此篇介绍的是调试器前台进行事件处理的方法,有关使用具体的调试器前台处理事件,可以参考[pywdbg](https://github.com/luzhlon/wdbg/wiki/pywdbg) 3 | 4 | 调试器前台需要向后台注册某个调试器事件的回调函数(名字),当发生相应的事件时,调试器会通过RPC调用前台相应的函数 5 | 6 | 比如前台通过setevent函数向后台注册了DEBUG_EVENT_CREATE_PROCESS的事件处理函数'createprocess',那么当创建进程的事件发生时,后台便会通过RPC调用前台的createprocess函数,来处理此事件;前台的事件处理函数需要返回一个值,指示调试器引擎该怎么继续处理,对于创建进程的事件,一般返回DEBUG_STATUS_GO_HANDLED,对于断点事件一般返回DEBUG_STATUS_BREAK让调试器中断下来,给用户处理的机会 7 | 8 | ## API 9 | 10 | * setevent(type:int, callback) 11 | - 向调试器后台注册事件回调函数,type为事件类型(参考DEBUG_EVENT_XXX),name一般为一个字符串,指示处理此事件的RPC回调函数名称 12 | 13 | --------------- 14 | 15 | 关于不同的事件回调函数所传递的参数,可以参考IDebugEventCallbacks接口,大多数都是按照此接口的参数顺序提供给前台,需要特别指出的是 16 | 17 | * 异常处理函数(ExceptionAddress, ExceptionCode, ExceptionFlags, ExceptionInformation, ExceptionRecord, FirstChance) 18 | - ExceptionAddress 异常发生的位置 19 | - ExceptionCode 异常错误码 20 | - ExceptionFlags 异常错误标志 21 | - ExceptionInformation 异常信息 22 | - ExceptionRecord 异常记录 23 | - FirstChance 是否为第一次处理异常 24 | - 此接口待完善 25 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | docs 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /docs/pywdbg-api.md: -------------------------------------------------------------------------------- 1 | 2 | ## WDbg对象 3 | 4 | WDbg对象代表了wdbg所提供的接口,所有的RPC调用都通过WDbg对象来进行,看起来就像本地调用一样 5 | 6 | WDbg的大多数方法都是对RPC-API简单的一层封装,不过有些RPC-API对用户并不友好,下面要介绍的是pwdbg特有的接口 7 | 8 | * WDbg.cmdloop() 9 | - 启动**命令循环**,和windbg的命令一致 10 | - 此函数会循环要求用户输入一行命令,并发送给wdbg后台执行,执行结果会通过wdbg内部的函数输出到控制台上 11 | - 按CTRL-C可以终止命令行循环 12 | 13 | * WDbg.stepover() ------ 单步跳过 14 | * WDbg.stepinto() ------ 单步进入 15 | * WDbg.go() ------------ 继续运行 16 | 17 | ### 断点处理 18 | 19 | 断点处理原本是事件处理的一部分,但是pywdbg在其之上封装了一层,便于用户使用 20 | 21 | 调用WDbg.addbp(offset)可获取到一个断点(Breakpoint)对象 22 | 23 | 属性: 24 | * callback --- 用户可以设置断点对象的callback属性,callback为一个callable对象,当断点触发时被调用,并传入断点对象作为参数 25 | * id --------- 此断点的ID 26 | * type ------- 断点类型,'code'或者'data' 27 | * cmd -------- 断点触发时执行的命令(windbg命令) 28 | * thread ----- 断点匹配的线程 29 | 30 | 方法: 31 | * enable(b) --- 启用/禁用此断点 32 | * remove() ---- 删除此断点 33 | 34 | ### 事件处理 35 | 36 | * WDbg.event(name, func = None) 37 | - 若传递func参数,表示**注册事件处理函数**;若不传递,表示**获取事件处理函数** 38 | - 在注册事件处理函数之前需要先获取事件处理函数,可以在自己的事件处理函数中调用之前获取到的事件处理函数。当然,这不是必须的 39 | - name可接受的事件类型(字符串),如果name参数不在这些里面,则会抛出异常 40 | * CREATE_PROCESS 41 | * CREATE_THREAD 42 | * EXIT_PROCESS 43 | * EXIT_THREAD 44 | * BREAKPOINT 45 | * EXCEPTION 46 | * LOAD_MODULE 47 | * UNLOAD_MODULE 48 | * SYSTEM_ERROR 49 | - 每个事件处理函数所接收的参数是不同的,不过第一个参数总是当前的RPC会话对象。具体的可以参考IDebugEventCallbacks,如果你不确定,可以使用变长参数 50 | 51 | ## wdbg模块 52 | 53 | pywdbg是wdbg调试器的前台,使用前需要先启动wdbg后台程序,根据输出的端口号调用wdbg.connect(addr, port)连接到后台,连接成功后会返回一个wdbg.WDbg对象 54 | 55 | 不过对于一般的本地调试,不需要手动去连接。使用wdbg.startup(arch = 'x86')函数,pywdbg可以自动搜寻wdbg的路径,然后wdbg作为子进程被启动,pywdbg调用connect连接到wdbg后台,返回结果和connect是一样的 56 | 57 | * wdbg.connect(addr, port) 58 | - 连接到wdbg后台,addr为wdbg后台所在的IP地址,port为wdbg监听的端口号 59 | - 成功返回WDbg对象,否则返回None 60 | * wdbg.startup(arch = 'x86') 61 | - 启动本地的wdbg并连接,arch为要启动的版本(32位的还是64位的),只能为'x86'或者'x64' 62 | - 返回值和connect一样,成功返回WDbg对象,否则返回None 63 | 64 | -------------------------------------------------------------------------------- /docs/quick-start.md: -------------------------------------------------------------------------------- 1 | 2 | ## 简介 3 | 4 | wdbg是基于微软发布的调试引擎`dbgeng`(WinDbg的调试引擎)开发的调试器,wdbg并不直接给用户提供UI,而是以`RPC`的形式提供API接口 5 | 6 | wdbg只是一个调试器后台,并不能直接与用户交互,所以本项目提供了一个python版本的前台 --- pywdbg,以下介绍皆基于pywdbg来说明 7 | 8 | ## 安装 9 | 10 | 使用pywdbg之前,先确认你的电脑已经安装了如下组件: 11 | * vs2017再发行包 12 | * python 3.2+,pip 13 | 14 | 从 https://github.com/luzhlon/wdbg/releases 获取最新版本,将压缩包里的目录出来并进入此目录,在此目录下打开命令行执行`python setup.py install`安装pywdbg 15 | 16 | ## 使用 17 | 18 | 如果安装过程顺利的话,你将得到**pywdbg**和**pywdbg64**两个命令。这两个命令的用法是一样的,不同的是启用的调试器架构不同,分别对应32位和64位的调试器,下面皆以pywdbg为例进行介绍 19 | 20 | ### 调试会话 21 | 22 | 开始一个调试会话有两种基本途径:创建进程或附加一个目标(附加到进程或者内核调试) 23 | 24 | * 创建进程:`pywdbg XXX.exe`,XXX.exe是所要调试的程序路径 25 | * 附加进程:`pywdbg -a PID`,PID是所要附加的进程的进程ID 26 | * 附加内核,双机调试:`pywdbg -k OPTIONS`,OPTIONS一般为`com:port=\\.\pipe\com_1,baud=115200,pipe,resets=0` 27 | 28 | ### 执行自定义脚本 29 | 30 | 使用pywdbg的-x参数指定所要执行的python脚本,如果指定了调试目标,这个脚本会在连接到调试目标后被pywdbg执行 31 | 32 | pywdbg会向被执行的脚本中传入一个全局变量wd,用于调用API接口 33 | 34 | > wd是一个WDbg类的实例,所有的API调用都要通过这个对象进行 35 | 36 | ### 交互式脚本执行环境 37 | 38 | 如果启动pywdbg时没有指定任何调试目标,用户需要手动调用wd.create或wd.attach等API来启动一个调试会话,进入调试会话后用户将进入一个python的交互式命令行(ptpython),使用wd.xxx(...)来调用API接口,xxx为具体的API函数名称 39 | 40 | 常用的流程控制API有: 41 | * wd.go() 继续运行 42 | * wd.stepinto() 单步进入 43 | * wd.stepover() 单步跳过 44 | 45 | ### 执行windbg命令 46 | 47 | 使用wd.cmdloop()来进入一个类似于cdbg和windbg的交互式命令行环境,在里面直接执行g,p,t等dbgeng支持的命令 48 | 49 | 使用退出命令可退出windbg命令的执行,默认的退出命令是`..`,你也可以通过`wd._exitcmd = XXX`来指定 50 | 51 | > 也可以使用Ctrl-C来退出,但是不建议使用,因为有时候会导致死循环,无法继续工作 52 | 53 | ### 下断点 54 | 55 | 使用wd.addbp来添加一个断点,添加成功后会返回一个Breakpoint对象,对断点的所有操作都会通过这个对象进行,具体的使用方法可参考PYWDBG-API中的说明 56 | 57 | ### 事件处理 58 | 59 | wdbg支持模块加载/卸载、线程创建/退出、进程创建/退出、异常等事件,具体使用方法可参考PYWDBG-API中的说明 60 | 61 | ### 退出(调试会话) 62 | 63 | * wd.end_detach() ----------- 分离调试目标 64 | * wd.end_terminate() -------- 终止调试目标 65 | -------------------------------------------------------------------------------- /docs/rpc-api.md: -------------------------------------------------------------------------------- 1 | 2 | ## 前言 3 | 4 | * 大多数函数失败返回nil(None),对于获取数据的函数,成功返回0;其它的大多数函数成功返回0 5 | 6 | ## 数据访问 7 | 8 | ### 读写内存 9 | 10 | * read(pos:int, size:int) -> bytes | nil 11 | - 读取(被调试进程的)内存 12 | - 成功返回存放所读数据的字节数组 13 | * write(pos:int, data:bytes) -> byteswritten | nil 14 | - 写入(被调试进程的)内存 15 | - 成功返回写入的字节数 16 | * readphy(pos:int, size:int) -> bytes | nil 17 | - 读取目标的**物理内存** 18 | - 成功返回存放所读数据的字节数组 19 | * writephy(pos:int, data:bytes) -> byteswritten | nil 20 | - 写入目标的**物理内存** 21 | - 成功返回写入的字节数 22 | * readstr(pos:int) -> str 23 | - 从pos位置读取字符串(ASCII编码) 24 | * readustr(pos:int) -> ustr 25 | - 从pos位置读取UNICODE字符串(ASCII编码) 26 | * readptr(pos:int, count = 1) -> (pointer ...) 27 | - 从pos位置开始读取count个指针 28 | - 若count == 1,返回所读的指针的值;`> 1`则返回这些指针的值构成的列表 29 | * search(pos:int, len:int, pattern:bytes) 30 | - 从pos位置开始搜索二进制数据pattern,最多搜索len个字节 31 | - 成功返回pattern所在的地址 32 | 33 | ### 读写寄存器 34 | 35 | * getreg(name:str | index:int) -> (value, type) 36 | - 获取寄存器的值,参数可以是寄存器的名字(字符串),也可以是寄存器的索引(整数) 37 | - 成功返回寄存器的值和类型 38 | - 参考DEBUG_VALUE_XXX 39 | * setreg(name:str | index:int, value:int, type = DEBUG_VALUE_INT64) 40 | - 设置寄存器的值,name参数可以是寄存器的名字(字符串),也可以是寄存器的索引(整数),value为要设置的值 41 | - 成功返回0 42 | * getregs(name:str | index:int, ...) -> (value ...) 43 | - 获取多个寄存器的值 44 | - 成功返回多个值组成的列表 45 | * iptr() -> current instruction position 46 | - 获取当前指令的地址 47 | 48 | ## 目标信息 49 | 50 | * is64() 51 | - 判断目标是否为64位程序 52 | * psid(id = nil) 53 | - 获取或设置当前进程的ID(这里的ID指的是调试引擎里的ID,不是系统里的进程ID) 54 | * thid(id = nil) 55 | - 获取或设置当前线程的ID(这里的ID指的是调试引擎里的ID,不是系统里的线程ID) 56 | * getpeb() 57 | - 获取PEB在目标内存中的位置 58 | * getteb() 59 | - 获取TEB在目标内存中的位置 60 | * exename() 61 | - 获取目标可执行文件的名字 62 | * retpos() 63 | - 获取当前函数的返回指令所在的地址 64 | 65 | ## 控制目标 66 | 67 | * create(path:str, flags = CREATE_NEW_CONSOLE | DEBUG_CREATE_PROCESS_NO_DEBUG_HEAP | DEBUG_PROCESS) -> result 68 | - 创建进程,成功返回0 69 | * attach(path:str | pid:int) -> success 70 | - 附加进程,第1个参数若为字符串,则调用CreateProcessAndAttach,第2个参数和第3个参数分别为创建进程的Flags和附加进程的Flags,若为整数,则调用AttachProcess,第2个参数为附加进程的Flags 71 | - 成功返回0 72 | * attachkernel(options:str) 73 | - 附加到内核目标,参数options和windbg的-k后面的参数一样,一般为`com:port=\\.\pipe\com_1,baud=115200,pipe,resets=0` 74 | - 成功返回0 75 | * terminate() 76 | - 终结当前(调试的)进程 77 | - 成功返回0 78 | * abandon() 79 | - 遗弃当前(调试的)进程 80 | - 成功返回0 81 | * exitcode() 82 | - 获取进程的返回值 83 | * status(status = nil) -> status 84 | - 获取或设置调试器状态,参考GetExecutionStatus和SetExecutionStatus 85 | * waitevent(timeout:int = INFINITE) -> result 86 | - 等待事件,参考WaitForEvent 87 | * interrupt(flags = DEBUG_INTERRUPT_ACTIVE) -> success 88 | - 中断调试器等待事件,参考SetInterrupt 89 | 90 | ## 模块和符号 91 | 92 | * name2pos(name:str) 93 | - 获取符号名的地址,比如`name2pos('kernel32!CreateFileA')`可以获取CreateFile函数的地址 94 | * symbolpath(path = nil) 95 | - 获取或设置符号搜索路径 96 | - path如果是字符串,则设置符号搜索路径为path,成功返回0 97 | - path不是字符串时,成功会返回获取到的符号搜索路径 98 | * modnum() 99 | - 获取模块数目 100 | - 返回[已加载的模块数, 未加载的模块数] 101 | * getmod(name:str | index:int) 102 | - 获取模块基址,第1个参数可以为模块名name,也可以为模块索引index 103 | - 通常可执行文件模块的索引为0 104 | * ptr2mod(pointer:int) 105 | - 通过指针获取模块基址 106 | * typeid 107 | * symboltype 108 | * fieldoffset 109 | 110 | ## 断点管理 111 | 112 | * addbp(offset:int|str) -> breakpoint:int 113 | - 在offset处添加断点,成功返回断点的ID 114 | * addba(offset:int, size = 1, type = DEBUG_BREAK_READ) -> breakpoint:int 115 | - 在offset处添加数据断点(硬件断点),成功返回断点的ID 116 | * rmbp(bpid) 117 | - 删除断点,bpid为addbp返回的断点id值 118 | * enablebp(id:int, enable = true) 119 | - 启用/禁用断点 120 | * bpcmd(id:int, cmd = nil) 121 | - 获取/设置断点触发时要执行的命令 122 | * bpthread(id:int, tid = nil) 123 | - 获取/设置断点匹配的线程,tid为目标线程在调试器里的ID 124 | 125 | ## DBGENG相关 126 | 127 | * addopts(options:int) -> success 128 | - 添加选项调试器选项,参考AddOptions 129 | * prompt() -> prompt 130 | - 获取调试器的提示文本,相当于WinDbg输入框左边的文字 131 | * disasm(offset, count = 1) -> asm_text 132 | - 反汇编offset处的代码。若count > 1,返回由count条汇编代码组成的列表。参考Disassemble 133 | * exec(command:str) -> debug_status | nil 134 | - 执行(WinDbg)命令, 135 | * eval(expr:str) -> value 136 | - 评估表达式expr 137 | * asmopts([options]) -> options:int | success:bool | nil 138 | - 设置或获取汇编(反汇编)选项,参考SetAssemblyOptions 139 | * setoutput(handler:str) 140 | - 设置调试器输出数据时回调的RPC函数,handler接受两个参数:当前的RPC会话、GBK编码的输出数据 141 | 142 | ## 事件处理 143 | 144 | > 有待完善 145 | 146 | * setevent(type:int, callback) 147 | - 向调试器后台注册事件回调函数,type为事件类型(参考DEBUG_EVENT_XXX),name一般为一个字符串,指示处理此事件的RPC回调函数名称 148 | 149 | 关于不同的事件回调函数所传递的参数,可以参考IDebugEventCallbacks接口,大多数都是按照此接口的参数顺序提供给前台,需要特别指出的是 150 | 151 | * 异常处理函数(ExceptionAddress, ExceptionCode, ExceptionFlags, ExceptionInformation, ExceptionRecord, FirstChance) 152 | - ExceptionAddress 异常发生的位置 153 | - ExceptionCode 异常错误码 154 | - ExceptionFlags 异常错误标志 155 | - ExceptionInformation 异常信息 156 | - ExceptionRecord 异常记录 157 | - FirstChance 是否为第一次处理异常 158 | - 此接口待完善 159 | 160 | ## 其它 161 | 162 | * peinfo([module,] attribute ...) 163 | - 获取module的PE文件属性,可指定多个 164 | - 若module为指定,则获取索引为0的module的信息 165 | - 目前可获取的属性有'EntryPoint' 'ImageSize' 'Machine' 'Subsystem' 166 | - 返回按attribute顺序排列的属性值列表 167 | -------------------------------------------------------------------------------- /docs/write-plugin.md: -------------------------------------------------------------------------------- 1 | 2 | > TODO 3 | -------------------------------------------------------------------------------- /pywdbg/K/ATTACH.py: -------------------------------------------------------------------------------- 1 | 2 | DEFAULT = 0x00000000 3 | INVASIVE_NO_INITIAL_BREAK = 0x00000008 4 | INVASIVE_RESUME_PROCESS = 0x00000010 5 | KERNEL_CONNECTION = 0x00000000 6 | EXDI_DRIVER = 0x00000002 7 | EXISTING = 0x00000002 8 | NONINVASIVE_ALLOW_PARTIAL = 0x00000020 9 | LOCAL_KERNEL = 0x00000001 10 | INSTALL_DRIVER = 0x00000004 11 | NONINVASIVE = 0x00000001 12 | NONINVASIVE_NO_SUSPEND = 0x00000004 13 | -------------------------------------------------------------------------------- /pywdbg/K/CP.py: -------------------------------------------------------------------------------- 1 | 2 | INSTALLED = 0x00000001 3 | SUPPORTED = 0x00000002 4 | ACP = 0 5 | OEMCP = 1 6 | MACCP = 2 7 | THREAD_ACP = 3 8 | SYMBOL = 42 9 | UTF7 = 65000 10 | UTF8 = 65001 11 | -------------------------------------------------------------------------------- /pywdbg/K/DUMP.py: -------------------------------------------------------------------------------- 1 | 2 | DEFAULT = 0x00000401 3 | TRACE_LOG = 0x00000404 4 | FILE_LOAD_FAILED_INDEX = 0xFFFFFFFF 5 | FULL = 0x00000402 6 | FILE_PAGE_FILE_DUMP = 0x00000000 7 | WINDOWS_CE = 0x00000405 8 | FILE_BASE = 0xFFFFFFFF 9 | IMAGE_FILE = 0x00000403 10 | SMALL = 0x00000400 11 | FILE_ORIGINAL_CAB_INDEX = 0xFFFFFFFE 12 | -------------------------------------------------------------------------------- /pywdbg/K/END.py: -------------------------------------------------------------------------------- 1 | 2 | PASSIVE = 0x00000000 3 | ACTIVE_TERMINATE = 0x00000001 4 | ACTIVE_DETACH = 0x00000002 5 | REENTRANT = 0x00000003 6 | DISCONNECT = 0x00000004 7 | -------------------------------------------------------------------------------- /pywdbg/K/ENGOPT.py: -------------------------------------------------------------------------------- 1 | 2 | KD_QUIET_MODE = 0x00002000 3 | INITIAL_MODULE_BREAK = 0x00000040 4 | DISABLE_MODULE_SYMBOL_LOAD = 0x00008000 5 | IGNORE_DBGHELP_VERSION = 0x00000001 6 | DISABLE_STEPLINES_OPTIONS = 0x00200000 7 | PREFER_DML = 0x00040000 8 | ALL = 0x002FFFFF 9 | DISABLESQM = 0x00080000 10 | NO_EXECUTE_REPEAT = 0x00000100 11 | DISALLOW_IMAGE_FILE_MAPPING = 0x00020000 12 | FINAL_BREAK = 0x00000080 13 | DISALLOW_SHELL_COMMANDS = 0x00001000 14 | SYNCHRONIZE_BREAKPOINTS = 0x00000800 15 | FAIL_INCOMPLETE_INFORMATION = 0x00000200 16 | IGNORE_LOADER_EXCEPTIONS = 0x00000010 17 | INITIAL_BREAK = 0x00000020 18 | DISABLE_MANAGED_SUPPORT = 0x00004000 19 | NETWORK_PATHS = 0x0000000C 20 | IGNORE_EXTENSION_VERSIONS = 0x00000002 21 | DISABLE_EXECUTION_COMMANDS = 0x00010000 22 | ALLOW_READ_ONLY_BREAKPOINTS = 0x00000400 23 | ALLOW_NETWORK_PATHS = 0x00000004 24 | DISALLOW_NETWORK_PATHS = 0x00000008 25 | -------------------------------------------------------------------------------- /pywdbg/K/ERROR.py: -------------------------------------------------------------------------------- 1 | 2 | S_OK = 0 3 | S_FALSE = 0x1 4 | E_PENDING = 0x8000000a 5 | E_UNEXPECTED = 0x8000ffff 6 | E_FAIL = 0x80004005 7 | 8 | d = { 9 | 0 : 'S_OK', 10 | 0x1 : 'S_FALSE', 11 | 0x8000000a : 'E_PENDING', 12 | 0x8000ffff : 'E_UNEXPECTED', 13 | 0x80004005 : 'E_FAIL' 14 | } 15 | 16 | def get(*args): 17 | global d 18 | return d.get(*args) 19 | -------------------------------------------------------------------------------- /pywdbg/K/EVENT.py: -------------------------------------------------------------------------------- 1 | 2 | CREATE_THREAD = 0x00000004 3 | BREAKPOINT = 0x00000001 4 | UNLOAD_MODULE = 0x00000080 5 | CHANGE_SYMBOL_STATE = 0x00001000 6 | LOAD_MODULE = 0x00000040 7 | EXIT_PROCESS = 0x00000020 8 | CHANGE_DEBUGGEE_STATE = 0x00000400 9 | SESSION_STATUS = 0x00000200 10 | SYSTEM_ERROR = 0x00000100 11 | CREATE_PROCESS = 0x00000010 12 | EXIT_THREAD = 0x00000008 13 | CHANGE_ENGINE_STATE = 0x00000800 14 | EXCEPTION = 0x00000002 15 | -------------------------------------------------------------------------------- /pywdbg/K/EXCEPTION.py: -------------------------------------------------------------------------------- 1 | 2 | NONCONTINUABLE = 0x00000001 3 | UNWINDING = 0x00000002 4 | EXIT_UNWIND = 0x00000004 5 | STACK_INVALID = 0x00000008 6 | NESTED_CALL = 0x00000010 7 | TARGET_UNWIND = 0x00000020 8 | COLLIDED_UNWIND = 0x00000040 9 | UNWIND = 0x00000066 10 | MAXIMUM_PARAMETERS = 0x0000000F 11 | -------------------------------------------------------------------------------- /pywdbg/K/FILTER.py: -------------------------------------------------------------------------------- 1 | 2 | LOAD_MODULE = 0x00000004 3 | SYSTEM_ERROR = 0x00000006 4 | CREATE_PROCESS = 0x00000002 5 | EXIT_PROCESS = 0x00000003 6 | REMOVE = 0x00000004 7 | DEBUGGEE_OUTPUT = 0x00000009 8 | IGNORE = 0x00000003 9 | SECOND_CHANCE_BREAK = 0x00000001 10 | BREAK = 0x00000000 11 | GO_NOT_HANDLED = 0x00000001 12 | GO_HANDLED = 0x00000000 13 | OUTPUT = 0x00000002 14 | UNLOAD_MODULE = 0x00000005 15 | INITIAL_MODULE_LOAD = 0x00000008 16 | CREATE_THREAD = 0x00000000 17 | INITIAL_BREAKPOINT = 0x00000007 18 | EXIT_THREAD = 0x00000001 19 | -------------------------------------------------------------------------------- /pywdbg/K/FORMAT.py: -------------------------------------------------------------------------------- 1 | 2 | USER_SMALL_FILTER_PATHS = 0x00000040 3 | CAB_SECONDARY_FILES = 0x40000000 4 | USER_SMALL_HANDLE_DATA = 0x00000002 5 | USER_SMALL_CODE_SEGMENTS = 0x00001000 6 | USER_SMALL_IGNORE_INACCESSIBLE_MEM = 0x08000000 7 | USER_SMALL_FILTER_MEMORY = 0x00000020 8 | USER_SMALL_MODULE_HEADERS = 0x00008000 9 | USER_SMALL_NO_OPTIONAL_DATA = 0x00000200 10 | USER_SMALL_FILTER_TRIAGE = 0x00010000 11 | USER_SMALL_FULL_AUXILIARY_STATE = 0x00004000 12 | USER_SMALL_NO_AUXILIARY_STATE = 0x00002000 13 | NO_OVERWRITE = 0x80000000 14 | USER_SMALL_THREAD_INFO = 0x00000800 15 | WRITE_CAB = 0x20000000 16 | USER_SMALL_UNLOADED_MODULES = 0x00000004 17 | USER_SMALL_FULL_MEMORY_INFO = 0x00000400 18 | USER_SMALL_PRIVATE_READ_WRITE_MEMORY = 0x00000100 19 | USER_SMALL_INDIRECT_MEMORY = 0x00000008 20 | USER_SMALL_FULL_MEMORY = 0x00000001 21 | USER_SMALL_DATA_SEGMENTS = 0x00000010 22 | CAB_SECONDARY_ALL_IMAGES = 0x10000000 23 | USER_SMALL_PROCESS_THREAD_DATA = 0x00000080 24 | DEFAULT = 0x00000000 25 | -------------------------------------------------------------------------------- /pywdbg/K/OUTCTL.py: -------------------------------------------------------------------------------- 1 | 2 | AMBIENT_TEXT = 0xFFFFFFFF 3 | SEND_MASK = 0x00000007 4 | DML = 0x00000020 5 | IGNORE = 0x00000003 6 | ALL_CLIENTS = 0x00000001 7 | OVERRIDE_MASK = 0x00000010 8 | AMBIENT = 0xFFFFFFFF 9 | LOG_ONLY = 0x00000004 10 | AMBIENT_DML = 0xFFFFFFFE 11 | ALL_OTHER_CLIENTS = 0x00000002 12 | NOT_LOGGED = 0x00000008 13 | THIS_CLIENT = 0x00000000 14 | -------------------------------------------------------------------------------- /pywdbg/K/OUTPUT.py: -------------------------------------------------------------------------------- 1 | 2 | IDENTITY_DEFAULT = 0x00000000 3 | SYMBOLS_NO_OFFSETS = 0x00000002 4 | SYMBOLS_NO_VALUES = 0x00000004 5 | DEBUGGEE_PROMPT = 0x00000100 6 | WARNING = 0x00000004 7 | VERBOSE = 0x00000008 8 | STATUS = 0x00000400 9 | SYMBOLS_DEFAULT = 0x00000000 10 | DEBUGGEE = 0x00000080 11 | PROMPT_REGISTERS = 0x00000020 12 | SYMBOLS_NO_TYPES = 0x00000010 13 | PROMPT = 0x00000010 14 | NORMAL = 0x00000001 15 | EXTENSION_WARNING = 0x00000040 16 | SYMBOLS_NO_NAMES = 0x00000001 17 | ERROR = 0x00000002 18 | SYMBOLS = 0x00000200 19 | -------------------------------------------------------------------------------- /pywdbg/K/STATUS.py: -------------------------------------------------------------------------------- 1 | 2 | REVERSE_GO = 0x0000000B 3 | GO_NOT_HANDLED = 0x00000003 4 | MASK = 0x0000001F 5 | OUT_OF_SYNC = 0x0000000F 6 | GO_HANDLED = 0x00000002 7 | REVERSE_STEP_OVER = 0x0000000D 8 | STEP_BRANCH = 0x00000008 9 | WAIT_INPUT = 0x00000010 10 | NO_CHANGE = 0x00000000 11 | NO_DEBUGGEE = 0x00000007 12 | REVERSE_STEP_INTO = 0x0000000E 13 | STEP_INTO = 0x00000005 14 | WAIT_TIMEOUT = 0x00000000 15 | BREAK = 0x00000006 16 | IGNORE_EVENT = 0x00000009 17 | INSIDE_WAIT = 0x00000000 18 | TIMEOUT = 0x00000011 19 | RESTART_REQUESTED = 0x0000000A 20 | REVERSE_STEP_BRANCH = 0x0000000C 21 | STEP_OVER = 0x00000004 22 | GO = 0x00000001 23 | -------------------------------------------------------------------------------- /pywdbg/K/SYMBOL.py: -------------------------------------------------------------------------------- 1 | 2 | IS_ARGUMENT = 0x00000100 3 | IS_FLOAT = 0x00000080 4 | IS_LOCAL = 0x00000200 5 | EXPANSION_LEVEL_MASK = 0x0000000F 6 | READ_ONLY = 0x00000020 7 | IS_ARRAY = 0x00000040 8 | EXPANDED = 0x00000010 9 | 10 | -------------------------------------------------------------------------------- /pywdbg/K/VALUE.py: -------------------------------------------------------------------------------- 1 | 2 | INVALID = 0 3 | INT8 = 1 4 | INT16 = 2 5 | INT32 = 3 6 | INT64 = 4 7 | FLOAT32 = 5 8 | FLOAT64 = 6 9 | FLOAT80 = 7 10 | FLOAT82 = 8 11 | FLOAT128 = 9 12 | VECTOR64 = 10 13 | VECTOR128 = 11 14 | -------------------------------------------------------------------------------- /pywdbg/K/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | from . import * 3 | 4 | INFINITE = 0xFFFFFFFF 5 | 6 | ASMOPT = { 7 | 'NO_CODE_BYTES' : 0x00000002, 8 | 'IGNORE_OUTPUT_WIDTH' : 0x00000004, 9 | 'DEFAULT' : 0x00000000, 10 | 'SOURCE_LINE_NUMBER' : 0x00000008, 11 | 'VERBOSE' : 0x00000001 12 | } 13 | 14 | FIND = { 15 | 'SOURCE_TOKEN_LOOKUP' : 0x00000008, 16 | 'SOURCE_DEFAULT' : 0x00000000, 17 | 'SOURCE_NO_SRCSRV' : 0x00000004, 18 | 'SOURCE_FULL_PATH' : 0x00000001, 19 | 'SOURCE_BEST_MATCH' : 0x00000002 20 | } 21 | 22 | TYPEOPTS = { 23 | 'MATCH_MAXSIZE' : 0x00000008, 24 | 'FORCERADIX_OUTPUT' : 0x00000004, 25 | 'UNICODE_DISPLAY' : 0x00000001, 26 | 'LONGSTATUS_DISPLAY' : 0x00000002 27 | } 28 | 29 | PROCESS = { 30 | 'DETACH_ON_EXIT' : 0x00000001, 31 | 'ONLY_THIS_PROCESS' : 0x00000002 32 | } 33 | -------------------------------------------------------------------------------- /pywdbg/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | # from . import * 3 | 4 | PATH = __file__ 5 | -------------------------------------------------------------------------------- /pywdbg/pywdbg.py: -------------------------------------------------------------------------------- 1 | 2 | import sys 3 | import argparse 4 | from . import wdbg 5 | from ptpython import repl 6 | 7 | wd = None 8 | 9 | def parse_args(): 10 | parser = argparse.ArgumentParser( 11 | prog = 'pywdbg', 12 | description = "PYWDBG", 13 | epilog = 'v0.1') 14 | parser.add_argument('path', 15 | metavar = 'PATH', nargs = '?', 16 | help = 'Create a process and debug it') 17 | parser.add_argument('-a', '--attach', 18 | metavar = 'PID|NAME', 19 | help = 'attach to PID or NAME') 20 | parser.add_argument('-k', '--kernel', 21 | metavar = 'OPTION', 22 | help = 'attach a kernel target') 23 | parser.add_argument('-g', 24 | action = 'store_true', 25 | help = 'ignore the initial breakpoint') 26 | parser.add_argument('-G', 27 | action = 'store_true', 28 | help = 'ignore the final breakpoint') 29 | parser.add_argument('--version', 30 | action='version', version='pywdbg 0.1') 31 | parser.add_argument('-x', 32 | metavar = 'SCRIPT', 33 | help = 'startup with a python script') 34 | return parser.parse_args() 35 | 36 | def run(args): 37 | global wd 38 | 39 | if args.path: 40 | assert wd.create(args.path) == 0 41 | wd.waitevent() 42 | elif args.attach: 43 | try: 44 | pid = int(args.attach) 45 | if wd.attach(pid): 46 | print('Attach failure', pid) 47 | except ValueError: 48 | if wd.attach(args.attach): 49 | print('Attach failure', args.attach) 50 | wd.waitevent() 51 | elif args.kernel: 52 | assert wd.attachkernel(args.kernel) == 0 53 | wd.waitevent() 54 | 55 | if args.x: 56 | with open(args.x) as f: 57 | exec(f.read(), {'wd': wd}) 58 | 59 | # waiting for initial breakpoint 60 | print('---------------------------------------------------------') 61 | repl.embed(globals(), locals()) 62 | 63 | def main64(): 64 | global wd 65 | 66 | wd = wdbg.startup('x64') 67 | run(parse_args()) 68 | 69 | def main(): 70 | global wd 71 | 72 | wd = wdbg.startup('x86') 73 | run(parse_args()) 74 | -------------------------------------------------------------------------------- /pywdbg/srpc.py: -------------------------------------------------------------------------------- 1 | 2 | import umsgpack as msgpack 3 | from .tstream import TcpStream 4 | from threading import RLock 5 | 6 | # Base type 7 | MSG_INVOKE = 0x10 8 | MSG_RETURN = 0x20 9 | # Concrete type 10 | MSG_CALL = MSG_INVOKE | 0x00 11 | MSG_NOTIFY = MSG_INVOKE | 0x01 12 | MSG_CLOSE = MSG_INVOKE | 0x02 13 | 14 | MSG_NOFUNC = MSG_RETURN | 0x00 15 | MSG_RETVAL = MSG_RETURN | 0x01 16 | 17 | class Handler: 18 | pass 19 | 20 | class Session: 21 | def __init__(self, io): 22 | self._io = io 23 | self._mutex = RLock() 24 | self.isclosed = False 25 | self.handler = Handler 26 | self.onopen = lambda session: None 27 | self.onclose = lambda session: None 28 | 29 | # Call a function 30 | def call(self, fid, *args): 31 | self.invoke(MSG_CALL, fid, args) 32 | return self._wait_return() 33 | 34 | def notify(self, fid, *args): 35 | self.invoke(MSG_NOTIFY, fid, args) 36 | 37 | def invoke(self, t, fid, args): 38 | self._mutex.acquire() 39 | self._pack = [t, fid, *args] 40 | b = self._send_pack() 41 | self._mutex.release() 42 | self._lastfid = fid 43 | return b 44 | 45 | def close(self): 46 | self._mutex.acquire() 47 | self._pack = [MSG_CLOSE] 48 | b = self._send_pack() 49 | self._mutex.release() 50 | self._io.close() 51 | self._closed = True 52 | return b 53 | 54 | def _return(self, v, t = MSG_RETVAL): 55 | self._mutex.acquire() 56 | self._pack = [t, v] 57 | b = self._send_pack() 58 | self._mutex.release() 59 | return b 60 | 61 | # Return a value 62 | def run(self): 63 | self.onopen(self) 64 | while self._recv_pack(): 65 | self._handle_invoke() 66 | if self.isclosed: 67 | break 68 | self.onclose(self) 69 | 70 | def _recv_pack(self): 71 | if self.isclosed: 72 | return False 73 | try: 74 | self._pack = msgpack.unpack(self._io) 75 | return True 76 | except msgpack.UnpackException as e: 77 | return False 78 | except Exception as e: 79 | print("Receive package failure", e) 80 | return False 81 | 82 | def _send_pack(self): 83 | if self.isclosed: 84 | return False 85 | try: 86 | msgpack.pack(self._pack, self._io) 87 | self._io.flush() 88 | return True 89 | except Exception as e: 90 | # print("Send package failure", e) 91 | return False 92 | 93 | def _type(self): 94 | return self._pack[0] 95 | 96 | # Wait a value from subprocess 97 | def _wait_return(self): 98 | while self._recv_pack(): 99 | t = self._type() 100 | if t & 0xF0 == MSG_INVOKE: 101 | self._handle_invoke() # handle invoke 102 | elif t == MSG_RETVAL: 103 | pack = self._pack 104 | return pack[1:] if len(pack) > 2 else pack[1] 105 | elif t == MSG_NOFUNC: 106 | raise Exception('No such function: ' + self._lastfid) 107 | else: 108 | raise Exception('Unkown flag') 109 | 110 | # Handle a invoke 111 | def _handle_invoke(self): 112 | t = self._type() 113 | assert t & 0xF0 == MSG_INVOKE 114 | if t == MSG_CLOSE: 115 | self.isclosed = True 116 | else: 117 | pack = self._pack 118 | fid = pack[1] 119 | ret = None 120 | try: 121 | fun = vars(self.handler).get(fid) 122 | if fun: 123 | ret = fun(self, *pack[2:]) 124 | elif self._type() == MSG_CALL: 125 | return self._return(None, MSG_NOFUNC) 126 | except Exception as e: 127 | print('Throwed a exception during:', fid, e) 128 | ret = None 129 | except TypeError as e: 130 | print('Args\'s number not matched:', fid, e) 131 | finally: 132 | if t == MSG_CALL: 133 | self._return(ret) 134 | 135 | 136 | class Client(Session): 137 | def __init__(self): 138 | Session.__init__(self, TcpStream()) 139 | 140 | def connect(self, addr, port): 141 | return self._io.connect(addr, port) 142 | 143 | 144 | class Server: 145 | def __init__(self, addr, port): 146 | import socket 147 | self._server = socket.socket() 148 | self._server.bind((addr, port)) 149 | self._server.listen() 150 | self.handler = {} 151 | 152 | def accept(self): 153 | from tstream import TcpStream 154 | sock, addr = self._server.accept() 155 | s = Session(TcpStream(sock), self.handler) 156 | return s 157 | -------------------------------------------------------------------------------- /pywdbg/tstream.py: -------------------------------------------------------------------------------- 1 | 2 | import socket, io 3 | 4 | class TcpStream(io.BufferedIOBase): 5 | def __init__(self, sock = None): 6 | if not sock is None: 7 | assert isinstance(sock, socket.socket) 8 | self._sock = sock 9 | self._sock = socket.socket() 10 | 11 | def read(self, size = -1): 12 | buf = bytearray() 13 | sock = self._sock 14 | # import pdb; pdb.set_trace() # XXX BREAKPOINT 15 | try: 16 | if size < 0: 17 | while True: 18 | buf += sock.recv(2048) 19 | else: 20 | rest = size 21 | while rest: 22 | data = sock.recv(rest) 23 | buf += data 24 | rest -= len(data) 25 | finally: 26 | return bytes(buf) 27 | 28 | def write(self, b): 29 | return len(b) if self._sock.sendall(b) is None else 0 30 | 31 | def flush(self): 32 | pass 33 | 34 | def connect(self, addr, port): 35 | return self._sock.connect((addr, port)) 36 | 37 | def close(self): 38 | return self._sock.close() 39 | 40 | def connect(host, port): 41 | sock = socket.socket() 42 | sock.connect((host, port)) 43 | return TcpStream(sock) 44 | -------------------------------------------------------------------------------- /pywdbg/wdbg.py: -------------------------------------------------------------------------------- 1 | 2 | import time, os, re 3 | 4 | from . import srpc 5 | from .K import ERROR, ENGOPT, EVENT, STATUS, END 6 | from threading import Thread, Event 7 | 8 | # WDbg object 9 | _wdbg = None 10 | 11 | class Breakpoint: 12 | ''' 13 | Breakpoint represents a breakpoint 14 | 15 | Attribute: 16 | callback:callable will be invoked when the breakpoint is occured 17 | ''' 18 | _dict = {} 19 | 20 | def __init__(self, id): 21 | self.id = id 22 | Breakpoint._dict[id] = self 23 | 24 | def _get(id): 25 | return Breakpoint._dict.get(id) 26 | 27 | def enable(self, b = True): 28 | '''enable or disable this breakpoint''' 29 | global _wdbg 30 | return _wdbg.enablebp(self.id, b) 31 | 32 | def remove(self): 33 | '''remove this breakpoint''' 34 | global _wdbg 35 | del Breakpoint._dict[self.id] 36 | return _wdbg.rmbp(self.id) 37 | 38 | # Auxiliary session's handler 39 | class AuxHandler: 40 | def heartbeat(ss, n): 41 | ss.notify('heartbeat', n) 42 | 43 | # Main session's handler 44 | class MainHandler: 45 | def tellinfo(ss, data): 46 | global _wdbg 47 | 48 | _wdbg._arch = data 49 | 50 | def Input(ss, data): 51 | return input() 52 | 53 | def output(ss, data): 54 | print(data.decode('gbk'), end = '') 55 | 56 | def BREAKPOINT(ss, id): 57 | bp = Breakpoint._get(id) 58 | if bp and 'callback' in vars(bp): 59 | try: 60 | return bp.callback(bp) 61 | except Exception as e: 62 | print(e) 63 | 64 | class WDbg: 65 | def __init__(self): 66 | self._taskev = Event() # event for waitting task 67 | self._compev = Event() # event for waitting to complete task 68 | self._exitcmd = '..' 69 | 70 | def backthread(): 71 | while True: 72 | self._taskev.clear() 73 | self._taskev.wait() 74 | self._result = self._back_fun(*self._back_args) 75 | self._compev.set() 76 | self._back = Thread(target = backthread) 77 | self._back.start() 78 | 79 | def __getattr__(self, name): 80 | mss = self._mss 81 | 82 | def func(*args): 83 | return mss.call(name, *args) 84 | setattr(self, name, func) 85 | return func 86 | 87 | def _back_run(self, fun, args = ()): 88 | '''run some procedure in background''' 89 | self._back_fun = fun 90 | self._back_args = args 91 | self._compev.clear() 92 | # run in background 93 | self._taskev.set() 94 | 95 | # wait the backrun return 96 | def _wait_back(self, timeout = None): 97 | if timeout: 98 | return self._compev.wait(timeout) 99 | while True: 100 | try: 101 | if self._compev.wait(0.2): 102 | break 103 | except KeyboardInterrupt: 104 | self.interrupt() 105 | return self._result 106 | 107 | def _load_config(self): 108 | f = os.path.expanduser('~/_wdbgconf.py') 109 | try: 110 | with open(f) as conf: 111 | exec(conf.read(), {'wdbg': self}) 112 | print('[configure] loaded success!') 113 | except OSError: 114 | pass 115 | except Exception as e: 116 | print('[configure] loaded failure:', e) 117 | 118 | def init(self): 119 | # output callback 120 | self.setoutput('output') 121 | self.event('BREAKPOINT', MainHandler.BREAKPOINT) 122 | # load configure file 123 | self._load_config() 124 | 125 | def waitevent(self, timeout = None): 126 | self._back_run(self._mss.call, ('waitevent', timeout)) 127 | return self._wait_back() 128 | 129 | def interrupt(self): 130 | self._ass.notify('interrupt') 131 | return self 132 | 133 | def addbp(self, pos, callback = None, *args): 134 | '''add a breakpoint''' 135 | id = self._mss.call('addbp', pos, *args) 136 | if type(id) is int: 137 | bp = Breakpoint(id) 138 | bp.pos = pos 139 | if callback: 140 | bp.callback = callback 141 | return bp 142 | 143 | def stepinto(self): 144 | self.status(STATUS.STEP_INTO) 145 | return self 146 | 147 | def stepover(self): 148 | self.status(STATUS.STEP_OVER) 149 | return self 150 | 151 | def go(self): 152 | '''continue to run''' 153 | self.status(STATUS.GO) 154 | return self 155 | 156 | def end_detach(self): 157 | '''end the debug session and detach target''' 158 | return self.end(END.ACTIVE_DETACH) 159 | 160 | def end_term(self): 161 | '''end the debug session and terminate target''' 162 | return self.end(END.ACTIVE_TERMINATE) 163 | 164 | def cmdloop(self): 165 | '''startup a command loop''' 166 | 167 | while True: 168 | try: 169 | cmd = input(self.prompt() + ' > ') 170 | if cmd == self._exitcmd: 171 | break 172 | b = self.exec(cmd) 173 | if b: 174 | print('[Execute failure]', cmd) 175 | if self.status() != STATUS.BREAK: 176 | self.waitevent() 177 | self.putstate() 178 | except EOFError: 179 | break 180 | except KeyboardInterrupt: 181 | break 182 | 183 | def event(self, name, func = None): 184 | '''register a event handler,''' 185 | try: 186 | if func: 187 | origin = self.event(name) 188 | code = getattr(EVENT, name) 189 | setattr(MainHandler, name, func) 190 | self.setevent(code, name) 191 | else: 192 | return MainHandler.__dict__.get(name) 193 | except AttributeError: 194 | raise Exception('No such event: ' + name) 195 | 196 | PATH = __file__ 197 | 198 | def search_wdbg(arch): 199 | '''search the path of the wdbg.exe''' 200 | 201 | global PATH 202 | assert arch == 'x86' or arch == 'x64' 203 | 204 | curpath = os.path.abspath(PATH) 205 | upath = os.path.split(os.path.dirname(curpath))[0] 206 | path = [upath + x + arch + '/wdbg.exe' for x in ['/build/debug/', '/bin/']] 207 | 208 | for p in path: 209 | if os.path.exists(p): 210 | return p 211 | 212 | def start_local(arch = 'x86', path = None): 213 | '''start the wdbg subprocess, and return its listened port''' 214 | 215 | path = path or search_wdbg(arch) 216 | if not path: 217 | return None 218 | 219 | from subprocess import STARTUPINFO, SW_HIDE, PIPE 220 | from subprocess import CREATE_NEW_CONSOLE, STARTF_USESHOWWINDOW 221 | from subprocess import SubprocessError, Popen 222 | 223 | si = STARTUPINFO() 224 | si.dwFlags = STARTF_USESHOWWINDOW 225 | si.wShowWindow = SW_HIDE 226 | try: 227 | return Popen([path, '-D', '-a', '127.0.0.1'], 228 | bufsize = 0, startupinfo = si, stdin = PIPE, 229 | creationflags = CREATE_NEW_CONSOLE, stdout = PIPE) 230 | except SubprocessError: 231 | return None 232 | 233 | def connect(addr = None, port = None): 234 | global _wdbg 235 | 236 | if not _wdbg: 237 | _wdbg = WDbg() 238 | _wdbg._mss = srpc.Client() 239 | _wdbg._mss.handler = MainHandler 240 | _wdbg._mss.connect(addr, port) 241 | 242 | _wdbg._ass = srpc.Client() 243 | _wdbg._ass.handler = AuxHandler 244 | _wdbg._ass.connect(addr, port) 245 | Thread(target = _wdbg._ass.run).start() 246 | 247 | _wdbg.init() 248 | return _wdbg 249 | 250 | def read_port(wdbg): 251 | line = wdbg.stdout.readline().decode() 252 | port = re.search(r'\d+', line) 253 | if port: 254 | return int(port.group(0)) 255 | 256 | def startup(arch = 'x86', path = None): 257 | '''startup the wdbg subprocess and connect it, return the WDbg object''' 258 | 259 | wdbg = start_local(arch, path) 260 | if not wdbg: 261 | return 262 | port = read_port(wdbg) 263 | if port: 264 | wd = connect('127.0.0.1', port) 265 | return wd 266 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | 2 | from setuptools import setup 3 | 4 | NAME = 'pywdbg' 5 | 6 | PACKAGES = ['pywdbg', 'pywdbg/K'] 7 | 8 | DESCRIPTION = "pywdbg --- wdbg's front end for python" 9 | 10 | CLASSIFIERS = [ 11 | 'Development Status :: 3 - Alpha', 12 | 'Intended Audience :: Developers', 13 | 'Intended Audience :: System Administrators', 14 | 'Intended Audience :: Information Technology', 15 | 'Topic :: Software Development :: Debuggers', 16 | 'License :: OSI Approved :: MIT License', 17 | 'Programming Language :: Python :: 3 :: Only' 18 | ] 19 | 20 | KEYWORDS = 'debug crack' 21 | 22 | AUTHOR = 'luzhlon' 23 | 24 | AUTHOR_EMAIL = 'luzhlon@outlook.com' 25 | 26 | URL = 'https://github.com/luzhlon/wdbg' 27 | 28 | VERSION = "0.2" 29 | 30 | LICENSE = "MIT" 31 | 32 | INSTALL_REQUIRES = ['u-msgpack-python', 'ptpython', 'prompt_toolkit'] 33 | 34 | ENTRY_POINTS = { 35 | 'console_scripts': [ 36 | 'pywdbg = pywdbg.pywdbg:main', 37 | 'pywdbg64 = pywdbg.pywdbg:main64' 38 | ] 39 | } 40 | 41 | setup( 42 | name = NAME, 43 | version = VERSION, 44 | description = DESCRIPTION, 45 | classifiers = CLASSIFIERS, 46 | keywords = KEYWORDS, 47 | author = AUTHOR, 48 | author_email = AUTHOR_EMAIL, 49 | url = URL, 50 | license = LICENSE, 51 | packages = PACKAGES, 52 | include_package_data = True, 53 | zip_safe = True, 54 | install_requires = INSTALL_REQUIRES, 55 | entry_points = ENTRY_POINTS, 56 | python_requires = '>=3.3' 57 | ) 58 | -------------------------------------------------------------------------------- /src/callback.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include "wdbg.h" 6 | #include "callback.h" 7 | 8 | enum EV_INDEX { 9 | BREAKPOINT = 0, 10 | EXCEPTION, 11 | CREATEPROCESS, 12 | EXITPROCESS, 13 | CREATETHREAD, 14 | EXITTHREAD, 15 | LOADMODULE, 16 | UNLOADMODULE, 17 | SYSTEMERROR, 18 | 19 | EV_COUNT 20 | }; 21 | // Event handler vector 22 | Value ev = Tuple::New(EV_COUNT); 23 | // Output handler 24 | Value OutputCallback::onoutput; 25 | 26 | bool InputCallback::isinputting = false; 27 | 28 | HRESULT InputCallback::StartInput(ULONG bufsize) { 29 | isinputting = true; 30 | auto str = g_ss->call("Input", (uint64_t)bufsize); 31 | if (str.isstr()) { 32 | const char *p = str; 33 | const char *e = p + str.str().size(); 34 | while (isinputting && p < e) { 35 | ULONG size; 36 | g_ctrl->ReturnInput(p); 37 | p += size; 38 | } 39 | } else 40 | printf("[Input] failure\n"); 41 | return S_OK; 42 | } 43 | 44 | HRESULT InputCallback::EndInput() { 45 | isinputting = false; return S_OK; 46 | } 47 | 48 | HRESULT OutputCallback::Output(IN ULONG Mask, IN PCSTR Text) { 49 | if (onoutput.isnil()) return S_OK; 50 | auto str = String::TRef(Text); 51 | str.str().isbin(true); 52 | g_ss->notify(onoutput, str); 53 | return S_OK; 54 | } 55 | 56 | namespace wdbg { 57 | HRESULT EventCallback::GetInterestMask(PULONG Mask) { 58 | *Mask = 59 | DEBUG_EVENT_BREAKPOINT | 60 | DEBUG_EVENT_LOAD_MODULE | 61 | DEBUG_EVENT_EXCEPTION | 62 | DEBUG_EVENT_CREATE_THREAD | 63 | DEBUG_EVENT_EXIT_THREAD | 64 | DEBUG_EVENT_CREATE_PROCESS | 65 | DEBUG_EVENT_EXIT_PROCESS | 66 | DEBUG_EVENT_UNLOAD_MODULE | 67 | DEBUG_EVENT_SYSTEM_ERROR | 68 | DEBUG_EVENT_SESSION_STATUS | 69 | DEBUG_EVENT_CHANGE_DEBUGGEE_STATE | 70 | DEBUG_EVENT_CHANGE_ENGINE_STATE | 71 | DEBUG_EVENT_CHANGE_SYMBOL_STATE; 72 | return S_OK; 73 | } 74 | 75 | bool EventCallback::RegisterEvent(ULONG event, const Value& fid) { 76 | int p = -1; 77 | switch (event) { 78 | case DEBUG_EVENT_BREAKPOINT: 79 | p = BREAKPOINT; break; 80 | case DEBUG_EVENT_EXCEPTION: 81 | p = EXCEPTION; break; 82 | case DEBUG_EVENT_LOAD_MODULE: 83 | p = LOADMODULE; break; 84 | case DEBUG_EVENT_UNLOAD_MODULE: 85 | p = UNLOADMODULE; break; 86 | case DEBUG_EVENT_CREATE_THREAD: 87 | p = CREATETHREAD; break; 88 | case DEBUG_EVENT_EXIT_THREAD: 89 | p = EXITTHREAD; break; 90 | case DEBUG_EVENT_CREATE_PROCESS: 91 | p = CREATEPROCESS; break; 92 | case DEBUG_EVENT_EXIT_PROCESS: 93 | p = EXITPROCESS; break; 94 | } 95 | if (p < 0) return false; 96 | ev.tuple().set(p, fid); 97 | return true; 98 | } 99 | 100 | HRESULT EventCallback::Breakpoint(IDebugBreakpoint *bp) { 101 | // ??? return NOT_HANDLED will cause exception 102 | auto h = ev.tuple()[BREAKPOINT]; 103 | if (h.isnil()) 104 | return DEBUG_STATUS_BREAK; 105 | ULONG id; bp->GetId(&id); 106 | return g_ss->call(h, (uint64_t)id).Int(DEBUG_STATUS_BREAK); 107 | } 108 | 109 | HRESULT EventCallback::ChangeDebuggeeState(ULONG Flags, ULONG64 Argument) { 110 | return DEBUG_STATUS_GO_NOT_HANDLED; 111 | } 112 | 113 | HRESULT EventCallback::ChangeEngineState(ULONG Flags, ULONG64 Argument) { 114 | return DEBUG_STATUS_GO_NOT_HANDLED; 115 | } 116 | 117 | HRESULT EventCallback::Exception(PEXCEPTION_RECORD64 Exception, ULONG FirstChance) { 118 | auto h = ev.tuple()[EXCEPTION]; 119 | if (h.isnil()) 120 | return DEBUG_STATUS_GO_NOT_HANDLED; 121 | return g_ss->call(h, { 122 | Exception->ExceptionAddress, 123 | (uint64_t)Exception->ExceptionCode, 124 | (uint64_t)Exception->ExceptionFlags, 125 | Exception->ExceptionInformation, 126 | Exception->ExceptionRecord, 127 | (bool)FirstChance 128 | }).Int(DEBUG_STATUS_GO_NOT_HANDLED); 129 | } 130 | 131 | HRESULT EventCallback::UnloadModule(PCSTR ImageBaseName, ULONG64 BaseOffset) { 132 | auto h = ev.tuple()[UNLOADMODULE]; 133 | if (h.isnil()) return DEBUG_STATUS_GO_NOT_HANDLED; 134 | return g_ss->call(h, { 135 | String::TRef(ImageBaseName), 136 | (bool)BaseOffset 137 | }).Int(DEBUG_STATUS_GO_NOT_HANDLED); 138 | } 139 | 140 | HRESULT EventCallback::ExitProcess(ULONG ExitCode) { 141 | auto h = ev.tuple()[EXITPROCESS]; 142 | if (h.isnil()) return DEBUG_STATUS_GO_NOT_HANDLED; 143 | return g_ss->call(h, (int64_t)ExitCode) 144 | .Int(DEBUG_STATUS_GO_NOT_HANDLED); 145 | } 146 | 147 | HRESULT EventCallback::SessionStatus(ULONG Status) { 148 | return DEBUG_STATUS_GO_NOT_HANDLED; 149 | } 150 | 151 | HRESULT EventCallback::ChangeSymbolState(ULONG Flags, ULONG64 Argument) { 152 | return DEBUG_STATUS_GO_NOT_HANDLED; 153 | } 154 | 155 | HRESULT EventCallback::SystemError(ULONG Error, ULONG Level) { 156 | auto h = ev.tuple()[SYSTEMERROR]; 157 | if (h.isnil()) 158 | return DEBUG_STATUS_BREAK; 159 | return g_ss->call(h, { 160 | (uint64_t)Error, 161 | (uint64_t)Level 162 | }).Int(DEBUG_STATUS_BREAK); 163 | } 164 | 165 | HRESULT EventCallback::CreateThread( 166 | ULONG64 Handle, 167 | ULONG64 DataOffset, 168 | ULONG64 StartOffset) { 169 | auto h = ev.tuple()[CREATETHREAD]; 170 | if (h.isnil()) return DEBUG_STATUS_GO_NOT_HANDLED; 171 | return g_ss->call(h, 172 | { Handle, DataOffset, StartOffset }).Int(DEBUG_STATUS_GO_NOT_HANDLED); 173 | } 174 | 175 | HRESULT EventCallback::ExitThread(ULONG ExitCode) { 176 | auto h = ev.tuple()[EXITTHREAD]; 177 | if (h.isnil()) return DEBUG_STATUS_GO_NOT_HANDLED; 178 | return g_ss->call(h, (int64_t)ExitCode) 179 | .Int(DEBUG_STATUS_GO_NOT_HANDLED); 180 | } 181 | 182 | HRESULT EventCallback::LoadModule( 183 | IN ULONG64 ImageFileHandle, 184 | IN ULONG64 BaseOffset, 185 | IN ULONG ModuleSize, 186 | IN PCSTR ModuleName, 187 | IN PCSTR ImageName, 188 | IN ULONG CheckSum, 189 | IN ULONG TimeDateStamp) { 190 | auto h = ev.tuple()[LOADMODULE]; 191 | if (h.isnil()) return DEBUG_STATUS_GO_NOT_HANDLED; 192 | return g_ss->call(h, { 193 | ImageFileHandle, 194 | BaseOffset, 195 | (uint64_t)ModuleSize, 196 | String::TRef(ModuleName), 197 | String::TRef(ImageName), 198 | (uint64_t)CheckSum, 199 | (uint64_t)TimeDateStamp 200 | }).Int(DEBUG_STATUS_GO_NOT_HANDLED); 201 | } 202 | 203 | HRESULT EventCallback::CreateProcess( 204 | IN ULONG64 ImageFileHandle, 205 | IN ULONG64 Handle, 206 | IN ULONG64 BaseOffset, 207 | IN ULONG ModuleSize, 208 | IN PCSTR ModuleName, 209 | IN PCSTR ImageName, 210 | IN ULONG CheckSum, 211 | IN ULONG TimeDateStamp, 212 | IN ULONG64 InitialThreadHandle, 213 | IN ULONG64 ThreadDataOffset, 214 | IN ULONG64 StartOffset) { 215 | auto h = ev.tuple()[CREATEPROCESS]; 216 | if (h.isnil()) return DEBUG_STATUS_GO_NOT_HANDLED; 217 | return g_ss->call(h, { 218 | ImageFileHandle, 219 | Handle, 220 | BaseOffset, 221 | (uint64_t)ModuleSize, 222 | String::TRef(ModuleName), 223 | String::TRef(ImageName), 224 | (uint64_t)CheckSum, 225 | (uint64_t)TimeDateStamp, 226 | InitialThreadHandle, 227 | ThreadDataOffset, 228 | StartOffset 229 | }).Int(DEBUG_STATUS_GO_NOT_HANDLED); 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /src/callback.h: -------------------------------------------------------------------------------- 1 | #ifndef __CALLBACK_H__ 2 | #define __CALLBACK_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "srpc.h" 9 | 10 | using namespace xval; 11 | 12 | class InputCallback : public IDebugInputCallbacks { 13 | public: 14 | virtual ULONG _stdcall AddRef() { return 0; } 15 | virtual ULONG _stdcall Release() { return 1; } 16 | virtual HRESULT _stdcall QueryInterface(REFIID id, void **pp) { 17 | *pp = NULL; 18 | if (IsEqualIID(id, __uuidof(IUnknown)) || 19 | IsEqualIID(id, __uuidof(IDebugInputCallbacks))) { 20 | *pp = this, AddRef(); 21 | return S_OK; 22 | } 23 | else 24 | return E_NOINTERFACE; 25 | } 26 | 27 | static bool isinputting; 28 | 29 | virtual HRESULT _stdcall StartInput(ULONG bufsize); 30 | virtual HRESULT _stdcall EndInput(); 31 | }; 32 | 33 | class OutputCallback : public IDebugOutputCallbacks { 34 | public: 35 | virtual ULONG _stdcall AddRef() { return 0; } 36 | virtual ULONG _stdcall Release() { return 1; } 37 | virtual HRESULT _stdcall QueryInterface(REFIID id, void **pp) { 38 | *pp = NULL; 39 | if (IsEqualIID(id, __uuidof(IUnknown)) || 40 | IsEqualIID(id, __uuidof(IDebugOutputCallbacks))) { 41 | *pp = this, AddRef(); 42 | return S_OK; 43 | } else 44 | return E_NOINTERFACE; 45 | } 46 | // Function's id to notify on output 47 | static Value onoutput; 48 | 49 | virtual HRESULT _stdcall Output(IN ULONG Mask, IN PCSTR Text); 50 | }; 51 | 52 | namespace wdbg { 53 | class EventCallback : public IDebugEventCallbacks { 54 | public: 55 | virtual ULONG _stdcall AddRef() { return 0; } 56 | virtual ULONG _stdcall Release() { return 1; } 57 | virtual HRESULT _stdcall QueryInterface(REFIID id, void **ppvObj) { 58 | *ppvObj = this; return NOERROR; 59 | } 60 | 61 | static bool RegisterEvent(ULONG event, const Value& fid); 62 | 63 | virtual HRESULT _stdcall GetInterestMask(PULONG Mask); 64 | 65 | virtual HRESULT _stdcall Breakpoint(IDebugBreakpoint *bp); 66 | virtual HRESULT _stdcall ChangeDebuggeeState(ULONG Flags, ULONG64 Argument); 67 | virtual HRESULT _stdcall ChangeEngineState(ULONG Flags, ULONG64 Argument); 68 | virtual HRESULT _stdcall Exception(PEXCEPTION_RECORD64 Exception, ULONG FirstChance); 69 | virtual HRESULT _stdcall UnloadModule(PCSTR ImageBaseName, ULONG64 BaseOffset); 70 | virtual HRESULT _stdcall ExitProcess(ULONG ExitCode); 71 | virtual HRESULT _stdcall SessionStatus(ULONG Status); 72 | virtual HRESULT _stdcall ChangeSymbolState(ULONG Flags, ULONG64 Argument); 73 | virtual HRESULT _stdcall SystemError(ULONG Error, ULONG Level); 74 | virtual HRESULT _stdcall CreateThread( 75 | ULONG64 Handle, 76 | ULONG64 DataOffset, 77 | ULONG64 StartOffset); 78 | virtual HRESULT _stdcall ExitThread(ULONG ExitCode); 79 | virtual HRESULT _stdcall LoadModule( 80 | IN ULONG64 ImageFileHandle, 81 | IN ULONG64 BaseOffset, 82 | IN ULONG ModuleSize, 83 | IN PCSTR ModuleName, 84 | IN PCSTR ImageName, 85 | IN ULONG CheckSum, 86 | IN ULONG TimeDateStamp); 87 | 88 | virtual HRESULT _stdcall CreateProcess( 89 | IN ULONG64 ImageFileHandle, 90 | IN ULONG64 Handle, 91 | IN ULONG64 BaseOffset, 92 | IN ULONG ModuleSize, 93 | IN PCSTR ModuleName, 94 | IN PCSTR ImageName, 95 | IN ULONG CheckSum, 96 | IN ULONG TimeDateStamp, 97 | IN ULONG64 InitialThreadHandle, 98 | IN ULONG64 ThreadDataOffset, 99 | IN ULONG64 StartOffset); 100 | }; 101 | } 102 | 103 | #endif /* __CALLBACK_H__ */ 104 | -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | #ifndef __CONFIG_H__ 2 | #define __CONFIG_H__ 3 | 4 | #ifdef _WIN64 5 | #define WDBG_ARCH "x64" 6 | #else 7 | #define WDBG_ARCH "x86" 8 | #endif // _WIN64 9 | 10 | #define WDBG_MAKE_VERSION(A, B, C) (A "." B "." C) 11 | #define WDBG_VERSION WDBG_MAKE_VERSION("0", "2", "0") 12 | 13 | #endif /* __CONFIG_H__ */ 14 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "wdbg.h" 3 | #include "config.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace std; 10 | using namespace srpc; 11 | 12 | // Main session and auxiliary session 13 | Session *g_ss = nullptr; 14 | Session *g_ass = nullptr; 15 | // HeartBeat number 16 | uint64_t last_num = 0; 17 | // Auxiliary thread, used to interrupt the WaitForEvent 18 | void aux_thread() { 19 | g_ass->addfunc("heartbeat", [](Session& rpc, Tuple& args) { 20 | auto n = args[0]; assert(n.isint()); last_num = n; 21 | }); 22 | extern void interrupt(Session& rpc, Tuple& args); 23 | g_ass->addfunc("interrupt", interrupt); 24 | // Startup the heartbeat-detect thread 25 | thread t([](){ 26 | for (uint64_t i = last_num; ; ++i) { 27 | if (g_ass->isclosed()) 28 | return; 29 | g_ass->notify("heartbeat", i); 30 | this_thread::sleep_for(chrono::seconds(1)); 31 | if (last_num != i) { 32 | printf("[DISCONNECTED]\n"); 33 | break; 34 | } 35 | } 36 | // Force the main session exit 37 | g_ctrl->SetInterrupt(DEBUG_INTERRUPT_ACTIVE); 38 | }); t.detach(); 39 | // loop 40 | g_ass->run(); 41 | } 42 | 43 | // command options 44 | static bool f_deamon = true; 45 | unsigned short f_port = 5100; 46 | const char *f_ipaddr = "0.0.0.0"; 47 | 48 | static void print_version() { 49 | printf( 50 | "wdbg(%s) v%s, A debugger for windows based Microsoft's dbgeng\n", 51 | WDBG_ARCH, WDBG_VERSION); 52 | } 53 | 54 | static void print_help() { 55 | printf( 56 | "Usage: wdbg [-D] [-p PORT] [-v]\n\n" 57 | "Options:\n" 58 | " -D non daemon mode\n" 59 | " -p PORT the port to listen, default 5100\n" 60 | " -a ADDR the adapter to listen, default 0.0.0.0\n" 61 | " -v show the version\n" 62 | " -h show help\n" 63 | ); 64 | } 65 | // if value <= 0, exit 66 | static int parse_args(int argc, char **argv) { 67 | for (size_t i = 1; i < argc; i++) { 68 | char *p = argv[i]; 69 | if (!strcmp(p, "-h")) 70 | return print_help(), 0; 71 | else if (!strcmp(p, "-D")) 72 | f_deamon = false; 73 | else if (!strcmp(p, "-v")) 74 | return print_version(), 0; 75 | else if (!strcmp(p, "-a")) { 76 | if (++i >= argc) 77 | return printf("no adapter specified\n"), -1; 78 | f_ipaddr = argv[i]; 79 | } else if (!strcmp(p, "-p")) { 80 | if (++i >= argc) 81 | return printf("no port specified\n"), -1; 82 | f_port = atoi(argv[i]); 83 | if (!f_port) 84 | return printf("error port\n"), -1; 85 | } else 86 | return printf("unknown option: %s\n", p), -1; 87 | } 88 | return 1; 89 | } 90 | 91 | int main(int argc, char **argv) { 92 | int code = parse_args(argc, argv); 93 | if (code <= 0) return code; 94 | // bind and listen 95 | tstream::server ser; 96 | while (!ser.bind(f_ipaddr, f_port)) 97 | ++f_port; 98 | ser.listen(); 99 | cout << "[PORT] " << f_port << endl; 100 | cout << "[ARCH] " << WDBG_ARCH << endl; 101 | // Main session and auxiliary session 102 | Session mss, ass; 103 | do { 104 | // Accept connection 105 | mss = ser.accept(); 106 | ass = ser.accept(); 107 | g_ss = &mss; g_ass = &ass; 108 | // Startup the auxiliary thread 109 | thread t(aux_thread); t.detach(); 110 | mss.onopen = [](Session& s) { 111 | wdbg::init(); 112 | g_ss->notify("tellinfo", WDBG_ARCH); 113 | }; 114 | mss.onclose = [](Session& s, bool exp) { 115 | if (exp) { 116 | printf("[EXIT] Abnormally\n"); 117 | } else { 118 | printf("[EXIT] Normally\n"); 119 | } 120 | g_client->EndSession(DEBUG_END_ACTIVE_TERMINATE); 121 | g_ass->close(); 122 | }; 123 | mss.run(); 124 | } while (f_deamon); 125 | return 0; 126 | } 127 | -------------------------------------------------------------------------------- /src/wdbg.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "wdbg.h" 4 | #include "callback.h" 5 | 6 | HRESULT g_hresult; 7 | 8 | extern FuncItem debug_control_funcs[]; 9 | extern FuncItem debug_client_funcs[]; 10 | extern FuncItem debug_spaces_funcs[]; 11 | extern FuncItem debug_regs_funcs[]; 12 | extern FuncItem debug_syms_funcs[]; 13 | extern FuncItem debug_sysobj_funcs[]; 14 | extern FuncItem wdbg_ext_funcs[]; 15 | 16 | void setextpath() { 17 | extern string wdbgpath(); 18 | SetEnvironmentVariable("_NT_EXECUTABLE_IMAGE_PATH", wdbgpath().c_str()); 19 | 20 | extern string wdbgdir(); 21 | auto dir = wdbgdir(); 22 | string t = dir + "\\winext;" + dir + "\\winxp;"; 23 | SetEnvironmentVariable("_NT_DEBUGGER_EXTENSION_PATH", t.c_str()); 24 | } 25 | 26 | namespace wdbg { 27 | using namespace xval; 28 | 29 | void init() { 30 | if (g_client) return; 31 | // Create debugger object's 32 | g_hresult = DebugCreate(__uuidof(DbgClient), (void **)&g_client); 33 | assert(g_hresult == S_OK); 34 | g_hresult = g_client->QueryInterface(__uuidof(DbgControl), (void **)&g_ctrl); 35 | assert(g_hresult == S_OK); 36 | g_hresult = g_client->QueryInterface(__uuidof(DbgRegs), (void **)&g_regs); 37 | assert(g_hresult == S_OK); 38 | g_hresult = g_client->QueryInterface(__uuidof(DbgSpaces), (void **)&g_spaces); 39 | assert(g_hresult == S_OK); 40 | g_hresult = g_client->QueryInterface(__uuidof(DbgSyms), (void **)&g_syms); 41 | assert(g_hresult == S_OK); 42 | g_hresult = g_client->QueryInterface(__uuidof(DbgSysobj), (void **)&g_sysobj); 43 | assert(g_hresult == S_OK); 44 | g_ctrl->AddEngineOptions(DEBUG_ENGOPT_INITIAL_BREAK | DEBUG_ENGOPT_FINAL_BREAK); 45 | g_ctrl->SetInterruptTimeout(1); 46 | // Set PATH 47 | setextpath(); 48 | // Set callback object's 49 | g_client->SetInputCallbacks(new InputCallback); 50 | g_client->SetOutputCallbacks(new OutputCallback); 51 | g_client->SetEventCallbacks(new EventCallback); 52 | // Register the RPC functions 53 | load_functions(debug_control_funcs); 54 | load_functions(debug_client_funcs); 55 | load_functions(debug_spaces_funcs); 56 | load_functions(debug_regs_funcs); 57 | load_functions(debug_syms_funcs); 58 | load_functions(debug_sysobj_funcs); 59 | load_functions(wdbg_ext_funcs); 60 | } 61 | 62 | void DValue2XValue(DEBUG_VALUE *p, Value *o, size_t n) { 63 | for (size_t i = 0; i < n; i++) 64 | switch (p[i].Type) { 65 | case DEBUG_VALUE_INT8: 66 | o[i] = (uint64_t)p[i].I8; break; 67 | case DEBUG_VALUE_INT16: 68 | o[i] = (uint64_t)p[i].I16; break; 69 | case DEBUG_VALUE_INT32: 70 | o[i] = (uint64_t)p[i].I32; break; 71 | case DEBUG_VALUE_INT64: 72 | o[i] = (uint64_t)p[i].I64; break; 73 | case DEBUG_VALUE_FLOAT32: 74 | o[i] = p[i].F32; break; 75 | case DEBUG_VALUE_FLOAT64: 76 | o[i] = p[i].F64; break; 77 | default: 78 | o[i].reset(); break; 79 | } 80 | } 81 | 82 | Value DValue2XValue(DEBUG_VALUE& p) { 83 | Value v; 84 | DValue2XValue(&p, &v); 85 | return v; 86 | } 87 | 88 | int load_functions(FuncItem* items) { 89 | int i = 0; 90 | for (; items[i].name; i++) 91 | g_ss->addfunc(items[i].name, items[i].function); 92 | return i; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/wdbg.h: -------------------------------------------------------------------------------- 1 | #ifndef __WDBG_H__ 2 | #define __WDBG_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #ifdef _WDBG_EXPORT 12 | #define WDBGEXPORT __declspec(dllexport) 13 | #else 14 | #define WDBGEXPORT __declspec(dllimport) 15 | #endif // _WDBG_EXPORT 16 | 17 | 18 | #define DbgClient IDebugClient5 19 | #define DbgControl IDebugControl4 20 | #define DbgSpaces IDebugDataSpaces4 21 | #define DbgRegs IDebugRegisters2 22 | #define DbgSyms IDebugSymbols3 23 | #define DbgSysobj IDebugSystemObjects4 24 | 25 | extern DbgClient WDBGEXPORT *g_client; 26 | extern DbgControl WDBGEXPORT *g_ctrl; 27 | extern DbgSpaces WDBGEXPORT *g_spaces; 28 | extern DbgRegs WDBGEXPORT *g_regs; 29 | extern DbgSyms WDBGEXPORT *g_syms; 30 | extern DbgSysobj WDBGEXPORT *g_sysobj; 31 | extern HRESULT g_hresult; 32 | extern srpc::Session *g_ss; 33 | 34 | struct FuncItem { 35 | const char *name; 36 | srpc::Function function; 37 | }; 38 | 39 | namespace wdbg { 40 | using namespace xval; 41 | using namespace srpc; 42 | 43 | void init(); 44 | void DValue2XValue(DEBUG_VALUE *p, Value *o, size_t n = 1); 45 | Value DValue2XValue(DEBUG_VALUE& p); 46 | 47 | int WDBGEXPORT load_functions(FuncItem* items); 48 | } 49 | 50 | extern "C" typedef FuncItem *(*WDBG_INIT_FUNC)(DbgClient*, DbgControl*, DbgSpaces*, DbgRegs*, DbgSyms*, DbgSysobj*); 51 | 52 | #endif /* __WDBG_H__ */ 53 | -------------------------------------------------------------------------------- /src/wdbg_client.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "wdbg.h" 3 | #include "callback.h" 4 | 5 | DbgClient *g_client = nullptr; 6 | 7 | static ULONG default_create_flags = CREATE_NEW_CONSOLE | DEBUG_CREATE_PROCESS_NO_DEBUG_HEAP | DEBUG_PROCESS; 8 | 9 | using namespace srpc; 10 | using namespace xval; 11 | 12 | static void create(Session& rpc, Tuple& args) { 13 | auto path = args[0]; 14 | ULONG flags = args[1].Int(default_create_flags); 15 | if (path.isstr()) { 16 | g_hresult = g_client->CreateProcess(0, (PSTR)path.str().c_str(), flags); 17 | } 18 | rpc.retn((uint64_t)g_hresult); 19 | } 20 | 21 | static void attach(Session& rpc, Tuple& args) { 22 | auto p = args[0]; 23 | if (p.isint()) { 24 | ULONG aflags = args[1].Int(DEBUG_ATTACH_INVASIVE_RESUME_PROCESS); 25 | g_hresult = g_client->AttachProcess(0, p.Int(), aflags); 26 | } else if (p.isstr()) { 27 | ULONG cflags = args[1].Int(default_create_flags); 28 | ULONG aflags = args[2].Int(DEBUG_ATTACH_INVASIVE_RESUME_PROCESS); 29 | g_hresult = g_client->CreateProcessAndAttach(0, (char *)(const char *)p, cflags, 0, aflags); 30 | } 31 | rpc.retn((uint64_t)g_hresult); 32 | } 33 | 34 | static void detach(Session& rpc, Tuple& args) { 35 | g_hresult = g_client->DetachCurrentProcess(); 36 | rpc.retn((uint64_t)g_hresult); 37 | } 38 | 39 | static void terminate(Session& rpc, Tuple& args) { 40 | g_hresult = g_client->TerminateCurrentProcess(); 41 | rpc.retn((uint64_t)g_hresult); 42 | } 43 | 44 | static void abandon(Session& rpc, Tuple& args) { 45 | g_hresult = g_client->AbandonCurrentProcess(); 46 | rpc.retn((uint64_t)g_hresult); 47 | } 48 | 49 | static void exitcode(Session& rpc, Tuple& args) { 50 | ULONG code; 51 | if (S_OK == g_client->GetExitCode(&code)) 52 | rpc.retn((uint64_t)code); 53 | } 54 | 55 | static void setinput(Session& rpc, Tuple& args) { 56 | // 57 | } 58 | 59 | static void setoutput(Session& rpc, Tuple& args) { 60 | OutputCallback::onoutput = args[0]; 61 | } 62 | 63 | static void setevent(Session& rpc, Tuple& args) { 64 | auto t = args[0]; 65 | auto f = args[1]; 66 | rpc.retn(wdbg::EventCallback::RegisterEvent((ULONG)t.Int(), f)); 67 | } 68 | 69 | static void attachkernel(Session& rpc, Tuple& args) { 70 | PCSTR options = args[0]; 71 | if (options) { 72 | g_hresult = g_client->AttachKernel(DEBUG_ATTACH_KERNEL_CONNECTION, options); 73 | rpc.retn((uint64_t)g_hresult); 74 | } 75 | } 76 | // End the current session 77 | static void end(Session& rpc, Tuple& args) { 78 | g_hresult = g_client->EndSession(args[0].Int(DEBUG_END_ACTIVE_TERMINATE)); 79 | rpc.retn((uint64_t)g_hresult); 80 | } 81 | 82 | FuncItem debug_client_funcs[] = { 83 | {"create", create}, 84 | {"end", end}, 85 | {"setinput", setinput}, 86 | {"setoutput", setoutput}, 87 | {"setevent", setevent}, 88 | {"attach", attach}, {"detach", detach}, 89 | {"terminate", terminate}, 90 | {"abandon", abandon}, {"exitcode", exitcode}, 91 | {"attachkernel", attachkernel}, 92 | {nullptr, nullptr} 93 | }; 94 | -------------------------------------------------------------------------------- /src/wdbg_ctrl.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "wdbg.h" 3 | 4 | #include 5 | 6 | DbgControl *g_ctrl = nullptr; 7 | 8 | using namespace xval; 9 | using namespace srpc; 10 | 11 | static void addopts(Session& rpc, Tuple& args) { 12 | auto opts = args[0]; 13 | if (opts.isint()) { 14 | g_hresult = g_ctrl->AddEngineOptions((uint64_t)opts); 15 | rpc.retn(S_OK == g_hresult); 16 | } 17 | } 18 | 19 | static void status(Session& rpc, Tuple& args) { 20 | ULONG status; 21 | if (args[0].isint()) 22 | g_hresult = g_ctrl->SetExecutionStatus(args[0].Int()), 23 | rpc.retn((uint64_t)g_hresult); 24 | else 25 | g_hresult = g_ctrl->GetExecutionStatus(&status), 26 | rpc.retn((uint64_t)status); 27 | } 28 | 29 | static void prompt(Session& rpc, Tuple& args) { 30 | char buf[1024]; 31 | ULONG len; 32 | g_hresult = g_ctrl->GetPromptText(buf, sizeof(buf), &len); 33 | if (g_hresult == S_OK) 34 | rpc.retn(String::TRef(buf, len)); 35 | } 36 | 37 | void interrupt(Session& rpc, Tuple& args) { 38 | ULONG flag = args[0].Int(DEBUG_INTERRUPT_ACTIVE); 39 | rpc.retn(S_OK == (g_hresult = g_ctrl->SetInterrupt(flag))); 40 | } 41 | 42 | static void asm_(Session& rpc, Tuple& args) { 43 | ULONG64 offset = args[0]; 44 | auto str = args[1]; 45 | if (str.isstr()) { 46 | ULONG64 endoffset; 47 | g_hresult = g_ctrl->Assemble(offset, str, &endoffset); 48 | if (S_OK == g_hresult) 49 | rpc.retn(endoffset); 50 | } 51 | } 52 | 53 | static void disasm(Session& rpc, Tuple& args) { 54 | char buffer[1024]; 55 | ULONG64 offset = args[0].Int(0); 56 | ULONG64 count = args[1].Int(0); 57 | ULONG64 endoffset; 58 | ULONG dissize; 59 | ULONG flags = 0; 60 | if (!offset) return rpc.retn(false); 61 | if (count) { 62 | auto t = Tuple::New(count); 63 | endoffset = offset; 64 | for (size_t i = 0; i < count; i++) { 65 | g_hresult = g_ctrl->Disassemble(endoffset, flags, 66 | buffer, sizeof(buffer), &dissize, &endoffset); 67 | if (S_OK == g_hresult) 68 | t.tuple().set(i, String::New(buffer, dissize)); 69 | else 70 | break; 71 | } 72 | rpc.retn(t); 73 | } else { 74 | g_hresult = g_ctrl->Disassemble(offset, flags, buffer, sizeof(buffer), &dissize, &endoffset); 75 | if (S_OK == g_hresult) 76 | rpc.retn(String::TRef(buffer, dissize)); 77 | } 78 | } 79 | 80 | static void waitevent(Session& rpc, Tuple& args) { 81 | g_hresult = g_ctrl->WaitForEvent(0, args[0].Int(INFINITE)); 82 | rpc.retn((uint64_t)g_hresult); 83 | } 84 | // Access breakpoint 85 | static void addba(Session& rpc, Tuple& args) { 86 | IDebugBreakpoint2 *bp; 87 | auto offset = args[0]; 88 | ULONG type = args[1].Int(DEBUG_BREAK_READ), 89 | size = args[2].Int(1), id; 90 | g_hresult = g_ctrl->AddBreakpoint2(DEBUG_BREAKPOINT_DATA, DEBUG_ANY_ID, &bp); 91 | if (S_OK == g_hresult) { 92 | if (offset.isint()) 93 | bp->SetOffset(offset); 94 | else if (offset.isstr()) 95 | bp->SetOffsetExpression(offset); 96 | bp->AddFlags(DEBUG_BREAKPOINT_ENABLED); 97 | bp->SetDataParameters(size, type); 98 | ULONG id; 99 | if (S_OK == bp->GetId(&id)) 100 | rpc.retn((uint64_t)id); 101 | } 102 | } 103 | // Code breakpoint 104 | static void addbp(Session& rpc, Tuple& args) { 105 | IDebugBreakpoint2 *bp; 106 | auto offset = args[0]; 107 | ULONG id; 108 | g_hresult = g_ctrl->AddBreakpoint2(DEBUG_BREAKPOINT_CODE, DEBUG_ANY_ID, &bp); 109 | if (S_OK == g_hresult) { 110 | if (offset.isint()) 111 | bp->SetOffset(offset); 112 | else if (offset.isstr()) 113 | bp->SetOffsetExpression(offset); 114 | bp->AddFlags(DEBUG_BREAKPOINT_ENABLED); 115 | if (S_OK == bp->GetId(&id)) 116 | rpc.retn((uint64_t)id); 117 | } 118 | } 119 | static IDebugBreakpoint2 *getbp(const Value& id) { 120 | IDebugBreakpoint2 *bp; 121 | if (id.isint() && g_ctrl->GetBreakpointById2(id.Int(), &bp) == S_OK) 122 | return bp; 123 | return nullptr; 124 | } 125 | // Remove a breakpoint 126 | static void rmbp(Session& rpc, Tuple& args) { 127 | auto bp = getbp(args[0]); 128 | if (bp) { 129 | g_hresult = g_ctrl->RemoveBreakpoint2(bp); 130 | rpc.retn((uint64_t)g_hresult); 131 | } 132 | } 133 | // Enable or disable a breakpoint 134 | static void enablebp(Session& rpc, Tuple& args) { 135 | auto bp = getbp(args[0]); 136 | if (bp) { 137 | if (args[1].Bool(true)) 138 | g_hresult = bp->AddFlags(DEBUG_BREAKPOINT_ENABLED); 139 | else 140 | g_hresult = bp->RemoveFlags(DEBUG_BREAKPOINT_ENABLED); 141 | rpc.retn((uint64_t)g_hresult); 142 | } 143 | } 144 | // Get/set breakpoint's command 145 | static void bpcmd(Session& rpc, Tuple& args) { 146 | auto bp = getbp(args[0]); 147 | if (bp) { 148 | if (args[1].isstr()) { 149 | g_hresult = bp->SetCommand(args[1]); 150 | } else { 151 | char buf[1024]; 152 | ULONG size; 153 | g_hresult = bp->GetCommand(buf, sizeof(buf), &size); 154 | if (g_hresult == S_OK) 155 | rpc.retn(String::TRef(buf, size)); 156 | } 157 | } 158 | } 159 | // Get/set breakpoint's match id 160 | static void bpthread(Session& rpc, Tuple& args) { 161 | auto bp = getbp(args[0]); 162 | if (bp) { 163 | if (args[1].isint()) { 164 | g_hresult = bp->SetMatchThreadId(args[1].Int()); 165 | } else { 166 | ULONG tid; 167 | g_hresult = bp->GetMatchThreadId(&tid); 168 | if (g_hresult == S_OK) 169 | rpc.retn((uint64_t)tid); 170 | } 171 | } 172 | } 173 | // Get the position of instruction 'ret' in this function 174 | static void retpos(Session& rpc, Tuple& args) { 175 | ULONG64 offset; 176 | g_hresult = g_ctrl->GetReturnOffset(&offset); 177 | if (S_OK == g_hresult) 178 | rpc.retn(offset); 179 | } 180 | // Get or set the assemble(disassemble) options 181 | static void asmopts(Session& rpc, Tuple& args) { 182 | auto opt = args[0]; 183 | if (opt.isint()) { 184 | g_hresult = g_ctrl->SetAssemblyOptions((uint64_t)opt); 185 | rpc.retn(g_hresult == S_OK); 186 | } else { 187 | ULONG options; 188 | g_hresult = g_ctrl->GetAssemblyOptions(&options); 189 | rpc.retn(g_hresult == S_OK ? (uint64_t)options : false); 190 | } 191 | } 192 | // Execute a command(windbg) 193 | static void exec(Session& rpc, Tuple& args) { 194 | PCSTR cmd = args[0]; 195 | ULONG flags = args[1].Int(DEBUG_EXECUTE_ECHO); 196 | if (cmd) { 197 | g_hresult = g_ctrl->Execute(DEBUG_OUTCTL_THIS_CLIENT, cmd, flags); 198 | rpc.retn((uint64_t)g_hresult); 199 | } 200 | } 201 | // Evaluate a expression 202 | static void eval(Session& rpc, Tuple& args) { 203 | auto expr = args[0]; 204 | ULONG desiredType = args[1].Int(DEBUG_VALUE_INVALID); 205 | DEBUG_VALUE dv; 206 | if (expr.isstr()) { 207 | g_hresult = g_ctrl->Evaluate(expr, desiredType, &dv, nullptr); 208 | if (S_OK == g_hresult) 209 | rpc.retn(wdbg::DValue2XValue(dv)); 210 | } 211 | } 212 | 213 | static void is64(Session& rpc, Tuple& args) { 214 | rpc.retn(g_ctrl->IsPointer64Bit() == S_OK); 215 | } 216 | 217 | static void putstate(Session& rpc, Tuple& args) { 218 | g_hresult = g_ctrl->OutputCurrentState(DEBUG_OUTCTL_THIS_CLIENT, DEBUG_CURRENT_DEFAULT); 219 | rpc.retn((uint64_t)g_hresult); 220 | } 221 | 222 | static void addext(Session& rpc, Tuple& args) { 223 | ULONG64 handle; 224 | g_hresult = g_ctrl->AddExtension(args[0], 0, &handle); 225 | if (S_OK == g_hresult) 226 | rpc.retn(handle); 227 | } 228 | 229 | static void callext(Session& rpc, Tuple& args) { 230 | ULONG64 handle = args[0].Int(0); 231 | if (handle) { 232 | g_hresult = g_ctrl->CallExtension(handle, args[1], args[2]); 233 | rpc.retn((uint64_t)g_hresult); 234 | } 235 | } 236 | 237 | FuncItem debug_control_funcs[] = { 238 | {"addopts", addopts}, 239 | {"status", status}, 240 | {"prompt", prompt}, 241 | {"putstate", putstate}, 242 | // {"asm", asm}, 243 | {"disasm", disasm}, 244 | {"addbp", addbp}, 245 | {"addba", addba}, 246 | {"rmbp", rmbp}, 247 | {"enablebp", enablebp}, 248 | {"bpcmd", bpcmd}, 249 | {"bpthread", bpthread}, 250 | {"retpos", retpos}, 251 | {"asmopts", asmopts}, 252 | {"interrupt", interrupt}, 253 | {"exec", exec}, 254 | {"eval", eval}, 255 | {"waitevent", waitevent}, 256 | {"is64", is64}, 257 | {"addext", addext}, 258 | {"callext", callext}, 259 | {nullptr, nullptr} 260 | }; 261 | -------------------------------------------------------------------------------- /src/wdbg_ext.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "wdbg.h" 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace xval; 8 | using namespace srpc; 9 | 10 | Value EntryPoint = "EntryPoint"_x; 11 | Value Machine = "Machine"_x; 12 | Value Subsystem = "Subsystem"_x; 13 | Value ImageSize = "ImageSize"_x; 14 | 15 | // ([module], attribute...) 16 | static void peinfo(Session& rpc, Tuple& args) { 17 | ULONG64 base = 0; 18 | size_t index = 0; 19 | 20 | if (args[0].isint()) 21 | base = args[0].Int(), index++; 22 | else 23 | g_syms->GetModuleByIndex(0, &base); 24 | 25 | IMAGE_NT_HEADERS64 ImgNt; 26 | g_spaces->ReadImageNtHeaders(base, &ImgNt); 27 | 28 | if (ImgNt.Signature == IMAGE_NT_SIGNATURE) { // "PE\0\0" 29 | auto t_ = Tuple::New(args.size() - index); 30 | auto& t = t_.tuple(); 31 | for (size_t i = 0; i < args.size(); i++, index++) { 32 | auto v = args[index]; 33 | if (v == EntryPoint) 34 | t.set(i, (uint64_t)ImgNt.OptionalHeader.AddressOfEntryPoint); 35 | else if (v == ImageSize) 36 | t.set(i, (uint64_t)ImgNt.OptionalHeader.SizeOfImage); 37 | else if (v == Machine) 38 | t.set(i, (uint64_t)ImgNt.FileHeader.Machine); 39 | else 40 | t.set(i, Value()); 41 | } 42 | rpc.retn(t); 43 | } 44 | } 45 | 46 | static void loadplug(Session& rpc, Tuple& args) { 47 | const char *path = args[0]; 48 | if (path) { 49 | HMODULE mod = LoadLibrary(path); 50 | rpc.retn(mod != NULL); 51 | } 52 | } 53 | 54 | string wdbgpath() { 55 | char buf[512]; 56 | auto len = GetModuleFileName(GetModuleHandle(NULL), buf, sizeof(buf)); 57 | if (len > sizeof(buf)) { 58 | string path(len, '\0'); 59 | GetModuleFileName(GetModuleHandle(NULL), (char *)path.c_str(), path.size()); 60 | } else { 61 | return buf; 62 | } 63 | } 64 | 65 | string wdbgdir() { 66 | auto path = wdbgpath(); 67 | auto pos = path.rfind('\\'); 68 | if (pos == string::npos) 69 | return ""; 70 | path.resize(pos); 71 | return path; 72 | } 73 | // Get the path of wdbg.exe 74 | static void wdbgpath(Session& rpc, Tuple& args) { 75 | auto path = wdbgpath(); 76 | rpc.retn(String::TRef(path.c_str(), path.size())); 77 | } 78 | // Get the directory of wdbg.exe 79 | static void wdbgdir(Session& rpc, Tuple& args) { 80 | auto dir = wdbgdir(); 81 | rpc.retn(String::TRef(dir.c_str(), dir.size())); 82 | } 83 | // Get or set the environment variable 84 | static void env(Session& rpc, Tuple& args) { 85 | auto e = args[0]; 86 | if (args[1].isstr()) 87 | rpc.retn((bool)SetEnvironmentVariable(e, args[1])); 88 | else 89 | rpc.retn(String::TRef(getenv(e))); 90 | } 91 | 92 | FuncItem wdbg_ext_funcs[] = { 93 | {"peinfo", peinfo}, 94 | {"wdbgpath", wdbgpath}, 95 | {"wdbgdir", wdbgdir}, 96 | {"env", env}, 97 | {"loadplug", loadplug}, 98 | {"echo", [](Session& rpc, Tuple& args) { 99 | rpc.retn(&args); 100 | }}, 101 | {nullptr, nullptr}, 102 | }; 103 | -------------------------------------------------------------------------------- /src/wdbg_regs.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "wdbg.h" 4 | 5 | DbgRegs *g_regs = nullptr; 6 | 7 | using namespace wdbg; 8 | using namespace xval; 9 | using namespace srpc; 10 | 11 | static bool GetRegIndexs(Value *p, ULONG *o, size_t n = 1) { 12 | for (size_t i = 0; i < n; i++) { 13 | if (p[i].isint()) 14 | o[i] = (uint64_t)p[i]; 15 | else if (p[i].isstr()) 16 | g_regs->GetIndexByName(p[i], o + i); 17 | else 18 | return false; 19 | } 20 | return true; 21 | } 22 | 23 | static void XValue2DValue(Value &o, DEBUG_VALUE *p, ULONG type) { 24 | p->Type = type; 25 | switch (type) { 26 | case DEBUG_VALUE_INT8: 27 | p->I8 = (uint64_t)o; break; 28 | case DEBUG_VALUE_INT16: 29 | p->I16 = (uint64_t)o; break; 30 | case DEBUG_VALUE_INT32: 31 | p->I32 = (uint64_t)o; break; 32 | case DEBUG_VALUE_INT64: 33 | p->I64 = (uint64_t)o; break; 34 | case DEBUG_VALUE_FLOAT32: 35 | p->F32 = o; break; 36 | case DEBUG_VALUE_FLOAT64: 37 | p->F64 = o; break; 38 | } 39 | } 40 | 41 | // value, type 42 | static void getreg(Session& rpc, Tuple& args) { 43 | ULONG index; 44 | DEBUG_VALUE val; 45 | if (GetRegIndexs(args.begin(), &index) && 46 | (g_hresult = g_regs->GetValue(index, &val)) == S_OK) 47 | rpc.retn({ DValue2XValue(val), (uint64_t)val.Type}); 48 | } 49 | // value, type 50 | static void setreg(Session& rpc, Tuple& args) { 51 | ULONG index; 52 | DEBUG_VALUE dv; 53 | GetRegIndexs(args.begin(), &index); 54 | XValue2DValue(args[1], &dv, args[2].Int(DEBUG_VALUE_INT64)); 55 | g_hresult = g_regs->SetValue(index, &dv); 56 | rpc.retn((uint64_t)g_hresult); 57 | } 58 | // {value} 59 | static void getregs(Session& rpc, Tuple& args) { 60 | auto indexs = (ULONG*)alloca(sizeof(ULONG) * args.size()); 61 | auto dvs = (DEBUG_VALUE*)alloca(sizeof(DEBUG_VALUE) * args.size()); 62 | GetRegIndexs(args.begin(), indexs, args.size()); 63 | auto ret = Tuple::New(args.size()); 64 | for (size_t i = 0; i < args.size(); i++) 65 | g_hresult = g_regs->GetValue(indexs[i], dvs+i); 66 | DValue2XValue(dvs, ret.tuple().begin(), args.size()); 67 | rpc.retn(ret); 68 | } 69 | 70 | static void iptr(Session& rpc, Tuple& args) { 71 | ULONG source = args[0].Int(DEBUG_REGSRC_DEBUGGEE); 72 | ULONG64 offset; 73 | if (S_OK == g_regs->GetInstructionOffset2(source, &offset)) 74 | rpc.retn(offset); 75 | } 76 | 77 | FuncItem debug_regs_funcs[] = { 78 | {"iptr", iptr}, 79 | {"getreg", getreg}, 80 | {"setreg", setreg}, 81 | {"getregs", getregs}, 82 | {nullptr, nullptr} 83 | }; 84 | -------------------------------------------------------------------------------- /src/wdbg_spaces.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "wdbg.h" 4 | 5 | DbgSpaces *g_spaces = nullptr; 6 | 7 | using namespace xval; 8 | using namespace srpc; 9 | // Read the virtual memory 10 | static void read(Session& rpc, Tuple& args) { 11 | ULONG64 offset = args[0].Int(0); 12 | if (offset) { 13 | ULONG size = args[1].Int(0); 14 | ULONG bytesread; 15 | auto buf = (char *)alloca(size); 16 | g_hresult = g_spaces->ReadVirtual(offset, buf, size, &bytesread); 17 | if (S_OK == g_hresult) { 18 | auto data = String::TRef(buf, bytesread); 19 | data.str().isbin(true); 20 | rpc.retn(data); 21 | } 22 | } 23 | } 24 | // Write the virtual memory 25 | static void write(Session& rpc, Tuple& args) { 26 | ULONG64 offset = args[0].Int(0); 27 | auto data = args[1]; 28 | if (offset && data.isstr()) { 29 | ULONG byteswritten; 30 | g_hresult = g_spaces->WriteVirtual( 31 | offset, 32 | (char *)data.str().c_str(), 33 | data.str().size(), 34 | &byteswritten); 35 | if (S_OK == g_hresult) 36 | rpc.retn((uint64_t)byteswritten); 37 | } 38 | } 39 | // Read the physical memory 40 | static void readphy(Session& rpc, Tuple& args) { 41 | auto pos = args[0]; 42 | if (pos.isint()) { 43 | ULONG bytesread; 44 | ULONG size = args[1].Int(1); 45 | auto buf = (char *)alloca(size); 46 | g_hresult = g_spaces->ReadPhysical(pos.Int(), buf, size, &bytesread); 47 | if (S_OK == g_hresult) { 48 | auto data = String::TRef(buf, bytesread); 49 | data.str().isbin(true); 50 | rpc.retn(data); 51 | } 52 | } 53 | } 54 | // Write the physical memory 55 | static void writephy(Session& rpc, Tuple& args) { 56 | auto pos = args[0], data = args[1]; 57 | if (pos.isint() && data.isstr()) { 58 | ULONG byteswritten; 59 | g_hresult = g_spaces->WritePhysical( 60 | pos.Int(), data, data.str().size(), &byteswritten); 61 | if (S_OK == g_hresult) 62 | rpc.retn((uint64_t)byteswritten); 63 | } 64 | } 65 | 66 | static void readstr(Session& rpc, Tuple& args) { 67 | ULONG64 offset = args[0].Int(0); 68 | ULONG len; 69 | char buf[10240]; 70 | g_hresult = g_spaces->ReadMultiByteStringVirtual(offset, sizeof(buf), buf, sizeof(buf), &len); 71 | if (S_OK == g_hresult) { 72 | auto str = String::TRef(buf, len); 73 | str.str().isbin(true); 74 | rpc.retn(str); 75 | } 76 | } 77 | 78 | static void readustr(Session& rpc, Tuple& args) { 79 | ULONG64 offset = args[0].Int(0); 80 | ULONG codePage = args[1].Int(CP_ACP); 81 | ULONG len = 0; 82 | char buf[10240]; 83 | g_hresult = g_spaces->ReadUnicodeStringVirtual(offset, sizeof(buf), codePage, buf, sizeof(buf), &len); 84 | // return S_OK == g_hresult ? String::New(buf, len, true) : Value(); 85 | rpc.retn(len ? String::New(buf, strlen(buf), true) : Value()); 86 | } 87 | 88 | static void readptr(Session& rpc, Tuple& args) { 89 | ULONG64 offset = args[0].Int(0); 90 | ULONG count = args[1].Int(1); 91 | auto p = (ULONG64 *)alloca(sizeof(ULONG64) * count); 92 | g_hresult = g_spaces->ReadPointersVirtual(count, offset, p); 93 | if (S_OK == g_hresult) { 94 | if (count > 1) { 95 | auto t = Tuple::New(count); 96 | for (size_t i = 0; i < count; i++) 97 | t.tuple().set(i, p[i]); 98 | rpc.retn(t); 99 | } else { 100 | rpc.retn((uint64_t)*p); 101 | } 102 | } 103 | } 104 | 105 | static void search(Session& rpc, Tuple& args) { 106 | ULONG64 Offset = args[0].Int(0); 107 | ULONG64 Length = args[1].Int(0); 108 | ULONG Flags = 0; 109 | PVOID Pattern = nullptr; 110 | ULONG PatternSize = 0; 111 | ULONG PatternGranularity = 1; 112 | 113 | if (args[2].isstr()) { 114 | ULONG64 MatchOffset; 115 | Pattern = (PVOID)args[2].str().c_str(); 116 | PatternSize = args[2].str().size(); 117 | if (g_spaces->SearchVirtual2(Offset, Length, Flags, Pattern, 118 | PatternSize, PatternGranularity, &MatchOffset) == S_OK) 119 | rpc.retn(MatchOffset); 120 | } 121 | } 122 | 123 | FuncItem debug_spaces_funcs[] = { 124 | {"read", read}, 125 | {"write", write}, 126 | {"readphy", readphy}, 127 | {"writephy", writephy}, 128 | {"readstr", readstr}, 129 | {"readustr", readustr}, 130 | {"readptr", readptr}, 131 | {"search", search}, 132 | {nullptr, nullptr} 133 | }; 134 | -------------------------------------------------------------------------------- /src/wdbg_syms.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "wdbg.h" 4 | 5 | DbgSyms *g_syms = nullptr; 6 | 7 | using namespace xval; 8 | using namespace srpc; 9 | // Get position by a symbol name 10 | static void name2pos(Session& rpc, Tuple& args) { 11 | auto name = args[0]; 12 | ULONG64 offset; 13 | if (name.isstr() && g_syms->GetOffsetByName(name, &offset) != S_FALSE) 14 | rpc.retn(offset); 15 | } 16 | // Get or set the symbol's path 17 | static void sympath(Session& rpc, Tuple& args) { 18 | char buf[4096]; 19 | ULONG size; 20 | if (args[0].isstr()) { 21 | g_hresult = g_syms->SetSymbolPath(args[0]); 22 | rpc.retn((uint64_t)g_hresult); 23 | } else { 24 | g_hresult == g_syms->GetSymbolPath(buf, sizeof(buf), &size); 25 | if (S_OK == g_hresult) 26 | rpc.retn(buf); 27 | } 28 | } 29 | // Get numbers of module 30 | static void modnum(Session& rpc, Tuple& args) { 31 | ULONG loaded, unloaded; 32 | g_syms->GetNumberModules(&loaded, &unloaded); 33 | rpc.retn({ (uint64_t)loaded, (uint64_t)unloaded }); 34 | } 35 | // Get module by id or name 36 | static void getmod(Session& rpc, Tuple& args) { 37 | auto id = args[0]; 38 | ULONG64 base = 0; 39 | if (id.isint()) { 40 | g_syms->GetModuleByIndex(id.Int(), &base); 41 | rpc.retn(base); 42 | } else if (id.isstr()) { 43 | ULONG index; 44 | g_syms->GetModuleByModuleName(id, args[1].Int(0), &index, &base); 45 | rpc.retn({ base, (uint64_t)index }); 46 | } 47 | } 48 | // Get module by offset 49 | static void pos2mod(Session& rpc, Tuple& args) { 50 | ULONG64 base; ULONG index; 51 | g_syms->GetModuleByOffset(args[0].Int(), args[0].Int(0), &index, &base); 52 | rpc.retn({ base, (uint64_t)index }); 53 | } 54 | // Get type's id by name and optional offset 55 | static void typeid_(Session& rpc, Tuple& args) { 56 | ULONG64 mod = 0; ULONG id; 57 | const char *p = nullptr; 58 | if (args[0].isint()) 59 | mod = args[0].Int(), p = args[1]; 60 | else if (args[0].isstr()) 61 | p = args[0]; 62 | g_hresult = g_syms->GetTypeId(mod, p, &id); 63 | if (S_OK == g_hresult) 64 | rpc.retn((uint64_t)id); 65 | } 66 | // Get the id and it's module by a symbol 67 | static void symtype(Session& rpc, Tuple& args) { 68 | ULONG id; ULONG64 mod; 69 | g_hresult = g_syms->GetSymbolTypeId(args[0], &id, &mod); 70 | if (S_OK == g_hresult) 71 | rpc.retn({ (uint64_t)id, mod }); 72 | } 73 | // (id | name, module = 0) 74 | static void typesize(Session& rpc, Tuple& args) { 75 | ULONG id = 0, size; 76 | auto n = args[0]; 77 | if (n.isint()) id = n.Int(); 78 | else if (n.isstr()) 79 | g_syms->GetTypeId(args[1].Int(0), n, &id); 80 | g_syms->GetTypeSize(args[1].Int(0), id, &size); 81 | rpc.retn((uint64_t)size); 82 | } 83 | // Get field offset in a type 84 | static void fieldoffset(Session& rpc, Tuple& args) { 85 | // ... 86 | ULONG64 module = 0; 87 | ULONG id = 0; 88 | if (args[0].isint()) id = args[0].Int(); 89 | else if (args[0].isstr()) g_syms->GetTypeId(0, args[0], &id); 90 | ULONG offset; 91 | g_syms->GetFieldOffset(module, id, args[1], &offset); 92 | rpc.retn((uint64_t)offset); 93 | } 94 | // Get the field name by it's module, typeid belongs to, index 95 | static void fieldname(Session& rpc, Tuple& args) { 96 | char buf[1024]; 97 | ULONG size; 98 | g_syms->GetFieldName(args[0].Int(), args[1].Int(), args[2].Int(), buf, sizeof(buf), &size); 99 | rpc.retn(String::TRef(buf, size)); 100 | } 101 | 102 | FuncItem debug_syms_funcs[] = { 103 | {"name2pos", name2pos}, 104 | {"sympath", sympath}, 105 | {"modnum", modnum}, 106 | {"getmod", getmod}, 107 | {"pos2mod", pos2mod}, 108 | {"typeid", typeid_}, 109 | {"symtype", symtype}, 110 | {"fieldoffset", fieldoffset}, 111 | {"fieldname", fieldname}, 112 | {nullptr, nullptr} 113 | }; 114 | -------------------------------------------------------------------------------- /src/wdbg_sysobj.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "wdbg.h" 3 | #include "xval_str.h" 4 | 5 | DbgSysobj *g_sysobj = nullptr; 6 | 7 | using namespace xval; 8 | using namespace srpc; 9 | 10 | static void psid(Session& rpc, Tuple& args) { 11 | ULONG id; 12 | if (args[0].isint()) { 13 | id = args[0].Int(); 14 | g_hresult = g_sysobj->SetCurrentProcessId(id); 15 | rpc.retn((uint64_t)S_OK == g_hresult); 16 | } else { 17 | g_hresult = g_sysobj->GetCurrentProcessId(&id); 18 | if (S_OK == g_hresult) 19 | rpc.retn((uint64_t)id); 20 | } 21 | } 22 | 23 | static void thid(Session& rpc, Tuple& args) { 24 | ULONG id; 25 | if (args[0].isint()) { 26 | id = args[0].Int(); 27 | g_hresult = g_sysobj->SetCurrentThreadId(id); 28 | rpc.retn((uint64_t)S_OK == g_hresult); 29 | } else { 30 | g_hresult = g_sysobj->GetCurrentThreadId(&id); 31 | if (S_OK == g_hresult) 32 | rpc.retn((uint64_t)id); 33 | } 34 | } 35 | 36 | static void getpeb(Session& rpc, Tuple& args) { 37 | ULONG64 peb; 38 | g_hresult = g_sysobj->GetCurrentProcessPeb(&peb); 39 | if (S_OK == g_hresult) 40 | rpc.retn(peb); 41 | } 42 | 43 | static void getteb(Session& rpc, Tuple& args) { 44 | ULONG64 teb; 45 | g_hresult = g_sysobj->GetCurrentThreadTeb(&teb); 46 | if (S_OK == g_hresult) 47 | rpc.retn(teb); 48 | } 49 | 50 | static void exename(Session& rpc, Tuple& args) { 51 | char path[1024]; 52 | ULONG size; 53 | g_hresult = g_sysobj->GetCurrentProcessExecutableName(path, sizeof(path), &size); 54 | if (S_OK == g_hresult) 55 | rpc.retn(String::TRef(path, size)); 56 | } 57 | 58 | FuncItem debug_sysobj_funcs[] = { 59 | {"psid", psid}, 60 | {"thid", thid}, 61 | {"getpeb", getpeb}, 62 | {"getteb", getteb}, 63 | {"exename", exename}, 64 | {nullptr, nullptr} 65 | }; 66 | -------------------------------------------------------------------------------- /src/xmake.lua: -------------------------------------------------------------------------------- 1 | 2 | local function copy_dbgengfiles(os, dest) 3 | os.cp('$(projectdir)/deps/dbgeng/$(arch)/*.dll', dest) 4 | os.cp('$(projectdir)/deps/dbgeng/$(arch)/winext', dest) 5 | os.cp('$(projectdir)/deps/dbgeng/$(arch)/winxp', dest) 6 | end 7 | 8 | target("wdbg") 9 | set_kind 'binary' 10 | 11 | set_targetdir('$(buildir)/$(mode)/$(arch)') 12 | 13 | add_includedirs('../deps/xval/src', 14 | '../deps/srpc/src', 15 | '../deps/dbgeng/inc') 16 | 17 | add_linkdirs('$(buildir)/$(mode)/$(arch)', 18 | '$(buildir)', 19 | '../deps/dbgeng/lib/$(arch)') 20 | add_links('srpc', 'dbgeng') 21 | 22 | add_files("*.cpp") 23 | add_defines('_WDBG_EXPORT') 24 | 25 | if is_mode 'debug' then 26 | add_cxxflags '/MDd' 27 | else 28 | add_cxxflags '/MD' 29 | end 30 | 31 | on_package(function(target) 32 | -- copy file for plugin development 33 | os.cp('$(projectdir)/deps/srpc/src/*.h*', 'wdbg-dist/inc') 34 | os.cp('$(projectdir)/deps/xval/src/*.h', 'wdbg-dist/inc') 35 | os.cp('$(projectdir)/src/*.h', 'wdbg-dist/inc') 36 | os.cp('$(buildir)/srpc.lib', 'wdbg-dist/lib/$(arch)') 37 | os.cp('$(buildir)/wdbg.lib', 'wdbg-dist/lib/$(arch)') 38 | os.cp('$(projectdir)/deps/dbgeng/inc/*.h', 'wdbg-dist/inc') 39 | os.cp('$(projectdir)/deps/dbgeng/lib/$(arch)', 'wdbg-dist/lib') 40 | -- copy file about dbgeng 41 | copy_dbgengfiles(os, 'wdbg-dist/bin/$(arch)') 42 | -- copy the binary files 43 | os.cp('$(buildir)/$(mode)/$(arch)/srpc.dll', 'wdbg-dist/bin/$(arch)') 44 | os.cp('$(buildir)/$(mode)/$(arch)/wdbg.exe', 'wdbg-dist/bin/$(arch)') 45 | -- copy the script files 46 | os.cp('$(projectdir)/pywdbg', 'wdbg-dist') 47 | os.cp('$(projectdir)/examples', 'wdbg-dist') 48 | end) 49 | 50 | add_deps 'srpc' 51 | 52 | task 'copyfiles' 53 | 54 | on_run(function () 55 | import 'core.project.config' 56 | config.load() 57 | copy_dbgengfiles(os, '$(buildir)/$(mode)/$(arch)') 58 | end) 59 | 60 | set_menu { 61 | usage = "xmake wdbgcopy [options]", 62 | description = "Copy the dbgeng files to build directory", 63 | options = {} 64 | } 65 | -------------------------------------------------------------------------------- /wdbg-dist/README.md: -------------------------------------------------------------------------------- 1 | 2 | 此目录为wdbg的二进制发布目录 3 | 4 | ## Directory's structure 5 | 6 | * bin: wdbg所需的二进制文件(x86, x64分别对应32位和64位) 7 | * inc: 开发wdbg后台插件所需的头文件 8 | * lib: 开发wdbg后台插件所需的库文件 9 | * pywdbg: 使用python封装的wdbg调试器前台 10 | 11 | ## How to run 12 | 13 | 1. 根据你的平台选择bin目录下x86或者x64子目录里的wdbg.exe启动,启动成功后wdbg会输出监听的端口号 14 | 2. 通过Python脚本加载pywdbg下的wdbg模块,并通过wdbg.connect函数连接到wdbg后台 15 | 16 | **Note**,运行pywdbg需要python3环境以及u-msgpack-python包(可通过pip3 install u-msgpack-python安装) 17 | -------------------------------------------------------------------------------- /xmake.lua: -------------------------------------------------------------------------------- 1 | set_project 'wdbg' 2 | -- the debug mode 3 | if is_mode("debug") then 4 | -- enable the debug symbols 5 | set_symbols("debug") 6 | -- disable optimization 7 | set_optimize("none") 8 | end 9 | 10 | -- the release mode 11 | if is_mode("release") then 12 | -- set the symbols visibility: hidden 13 | set_symbols("hidden") 14 | -- enable fastest optimization 15 | set_optimize("fastest") 16 | -- strip all symbols 17 | set_strip("all") 18 | end 19 | 20 | add_subdirs 'deps/srpc/src' 21 | 22 | add_subdirs 'src' 23 | --------------------------------------------------------------------------------