├── LICENSE ├── README.md ├── TWindbg ├── TWindbg.py ├── color.py ├── command.py ├── command_handler.py ├── context.py └── utils.py ├── batch ├── TWindbg_x64.bat ├── TWindbg_x86.bat └── del_windbg_reg.bat ├── img ├── context.PNG └── tel.PNG ├── matrix_theme.WEW └── matrix_theme.reg /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Bruce Chen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > [!Caution] 2 | > This project is no longer maintained due to several reasons: 3 | > * The official pykd is no longer updated. 4 | > * Windbg has become an excellent tool. It has a nice UI, and if you need telescope there's also a [script](https://github.com/0vercl0k/windbg-scripts/tree/master/telescope) that'll do the work. IMO there's no need to use this tool anymore. 5 | > 6 | > I would like to thank anyone that has supported this project. I had a lot of fun writing this tool :) 7 | 8 | --- 9 | 10 | [![Python 3](https://img.shields.io/badge/Python-3-green.svg)](https://github.com/bruce30262/TWindbg/) 11 | [![Code Climate](https://codeclimate.com/github/bruce30262/TWindbg/badges/gpa.svg)](https://codeclimate.com/github/bruce30262/TWindbg) 12 | [![Issue Count](https://codeclimate.com/github/bruce30262/TWindbg/badges/issue_count.svg)](https://codeclimate.com/github/bruce30262/TWindbg) 13 | [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](http://choosealicense.com/licenses/mit/) 14 | 15 | 16 | # TWindbg 17 | PEDA-like debugger UI for WinDbg 18 | 19 | ![context img](/img/context.PNG?raw=true) 20 | 21 | # Introduction 22 | This is a windbg extension ( using [pykd](https://githomelab.ru/pykd/pykd) ) to let user having a [PEDA-like](https://github.com/longld/peda) debugger UI in WinDbg. 23 | It will display the following context in each step/trace: 24 | - Registers 25 | - Disassembled code near PC 26 | - Contents of the stack pointer ( with basic smart dereference ) 27 | 28 | It also supports some peda-like commands ( see the [support commands](#support-commands) section ) 29 | 30 | For now it supports both x86 & x64 WinDbg. 31 | 32 | # Dependencies 33 | * Python 3 34 | 35 | > I decided to drop the support of Python2.7 since it has [reached the EOL](https://www.python.org/doc/sunset-python-2/). I believe the project is Python2/3 compatible, however there might exist some issues in pykd and can cause different behavior in Python2/3. Since now the project will only be tested on Python3, I strongly suggest using TWindbg on Python3 instead of Python 2.7. If you still want to use it on Python 2.7, feel free to fork the project and do the development. 36 | 37 | * [pykd](https://githomelab.ru/pykd/pykd) 38 | 39 | # Installation 40 | * Install Python3 41 | * Install pykd 42 | - Download [Pykd-Ext](https://githomelab.ru/pykd/pykd-ext/-/wikis/Downloads), unpack `pykd.dll` to the `[WinDbg Directory]\x86(or x64)\winext\` directory. 43 | + This will allow you to run python in Windbg. 44 | - In the Windbg command line, enter command `.load pykd` to load the pykd module. 45 | - Enter `!pip install pykd` to install the pykd python package. 46 | + Upgrade the pykd module with command `!pip install --upgrade pykd`. 47 | + If something went wrong during the installation with `pip install`, try installing the wheel package instead of the one on PyPI. You can download the wheel package [here](https://githomelab.ru/pykd/pykd/-/wikis/All%20Releases). 48 | * Download the repository 49 | * Install the matrix theme by double-clicking the [matrix_theme.reg](/matrix_theme.reg) 50 | - The matrix theme is required for letting the [color theme](/TWindbg/color.py) work in TWindbg 51 | - You can preview the theme by importing the [matrix_theme.WEW](/matrix_theme.WEW) workspace into WinDbg. 52 | * Copy the [TWindbg](/TWindbg) folder into `[WinDbg Directory]\x64\winext\` & `[WinDbg Directory]\x86\winext\` 53 | 54 | # Usage 55 | ## Launch TWindbg manually 56 | * Open an executable or attach to a process with WinDbg 57 | * Use `.load pykd` to load the `pykd` extension 58 | * Use `!py -g winext\TWindbg\TWindbg.py` to launch TWindbg 59 | 60 | ## Launch TWindbg with command 61 | ``` 62 | [PATH_TO_WINDBG] -a pykd -c "!py -g winext\TWindbg\TWindbg.py" 63 | ``` 64 | Or you can write a [simple batch file](/batch/TWindbg_x64.bat) for the sake of convenience. 65 | 66 | After that you can just use `t` or `p` to see if the extension is working. 67 | 68 | # Support Commands 69 | * `TWindbg`: List all the command in TWindbg 70 | * `ctx`: Print out the current context 71 | * `tel / telescope`: Display memory content at an address with smart dereferences 72 | ![tel img](/img/tel.PNG?raw=true) 73 | 74 | # Note 75 | Maybe ( just maybe ) I'll add more command to make WinDbg behave more like PEDA ( or other debugger like pwndbg, GEF... ) in the future. 76 | -------------------------------------------------------------------------------- /TWindbg/TWindbg.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import pykd 4 | import color 5 | import sys 6 | import command_handler 7 | import context 8 | 9 | # Init arch & context handler 10 | context.init_arch() 11 | context.init_context_handler() 12 | # TWindbg command wrapper 13 | command_handler.wrap_all_commands() 14 | # Print out the current context 15 | context.context_handler.print_context() 16 | -------------------------------------------------------------------------------- /TWindbg/color.py: -------------------------------------------------------------------------------- 1 | RED = ("errfg", "wbg") 2 | ORANGE = ("warnfg", "wbg") 3 | BLUE = ("verbfg", "wbg") 4 | GRAY = ("subfg", "wbg") 5 | DARK_RED = ("srcstr", "wbg") 6 | GREEN = ("srccmnt", "wbg") 7 | WHITE = ("emphbg", "wbg") 8 | YELLOW = ("srcdrct", "wbg") 9 | LIME = ("wfg", "wbg") 10 | LIME_HIGHLIGHT = ("wbg", "wfg") 11 | 12 | def red(content): return colorize(RED, content) 13 | def yellow(content): return colorize(YELLOW, content) 14 | def orange(content): return colorize(ORANGE, content) 15 | def blue(content): return colorize(BLUE, content) 16 | def gray(content): return colorize(GRAY, content) 17 | def dark_red(content): return colorize(DARK_RED, content) 18 | def green(content): return colorize(GREEN, content) 19 | def white(content): return colorize(WHITE, content) 20 | def lime(content): return colorize(LIME, content) 21 | def lime_highlight(content): return colorize(LIME_HIGHLIGHT, content) 22 | def colorize(col, content): return "{}".format(col[0], col[1], content) 23 | -------------------------------------------------------------------------------- /TWindbg/command.py: -------------------------------------------------------------------------------- 1 | import pykd 2 | import sys 3 | import context 4 | import color 5 | import traceback 6 | 7 | from utils import * 8 | from functools import wraps 9 | 10 | all_commands = [ 11 | 'TWindbg', 12 | 'telescope', 13 | 'ctx' 14 | ] 15 | 16 | class CmdExecError(Exception): 17 | def __init__(self, errmsg): 18 | self.errmsg = errmsg 19 | 20 | def wrap_usage(func): 21 | @wraps(func) 22 | def wrap(*args, **kwargs): 23 | if len(args[0]) == 1 and args[0][0] == "help": 24 | print_usage(func.__doc__) 25 | return 26 | func(*args, **kwargs) 27 | return wrap 28 | 29 | def print_all_usage(): 30 | global all_commands 31 | for cmd in all_commands: 32 | cmd_info = globals()[cmd].__doc__.split("\n")[0].split(":")[1] 33 | pykd.dprintln("{:15s}{}".format(cmd, cmd_info)) 34 | 35 | @wrap_usage 36 | def TWindbg(args): 37 | """TWindbg: List all the command in TWindbg """ 38 | if len(args) != 0: 39 | raise CmdExecError("Invalid argument number") 40 | banner = color.yellow("TWindbg: PEDA-like debugger UI for WinDbg\n") 41 | banner += color.gray("For latest update, check the project page: ") 42 | banner += color.white("https://github.com/bruce30262/TWindbg\n") 43 | banner += color.orange("Use \"[cmd] help\" for further command usage\n") 44 | pykd.dprintln(banner, dml=True) 45 | print_all_usage() 46 | 47 | @wrap_usage 48 | def ctx(args): 49 | """ctx: Print the current context""" 50 | context.context_handler.print_context() 51 | 52 | @wrap_usage 53 | def telescope(args): 54 | """telescope: Display memory content at an address with smart dereferences 55 | Usage: telescope/tel [addr] [line to display, default=8, maximum=100] 56 | """ 57 | # check argument number 58 | if not arg_num_in_range(args, 1, 2): 59 | raise CmdExecError("Invalid argument number") 60 | # get start address and line number 61 | start_addr = to_addr(args[0]) 62 | line_num = 8 if len(args) == 1 else to_int(args[1]) 63 | # check valid address 64 | is_addr, errmsg = check_valid_addr(args[0]) 65 | if not is_addr: 66 | raise CmdExecError(errmsg) 67 | # check valid line number 68 | if not check_in_range(line_num, 1, 100): 69 | raise CmdExecError("Invalid line number: {}, should be 1 ~ 100".format(args[1])) 70 | 71 | context.context_handler.print_nline_ptrs(start_addr, line_num) 72 | -------------------------------------------------------------------------------- /TWindbg/command_handler.py: -------------------------------------------------------------------------------- 1 | import pykd 2 | import sys 3 | import traceback 4 | import command 5 | 6 | from utils import * 7 | 8 | def set_alias(a, v): 9 | pykd.dbgCommand("as {} {}".format(a, v)) 10 | 11 | def wrap_command(cmd, real_cmd): 12 | set_alias(cmd, "!py -g winext\TWindbg\command_handler.py {}".format(real_cmd)) 13 | 14 | def wrap_all_commands(): 15 | for cmd in command.all_commands: 16 | wrap_command(cmd, cmd) 17 | wrap_command("tel", "telescope") 18 | 19 | class CommandHandler(): 20 | def __init__(self, func): 21 | self.func = func 22 | 23 | def invoke(self, args): 24 | try: 25 | self.func(args) 26 | except command.CmdExecError as e: 27 | print_err(e.errmsg) 28 | print_usage(self.func.__doc__) 29 | except Exception: 30 | traceback.print_exc() 31 | 32 | if __name__ == "__main__": 33 | if len(sys.argv) >= 2: # user input TWindbg command 34 | cmd = sys.argv[1] 35 | if cmd in command.all_commands and hasattr(command, cmd): # if the command mudule has the corresponded method, call it 36 | func = getattr(command, cmd) 37 | CommandHandler(func).invoke(sys.argv[2::]) 38 | else: 39 | print_err("Command: {} not found in TWindbg.".format(cmd)) 40 | 41 | -------------------------------------------------------------------------------- /TWindbg/context.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import pykd 4 | import color 5 | import sys 6 | import traceback 7 | 8 | from utils import * 9 | 10 | ARCH = None 11 | PTRMASK = None 12 | PTRSIZE = None 13 | MAX_DEREF_DEPTH = 20 14 | 15 | def init_arch(): 16 | global ARCH, PTRMASK, PTRSIZE 17 | cpu_mode = pykd.getCPUMode() 18 | if cpu_mode == pykd.CPUType.I386: 19 | ARCH = 'x86' 20 | PTRMASK = 0xffffffff 21 | PTRSIZE = 4 22 | elif cpu_mode == pykd.CPUType.AMD64: 23 | ARCH = 'x64' 24 | PTRMASK = 0xffffffffffffffff 25 | PTRSIZE = 8 26 | else: 27 | print_err("CPU mode: {} not supported.".format(cpu_mode)) 28 | sys.exit(-1) 29 | 30 | def init_context_handler(): 31 | global context_handler 32 | if 'context_handler' not in globals(): 33 | context_handler = ContextHandler(Context()) 34 | 35 | class Context(): 36 | def __init__(self): 37 | self.regs_name = [] 38 | self.seg_regs_name = ['cs', 'ds', 'es', 'fs', 'gs', 'ss'] 39 | self.regs = {} 40 | self.eflags_tbl = { 41 | 0: "carry", 42 | 2: "parity", 43 | 4: "auxiliary", 44 | 6: "zero", 45 | 7: "sign", 46 | 8: "trap", 47 | 9: "interrupt", 48 | 10: "direction", 49 | 11: "overflow", 50 | 14: "nested", 51 | 16: "resume", 52 | 17: "virtualx86" 53 | } 54 | self.is_changed = {} 55 | self.sp_name = "" 56 | self.sp = None 57 | self.pc_name = "" 58 | self.pc = None 59 | 60 | self.init_regs_name() 61 | self.init_regs() 62 | 63 | def init_regs_name(self): 64 | if ARCH == 'x86': 65 | self.regs_name = ['eax', 'ebx', 'ecx', 'edx', 'edi', 'esi', 'ebp', 'esp', 'eip'] 66 | self.sp_name = 'esp' 67 | self.pc_name = 'eip' 68 | else: 69 | self.regs_name = ['rax', 'rbx', 'rcx', 'rdx', 'rdi', 'rsi', 'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15', 'rbp', 'rsp', 'rip'] 70 | self.sp_name = 'rsp' 71 | self.pc_name = 'rip' 72 | 73 | def init_regs(self): 74 | for reg_name in self.regs_name + self.seg_regs_name: 75 | self.regs[reg_name] = None 76 | self.is_changed[reg_name] = False 77 | 78 | def update_regs(self): 79 | for reg_name in self.regs_name + self.seg_regs_name: 80 | reg_data = pykd.reg(reg_name) 81 | if reg_data != self.regs[reg_name]: # is changed 82 | self.is_changed[reg_name] = True 83 | else: 84 | self.is_changed[reg_name] = False 85 | 86 | self.regs[reg_name] = reg_data 87 | # update sp & pc 88 | self.sp = self.regs[self.sp_name] 89 | self.pc = self.regs[self.pc_name] 90 | 91 | class ContextHandler(pykd.eventHandler): 92 | def __init__(self, context): 93 | pykd.eventHandler.__init__(self) 94 | self.context = context 95 | 96 | def onExecutionStatusChange(self, status): 97 | if status == pykd.executionStatus.Break: # step, trace, ... 98 | self.print_context() 99 | 100 | def print_context(self): 101 | self.context.update_regs() 102 | pykd.dprintln(color.yellow("[------ Register --------------------------------------------------------------------------------------------]"), dml=True) 103 | self.print_regs() 104 | pykd.dprintln(color.yellow("[------ Code ------------------------------------------------------------------------------------------------]"), dml=True) 105 | self.print_code() 106 | pykd.dprintln(color.yellow("[------ Stack -----------------------------------------------------------------------------------------------]"), dml=True) 107 | self.print_stack() 108 | pykd.dprintln(color.yellow("[------------------------------------------------------------------------------------------------------------]"), dml=True) 109 | 110 | def print_regs(self): 111 | self.print_general_regs() 112 | self.print_seg_regs() 113 | self.print_eflags() 114 | 115 | def print_general_regs(self): 116 | for reg_name in self.context.regs_name: 117 | reg_data = self.context.regs[reg_name] 118 | reg_str = '{:3}: '.format(reg_name.upper()) 119 | reg_color = self.set_reg_color(reg_name, color_changed=color.red, color_unchanged=color.lime) 120 | pykd.dprint(reg_color(reg_str), dml=True) 121 | 122 | # if reg_data is a valid virtual address and is able to be dereferenced, print it with print_ptrs(), or else just print it directly 123 | if pykd.isValid(reg_data) and ( deref_ptr(reg_data) != None ): 124 | self.print_ptrs(reg_data) 125 | else: 126 | pykd.dprintln("{:#x}".format(reg_data)) 127 | 128 | def print_seg_regs(self): 129 | first_print = True 130 | for reg_name in self.context.seg_regs_name: 131 | reg_data = self.context.regs[reg_name] 132 | reg_str = '{:2}={:#x}'.format(reg_name.upper(), reg_data) 133 | reg_color = self.set_reg_color(reg_name, color_changed=color.red, color_unchanged=color.green) 134 | 135 | if first_print: 136 | pykd.dprint(reg_color(reg_str), dml=True) 137 | first_print = False 138 | else: 139 | pykd.dprint(" | " + reg_color(reg_str), dml=True) 140 | pykd.dprintln("") 141 | 142 | def print_eflags(self): 143 | eflags = pykd.reg('efl') 144 | eflags_str = color.green("EFLAGS: {:#x}".format(eflags)) 145 | eflags_str += " [" 146 | for bit, flag_name in self.context.eflags_tbl.items(): 147 | is_set = eflags & (1< max_length: 89 | return s[:max_length:] + "..." 90 | else: 91 | return s 92 | except: 93 | return None 94 | 95 | def get_string(ptr): 96 | """ try to get string from a pointer """ 97 | 98 | ret = load_str(pykd.loadWStr, ptr) # try load WString (unicode) first 99 | if not ret: ret = load_str(pykd.loadCStr, ptr) # if failed, load CStr ( ascii ) 100 | 101 | return ret 102 | 103 | def deref_ptr(ptr): 104 | try: 105 | return pykd.loadPtrs(ptr, 1)[0] & context.PTRMASK 106 | except pykd.MemoryException: 107 | return None 108 | -------------------------------------------------------------------------------- /batch/TWindbg_x64.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | "C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\windbg.exe" -a pykd -c "!py -g winext\TWindbg\TWindbg.py" 3 | -------------------------------------------------------------------------------- /batch/TWindbg_x86.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | "C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\windbg.exe" -a pykd -c "!py -g winext\TWindbg\TWindbg.py" 3 | -------------------------------------------------------------------------------- /batch/del_windbg_reg.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | reg delete HKCU\Software\Microsoft\Windbg 3 | -------------------------------------------------------------------------------- /img/context.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bruce30262/TWindbg/afb9f7851917cc13aeb1583fae825d42d7115b0e/img/context.PNG -------------------------------------------------------------------------------- /img/tel.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bruce30262/TWindbg/afb9f7851917cc13aeb1583fae825d42d7115b0e/img/tel.PNG -------------------------------------------------------------------------------- /matrix_theme.WEW: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bruce30262/TWindbg/afb9f7851917cc13aeb1583fae825d42d7115b0e/matrix_theme.WEW -------------------------------------------------------------------------------- /matrix_theme.reg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bruce30262/TWindbg/afb9f7851917cc13aeb1583fae825d42d7115b0e/matrix_theme.reg --------------------------------------------------------------------------------