]> git.siccegge.de Git - frida/frida.git/blob - src/gui/Mainwindow.cxx
Move Interpreter to its own class
[frida/frida.git] / src / gui / Mainwindow.cxx
1 #include "Mainwindow.hxx"
2 #include "qt.hxx"
3 #include "bindings/Guile.hxx"
4 #include "disassembler/llvm/LLVMDisassembler.hxx"
5 #include "core/Function.hxx"
6 #include "core/BasicBlock.hxx"
7 #include "core/InformationManager.hxx"
8 #include "core/events/RenameFunctionEvent.hxx"
9 #include "widgets/ScriptingDock.hxx"
10 #include "widgets/CFGScene.hxx"
11 #include "widgets/FunctionWidget.hxx"
12 #include "dialogs/NewFunctionDialog.hxx"
13 #include "dialogs/SimpleStringDialog.hxx"
14
15 #include <sstream>
16
17 namespace {
18 BasicBlockWidget *
19 local__add_basic_block(BasicBlock * block,
20 Mainwindow * mainwindow, InformationManager * manager,
21 std::map<uint64_t, BasicBlockWidget*>& known_blocks,
22 CFGScene * scene, uint64_t starty, uint64_t startx);
23 }
24
25 Mainwindow::Mainwindow(InformationManager* mgr)
26 : manager(mgr)
27 , logger(log4cxx::Logger::getLogger("Mainwindow")) {
28 openAction = new QAction(tr("&Open"), this);
29 loadAction = new QAction(tr("&Load"), this);
30 saveAction = new QAction(tr("&Save"), this);
31 exitAction = new QAction(tr("E&xit"), this);
32
33 connect(openAction, &QAction::triggered,
34 this, &Mainwindow::open);
35 connect(loadAction, &QAction::triggered,
36 this, &Mainwindow::load);
37 connect(saveAction, &QAction::triggered,
38 this, &Mainwindow::save);
39 connect(exitAction, &QAction::triggered,
40 qApp, &QApplication::quit);
41
42 fileMenu = menuBar()->addMenu(tr("&File"));
43 fileMenu->addAction(openAction);
44 fileMenu->addAction(loadAction);
45 fileMenu->addAction(saveAction);
46 fileMenu->addSeparator();
47 fileMenu->addAction(exitAction);
48
49 scripting = new ScriptingDock(new GuileInterpreter, tr("Scripting"), this);
50 scripting->setAllowedAreas(Qt::BottomDockWidgetArea);
51 addDockWidget(Qt::BottomDockWidgetArea, scripting);
52
53 listWidget = new QTreeWidget();
54 listWidget->setColumnCount(1);
55 listWidget->setDragDropMode(QAbstractItemView::InternalMove);
56 listWidget->setContextMenuPolicy(Qt::CustomContextMenu);
57 connect(listWidget, SIGNAL(customContextMenuRequested(const QPoint&)),
58 this, SLOT(showListContextMenu(const QPoint&)));
59
60 stackedWidget = new QStackedWidget();
61 dockWidget = new QDockWidget(tr("Functions"), this);
62 dockWidget->setAllowedAreas(Qt::LeftDockWidgetArea |
63 Qt::RightDockWidgetArea);
64 dockWidget->setWidget(listWidget);
65 addDockWidget(Qt::LeftDockWidgetArea, dockWidget);
66 setCentralWidget(stackedWidget);
67
68 connect(listWidget, &QTreeWidget::currentItemChanged,
69 [=] (QTreeWidgetItem* current, QTreeWidgetItem*) {
70 switchMainPlane(current);
71 });
72
73 setWindowTitle(tr("FRIDA"));
74
75 QTreeWidgetItem * external = new QTreeWidgetItem(listWidget, QStringList("External Functions"));
76 external->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator);
77 external->setBackground(0, QBrush(QColor(0xff, 0xdd, 0xdd)));
78 mgr->connect_new_function_signal([&] (Function* fun) {addFunction(fun);});
79 mgr->connect_new_dyn_symbol_signal([=] (const std::string& name) {
80 auto item = new QTreeWidgetItem(external, QStringList(name.c_str()));
81 item->setBackground(0, QBrush(QColor(0xff, 0xdd, 0xdd)));
82 });
83 mgr->connect_rename_function_signal([&](RenameFunctionEvent* event) {
84 if (objects_list_by_address.find(event->address) == objects_list_by_address.end())
85 return;
86 auto item = objects_list_by_address[event->address];
87 if (item) item->setText(0, event->new_name.c_str());
88 });
89 setGlobalHotkeys();
90 }
91
92 void Mainwindow::setGlobalHotkeys() {
93 QShortcut *shortcut = new QShortcut(QKeySequence("f"), this);
94 connect(shortcut, &QShortcut::activated, this, &Mainwindow::requestNewFunction);
95
96 shortcut = new QShortcut(QKeySequence("r"), listWidget);
97 connect(shortcut, &QShortcut::activated, [=]() {
98 QTreeWidgetItem * item = listWidget->currentItem();
99 if (item) renameFunction(objects_list[item]->getFunction());
100 });
101 }
102
103 void Mainwindow::quit()
104 {
105 QMessageBox messageBox;
106 messageBox.setWindowTitle(tr("Frida"));
107 messageBox.setText(tr("Do you really want to quit?"));
108 messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
109 messageBox.setDefaultButton(QMessageBox::No);
110 if (messageBox.exec() == QMessageBox::Yes)
111 qApp->quit();
112 }
113
114 void Mainwindow::open() {
115 QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"), "",
116 tr("Binaries (*)"));
117 manager->reset(fileName.toStdString());
118 }
119
120 void Mainwindow::load() {
121 QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"), "",
122 tr("Frida Archives (*.frida)"));
123 manager->load(fileName.toStdString());
124 }
125
126 void Mainwindow::save() {
127 QString filename = QFileDialog::getSaveFileName(this, tr("Save File"), "", tr("Frida Archives (*.frida)"));
128 manager->save(filename.toStdString());
129 }
130
131 void Mainwindow::switchMainPlaneToAddress(uint64_t address) {
132 if (objects_list_by_address.find(address) != objects_list_by_address.end()) {
133 LOG4CXX_DEBUG(logger, "Switching to function " << std::hex << address);
134 QTreeWidgetItem * item = objects_list_by_address[address];
135 listWidget->setCurrentItem(item);
136 stackedWidget->setCurrentWidget(objects_list[item]);
137 } else {
138 LOG4CXX_DEBUG(logger, "No function at " << std::hex << address
139 << " -- it's probably an imported Symbol");
140 }
141 }
142
143 void Mainwindow::switchMainPlane(QTreeWidgetItem* to) {
144 if (objects_list.end() != objects_list.find(to))
145 stackedWidget->setCurrentWidget(objects_list[to]);
146 }
147
148 void Mainwindow::showListContextMenu(const QPoint& point) {
149 QAction * act;
150 QTreeWidgetItem * item = listWidget->itemAt(point);
151 QMenu menu(this);
152
153 act = menu.addAction("Add Function");
154 connect(act, &QAction::triggered, this, &Mainwindow::requestNewFunction);
155
156 act = menu.addAction("Add Group");
157 connect(act, &QAction::triggered, this, &Mainwindow::requestNewGroup);
158
159 if (item) {
160 if (objects_list.find(item) != objects_list.end()) {
161 act = menu.addAction("Rename Function");
162 connect(act, &QAction::triggered, [=]() {this->renameFunction(objects_list[item]->getFunction());});
163 } else {
164 act = menu.addAction("Rename Group");
165 connect(act, &QAction::triggered, [=]() {renameGroup(item);});
166 }
167
168
169 QMenu* submenu = menu.addMenu("Move to group");
170
171 for (QTreeWidgetItem* groupitem : group_list) {
172 act = submenu->addAction(groupitem->text(0));
173 connect(act, &QAction::triggered,
174 [=] () {
175 listWidget->invisibleRootItem()->removeChild(item);
176 groupitem->addChild(item);
177 });
178 }
179 }
180
181 menu.exec(listWidget->mapToGlobal(point));
182 }
183
184 void Mainwindow::requestNewFunction() {
185 NewFunctionDialog dialog;
186 int result = dialog.exec();
187 if (QDialog::Accepted == result) {
188 requestNewFunctionByAddress(dialog.result());
189 } else {
190 LOG4CXX_DEBUG(logger, "requestNewFunction aborted");
191 }
192 }
193
194 void Mainwindow::requestNewGroup() {
195 SimpleStringDialog dialog("New Group");
196 int result = dialog.exec();
197 if (QDialog::Accepted == result) {
198 QTreeWidgetItem * external = new QTreeWidgetItem(listWidget, QStringList(dialog.result()));
199 external->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator);
200 group_list.push_back(external);
201 } else {
202 LOG4CXX_DEBUG(logger, "requestNewGroup aborted");
203 }
204 }
205
206 void Mainwindow::requestNewFunctionByAddress(uint64_t address) {
207 LOG4CXX_DEBUG(logger, "requesting Function at " << std::hex << address);
208 manager->getDisassembler()->disassembleFunctionAt(address);
209 switchMainPlaneToAddress(address);
210 }
211
212 void Mainwindow::renameFunction(Function* function) {
213 SimpleStringDialog dialog("New name");
214 int result = dialog.exec();
215 if (QDialog::Accepted == result) {
216 LOG4CXX_DEBUG(logger, "renaming Function " << function->getName()
217 << " to " << dialog.result().toStdString());
218 function->setName(dialog.result().toStdString());
219 } else {
220 LOG4CXX_DEBUG(logger, "renameFunction aborted");
221 }
222 }
223
224 void Mainwindow::renameGroup(QTreeWidgetItem* item) {
225 SimpleStringDialog dialog("New name");
226 int result = dialog.exec();
227 if (QDialog::Accepted == result) {
228 LOG4CXX_DEBUG(logger, "renaming group " << item->text(0).toStdString()
229 << " to " << dialog.result().toStdString());
230 item->setText(0, dialog.result());
231 } else {
232 LOG4CXX_DEBUG(logger, "renameFunction aborted");
233 }
234 }
235
236 void Mainwindow::addFunction(Function* fun) {
237 if (functions.find(fun->getStartAddress()) != functions.end())
238 return;
239
240 functions.insert(std::make_pair(fun->getStartAddress(), fun));
241
242 FunctionWidget * w = new FunctionWidget(fun);
243
244 // CFG
245 CFGScene * scene = new CFGScene;
246
247 BasicBlock * block = manager->getBasicBlock(fun->getStartAddress());
248
249 uint64_t start_address(std::numeric_limits<uint64_t>::max());
250 for (auto b : fun->blocks()) {
251 if (b.first < start_address)
252 start_address = b.first;
253 }
254
255 std::map<uint64_t, BasicBlockWidget*> _blocks;
256 local__add_basic_block(block, this,
257 manager, _blocks, scene, start_address, 100);
258
259 QGraphicsView * view = new QGraphicsView(scene);
260 w->addTab(view, "CFG");
261
262 // Listing
263 QTableWidget * t = new QTableWidget();
264 t->setColumnCount(3);
265 t->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
266
267 w->addTab(t, "Listing");
268
269 QTreeWidgetItem * item = new QTreeWidgetItem(listWidget, QStringList(fun->getName().c_str()));
270 stackedWidget->addWidget(w);
271 objects_list.insert(std::make_pair(item, w));
272 LOG4CXX_DEBUG(logger, "Adding function widget at " << std::hex
273 << fun->getStartAddress());
274 objects_list_by_address.insert(std::make_pair(fun->getStartAddress(), item));
275 }
276
277 namespace {
278 BasicBlockWidget *
279 local__add_basic_block(BasicBlock * block,
280 Mainwindow * mainwindow, InformationManager * manager,
281 std::map<uint64_t, BasicBlockWidget*>& known_blocks,
282 CFGScene * scene, uint64_t starty, uint64_t startx) {
283
284 decltype(known_blocks.begin()) old;
285 if ((old = known_blocks.find(block->getStartAddress())) != known_blocks.end())
286 return old->second;
287
288 std::stringstream s;
289 s << "BLOCK_" << std::hex << block->getStartAddress()
290 << "_" << block->getEndAddress();
291 BasicBlockWidget * widget = new BasicBlockWidget(s.str().c_str(),
292 block, mainwindow);
293
294 known_blocks.insert(std::make_pair(block->getStartAddress(), widget));
295
296 scene->addItem(widget);
297 widget->setFlag(QGraphicsItem::ItemIsMovable, true);
298 widget->moveBy(100*startx, block->getStartAddress() - starty);
299
300 manager->getDisassembler()
301 ->printEachInstruction(block->getStartAddress(),
302 block->getEndAddress(),
303 [&](uint8_t* bytes,
304 size_t byte_count,
305 const std::string& line,
306 const std::string& ref) {
307 widget->addItem(bytes, byte_count,
308 line.c_str() + 1, // remove \t
309 ref.c_str());
310 });
311
312 BasicBlockWidget *tmp, *nextl(NULL), *nextr(NULL);
313 BasicBlock * tmpblock;
314 if (block->getNextBlock(0) != 0) {
315 int xshift = 0;
316 if (block->getNextBlock(1) != 0)
317 xshift = 1;
318 tmpblock = manager->getBasicBlock(block->getNextBlock(0));
319 tmp = local__add_basic_block(tmpblock, mainwindow, manager,
320 known_blocks,
321 scene, starty, startx+xshift);
322 nextl = tmp;
323 tmp->addPrevious(widget);
324 }
325 if (block->getNextBlock(1) != 0) {
326 tmpblock = manager->getBasicBlock(block->getNextBlock(1));
327 tmp = local__add_basic_block(tmpblock, mainwindow, manager,
328 known_blocks,
329 scene, starty, startx-1);
330 nextr = tmp;
331 tmp->addPrevious(widget);
332 }
333 widget->addNext(nextl, nextr);
334 return widget;
335 }
336 }