├── 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()
--------------------------------------------------------------------------------