Skip to content

Commit 99ce27e

Browse files
committed
Create annotate_IDB_MSDN_structures.py
1 parent d8b91a7 commit 99ce27e

File tree

1 file changed

+243
-0
lines changed

1 file changed

+243
-0
lines changed
Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
"""
2+
IDAPython script providing an user interface for setting the configuration for
3+
annotating database files with MSDN information.
4+
5+
Authors: Bingchang, Liu
6+
Copyright 2016 VARAS, IIE of CAS
7+
8+
This work is based on Moritz Raabe and William Ballenthin's work at:
9+
https://github.com/fireeye/flare-ida
10+
11+
Mandiant licenses this file to you under the Apache License, Version
12+
2.0 (the "License"); you may not use this file except in compliance with the
13+
License. You may obtain a copy of the License at:
14+
15+
http://www.apache.org/licenses/LICENSE-2.0
16+
17+
Unless required by applicable law or agreed to in writing, software
18+
distributed under the License is distributed on an "AS IS" BASIS,
19+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
20+
implied. See the License for the specific language governing
21+
permissions and limitations under the License.
22+
"""
23+
24+
import sys
25+
import logging
26+
import traceback
27+
import ConfigParser
28+
from ConfigParser import SafeConfigParser
29+
from PySide import QtGui
30+
from PySide import QtCore
31+
from PySide.QtCore import Qt
32+
33+
idaapi.require("IDB_MSDN_Annotator")
34+
35+
36+
CONFIG_FILE = 'MSDN_structure_annotations.cfg'
37+
38+
39+
g_logger = logging.getLogger(__name__)
40+
41+
def getDefaultMsdnDataDir():
42+
return os.path.abspath(os.path.join(idaapi.get_user_idadir(), 'MSDN_data'))
43+
44+
class MSDNAnnotationDialog(QtGui.QDialog):
45+
46+
def read_config(self):
47+
config = {}
48+
if not self.config_parser.has_section('Structures') or \
49+
not self.config_parser.has_section('Members') or \
50+
not self.config_parser.has_section('Constants'):
51+
# Create default
52+
self.config_parser.add_section('Structures')
53+
self.config_parser.add_section('Members')
54+
self.config_parser.add_section('Constants')
55+
config['structures_annotate'] = True
56+
config['structures_repeatable_comment'] = False
57+
config['members_annotate'] = True
58+
config['constants_import'] = True
59+
config['msdn_data_dir'] = getDefaultMsdnDataDir()
60+
61+
else:
62+
# Read existing
63+
config['structures_annotate'] = self.config_parser.getboolean('Structures', 'annotate')
64+
config['structures_repeatable_comment'] = self.config_parser.getboolean('Structures', 'repeatable_comment')
65+
config['members_annotate'] = self.config_parser.getboolean('Members', 'annotate')
66+
config['constants_import'] = self.config_parser.getboolean('Constants', 'import')
67+
try:
68+
config['msdn_data_dir'] = self.config_parser.get('Constants', 'msdn_data_dir')
69+
except ConfigParser.NoOptionError:
70+
config['msdn_data_dir'] = getDefaultMsdnDataDir()
71+
72+
return config
73+
74+
def save_config(self):
75+
self.config_parser.set('Structures', 'annotate', str(self.chkStructuresAnnotate.isChecked()))
76+
self.config_parser.set('Structures', 'repeatable_comment', str(self.chkStrucsRepeatable.isChecked()))
77+
self.config_parser.set('Members', 'annotate', str(self.chkMembersAnnotate.isChecked()))
78+
self.config_parser.set('Constants', 'import', str(self.chkConstantsImport.isChecked()))
79+
self.config_parser.set('Constants', 'msdn_data_dir', str(self.dirText.text()) )
80+
81+
with open(self.file_path, 'wb') as conffile:
82+
self.config_parser.write(conffile)
83+
84+
def change_image(self):
85+
struc = self.chkStructuresAnnotate.isChecked() and \
86+
self.chkStrucsRepeatable.isChecked()
87+
image = "struc-{}-{}-{}.png".format(int(self.chkStructuresAnnotate.isChecked()),
88+
int(self.chkStrucsRepeatable.isChecked()),
89+
int(self.chkMembersAnnotate.isChecked()))
90+
img_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'IDB_MSDN_Annotator', 'img'))
91+
self.pic.setPixmap(QtGui.QPixmap(os.path.join(img_path, image)))
92+
93+
def on_select_dir(self):
94+
msdnDir = QtGui.QFileDialog.getExistingDirectory(caption='Select directory containing MSDN XML Database')
95+
if len(msdnDir) != 0:
96+
self.dirText.setText(msdnDir)
97+
98+
def toggle_option(self):
99+
disable = not self.chkStructuresAnnotate.isChecked()
100+
self.chkStrucsRepeatable.setDisabled(disable)
101+
self.change_image()
102+
103+
def on_ok_button(self):
104+
#test the msdn data dir
105+
106+
msdnpath = os.path.join(self.dirText.text(), IDB_MSDN_Annotator.MSDN_STRUCTURE_INFO_FILE)
107+
if not os.path.exists(msdnpath):
108+
g_logger.info('Error - no msdn info file: %s', msdnpath)
109+
ret = QtGui.QMessageBox.warning(self, 'MSDN Info Not Found', 'The file %s was not found in the specified MSDN Data Directory' % IDB_MSDN_Annotator.MSDN_STRUCTURE_INFO_FILE, QtGui.QMessageBox.Ok)
110+
#self.done(QtGui.QDialog.Rejected)
111+
return
112+
113+
self.done(QtGui.QDialog.Accepted)
114+
g_logger.info('Saving config')
115+
self.save_config()
116+
config = self.read_config()
117+
idaapi.set_script_timeout(1)
118+
IDB_MSDN_Annotator.add_structures_annotations(config)
119+
idaapi.set_script_timeout(0)
120+
121+
def set_form_values(self):
122+
# Set values according to configuration file
123+
if self.config['structures_annotate']:
124+
self.chkStructuresAnnotate.setCheckState(QtCore.Qt.Checked)
125+
if self.config['structures_repeatable_comment']:
126+
self.chkStrucsRepeatable.setCheckState(QtCore.Qt.Checked)
127+
else:
128+
self.chkStrucsRepeatable.setDisabled(True)
129+
self.chkStrucsRepeatable.setCheckState(QtCore.Qt.Unchecked)
130+
if self.config['members_annotate']:
131+
self.chkMembersAnnotate.setCheckState(QtCore.Qt.Checked)
132+
if self.config['constants_import']:
133+
self.chkConstantsImport.setCheckState(QtCore.Qt.Checked)
134+
self.dirText.setText(self.config['msdn_data_dir'])
135+
136+
def populate_form(self):
137+
layout = QtGui.QVBoxLayout()
138+
139+
# Structures
140+
layout1 = QtGui.QVBoxLayout()
141+
groupBox = QtGui.QGroupBox('Markup Options')
142+
self.chkStructuresAnnotate = QtGui.QCheckBox("Annotate structure names"
143+
" (see note)")
144+
layout1.addWidget(self.chkStructuresAnnotate)
145+
self.chkStrucsRepeatable = QtGui.QCheckBox("Use repeatable comments "
146+
"for structure name "
147+
"annotations")
148+
layout1.addWidget(self.chkStrucsRepeatable)
149+
150+
# Members
151+
self.chkMembersAnnotate = QtGui.QCheckBox("Annotate structure "
152+
"members (see note)")
153+
layout1.addWidget(self.chkMembersAnnotate)
154+
155+
# Constants
156+
self.chkConstantsImport = QtGui.QCheckBox("Rename constants (no preview)")
157+
layout1.addWidget(self.chkConstantsImport)
158+
159+
groupBox.setLayout(layout1)
160+
layout.addWidget(groupBox)
161+
162+
#MSDN data dir
163+
hlayout = QtGui.QHBoxLayout()
164+
self.selectDirButton = QtGui.QPushButton('...')
165+
self.selectDirButton.clicked.connect(self.on_select_dir)
166+
hlayout.addWidget(self.selectDirButton)
167+
self.dirText = QtGui.QLineEdit()
168+
self.dirText.setReadOnly(True)
169+
hlayout.addWidget(self.dirText)
170+
groupBox = QtGui.QGroupBox('MSDN Data Directory')
171+
groupBox.setLayout(hlayout)
172+
layout.addWidget(groupBox)
173+
174+
# Toggle
175+
self.chkStructuresAnnotate.clicked.connect(self.toggle_option)
176+
self.chkStrucsRepeatable.clicked.connect(self.change_image)
177+
self.chkMembersAnnotate.clicked.connect(self.change_image)
178+
self.chkConstantsImport.clicked.connect(self.change_image)
179+
180+
self.set_form_values()
181+
182+
info_string = "Note: Annotating structures and/or members allows " \
183+
"you to hover\nthe respective element in order to " \
184+
"show its description."
185+
layout.addWidget(QtGui.QLabel(info_string))
186+
187+
# Buttons
188+
button_ok = QtGui.QPushButton('&OK')
189+
button_ok.setDefault(True)
190+
button_ok.clicked.connect(self.on_ok_button)
191+
#button_ok.clicked.connect(self.close)
192+
layout.addWidget(button_ok)
193+
button_cancel = QtGui.QPushButton('&Cancel')
194+
button_cancel.clicked.connect(self.close)
195+
layout.addWidget(button_cancel)
196+
197+
# Image
198+
self.pic = QtGui.QLabel()
199+
self.pic.setGeometry(0, 0, 663, 203)
200+
self.change_image()
201+
202+
# Layout right
203+
layout_r = QtGui.QVBoxLayout()
204+
#layout_r.addWidget(QtGui.QLabel("Annotation preview"))
205+
layout_r.addWidget(self.pic)
206+
groupBox = QtGui.QGroupBox('Annotation preview')
207+
groupBox.setLayout(layout_r)
208+
209+
# Setup layouts
210+
h_layout = QtGui.QHBoxLayout()
211+
h_layout.addLayout(layout)
212+
#h_layout.addLayout(layout_r)
213+
h_layout.addWidget(groupBox)
214+
self.setLayout(h_layout)
215+
216+
def __init__(self, parent=None):
217+
self._logger = logging.getLogger(__name__ + '.' +
218+
self.__class__.__name__)
219+
self._logger.debug('Starting UI')
220+
QtGui.QDialog.__init__(self, parent, QtCore.Qt.WindowSystemMenuHint |
221+
QtCore.Qt.WindowTitleHint)
222+
self.setWindowTitle("MSDN Structure Annotations Configuration")
223+
224+
# Parse configuration file to dictionary
225+
self.file_path = os.path.abspath(os.path.join(idaapi.get_user_idadir(), CONFIG_FILE))
226+
self.config_parser = SafeConfigParser()
227+
self.config_parser.read(self.file_path)
228+
self.config = self.read_config()
229+
230+
self.populate_form()
231+
232+
233+
if __name__ == '__main__':
234+
#logging.basicConfig(level=logging.DEBUG)
235+
logging.basicConfig(level=logging.INFO)
236+
237+
dlg = MSDNAnnotationDialog()
238+
# Disable script timeout -> otherwise cancel script dialog pops up
239+
oldTo = idaapi.set_script_timeout(0)
240+
dlg.exec_()
241+
# Restore the timeout
242+
idaapi.set_script_timeout(oldTo)
243+
g_logger.debug('UI closed')

0 commit comments

Comments
 (0)