├── .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 | 
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 |
--------------------------------------------------------------------------------