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