├── LICENSE ├── README.md ├── README_zh_CN.md └── UserComment.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 JayRE 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 | # UserComment 2 | 3 | [简体中文](./README_zh_CN.md) 4 | 5 | An IDA plugin to display user-added comments in disassembly and pseudocode views. 6 | 7 | ## Note 8 | 9 | This plugin is implemented based on hooking, meaning it can only capture and save the user-added comments after the plugin is installed. 10 | 11 | Any user comments added prior to the installation of the plugin will not be captured. 12 | 13 | The plugin is written in Python 3, and it's suitable for IDA 7.x versions that use Python 3. For IDA 8.x, please test it yourself. 14 | 15 | ## Installation 16 | 17 | Copy the `UserComment.py` file to the `plugins` folder in the IDA installation directory. 18 | 19 | ## Usage 20 | 21 | There are three ways to open the comment window: 22 | 1. Choose 'View/Open subviews/Comments' from the menu 23 | 2. Use the shortcut (Ctrl-Shift-C) 24 | 3. Press `Ctrl-!`, then choose "Comments" 25 | 26 | ## Features 27 | 28 | - Provides a comment window, displaying user-added comments, including comments in assembly code and pseudocode. 29 | - Support for different types of comments (common, repeatable, anterior, posterior, pseudocode and function comments). 30 | - Captured user-added comments will be preserved in the IDB. 31 | - Double-click on a comment entry to quickly navigate to the corresponding location. 32 | - Use IDA's built-in chooser, providing a handy filter functionality. 33 | 34 | 35 | ## Contribution 36 | 37 | If you encounter any issues, have suggestions for improvements, or want to add new features, please submit an issue or a pull request. 38 | If you have any other questions, please feel free to ask. 39 | -------------------------------------------------------------------------------- /README_zh_CN.md: -------------------------------------------------------------------------------- 1 | # UserComment Plugin 2 | 3 | UserComment Plugin 是一个用于IDA的插件,用于显示用户添加的注释。 4 | 5 | ## 注意 6 | 7 | 这个插件是基于Hook的方式实现的,这意味着它只能获取并保存安装该插件之后用户添加的注释。 8 | 9 | 对于在安装插件之前添加的用户注释,该插件无法获取到。 10 | 11 | 插件使用python3编写, 适用于使用python3的IDA7.x,IDA8.x请自行测试。 12 | 13 | ## 安装 14 | 15 | 将 `UserComment.py` 文件复制到IDA插件目录的 `plugins` 文件夹下。 16 | 17 | ## 使用方法 18 | 19 | 打开注释窗口的三种方式: 20 | 1. 菜单中选择 View/Open subviews/Comments 21 | 2. 使用快捷键(Ctrl-Shift-C) 22 | 3. 按下 Ctrl-!,然后选择 "Comments" 23 | 24 | ## 功能 25 | 26 | - 提供注释窗口, 显示用户添加的注释,包括汇编代码和伪代码中的注释。 27 | - 支持不同类型的注释,包括常规注释、可重复注释、前置注释、后置注释、伪代码注释和函数注释。 28 | - 用户注释将在IDB文件中保留。 29 | - 双击注释条目可以快速跳转到对应的位置。 30 | - 使用IDA内置的选择器,提供便捷的筛选功能。 31 | 32 | ## 贡献 33 | 34 | 如果你发现任何问题、有改进建议或想要添加新功能,请提交 issue 或 pull request。 35 | 36 | 希望这对你有所帮助!如果有其他问题,请随时提问。 37 | -------------------------------------------------------------------------------- /UserComment.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import ida_idaapi 3 | import ida_kernwin 4 | import ida_idp 5 | import ida_netnode 6 | import idc 7 | import ida_bytes 8 | import ida_hexrays 9 | import ida_nalt 10 | import pickle 11 | 12 | title = "Comments" 13 | 14 | # def show_warning(msg): 15 | # ida_kernwin.warning(msg) 16 | 17 | class UserAddedComments(): 18 | def __init__(self): 19 | self.netnode = ida_netnode.netnode() 20 | self.netnode.create("$ UserAddedComments") 21 | self.imagebase = ida_nalt.get_imagebase() 22 | self.load_comments() 23 | 24 | def save_comments(self): 25 | blob = pickle.dumps(self.comments) 26 | self.netnode.setblob(blob, 0, 'C') 27 | 28 | def load_comments(self): 29 | blob = self.netnode.getblob(0, 'C') 30 | if blob is not None: 31 | self.comments = pickle.loads(blob) 32 | else: 33 | self.comments = {} 34 | 35 | def add_comment(self, ea, cmt_type, comment, line_num=None): 36 | offset = ea - self.imagebase 37 | key = (offset, cmt_type, line_num) 38 | if not comment: 39 | self.comments.pop(key, 0) 40 | else: 41 | self.comments[key] = comment 42 | self.save_comments() 43 | 44 | 45 | class UIHooks(ida_kernwin.UI_Hooks): 46 | def __init__(self, cmt_view): 47 | ida_kernwin.UI_Hooks.__init__(self) 48 | self.cmt_view = cmt_view 49 | 50 | def current_widget_changed(self, widget, prev_widget): 51 | if ida_kernwin.get_widget_title(widget) == title: 52 | self.cmt_view.Refresh() 53 | 54 | 55 | class PseudoHooks(ida_hexrays.Hexrays_Hooks): 56 | def __init__(self, usr_cmt): 57 | ida_hexrays.Hexrays_Hooks.__init__(self) 58 | self.usr_cmt = usr_cmt 59 | 60 | def cmt_changed(self, cfunc, loc, cmt): 61 | self.usr_cmt.add_comment(loc.ea, 'pseudocode', cmt) 62 | return 0 63 | 64 | 65 | class DisasmHooks(ida_idp.IDB_Hooks): 66 | def __init__(self, usr_cmt): 67 | ida_idp.IDB_Hooks.__init__(self) 68 | self.usr_cmt = usr_cmt 69 | self.rebased = False 70 | 71 | # hook common and repeatable cmts 72 | def changing_cmt(self, ea, is_repeatable, new_comment): 73 | cur_ea = idc.here() 74 | if cur_ea == ea: 75 | # solve start_ea problems 76 | cur = ida_kernwin.get_cursor() 77 | if (cur != (True, 0, 0)): 78 | # Fix rebasing bug: Rebase pragram will trigger 'changing_cmt', causing to capture auto cmts at ea. 79 | if self.rebased: 80 | self.rebased = False 81 | return 0 82 | if is_repeatable: 83 | self.usr_cmt.add_comment(ea, 'repeatable', new_comment) 84 | else: 85 | self.usr_cmt.add_comment(ea, 'common', new_comment) 86 | return 0 87 | 88 | # hook anterior and posterior cmts 89 | def extra_cmt_changed(self, ea, line_idx, cmt): 90 | cur_ea = idc.here() 91 | if cur_ea == ea: 92 | cur = ida_kernwin.get_cursor() 93 | if (cur != (True, 0, 0)): 94 | if line_idx // 1000 == 1: # line_idx = 1xxx 95 | self.usr_cmt.add_comment(ea, 'anterior', cmt, line_num=line_idx % 1000) 96 | if line_idx // 1000 == 2: # line_idx = 2xxx 97 | self.usr_cmt.add_comment(ea, 'posterior', cmt, line_num=line_idx % 1000) 98 | return 0 99 | 100 | # hook Function cmts and repeatable Function cmts 101 | def changing_range_cmt(self, kind, a, cmt, is_repeatable): 102 | if is_repeatable: 103 | self.usr_cmt.add_comment(a.start_ea, 'func_repeatable', cmt) 104 | else: 105 | self.usr_cmt.add_comment(a.start_ea, 'func_common', cmt) 106 | return 0 107 | 108 | # program image rebased 109 | def allsegs_moved(self, info): 110 | self.rebased = True 111 | self.usr_cmt.imagebase = ida_nalt.get_imagebase() 112 | 113 | 114 | class CommentViewer(ida_kernwin.Choose): 115 | def __init__(self, usr_cmt): 116 | ida_kernwin.Choose.__init__( 117 | self, 118 | title, 119 | [ ["Address", 10 | ida_kernwin.Choose.CHCOL_HEX], 120 | ["Type", 20 | ida_kernwin.Choose.CHCOL_PLAIN], 121 | ["Comments", 30 | ida_kernwin.Choose.CHCOL_PLAIN]], 122 | flags = ida_kernwin.Choose.CH_CAN_REFRESH) 123 | self.usr_cmt = usr_cmt 124 | self.items = [] 125 | 126 | def OnInit(self): 127 | self.usr_cmt.load_comments() # load comments again 128 | self.items = [ [hex(k[0] + self.usr_cmt.imagebase), k[1], v] for k, v in self.usr_cmt.comments.items() ] 129 | return True 130 | 131 | def OnGetSize(self): 132 | return len(self.items) 133 | 134 | def OnGetLine(self, n): 135 | return self.items[n] 136 | 137 | def OnRefresh(self, n): 138 | self.OnInit() 139 | if self.items: 140 | return [ida_kernwin.Choose.ALL_CHANGED] + self.adjust_last_item(n) 141 | return None # call standard refresh 142 | 143 | def OnSelectLine(self, n): 144 | selected_item = self.items[n] # for single selection chooser 145 | addr = int(selected_item[0], 16) 146 | ida_kernwin.jumpto(addr) 147 | 148 | 149 | def register_open_action(cmt_view): 150 | """ 151 | Provide the action that will create the widget 152 | when the user asks for it. 153 | """ 154 | class create_widget_t(ida_kernwin.action_handler_t): 155 | def activate(self, ctx): 156 | cmt_view.Show() 157 | 158 | def update(self, ctx): 159 | return ida_kernwin.AST_ENABLE_ALWAYS 160 | 161 | action_name = "UserAddedComments:Show" 162 | action_shortcut = "Ctrl-Shift-C" 163 | ida_kernwin.register_action( 164 | ida_kernwin.action_desc_t( 165 | action_name, 166 | title, 167 | create_widget_t(), 168 | action_shortcut)) 169 | ida_kernwin.attach_action_to_menu( 170 | f"View/Open subviews/{title}", 171 | action_name, 172 | ida_kernwin.SETMENU_APP) 173 | 174 | 175 | class my_plugin_t(ida_idaapi.plugin_t): 176 | flags = ida_idaapi.PLUGIN_HIDE # Plugin should not appear in the Edit, Plugins menu. 177 | wanted_name = "Hook and display user-added comments" 178 | wanted_hotkey = "" 179 | comment = "Hook and display user-added comments" 180 | help = "" 181 | 182 | def init(self): 183 | self.usr_cmt = UserAddedComments() # Create Comments instance here 184 | 185 | self.idb_hook = DisasmHooks(self.usr_cmt) # Hook disassembly comments(common, repeatable, anterior, posterior cmts) 186 | self.idb_hook.hook() 187 | 188 | self.ray_hook = PseudoHooks(self.usr_cmt) # Hook pseudo-code comments(cmts in "F5" pseudo-code view) 189 | self.ray_hook.hook() 190 | 191 | self.cmt_view = CommentViewer(self.usr_cmt) # Create comment viewer instance 192 | 193 | register_open_action(self.cmt_view) # Register to desktop widget and bind shortcut 194 | 195 | self.ui_hook = UIHooks(self.cmt_view) # Refresh commnets viewer in real time 196 | self.ui_hook.hook() 197 | return ida_idaapi.PLUGIN_KEEP # Keep us in the memory 198 | 199 | def run(self, arg): 200 | #self.cmt_view.Show() 201 | pass 202 | 203 | def term(self): 204 | self.ui_hook.unhook() 205 | self.ray_hook.unhook() 206 | self.idb_hook.unhook() 207 | return 208 | 209 | 210 | def PLUGIN_ENTRY(): 211 | return my_plugin_t() --------------------------------------------------------------------------------