" + w + "
Filename | " + fn + " |
Binary Filename | " + efn + " |
SHA256 | " + hash + " |
"
158 | + "The external BinDiff database has been loaded. "
159 | + "Below, you need to select which of the files in the database matches the file loaded in Ghidra.
"
160 | + "The other file will be used to import function names from.
BinDiff binary not selected or BinExport " 273 | + "plugin not detected so this feature is not available. Check the settings menu.
")); 274 | } 275 | 276 | projectPanel.setBorder(BorderFactory.createTitledBorder("Diff with another file from Ghidra project")); 277 | projectPanel.setAlignmentX(Component.LEFT_ALIGNMENT); 278 | panel.add(projectPanel); 279 | 280 | panel.add(Box.createRigidArea(new Dimension(0, 20))); 281 | add(panel); 282 | } 283 | 284 | @Override 285 | public String getTitle() { 286 | return "Select a file that will be diffed to the file which you have currently opened in the Code Explorer"; 287 | } 288 | 289 | @Override 290 | public boolean isValidInformation() { 291 | if (tp == null || tp.getSelectedItemCount() != 1) 292 | return false; 293 | 294 | if (tp.getSelectedDomainFolder() != null) 295 | return false; 296 | 297 | var df = tp.getSelectedDomainFile(); 298 | return df != null; 299 | } 300 | 301 | @Override 302 | public void initialize() { 303 | } 304 | 305 | public DomainFile getDf() { 306 | return tp.getSelectedDomainFile(); 307 | } 308 | } 309 | 310 | class Program2Panel extends AbstractWizardJPanel { 311 | private BinDiffHelperPlugin plugin; 312 | private ProjectDataTreePanel tp; 313 | private JCheckBox cb; 314 | 315 | private Program program; 316 | private CodeViewerService cvs; 317 | 318 | Program2Panel(BinDiffHelperPlugin plugin) { 319 | this.plugin = plugin; 320 | setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); 321 | 322 | cb = new JCheckBox("Attach another ghidra file to the secondary diff file"); 323 | 324 | tp = new ProjectDataTreePanel(null); 325 | tp.setProjectData(AppInfo.getActiveProject().getName(), AppInfo.getActiveProject().getProjectData()); 326 | 327 | tp.setPreferredSize(new Dimension(400, 300)); 328 | 329 | tp.addTreeSelectionListener(new GTreeSelectionListener() { 330 | @Override 331 | public void valueChanged(GTreeSelectionEvent e) { 332 | notifyListenersOfValidityChanged(); 333 | } 334 | }); 335 | 336 | add(cb); 337 | add(tp); 338 | cb.addActionListener(new ActionListener() { 339 | @Override 340 | public void actionPerformed(ActionEvent e) { 341 | notifyListenersOfValidityChanged(); 342 | } 343 | }); 344 | } 345 | 346 | @Override 347 | public String getTitle() { 348 | return "Optionally attach another ghidra file to the secondary diff file"; 349 | } 350 | 351 | @Override 352 | public boolean isValidInformation() { 353 | if (!cb.isSelected()) 354 | return true; 355 | 356 | if (tp == null || tp.getSelectedItemCount() != 1) 357 | return false; 358 | 359 | if (tp.getSelectedDomainFolder() != null) 360 | return false; 361 | 362 | var df = tp.getSelectedDomainFile(); 363 | return df != null; 364 | } 365 | 366 | @Override 367 | public void initialize() { 368 | } 369 | 370 | public void open() { 371 | try { 372 | DomainFile df = tp.getSelectedDomainFile(); 373 | Tool newTool = plugin.getTool().getToolServices().launchDefaultTool(Collections.singletonList(df)); 374 | DomainObject domainObject = df.getDomainObject(this, true, false, TaskMonitor.DUMMY); 375 | program = (Program) domainObject; 376 | cvs = newTool.getService(CodeViewerService.class); 377 | } catch (Exception e) { 378 | Msg.showError(this, this, "Error", "Failed to open the program in new window: " + e.getMessage()); 379 | } 380 | } 381 | 382 | public boolean useProgram2() { 383 | return cb.isSelected(); 384 | } 385 | 386 | public CodeViewerService getCvs() { 387 | return cvs; 388 | } 389 | 390 | public Program getProg() { 391 | return program; 392 | } 393 | 394 | public DomainFile getDf() { 395 | return tp.getSelectedDomainFile(); 396 | } 397 | } 398 | 399 | class DiffPanelManager implements PanelManager { 400 | private WizardManager wizardMgr; 401 | private DiffKindPanel dkPanel; 402 | private MatchPanel matchPanel; 403 | private FromProjectPanel fromProjectPanel; 404 | private Program2Panel program2Panel; 405 | 406 | private BinDiffHelperPlugin plugin; 407 | 408 | DiffPanelManager(BinDiffHelperPlugin plugin) { 409 | dkPanel = new DiffKindPanel(plugin); 410 | this.plugin = plugin; 411 | } 412 | 413 | @Override 414 | public boolean canFinish() { 415 | WizardPanel curPanel = wizardMgr.getCurrentWizardPanel(); 416 | if (curPanel == null) 417 | return false; 418 | if (curPanel == fromProjectPanel) 419 | return true; 420 | if (curPanel == program2Panel) 421 | return true; 422 | return false; 423 | } 424 | 425 | @Override 426 | public boolean hasNextPanel() { 427 | WizardPanel curPanel = wizardMgr.getCurrentWizardPanel(); 428 | if (curPanel == null) 429 | return true; 430 | if (curPanel == fromProjectPanel) 431 | return false; 432 | if (curPanel == matchPanel) 433 | return true; 434 | if (curPanel == dkPanel) 435 | return true; 436 | if (curPanel == program2Panel) 437 | return false; 438 | return false; 439 | } 440 | 441 | @Override 442 | public boolean hasPreviousPanel() { 443 | WizardPanel curPanel = wizardMgr.getCurrentWizardPanel(); 444 | if (curPanel == null) 445 | return false; 446 | if (curPanel == fromProjectPanel) 447 | return true; 448 | if (curPanel == matchPanel) 449 | return true; 450 | if (curPanel == dkPanel) 451 | return false; 452 | if (curPanel == program2Panel) 453 | return true; 454 | return false; 455 | } 456 | 457 | @Override 458 | public WizardPanel getNextPanel() throws IllegalPanelStateException { 459 | 460 | if (wizardMgr.getCurrentWizardPanel() == null) 461 | return dkPanel; 462 | 463 | if (wizardMgr.getCurrentWizardPanel() == dkPanel) { 464 | if (dkPanel.isFromProject()) { 465 | fromProjectPanel = new FromProjectPanel(plugin); 466 | return fromProjectPanel; 467 | } else { 468 | matchPanel = new MatchPanel(plugin); 469 | return matchPanel; 470 | } 471 | } 472 | 473 | if (wizardMgr.getCurrentWizardPanel() == matchPanel) { 474 | program2Panel = new Program2Panel(plugin); 475 | return program2Panel; 476 | } 477 | 478 | return null; 479 | } 480 | 481 | @Override 482 | public WizardPanel getInitialPanel() throws IllegalPanelStateException { 483 | return dkPanel; 484 | } 485 | 486 | @Override 487 | public WizardPanel getPreviousPanel() throws IllegalPanelStateException { 488 | WizardPanel curPanel = wizardMgr.getCurrentWizardPanel(); 489 | if (curPanel == null) 490 | return null; 491 | if (curPanel == fromProjectPanel) 492 | return dkPanel; 493 | if (curPanel == matchPanel) 494 | return dkPanel; 495 | if (curPanel == dkPanel) 496 | return null; 497 | if (curPanel == program2Panel) 498 | return matchPanel; 499 | return null; 500 | } 501 | 502 | @Override 503 | public String getStatusMessage() { 504 | // TODO Auto-generated method stub 505 | return null; 506 | } 507 | 508 | @Override 509 | public void finish() throws IllegalPanelStateException { 510 | WizardPanel curPanel = wizardMgr.getCurrentWizardPanel(); 511 | if (curPanel == program2Panel) { 512 | if (matchPanel.isSwapped()) { 513 | DiffState temp = plugin.provider.secondary; 514 | plugin.provider.secondary = plugin.provider.primary; 515 | plugin.provider.primary = temp; 516 | } 517 | 518 | if (program2Panel.useProgram2()) 519 | program2Panel.open(); 520 | 521 | plugin.provider.secondary.cvs = program2Panel.getCvs(); 522 | plugin.provider.secondary.prog = program2Panel.getProg(); 523 | plugin.provider.secondary.df = program2Panel.getDf(); 524 | plugin.provider.doDiffWork(); 525 | wizardMgr.close(); 526 | } else { 527 | // from project 528 | DomainFile df = fromProjectPanel.getDf(); 529 | plugin.callBinDiff(df, files -> { 530 | if (files != null) { 531 | try { 532 | plugin.provider.openExternalDBWithBinExports(files[2].getAbsolutePath(), files[0], files[1]); 533 | } catch (Exception e) { 534 | e.printStackTrace(); 535 | } 536 | plugin.provider.secondary.df = df; 537 | try { 538 | var dof = df.getReadOnlyDomainObject(plugin, DomainFile.DEFAULT_VERSION, TaskMonitor.DUMMY); 539 | if (dof instanceof Program p) 540 | plugin.provider.secondary.prog = p; 541 | } catch (Exception e) { 542 | 543 | } 544 | plugin.provider.doDiffWork(); 545 | wizardMgr.close(); 546 | } 547 | }); 548 | } 549 | } 550 | 551 | @Override 552 | public void cancel() { 553 | // TODO Auto-generated method stub 554 | } 555 | 556 | @Override 557 | public void initialize() { 558 | // TODO Auto-generated method stub 559 | } 560 | 561 | @Override 562 | public Dimension getPanelSize() { 563 | return new Dimension(1000, 400); 564 | } 565 | 566 | @Override 567 | public void setWizardManager(WizardManager wm) { 568 | wizardMgr = wm; 569 | } 570 | 571 | @Override 572 | public WizardManager getWizardManager() { 573 | return wizardMgr; 574 | } 575 | } 576 | -------------------------------------------------------------------------------- /src/main/java/bindiffhelper/ImportFunctionNamesAction.java: -------------------------------------------------------------------------------- 1 | package bindiffhelper; 2 | 3 | import java.util.HashMap; 4 | import java.util.List; 5 | import java.util.Map; 6 | 7 | import docking.ActionContext; 8 | import docking.action.DockingAction; 9 | import docking.action.MenuData; 10 | import docking.action.ToolBarData; 11 | import ghidra.program.model.listing.Program; 12 | import ghidra.program.model.symbol.Namespace; 13 | import ghidra.program.model.symbol.SourceType; 14 | import ghidra.program.model.symbol.SymbolTable; 15 | import ghidra.util.HTMLUtilities; 16 | import ghidra.util.Msg; 17 | import resources.ResourceManager; 18 | 19 | class ImportFunctionNamesAction extends DockingAction { 20 | 21 | protected BinDiffHelperPlugin plugin; 22 | 23 | List32 | * This is a utility function I use to update comments for updating 33 | * plate-comments at the top of functions 34 | *
35 | * This function automatically adds the key as the leader for rep 36 | * 37 | * @param str The string we are scanning for a line to replace 38 | * @param key The leader for the line we want to replace 39 | * @param rep The value we want in the string 40 | * @return A string with the line matching key replaced. If no line matched, 41 | * then we return rep 42 | */ 43 | public String updateKey(String str, String key, String rep) { 44 | String actualRep = String.format("%s: %s", key, rep); 45 | if (str == null) 46 | return actualRep; 47 | var strArr = str.split("\r?\n"); 48 | StringBuilder res = new StringBuilder(); 49 | 50 | boolean replaced = false; 51 | boolean prependSep = false; 52 | 53 | for (String s : strArr) { 54 | if (prependSep) 55 | res.append("\n"); 56 | if (s.startsWith(key)) { 57 | res.append(actualRep); 58 | replaced = true; 59 | prependSep = true; 60 | } else { 61 | res.append(s); 62 | prependSep = true; 63 | } 64 | } 65 | 66 | if (replaced == false) { 67 | if (prependSep) 68 | res.append("\n"); 69 | res.append(actualRep); 70 | } 71 | return res.toString(); 72 | } 73 | 74 | @Override 75 | public void actionPerformed(ActionContext arg0) { 76 | int trans = plugin.program.startTransaction("Create Plate Comments"); 77 | var symtab = plugin.program.getSymbolTable(); 78 | var funmgr = plugin.program.getFunctionManager(); 79 | 80 | for (ComparisonTableModel.Entry e : entries) { 81 | if (e.primaryAddress == null) 82 | continue; // If we don't have a primary address, there is nothing to comment. 83 | if (symtab.hasSymbol(e.primaryAddress)) { 84 | var f = funmgr.getFunctionAt(e.primaryAddress); 85 | if (f == null) // skip this if it is a symbol, but not a function 86 | continue; 87 | 88 | String origComment = f.getComment(); 89 | String newBindiffComment = String.format("*** %2f%% match with %2f%% confidence using %s ***", 90 | e.similarity * 100, e.confidence * 100, e.algorithm); 91 | String newFunctionLinkComment = String.format("*** %s@%08x {@program %s@%08x} ***", 92 | e.secondaryFunctionName, e.secondaryAddress, plugin.provider.otherProg, e.secondaryAddress); 93 | String newComment = updateKey(origComment, "BINDIFF_COMMENT", newBindiffComment); 94 | newComment = updateKey(newComment, "BINDIFF_MATCHED_FN", newFunctionLinkComment); 95 | f.setComment(newComment); 96 | } 97 | } 98 | plugin.program.endTransaction(trans, true); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/resources/images/BDH.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ubfx/BinDiffHelper/417a73d6955a8fd868d079374d9220f138903368/src/main/resources/images/BDH.png -------------------------------------------------------------------------------- /src/main/resources/images/arrow_merge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ubfx/BinDiffHelper/417a73d6955a8fd868d079374d9220f138903368/src/main/resources/images/arrow_merge.png -------------------------------------------------------------------------------- /src/main/resources/images/bd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ubfx/BinDiffHelper/417a73d6955a8fd868d079374d9220f138903368/src/main/resources/images/bd.png -------------------------------------------------------------------------------- /src/main/resources/images/bindiff-48x48-rgba.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ubfx/BinDiffHelper/417a73d6955a8fd868d079374d9220f138903368/src/main/resources/images/bindiff-48x48-rgba.png -------------------------------------------------------------------------------- /src/main/resources/images/check_box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ubfx/BinDiffHelper/417a73d6955a8fd868d079374d9220f138903368/src/main/resources/images/check_box.png -------------------------------------------------------------------------------- /src/main/resources/images/color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ubfx/BinDiffHelper/417a73d6955a8fd868d079374d9220f138903368/src/main/resources/images/color.png -------------------------------------------------------------------------------- /src/main/resources/images/comment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ubfx/BinDiffHelper/417a73d6955a8fd868d079374d9220f138903368/src/main/resources/images/comment.png -------------------------------------------------------------------------------- /src/main/resources/images/open_db.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ubfx/BinDiffHelper/417a73d6955a8fd868d079374d9220f138903368/src/main/resources/images/open_db.png -------------------------------------------------------------------------------- /src/main/resources/images/open_from_project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ubfx/BinDiffHelper/417a73d6955a8fd868d079374d9220f138903368/src/main/resources/images/open_from_project.png -------------------------------------------------------------------------------- /src/main/resources/images/setting_tools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ubfx/BinDiffHelper/417a73d6955a8fd868d079374d9220f138903368/src/main/resources/images/setting_tools.png -------------------------------------------------------------------------------- /src/main/resources/images/table_go.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ubfx/BinDiffHelper/417a73d6955a8fd868d079374d9220f138903368/src/main/resources/images/table_go.png -------------------------------------------------------------------------------- /src/main/resources/images/table_lightning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ubfx/BinDiffHelper/417a73d6955a8fd868d079374d9220f138903368/src/main/resources/images/table_lightning.png --------------------------------------------------------------------------------