mirror of
https://github.com/gmh5225/awesome-game-security
synced 2026-06-21 13:56:22 +00:00
archive: add 5 repo prompt(s) [skip ci]
This commit is contained in:
@@ -0,0 +1,785 @@
|
||||
Project Path: arc_giladreich_ida_migrator_0577ih9p
|
||||
|
||||
Source Tree:
|
||||
|
||||
```txt
|
||||
arc_giladreich_ida_migrator_0577ih9p
|
||||
├── LICENSE
|
||||
├── docs
|
||||
│ └── README.md
|
||||
├── media
|
||||
│ ├── exporter_1.png
|
||||
│ ├── exporter_2.png
|
||||
│ ├── ida_migrator.png
|
||||
│ ├── ida_migrator.psd
|
||||
│ ├── importer_1.png
|
||||
│ ├── importer_2.png
|
||||
│ └── intro.png
|
||||
└── source
|
||||
├── ida_migrator
|
||||
│ ├── __init__.py
|
||||
│ ├── export_dialog.py
|
||||
│ ├── import_dialog.py
|
||||
│ ├── intro_dialog.py
|
||||
│ ├── migrator_dialog.py
|
||||
│ ├── plugin.py
|
||||
│ ├── ui
|
||||
│ │ ├── IntroDialog.ui
|
||||
│ │ └── MigratorDialog.ui
|
||||
│ └── utility.py
|
||||
└── ida_migrator.py
|
||||
|
||||
```
|
||||
|
||||
`LICENSE`:
|
||||
|
||||
```
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020-present Gilad Reich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
```
|
||||
|
||||
`docs/README.md`:
|
||||
|
||||
```md
|
||||
|
||||
<p align="center"><img src="/media/ida_migrator.png" width=700 height=200></p>
|
||||
|
||||
# IDA Migrator Plugin
|
||||
|
||||
IDA Migrator plugin aids migrating function names, structures and enums from one database instance to another.
|
||||
|
||||
This comes in handy when:
|
||||
* Moving to a newer version of IDA that does better analysis and you don't want to change in the new instance type information or variable names of the decompiled functions.
|
||||
* The current idb instance fails to decompile a function or the decompilation looks wrong in comparison to another idb instance of the same binary.
|
||||
* Experimenting on another idb instance before making major changes on the current instance.
|
||||
* A lightweight easy way of creating small incremental backups from the current work.
|
||||
* For w/e reason, the current idb instance you're working on gets corrupted.
|
||||
|
||||
|
||||
IDA Migrator plugin developed using PyQt, hence should work on all platforms.
|
||||
|
||||
## Getting Started
|
||||
|
||||
Download links can be found [here](https://github.com/giladreich/ida_migrator/releases).
|
||||
|
||||
Copy the files under the `source` directory and put them under your IDA installation `plugins` directory.
|
||||
|
||||
Start your current IDA instance you want to migrate from and then press `CTRL+SHIFT+D` to show the plugin's UI. Alternative; open it through the `Edit -> Plugins -> IDA Migrator` menu:
|
||||
|
||||

|
||||
|
||||
|
||||
### Step 1 - Exporting Data
|
||||
|
||||
Clicking the `Export` button will show all functions of the current database instance:
|
||||
|
||||

|
||||
|
||||
Hint: You can uncheck any functions you want to exclude from exporting.
|
||||
|
||||
Once you click the `Start Export` button, it will ask you where would you like to export the files; One is the `*symbols*.json` storing addresses and function names and the other is `*types*.idc` having all the structures and enums information:
|
||||
|
||||

|
||||
|
||||
|
||||
### Step 2 - Importing Data
|
||||
|
||||
In the new idb instance, open the plugin again and click on the `Import` button, which will then ask you to provide the `*symbols*.json` file:
|
||||
|
||||

|
||||
|
||||
Same procedure from here, just that once you click the `Start Import` button, it will ask you if you would like to import structures and enums as well from the exported `*types*.idc` file, that's optional for you to choose.
|
||||
|
||||
Note that it will only rename functions that does not have the same name and will output what functions has been affected in IDA's console:
|
||||
|
||||

|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
Pull-Requests are greatly appreciated should you like to contribute to the project.
|
||||
|
||||
Same goes for opening issues; if you have any suggestions, feedback or you found any bugs, please do not hesitate to open an [issue](https://github.com/giladreich/ida_migrator/issues).
|
||||
|
||||
## Authors
|
||||
|
||||
* **Gilad Reich** - *Initial work* - [giladreich](https://github.com/giladreich)
|
||||
|
||||
See also the list of [contributors](https://github.com/giladreich/ida_migrator/graphs/contributors) who participated in this project.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
||||
|
||||
|
||||
```
|
||||
|
||||
`source/ida_migrator.py`:
|
||||
|
||||
```py
|
||||
from ida_migrator.plugin import IdaMigratorPlugin
|
||||
|
||||
def PLUGIN_ENTRY(*args, **kwargs):
|
||||
return IdaMigratorPlugin(*args, **kwargs)
|
||||
|
||||
```
|
||||
|
||||
`source/ida_migrator/__init__.py`:
|
||||
|
||||
```py
|
||||
import os
|
||||
|
||||
VERSION = '2.0.1'
|
||||
PLUGIN_NAME = "IDA Migrator"
|
||||
PLUGIN_DIR = os.path.dirname(os.path.realpath(__file__))
|
||||
IDA_DIR = os.path.abspath(os.path.join(PLUGIN_DIR, '..', '..'))
|
||||
UI_DIR = os.path.join(PLUGIN_DIR, 'ui')
|
||||
|
||||
```
|
||||
|
||||
`source/ida_migrator/export_dialog.py`:
|
||||
|
||||
```py
|
||||
from ida_migrator.migrator_dialog import *
|
||||
from ida_migrator.utility import log_info
|
||||
|
||||
|
||||
class ExportDialog(MigratorDialog):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ExportDialog, self).__init__(*args, **kwargs)
|
||||
self.setWindowTitle("Exporter")
|
||||
self._ui.lblTitle.setText(self._ui.lblTitle.text().replace('XXXX', 'export'))
|
||||
self._ui.btnStart.setText("Start Export")
|
||||
|
||||
def populate_function_names(self):
|
||||
log_info('Loading functions...')
|
||||
total = 0
|
||||
for seg in idautils.Segments():
|
||||
total += len(list(idautils.Functions(seg, idc.get_segm_end(seg))))
|
||||
|
||||
self.tblFunctions.setRowCount(total)
|
||||
index = 0
|
||||
for seg in idautils.Segments():
|
||||
for func in idautils.Functions(seg, idc.get_segm_end(seg)):
|
||||
address = POINTER_FMT.format(func)
|
||||
function = idc.get_func_name(func)
|
||||
self.append_table_item(index, address, function)
|
||||
index += 1
|
||||
log_info('Finished loading functions.')
|
||||
|
||||
def process_pe_info(self):
|
||||
info = idaapi.get_inf_structure()
|
||||
|
||||
bits = 0xFF
|
||||
if info.is_64bit():
|
||||
bits = 64
|
||||
elif info.is_32bit():
|
||||
bits = 32
|
||||
|
||||
data = {
|
||||
'exe': idc.get_root_filename(),
|
||||
'arch': "x{}_bit".format(bits),
|
||||
'file_type': idaapi.get_file_type_name(),
|
||||
'base_addr': POINTER_FMT.format(idaapi.get_imagebase()),
|
||||
'db_path': idaapi.get_input_file_path()
|
||||
}
|
||||
|
||||
return data
|
||||
|
||||
def process_functions(self):
|
||||
names = list()
|
||||
rowCount = self.tblFunctions.rowCount()
|
||||
count_added = 0
|
||||
for row in range(rowCount):
|
||||
cbx = self.tblFunctions.item(row, col_CheckBox)
|
||||
if not cbx or cbx.checkState() != Qt.Checked:
|
||||
continue
|
||||
|
||||
name = {
|
||||
'address': self.tblFunctions.item(row, col_Address).text(),
|
||||
'name': self.tblFunctions.item(row, col_Name).text()
|
||||
}
|
||||
count_added += 1
|
||||
names.append(name)
|
||||
|
||||
return names, count_added
|
||||
|
||||
def on_start_clicked(self):
|
||||
file, ext = os.path.splitext(idc.get_idb_path())
|
||||
selected_dir = QFileDialog.getExistingDirectory(self, "Select Path to Export Files", os.path.dirname(file))
|
||||
if not selected_dir:
|
||||
return
|
||||
|
||||
if os.name == 'nt':
|
||||
selected_dir = selected_dir.replace('/', '\\')
|
||||
|
||||
file_name = os.path.basename(file)
|
||||
|
||||
datetime = time.strftime("%Y%m%d-%H%M%S")
|
||||
file_json = "{}_symbols_{}.json".format(file_name, datetime)
|
||||
file_path_json = os.path.join(selected_dir, file_json)
|
||||
log_info("Exporting to {}", file_json)
|
||||
functions, count = self.process_functions()
|
||||
payload = {
|
||||
'bpe_info': self.process_pe_info(),
|
||||
'functions_count': count,
|
||||
'functions': functions
|
||||
}
|
||||
with open(file_path_json, "w") as f:
|
||||
json.dump(payload, f, indent=4, sort_keys=True)
|
||||
|
||||
# NOTE(Gilad): Alternative Solution:
|
||||
# Call idc.process_ui_action('ProduceHeader') to produce C header file and then
|
||||
# on import idc.process_ui_action('LoadHeaderFile'). Only issue might be with parsing errors.
|
||||
# Parsing .IDC file is less error prone in this scenario.
|
||||
file_types = "{}_types_{}.idc".format(file_name, datetime)
|
||||
file_path_types = os.path.join(selected_dir, file_types)
|
||||
if not idc.gen_file(idc.OFILE_IDC, file_types, 0, idc.BADADDR, idc.GENFLG_IDCTYPE):
|
||||
QMessageBox.error(self, "FAILED", "Failed to generate type information file.")
|
||||
os.rename(file_types, file_path_types)
|
||||
|
||||
QMessageBox.information(self, "Successfully Exported",
|
||||
"""Exported path:\n{}\n{}
|
||||
\nYou can now use the Importer and select these files to import in another database instance."""
|
||||
.format(file_path_json, file_path_types))
|
||||
|
||||
```
|
||||
|
||||
`source/ida_migrator/import_dialog.py`:
|
||||
|
||||
```py
|
||||
from ida_migrator.migrator_dialog import *
|
||||
from ida_migrator.utility import log_info
|
||||
|
||||
|
||||
class ImportDialog(MigratorDialog):
|
||||
|
||||
def __init__(self, file_path, *args, **kwargs):
|
||||
self.file_path = file_path
|
||||
super(ImportDialog, self).__init__(*args, **kwargs)
|
||||
self.setWindowTitle("Importer")
|
||||
self._ui.lblTitle.setText(self._ui.lblTitle.text().replace('XXXX', 'import'))
|
||||
self._ui.btnStart.setText("Start Import")
|
||||
|
||||
def populate_function_names(self):
|
||||
log_info('Loading functions...')
|
||||
with open(self.file_path, "r") as f:
|
||||
parsed_json = json.loads(f.read())
|
||||
self.tblFunctions.setRowCount(parsed_json['functions_count'])
|
||||
index = 0
|
||||
for func in parsed_json['functions']:
|
||||
self.append_table_item(index, func['address'], func['name'])
|
||||
index += 1
|
||||
log_info('Finished loading functions.')
|
||||
|
||||
def rename_functions(self):
|
||||
row_count = self.tblFunctions.rowCount()
|
||||
renamed_count = 0
|
||||
for row in range(row_count):
|
||||
cbx = self.tblFunctions.item(row, col_CheckBox)
|
||||
if not cbx or cbx.checkState() != Qt.Checked:
|
||||
continue
|
||||
|
||||
address_str = self.tblFunctions.item(row, col_Address).text()
|
||||
address = int(address_str, 16)
|
||||
name = self.tblFunctions.item(row, col_Name).text()
|
||||
curr_name = idc.get_func_name(address)
|
||||
if not name or not curr_name or name == curr_name:
|
||||
continue
|
||||
|
||||
if idaapi.set_name(address, str(name), idaapi.SN_NOWARN):
|
||||
log_info("{} - Renamed {} to {}", address_str, curr_name, name)
|
||||
renamed_count += 1
|
||||
else:
|
||||
log_info("Failed renaming: {}. Disable SN_NOWARN to see why.", address_str)
|
||||
|
||||
return renamed_count
|
||||
|
||||
def on_start_clicked(self):
|
||||
count = self.rename_functions()
|
||||
log_info("{} functions has been renamed.", count)
|
||||
|
||||
answer = QMessageBox.question(self, 'Yes | No',
|
||||
"""Would you like to import type information as well? (structs, enums)
|
||||
\nYou will need to provide the exported IDC file.""",
|
||||
QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
|
||||
|
||||
if answer == QMessageBox.Yes:
|
||||
# idc.process_ui_action('Execute')
|
||||
dir_path = os.path.dirname(idc.get_idb_path())
|
||||
file_path, _ = QFileDialog.getOpenFileName(self, "Select File to Import", dir_path, "IDC Script (*.idc)")
|
||||
if file_path:
|
||||
ida_expr.exec_idc_script(None, str(file_path), "main", None, 0)
|
||||
|
||||
QMessageBox.information(self, "Successfully Imported",
|
||||
"Successfully renamed {} functions.".format(count))
|
||||
|
||||
```
|
||||
|
||||
`source/ida_migrator/intro_dialog.py`:
|
||||
|
||||
```py
|
||||
import os
|
||||
import json
|
||||
|
||||
import idc
|
||||
|
||||
from PyQt5 import uic
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import QFileDialog
|
||||
|
||||
from ida_migrator import UI_DIR
|
||||
from ida_migrator.utility import log_info
|
||||
from ida_migrator.export_dialog import ExportDialog
|
||||
from ida_migrator.import_dialog import ImportDialog
|
||||
|
||||
|
||||
Ui_IntroDialog, IntroDialogBase = uic.loadUiType(
|
||||
os.path.join(UI_DIR, 'IntroDialog.ui')
|
||||
)
|
||||
|
||||
class IntroDialog(IntroDialogBase):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(IntroDialog, self).__init__(*args, **kwargs)
|
||||
self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
|
||||
|
||||
self._export_dialog = None
|
||||
self._import_dialog = None
|
||||
|
||||
self._ui = Ui_IntroDialog()
|
||||
self._ui.setupUi(self)
|
||||
self._ui.btnExport.clicked.connect(self.on_export_clicked)
|
||||
self._ui.btnImport.clicked.connect(self.on_import_clicked)
|
||||
|
||||
def on_export_clicked(self):
|
||||
self._export_dialog = ExportDialog(self)
|
||||
self._export_dialog.show()
|
||||
|
||||
def on_import_clicked(self):
|
||||
dir_path = os.path.dirname(idc.get_idb_path())
|
||||
file_path, _ = QFileDialog.getOpenFileName(self, 'Select File to Import',
|
||||
dir_path, 'Dump file (*.json)')
|
||||
if not file_path:
|
||||
return
|
||||
|
||||
self._import_dialog = ImportDialog(file_path, self)
|
||||
self._import_dialog.show()
|
||||
|
||||
```
|
||||
|
||||
`source/ida_migrator/migrator_dialog.py`:
|
||||
|
||||
```py
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
|
||||
import idc
|
||||
import idaapi
|
||||
import idautils
|
||||
import ida_expr
|
||||
|
||||
from ida_migrator import UI_DIR
|
||||
from ida_migrator.utility import log_info
|
||||
|
||||
from PyQt5 import uic
|
||||
from PyQt5.QtCore import Qt, QTimer
|
||||
from PyQt5.QtWidgets import (QMessageBox, QHeaderView,
|
||||
QTableWidget, QTableWidgetItem,
|
||||
QLineEdit, QFileDialog )
|
||||
|
||||
Ui_MigratorDialog, MigratorDialogBase = uic.loadUiType(
|
||||
os.path.join(UI_DIR, 'MigratorDialog.ui')
|
||||
)
|
||||
|
||||
POINTER_FMT = "0x{:016X}" if idaapi.get_inf_structure().is_64bit() else "0x{:08X}"
|
||||
|
||||
# Table columns
|
||||
col_CheckBox = 0
|
||||
col_Address = 1
|
||||
col_Name = 2
|
||||
|
||||
# Search debounce/delay time
|
||||
FILTER_DEBOUNCE_PERIOD = 500
|
||||
|
||||
|
||||
class MigratorDialog(MigratorDialogBase):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(MigratorDialog, self).__init__(*args, **kwargs)
|
||||
self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
|
||||
self._ui = Ui_MigratorDialog()
|
||||
self._ui.setupUi(self)
|
||||
|
||||
self.filter_timer = QTimer()
|
||||
self.filter_timer.setSingleShot(True)
|
||||
self.filter_text = ""
|
||||
self.tblFunctions = self._ui.tblFunctions
|
||||
|
||||
self.connect_slots()
|
||||
self.adjust_table_layout()
|
||||
self.populate_function_names()
|
||||
|
||||
def connect_slots(self):
|
||||
self._ui.btnStart.clicked.connect(self.on_start_clicked)
|
||||
self._ui.tbxSearch.textChanged.connect(self.on_search_textchanged)
|
||||
self.filter_timer.timeout.connect(self.filter_items)
|
||||
|
||||
def adjust_table_layout(self):
|
||||
checkbox_width = 22
|
||||
self.tblFunctions.setColumnWidth(col_CheckBox, checkbox_width)
|
||||
self.tblFunctions.horizontalHeader().setSectionResizeMode(col_CheckBox, QHeaderView.Fixed)
|
||||
self.tblFunctions.horizontalHeader().setStretchLastSection(True)
|
||||
# self.tblFunctions.setSelectionMode(QAbstractItemView.NoSelection)
|
||||
self.tblFunctions.setFocusPolicy(Qt.NoFocus)
|
||||
|
||||
# virtual
|
||||
def populate_function_names(self):
|
||||
log_info("BASE - populate_function_names")
|
||||
|
||||
def append_table_item(self, index, address, function):
|
||||
# cbx = QCheckBox(self)
|
||||
# cbx.setChecked(True)
|
||||
# cbx.setStyleSheet("margin-left:10%;")
|
||||
# self.tblFunctions.setCellWidget(index, col_CheckBox, cbx)
|
||||
cbx = QTableWidgetItem()
|
||||
cbx.setCheckState(Qt.Checked)
|
||||
cbx.setTextAlignment(Qt.AlignCenter)
|
||||
cbx.setFlags(Qt.NoItemFlags | Qt.ItemIsEnabled | Qt.ItemIsUserCheckable | Qt.ItemIsSelectable)
|
||||
self.tblFunctions.setItem(index, col_CheckBox, cbx)
|
||||
self.tblFunctions.setItem(index, col_Address, QTableWidgetItem(address))
|
||||
self.tblFunctions.setItem(index, col_Name, QTableWidgetItem(function))
|
||||
|
||||
# virtual
|
||||
def on_start_clicked(self):
|
||||
log_info("BASE - on_start_clicked")
|
||||
|
||||
def on_search_textchanged(self, text):
|
||||
self.filter_text = text
|
||||
self.filter_timer.start(FILTER_DEBOUNCE_PERIOD)
|
||||
|
||||
# TODO(Gilad): Convert this to use Qt Model/View architecture (QAbstractItemView)
|
||||
def filter_items(self):
|
||||
filter = self.filter_text.lower()
|
||||
rowCount = self.tblFunctions.rowCount()
|
||||
colCount = self.tblFunctions.columnCount()
|
||||
for row in range(rowCount):
|
||||
match = False
|
||||
for col in range(1, colCount):
|
||||
item = self.tblFunctions.item(row, col)
|
||||
if item.text().lower().find(filter) != -1:
|
||||
match = True
|
||||
break
|
||||
self.tblFunctions.setRowHidden(row, not match)
|
||||
```
|
||||
|
||||
`source/ida_migrator/plugin.py`:
|
||||
|
||||
```py
|
||||
import os
|
||||
|
||||
import idaapi
|
||||
|
||||
from PyQt5.Qt import qApp
|
||||
from PyQt5.QtCore import QObject
|
||||
|
||||
from ida_migrator import VERSION, PLUGIN_NAME
|
||||
from ida_migrator.utility import log_info
|
||||
from ida_migrator.intro_dialog import IntroDialog
|
||||
|
||||
|
||||
class IdaMigratorPlugin(QObject, idaapi.plugin_t):
|
||||
|
||||
flags = idaapi.PLUGIN_FIX
|
||||
comment = "Migrating from one database instance to another."
|
||||
help = "help"
|
||||
wanted_name = PLUGIN_NAME
|
||||
wanted_hotkey = "Ctrl-Shift-D"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
QObject.__init__(self, *args, **kwargs)
|
||||
idaapi.plugin_t.__init__(self)
|
||||
self._intro_dialog = None
|
||||
|
||||
def init(self):
|
||||
log_info("Successfully loaded plugin - v{}", VERSION)
|
||||
return idaapi.PLUGIN_KEEP
|
||||
|
||||
def run(self, arg):
|
||||
self._intro_dialog = IntroDialog(qApp.activeWindow())
|
||||
self._intro_dialog.setWindowTitle("{} - v{}".format(PLUGIN_NAME, VERSION))
|
||||
self._intro_dialog.show()
|
||||
|
||||
def term(self):
|
||||
pass
|
||||
|
||||
```
|
||||
|
||||
`source/ida_migrator/ui/IntroDialog.ui`:
|
||||
|
||||
```ui
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>IntroDialog</class>
|
||||
<widget class="QDialog" name="IntroDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>393</width>
|
||||
<height>194</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>IDA Migrator</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="lblIntro">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>IDA Migrator allows you to easily migrate function names, structures and enums from one database instance to another.</p><p><a href="https://github.com/giladreich/ida_migrator"><span style=" text-decoration: underline; color:#0000ff;">github.com/giladreich/ida_migrator</span></a></p><p>What would you like to do?</p></body></html></string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnExport">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>MS Shell Dlg 2</family>
|
||||
<pointsize>12</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Export</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnImport">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>MS Shell Dlg 2</family>
|
||||
<pointsize>12</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Import</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnCancel">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>MS Shell Dlg 2</family>
|
||||
<pointsize>12</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Cancel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>btnCancel</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>IntroDialog</receiver>
|
||||
<slot>close()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>352</x>
|
||||
<y>100</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>394</x>
|
||||
<y>120</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
|
||||
```
|
||||
|
||||
`source/ida_migrator/ui/MigratorDialog.ui`:
|
||||
|
||||
```ui
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MigratorDialog</class>
|
||||
<widget class="QDialog" name="MigratorDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>467</width>
|
||||
<height>344</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>XXXX</string>
|
||||
</property>
|
||||
<property name="sizeGripEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="lblTitle">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>11</pointsize>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
<underline>false</underline>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Functions to XXXX:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="tbxSearch">
|
||||
<property name="placeholderText">
|
||||
<string>Search...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTableWidget" name="tblFunctions">
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QAbstractScrollArea::AdjustToContents</enum>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderCascadingSectionResizes">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderCascadingSectionResizes">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Address</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnStart">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>XXXX</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnCancel">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Cancel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>btnCancel</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>MigratorDialog</receiver>
|
||||
<slot>close()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>436</x>
|
||||
<y>324</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>465</x>
|
||||
<y>306</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
|
||||
```
|
||||
|
||||
`source/ida_migrator/utility.py`:
|
||||
|
||||
```py
|
||||
from ida_migrator import PLUGIN_NAME
|
||||
|
||||
# log_info("Message: {} - {}", "Hello", 123)
|
||||
def log_info(fmt, *args):
|
||||
print("[{}]: {}".format(PLUGIN_NAME, fmt.format(*args)))
|
||||
|
||||
```
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,943 @@
|
||||
Project Path: arc_gmh5225_ida_bitfields_4gk4frxe
|
||||
|
||||
Source Tree:
|
||||
|
||||
```txt
|
||||
arc_gmh5225_ida_bitfields_4gk4frxe
|
||||
├── CMakeLists.txt
|
||||
├── LICENSE
|
||||
├── plugin.cpp
|
||||
└── readme.md
|
||||
|
||||
```
|
||||
|
||||
`CMakeLists.txt`:
|
||||
|
||||
```txt
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
|
||||
project(ida_bitfields)
|
||||
|
||||
include(FetchContent)
|
||||
|
||||
FetchContent_Declare(
|
||||
HexSuite
|
||||
GIT_REPOSITORY https://github.com/can1357/hexsuite
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(HexSuite)
|
||||
|
||||
add_library(bitfields SHARED ${CMAKE_CURRENT_SOURCE_DIR}/plugin.cpp)
|
||||
|
||||
target_compile_features(bitfields PRIVATE cxx_std_20)
|
||||
|
||||
target_include_directories(bitfields SYSTEM PRIVATE
|
||||
${hexsuite_SOURCE_DIR}
|
||||
$ENV{IDA_PATH}/sdk/include
|
||||
$ENV{IDA_PATH}/plugins/hexrays_sdk/include)
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
||||
set(IDA_SDK_LIB_OS "win_vc")
|
||||
elseif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
set(IDA_SDK_LIB_OS "mac_clang")
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
set(IDA_SDK_LIB_OS "linux_gcc")
|
||||
else()
|
||||
message(FATAL_ERROR "unrecognized system: ${CMAKE_SYSTEM_NAME} (Windows/Darwin/Linux expected)")
|
||||
endif()
|
||||
|
||||
if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm")
|
||||
set(IDA_SDK_LIB_ARCH "arm64")
|
||||
else()
|
||||
set(IDA_SDK_LIB_ARCH "x64")
|
||||
endif()
|
||||
|
||||
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8")
|
||||
set(IDA_SDK_LIB_BITS "64")
|
||||
set_target_properties(bitfields PROPERTIES OUTPUT_NAME "bitfields64")
|
||||
else()
|
||||
set(IDA_SDK_LIB_BITS "32")
|
||||
endif()
|
||||
|
||||
target_link_libraries(bitfields PRIVATE "ida.lib" "pro.lib")
|
||||
target_link_directories(bitfields PRIVATE $ENV{IDA_PATH}/sdk/lib/${IDA_SDK_LIB_ARCH}_${IDA_SDK_LIB_OS}_${IDA_SDK_LIB_BITS})
|
||||
|
||||
```
|
||||
|
||||
`LICENSE`:
|
||||
|
||||
```
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
||||
```
|
||||
|
||||
`plugin.cpp`:
|
||||
|
||||
```cpp
|
||||
#include <hexsuite.hpp>
|
||||
|
||||
struct access_info
|
||||
{
|
||||
cexpr_t* underlying_expr = nullptr;
|
||||
uint64_t mask;
|
||||
ea_t ea;
|
||||
uint8_t shift_value;
|
||||
|
||||
explicit operator bool() const { return underlying_expr != nullptr; }
|
||||
};
|
||||
|
||||
// makes sure that the immediate / cot_num is on the right hand side
|
||||
inline std::pair<cexpr_t*, cexpr_t*> normalize_binop( cexpr_t* expr )
|
||||
{
|
||||
const auto num = expr->find_num_op();
|
||||
return { expr->theother( num ), num ? num : expr->y };
|
||||
}
|
||||
|
||||
inline void replace_or_delete( cexpr_t* expr, cexpr_t* replacement, bool success )
|
||||
{
|
||||
if ( !replacement )
|
||||
return;
|
||||
|
||||
if ( success )
|
||||
expr->replace_by( replacement );
|
||||
else
|
||||
delete replacement;
|
||||
}
|
||||
|
||||
inline void merge_accesses( cexpr_t*& original, cexpr_t* access, ctype_t op, ea_t ea, tinfo_t type )
|
||||
{
|
||||
if ( !access )
|
||||
return;
|
||||
|
||||
if ( !original )
|
||||
original = access;
|
||||
else
|
||||
{
|
||||
original = new cexpr_t( op, original, access );
|
||||
original->type = std::move( type );
|
||||
original->exflags = 0;
|
||||
original->ea = ea;
|
||||
}
|
||||
}
|
||||
|
||||
// used for the allocation of helper names
|
||||
inline char* alloc_cstr( const char* str )
|
||||
{
|
||||
const auto len = strlen( str ) + 1;
|
||||
auto alloc = hexrays_alloc( len );
|
||||
if ( alloc )
|
||||
memcpy( alloc, str, len );
|
||||
return ( char* ) alloc;
|
||||
}
|
||||
|
||||
// selects (adds memref expr) for the first member that is a struct inside of an union
|
||||
inline void select_first_union_field( cexpr_t*& expr )
|
||||
{
|
||||
if ( !expr->type.is_union() )
|
||||
return;
|
||||
|
||||
udt_member_t member;
|
||||
for ( int i = 0; ; ++i )
|
||||
{
|
||||
member.offset = i;
|
||||
if ( expr->type.find_udt_member( &member, STRMEM_INDEX ) == -1 )
|
||||
break;
|
||||
|
||||
if ( !member.type.is_struct() )
|
||||
continue;
|
||||
|
||||
expr = new cexpr_t( cot_memref, expr );
|
||||
expr->type = member.type;
|
||||
expr->m = i;
|
||||
expr->exflags = 0;
|
||||
expr->ea = expr->x->ea;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
inline cexpr_t* create_bitfield_access( access_info& info, udt_member_t& member, ea_t original_ea, tinfo_t& common_type )
|
||||
{
|
||||
func_type_data_t data;
|
||||
data.flags = FTI_PURE;
|
||||
data.rettype = member.size == 1 ? tinfo_t{ BTF_BOOL } : common_type;
|
||||
data.cc = CM_CC_UNKNOWN;
|
||||
data.push_back( funcarg_t{ .type = info.underlying_expr->type } );
|
||||
data.push_back( funcarg_t{ .type = common_type } );
|
||||
|
||||
tinfo_t functype;
|
||||
if ( !functype.create_func( data ) )
|
||||
{
|
||||
msg( "[bitfields] failed to create a bitfield access function type.\n" );
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// construct the callable
|
||||
auto call_fn = new cexpr_t();
|
||||
call_fn->op = cot_helper;
|
||||
call_fn->type = functype;
|
||||
call_fn->exflags = 0;
|
||||
call_fn->helper = alloc_cstr( "b" );
|
||||
|
||||
// construct the call args
|
||||
auto call_args = new carglist_t( std::move( functype ) );
|
||||
|
||||
call_args->push_back( carg_t{} );
|
||||
auto& arg0 = ( *call_args )[ 0 ];
|
||||
static_cast< cexpr_t& >( arg0 ) = *info.underlying_expr;
|
||||
arg0.ea = info.ea;
|
||||
|
||||
call_args->push_back( carg_t{} );
|
||||
auto& arg1 = ( *call_args )[ 1 ];
|
||||
arg1.op = cot_helper;
|
||||
arg1.type = common_type;
|
||||
arg1.exflags = EXFL_ALONE;
|
||||
arg1.helper = alloc_cstr( member.name.c_str() );
|
||||
|
||||
// construct the call / access itself
|
||||
auto access = new cexpr_t( cot_call, call_fn );
|
||||
access->type = member.size == 1 ? tinfo_t{ BTF_BOOL } : common_type;
|
||||
access->exflags = 0;
|
||||
access->a = call_args;
|
||||
access->ea = original_ea;
|
||||
|
||||
return access;
|
||||
}
|
||||
|
||||
inline uint64_t bitfield_access_mask( udt_member_t& member )
|
||||
{
|
||||
uint64_t mask = 0;
|
||||
for ( int i = member.offset; i < member.offset + member.size; ++i )
|
||||
mask |= ( 1ull << i );
|
||||
return mask;
|
||||
}
|
||||
|
||||
// executes callback for each member in `type` where its offset coincides with `and_mask`.
|
||||
// `cmp_mask` is used to calculate enabled bits in the bitfield.
|
||||
template<class Callback> bool for_each_bitfield( Callback cb, tinfo_t type, uint64_t and_mask )
|
||||
{
|
||||
udt_member_t member;
|
||||
for ( size_t i = 0; i < 64; ++i )
|
||||
{
|
||||
if ( !( and_mask & ( 1ull << i ) ) )
|
||||
continue;
|
||||
|
||||
member.offset = i;
|
||||
if ( type.find_udt_member( &member, STRMEM_OFFSET ) == -1 )
|
||||
continue;
|
||||
|
||||
if ( !member.is_bitfield() )
|
||||
continue;
|
||||
|
||||
if ( member.offset != i )
|
||||
continue;
|
||||
|
||||
uint64_t mask = bitfield_access_mask( member );
|
||||
if ( member.size != 1 && ( and_mask & mask ) != mask )
|
||||
{
|
||||
msg( "[bitfields] bad offset (%ull) and size (%ull) combo of a field for given mask (%ull)\n", member.offset, member.size, and_mask );
|
||||
return false;
|
||||
}
|
||||
|
||||
cb( member );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// handles various cases of potential bitfield access.
|
||||
// * (*(type*)&x >> imm1) & imm2
|
||||
// * *(type*)&x & imm
|
||||
// * HIDWORD(*(type*)&x)
|
||||
inline access_info unwrap_access( cexpr_t* expr )
|
||||
{
|
||||
access_info res;
|
||||
if ( expr->op == cot_band )
|
||||
{
|
||||
auto num = expr->find_num_op();
|
||||
if ( !num )
|
||||
return res;
|
||||
|
||||
res.mask = num->n->_value;
|
||||
res.shift_value = 0;
|
||||
expr = expr->theother( num );
|
||||
}
|
||||
else if ( expr->op == cot_call )
|
||||
{
|
||||
if ( expr->x->op != cot_helper || expr->a->size() != 1 )
|
||||
return res;
|
||||
|
||||
constexpr static std::tuple<std::string_view, uint64_t, uint8_t> functions[] = {
|
||||
{"LOBYTE", 0x00'00'00'00'00'00'00'FF, 0 * 8},
|
||||
{"LOWORD", 0x00'00'00'00'00'00'FF'FF, 0 * 8},
|
||||
{"LODWORD", 0x00'00'00'00'FF'FF'FF'FF, 0 * 8},
|
||||
{"HIBYTE", 0xFF'00'00'00'00'00'00'00, 7 * 8},
|
||||
{"HIWORD", 0xFF'FF'00'00'00'00'00'00, 6 * 8},
|
||||
{"HIDWORD", 0xFF'FF'FF'FF'00'00'00'00, 4 * 8},
|
||||
{"BYTE1", 0x00'00'00'00'00'00'FF'00, 1 * 8},
|
||||
{"BYTE2", 0x00'00'00'00'00'FF'00'00, 2 * 8},
|
||||
{"BYTE3", 0x00'00'00'00'FF'00'00'00, 3 * 8},
|
||||
{"BYTE4", 0x00'00'00'FF'00'00'00'00, 4 * 8},
|
||||
{"BYTE5", 0x00'00'FF'00'00'00'00'00, 5 * 8},
|
||||
{"BYTE6", 0x00'FF'00'00'00'00'00'00, 6 * 8},
|
||||
{"WORD1", 0x00'00'00'00'FF'FF'00'00, 2 * 8},
|
||||
{"WORD2", 0x00'00'FF'FF'00'00'00'00, 4 * 8},
|
||||
};
|
||||
|
||||
// check if it's one of the functions we care for
|
||||
auto it = std::ranges::find( functions, expr->x->helper, [ ] ( auto&& func ) { return std::get<0>( func ); } );
|
||||
if ( it == std::end( functions ) )
|
||||
return res;
|
||||
|
||||
expr = &( *expr->a )[ 0 ];
|
||||
res.mask = std::get<1>( *it );
|
||||
res.shift_value = std::get<2>( *it );
|
||||
}
|
||||
else
|
||||
return res;
|
||||
|
||||
if ( expr->op == cot_ushr )
|
||||
{
|
||||
auto shiftnum = expr->find_num_op();
|
||||
if ( !shiftnum )
|
||||
return res;
|
||||
|
||||
expr = expr->theother( shiftnum );
|
||||
if ( res.shift_value == 0 )
|
||||
res.mask <<= shiftnum->n->_value;
|
||||
|
||||
res.shift_value += ( uint8_t ) shiftnum->n->_value;
|
||||
}
|
||||
|
||||
if ( expr->op != cot_ptr || expr->x->op != cot_cast || expr->x->x->op != cot_ref )
|
||||
return res;
|
||||
|
||||
res.underlying_expr = expr->x->x->x;
|
||||
|
||||
// extract the ea from one of the expression parts for union selection to work
|
||||
// thanks to @RolfRolles for help with making it work
|
||||
ea_t use_ea = expr->x->x->ea;
|
||||
use_ea = use_ea != BADADDR ? use_ea : expr->x->ea;
|
||||
use_ea = use_ea != BADADDR ? use_ea : expr->ea;
|
||||
if ( use_ea == BADADDR )
|
||||
msg( "[bitfields] can't find parent ea - won't be able to save union selection\n" );
|
||||
|
||||
res.ea = use_ea;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
inline void handle_comparison( cexpr_t* expr )
|
||||
{
|
||||
auto [eq, eq_num] = normalize_binop( expr );
|
||||
if ( eq_num->op != cot_num )
|
||||
return;
|
||||
|
||||
auto info = unwrap_access( eq );
|
||||
if ( !info )
|
||||
return;
|
||||
|
||||
cexpr_t* replacement = nullptr;
|
||||
auto success = for_each_bitfield(
|
||||
[ &, eq_num = eq_num ] ( udt_member_t& member )
|
||||
{
|
||||
// construct the call / access itself
|
||||
auto access = create_bitfield_access( info, member, expr->ea, eq_num->type );
|
||||
if ( !access )
|
||||
return;
|
||||
|
||||
const auto mask = bitfield_access_mask( member );
|
||||
const auto value = ( ( eq_num->n->_value << info.shift_value ) & mask ) >> member.offset;
|
||||
|
||||
// if the flag is multi byte, reconstruct the comparison
|
||||
if ( member.size > 1 )
|
||||
{
|
||||
auto num = new cnumber_t();
|
||||
num->assign( value, access->type.get_size(), member.type.is_signed() ? type_signed : type_unsigned );
|
||||
|
||||
auto num_expr = new cexpr_t();
|
||||
num_expr->op = cot_num;
|
||||
num_expr->type = access->type;
|
||||
num_expr->n = num;
|
||||
num_expr->exflags = 0;
|
||||
|
||||
access = new cexpr_t( expr->op, access, num_expr );
|
||||
access->type = tinfo_t{ BTF_BOOL };
|
||||
access->exflags = 0;
|
||||
access->ea = expr->ea;
|
||||
}
|
||||
// otherwise the flag is single bit; if the flag is disabled, add logical not
|
||||
else if ( value ^ ( expr->op == cot_eq ) )
|
||||
{
|
||||
access = new cexpr_t( cot_lnot, access );
|
||||
access->type = tinfo_t{ BTF_BOOL };
|
||||
access->exflags = 0;
|
||||
access->ea = expr->ea;
|
||||
}
|
||||
|
||||
merge_accesses( replacement, access, cot_land, expr->ea, tinfo_t{ BTF_BOOL } );
|
||||
}, info.underlying_expr->type, info.mask );
|
||||
|
||||
replace_or_delete( expr, replacement, success );
|
||||
}
|
||||
|
||||
inline void handle_assignment( cexpr_t* expr )
|
||||
{
|
||||
auto rhs = expr->y;
|
||||
auto info = unwrap_access( rhs );
|
||||
if ( !info )
|
||||
return;
|
||||
|
||||
cexpr_t* replacement = nullptr;
|
||||
auto success = for_each_bitfield(
|
||||
[ & ] ( udt_member_t& member )
|
||||
{
|
||||
// TODO: for assignment where more than 1 field is being accessed create a new bitfield type for the result
|
||||
// that would contain the correctly masked and shifted fields
|
||||
const auto access = create_bitfield_access( info, member, expr->y->ea, expr->x->type );
|
||||
merge_accesses( replacement, access, cot_bor, rhs->ea, expr->x->type );
|
||||
}, info.underlying_expr->type, info.mask );
|
||||
|
||||
replace_or_delete( expr->y, replacement, success );
|
||||
}
|
||||
|
||||
// match special bit functions
|
||||
inline void handle_call( cexpr_t* expr )
|
||||
{
|
||||
constexpr static size_t num_bitmask_funcs = 8;
|
||||
constexpr static std::string_view functions[] = {
|
||||
// bit mask functions
|
||||
"_InterlockedOr8",
|
||||
"_InterlockedOr16",
|
||||
"_InterlockedOr",
|
||||
"_InterlockedOr64",
|
||||
"_InterlockedAnd8",
|
||||
"_InterlockedAnd16",
|
||||
"_InterlockedAnd",
|
||||
"_InterlockedAnd64",
|
||||
// bit index functions
|
||||
"_bittest",
|
||||
"_bittest64",
|
||||
"_bittestandreset",
|
||||
"_bittestandreset64",
|
||||
"_bittestandset",
|
||||
"_bittestandset64",
|
||||
"_interlockedbittestandset",
|
||||
"_interlockedbittestandset64"
|
||||
};
|
||||
|
||||
// we expect a helper whose name is one of special functions
|
||||
if ( expr->x->op != cot_helper )
|
||||
return;
|
||||
|
||||
// 2 args
|
||||
if ( expr->a->size() != 2 )
|
||||
return;
|
||||
|
||||
// (type*)& is expected for first arg
|
||||
cexpr_t* arg0 = &( *expr->a )[ 0 ];
|
||||
if ( arg0->op != cot_cast || arg0->x->op != cot_ref )
|
||||
return;
|
||||
|
||||
// second arg has to be a number
|
||||
auto& arg1 = ( *expr->a )[ 1 ];
|
||||
if ( arg1.op != cot_num )
|
||||
return;
|
||||
|
||||
// these functions will reference the union directly, so select a field for a start
|
||||
select_first_union_field( arg0->x->x );
|
||||
arg0 = arg0->x->x;
|
||||
|
||||
// check if it's one of the functions we care for
|
||||
auto it = std::ranges::find( functions, expr->x->helper );
|
||||
if ( it == std::end( functions ) )
|
||||
return;
|
||||
|
||||
auto mask = arg1.n->_value;
|
||||
|
||||
// if it's a bitmask function make the mask 1 << n
|
||||
if ( std::distance( functions, it ) >= num_bitmask_funcs )
|
||||
mask = ( 1ull << mask );
|
||||
|
||||
cexpr_t* replacement = nullptr;
|
||||
bool success = for_each_bitfield(
|
||||
[ & ] ( udt_member_t& member )
|
||||
{
|
||||
auto helper = new cexpr_t();
|
||||
helper->op = cot_helper;
|
||||
helper->type = arg1.type;
|
||||
helper->ea = arg1.ea;
|
||||
helper->exflags = EXFL_ALONE;
|
||||
helper->helper = alloc_cstr( member.name.c_str() );
|
||||
|
||||
merge_accesses( replacement, helper, cot_bor, arg1.ea, arg1.type );
|
||||
}, arg0->type, mask );
|
||||
|
||||
replace_or_delete( &arg1, replacement, success );
|
||||
}
|
||||
|
||||
inline auto bitfields_optimizer = hex::hexrays_callback_for<hxe_maturity>(
|
||||
[ ] ( cfunc_t* cfunc, ctree_maturity_t maturity )->ssize_t
|
||||
{
|
||||
if ( maturity != CMAT_FINAL )
|
||||
return 0;
|
||||
|
||||
struct visitor : ctree_visitor_t
|
||||
{
|
||||
visitor() : ctree_visitor_t( CV_FAST ) {}
|
||||
|
||||
int idaapi visit_expr( cexpr_t* expr ) override
|
||||
{
|
||||
if ( expr->op == cot_eq || expr->op == cot_ne )
|
||||
handle_comparison( expr );
|
||||
else if ( expr->op == cot_call )
|
||||
handle_call( expr );
|
||||
else if ( expr->op == cot_asg )
|
||||
handle_assignment( expr );
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
visitor{}.apply_to( &cfunc->body, nullptr );
|
||||
|
||||
return 0;
|
||||
} );
|
||||
|
||||
struct bitfields : plugmod_t
|
||||
{
|
||||
netnode nn = { "$ bitfields", 0, true };
|
||||
|
||||
void set_state( bool s )
|
||||
{
|
||||
bitfields_optimizer.set_state( s );
|
||||
}
|
||||
bitfields()
|
||||
{
|
||||
set_state( nn.altval( 0 ) == 0 );
|
||||
}
|
||||
~bitfields()
|
||||
{
|
||||
bitfields_optimizer.uninstall();
|
||||
}
|
||||
|
||||
bool run( size_t ) override
|
||||
{
|
||||
constexpr const char* format = R"(
|
||||
AUTOHIDE NONE
|
||||
bitfields plugin for Hex-Rays decompiler.
|
||||
State: %s)";
|
||||
int code = ask_buttons( "~E~nable", "~D~isable", "~C~lose", -1, format + 1, nn.altval( 0 ) == 0 ? "Enabled" : "Disabled" );
|
||||
if ( code < 0 )
|
||||
return true;
|
||||
nn.altset( 0, code ? 0 : 1 );
|
||||
set_state( code );
|
||||
return true;
|
||||
}
|
||||
};
|
||||
plugin_t PLUGIN = { IDP_INTERFACE_VERSION, PLUGIN_MULTI, hex::init_hexray<bitfields>, nullptr, nullptr, "bitfields", nullptr, "bitfields", nullptr, };
|
||||
```
|
||||
|
||||
`readme.md`:
|
||||
|
||||
```md
|
||||
# ida bitfields
|
||||
|
||||
A simple IDA Pro plugin to make bitfields and bitflags in them easier to reason about.
|
||||
|
||||
It hasn't yet been tested very much and will have some rough edges, but it works well enough for me. Feel free to open an issue if something is wrong.
|
||||
|
||||
## Example
|
||||
|
||||
### Before
|
||||
|
||||

|
||||
|
||||
### After
|
||||
|
||||

|
||||
|
||||
## Usage
|
||||
|
||||
Plugin will either automatically convert the code or you will need to switch which union field is selected to the one that contains the bitfields.
|
||||
|
||||
To do that right click on the union member and and perform `select union field` (`alt + y`).
|
||||
|
||||
## Installation
|
||||
|
||||
Copy the DLLs to the plugins folder in your IDA installation directory.
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
Huge thanks to [@RolfRolles](https://github.com/rolfrolles) for help and feedback, and [@can1357](http://github.com/can1357) for his HexSuite/NtRays projects which essentially gave me the motivation to write this plugin.
|
||||
|
||||
```
|
||||
Reference in New Issue
Block a user