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