diff --git a/.github/workflows/build_docs.yml b/.github/workflows/build_docs.yml new file mode 100644 index 00000000..07e05ee9 --- /dev/null +++ b/.github/workflows/build_docs.yml @@ -0,0 +1,48 @@ +name: Deploy Sphinx Docs to GitHub Pages + +on: + push: + tags: + - "v*.*" + - "v*.*.*" + workflow_dispatch: + +jobs: + deploy: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v3 + with: + python-version: '3.12' + + - name: Install dependencies + run: | + sudo apt install libegl1 libgirepository1.0-dev + python3 -m venv .venv/PINCE + . .venv/PINCE/bin/activate + pip3 install --upgrade pip + pip3 install -r requirements.txt + + - name: Install Sphinx + run: | + cd docs + ./install_sphinx.sh + + - name: Build docs + run: | + cd docs + ./build_html.sh + + - name: Deploy to GitHub Pages + uses: peaceiris/actions-gh-pages@v4 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./docs/build/html + publish_branch: gh-pages + force_orphan: true + keep_files: false diff --git a/.github/workflows/release_appimage.yml b/.github/workflows/release_appimage.yml new file mode 100644 index 00000000..88757849 --- /dev/null +++ b/.github/workflows/release_appimage.yml @@ -0,0 +1,25 @@ +name: Release AppImage + +on: + push: + tags: + - "v*.*" + - "v*.*.*" + +jobs: + build: + runs-on: ubuntu-22.04 + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Install Dependencies + run: sudo apt install libfuse2 libmpc-dev libmpfr-dev gmpc-dev appstream qt6-l10n-tools libcairo2-dev libgirepository1.0-dev patchelf + - name: Build + run: | + export ARCH=x86_64 + ./ci/package.sh + - name: Release + uses: softprops/action-gh-release@v2 + with: + make_latest: true + files: ci/PINCE-x86_64.AppImage diff --git a/.gitignore b/.gitignore index d924e09a..1e453ceb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,17 @@ __pycache__/ .idea/* .vscode/* -libscanmem.so* +!.vscode/launch.json +!.vscode/settings.json libpince/libscanmem/* +libpince/libptrscan/* +docs/build/* *.directory *.lprof +*.qm *.py[cod] *$py.class gdb_pince/* +*.gnbs.conf +.venv/* +*.AppImage diff --git a/.gitmodules b/.gitmodules index 1f513c4c..9e555db3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ -[submodule "scanmem"] - path = scanmem - url = https://github.com/scanmem/scanmem.git +[submodule "libscanmem-PINCE"] + path = libscanmem-PINCE + url = https://github.com/brkzlr/libscanmem-PINCE diff --git a/.travis.yml b/.travis.yml index aedb5ea1..c9499fe5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,7 +34,7 @@ before_install: - wget https://bootstrap.pypa.io/get-pip.py - sudo python3 get-pip.py - - sudo pip3 install psutil pexpect distorm3 pygdbmi + - sudo pip3 install pexpect distorm3 pygdbmi - sudo mv /usr/bin/lsb_release_masked /usr/bin/lsb_release diff --git a/.vimspector.json b/.vimspector.json new file mode 100644 index 00000000..8955a556 --- /dev/null +++ b/.vimspector.json @@ -0,0 +1,36 @@ +{ + "configurations": { + "Debug PINCE": { + "adapter": "debugpy", + "default": true, + "variables": { + "Python": { + "shell": "/bin/sh -c 'if [ -z \"${dollar}VIRTUAL_ENV\" ]; then echo $$(which python3); else echo \"${dollar}VIRTUAL_ENV/bin/python\"; fi'" + } + }, + "filetypes": [ "python" ], + "configuration": { + "name": "Debug PINCE", + "type": "python", + "request": "launch", + "cwd": "${workspaceFolder}", + "python": "$Python", + "stopOnEntry": false, + "justMyCode": false, + "sudo": true, + "console": "integratedTerminal", + "program": "${workspaceFolder}/PINCE.py", + "env": { + "PATH": "${PATH}" + } + }, + "breakpoints": { + "exception": { + "uncaught": "Y", + "raised": "N", + "userUnhandled": "N" + } + } + } + } +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..c9a2cf62 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,20 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Debug PINCE", + "type": "python", + "request": "launch", + "cwd": "${workspaceFolder}", + "stopOnEntry": false, + "justMyCode": false, + "sudo": true, + "console": "integratedTerminal", + "program": "${workspaceFolder}/PINCE.py", + "python": "${workspaceFolder}/.venv/PINCE/bin/python3" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..945f0dd4 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,14 @@ +{ + "[python]": { + "diffEditor.ignoreTrimWhitespace": false, + "editor.formatOnType": true, + "editor.wordBasedSuggestions": "off", + "editor.defaultFormatter": "ms-python.black-formatter", + "editor.formatOnSave": true, + "editor.formatOnSaveTimeout": 2000 + }, + "black-formatter.args": [ + "--line-length=120" + ], + "editor.tabSize": 4 +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..d0571dac --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,192 @@ +# Code Structure +- [PINCE.py](./PINCE.py) - The main file, it contains everything from GUI logic to libpince communication. A chonky boi, will be trimmed in the future +- [PINCE.sh](./PINCE.sh) - Launch script +- [install.sh](./install.sh) - Installation script +- [compile_ts.sh](./compile_ts.sh) - Gathers translation information from various sources and compiles them into ts files +- [fix_ts.py](./fix_ts.py) - Fixes line information issue, used within [compile_ts.sh](./compile_ts.sh) +- [compile_gdb.sh](./compile_gdb.sh) - PINCE normally uses system GDB but in cases where system GDB is unavailable, this script is used to compile GDB locally +- [GUI](./GUI) - Contains Qt Designer forms and their respective codes along with utility functions and custom Qt classes +- [media](./media) - Contains media files such as logos and icons +- [tr](./tr) - Contains translation constants +- [docs](./docs) - Contains Sphinx documentation. The build files are automatically generated in the `gh-pages` branch. +- [i18n](./i18n) - Contains translation files. `ts` files are created with Qt Linguist and [compile_ts.sh](./compile_ts.sh), `qm` files are created within the last section of [install.sh](./install.sh) +- ### **[libpince](./libpince)** + - [debugcore.py](./libpince/debugcore.py) - Everything related to communicating with GDB and debugging + - [utils.py](./libpince/utils.py) - Contains generic utility functions such as parsing, file creation, process querying etc + - [typedefs.py](./libpince/typedefs.py) - Contains all constants and variable definitions + - [regexes.py](./libpince/regexes.py) - Contains regexes for parsing GDB output and other things + - [injection](./libpince/injection) - An example for injecting .so files + - ### **[gdb_python_scripts](./libpince/gdb_python_scripts)** + - [gdbextensions.py](./libpince/gdb_python_scripts/gdbextensions.py) - Contains custom GDB commands + - [gdbutils.py](./libpince/gdb_python_scripts/gdbutils.py) - Contains utility functions for GDB commands + - [tests](./libpince/gdb_python_scripts/tests) - An example for .so extension, read more [here](https://github.com/korcankaraokcu/PINCE/wiki/Extending-PINCE-with-.so-files) + +# Code Style +Formatting style is [Black](https://github.com/psf/black) defaults, except line length is 120. You can use Black without parameters since we already use `pyproject.toml` for this setting. + +***You must format the files you changed using Black before you open a PR!*** Please do not format automatically generated files under GUI folder. Your changes will be overwritten by Qt Designer. More info at [UI Files](#ui-files) +- Max characters per line: 120 +- Variable naming for libpince: + - Classes: PascalCase + - Class members: snake_case + - Variables: snake_case + - Functions: snake_case + - Constants: SCREAMING_SNAKE_CASE + - Modules: flatcase + - Standalone scripts: snake_case +- Variable naming for Qt: + - Classes: PascalCase + - Class members: + - non-Qt: snake_case + - Qt: objectType + PascalCase + For example: `keySequenceEdit_Hotkey` in [PINCE.py](./PINCE.py) + - Variables: snake_case + - Functions: + - non-Qt: snake_case + - Qt: objectName + snake_case + Here's an example: `keySequenceEdit_Hotkey_key_sequence_changed` in [PINCE.py](./PINCE.py) + - Constants: SCREAMING_SNAKE_CASE + - Modules: PascalCase + - Standalone scripts: snake_case + +For convenience, I'm using auto-format tool of vscode. Any modern IDE will most likely have an auto-formatting tool. +Readability and being clear is the most important aspect, so if you decide to not follow the rules, make sure that your code still reads nice and plays well with others. +If you feel unsure to which naming convention you should use, try to check out similar patterns in the code or just ask away in the PINCE discord server! + +The reason behind Qt class member naming convention is that when this project first started, supported python version didn't have type hints. +So, to have an idea about the type of the variable we are working with, I've come up with that naming idea. It's an old habit if anything. +It could maybe replaced with something else after a refactorization + +About the max characters per line, I used to use PyCharm when I first started this project years ago. 120 characters is a limit brought by PyCharm, I've quit using PyCharm eventually but I think the limit makes the code look quite nice. Black suggests a limit of 88 characters, which is a bit short to be frank. So I think it's good to keep this old habit, especially considering that docstrings have also followed this rule for a long time now. This limit for docstrings however, is not strict at all. A few characters passing the limit is ok, sometimes going for a newline messes up the readability, trust your guts and decide for yourself + +# Documentation +We use Google style documentation and type hints. A good example would be `get_breakpoints_in_range` function in [debugcore.py](./libpince/debugcore.py). Root folder of libpince has 100% documentation coverage so a pull request regarding libpince has to be documented. For other places, it's enough to document the parts you think that'd be confusing to read later on. You are not obliged to document everything in other places as we are also quite lax with it + +We use Sphinx to automatically generate html files from the docs and napoleon extension to convert Google style docs to reStructuredText. To test locally, `cd` into the [docs](./docs) directory and execute `sh install_sphinx.sh`. This will install Sphinx and its extensions within the venv. After this, You can modify [source files](./docs/source) and then build html files with `sh build_html.sh` to test your changes. To create source files for multiple modules automatically, `sphinx-apidoc` can be used. For single modules, you can edit the source files manually (like I did with `guiutils`) + +[build_docs.yml](.github/workflows/build_docs.yml) workflow is responsible for automatic html generation, it gets triggered automatically whenever there's a new release or manually whenever necessary. The workflow generates files within the `gh-pages` branch. It's an orphaned branch so it can be deleted without affecting the history + +# UI Files +You need to have [Qt6 Designer](https://pkgs.org/search/?q=designer&on=files) and [pyuic6](https://pkgs.org/search/?q=pyuic6&on=files) installed. If there are no available packages for your distro, install [pyqt6-tools](https://pypi.org/project/pyqt6-tools/) instead + +Follow the steps below: +- Edit or create ui files with the designer and then save them +- After saving the files, use pyuic6 to convert them into py files: `pyuic6 SomeDialog.ui -o SomeDialog.py` + +The py files that contains the same name with the ui files are auto-generated, please edit the ui files with designer instead of messing with the py files + +# Translation +You need to have [Qt6 Linguist](https://pkgs.org/search/?q=linguist&on=files) and [pylupdate6](https://pkgs.org/search/?q=pylupdate6&on=files) installed. If there are no available packages for your distro, install [pyqt6-tools](https://pypi.org/project/pyqt6-tools/) instead + +Follow the steps below: +- To create a new translation file, use [compile_ts.sh](./compile_ts.sh) with the locale as the parameter, such as `sh compile.sh ja_JP`. This will create a ts file with the locale you entered. +You can skip this step if you only want to edit already existing files +- Edit ts files in [/i18n/ts](./i18n/ts) with the linguist and then save them. After saving the files, run the [compile_ts.sh](./compile_ts.sh) script. +This script fixes inconsistencies between Qt6 Linguist and pylupdate6, also removes line information so the git history stays cleaner +- To test your translations, use [install.sh](./install.sh). The last part of the installation script also compiles ts files to qm files so PINCE can process them. +When asked to recompile libscanmem, enter no + +Make sure that you read the comments in [tr.py](./tr/tr.py). Some of the translations have caveats that might interest you + +About the untranslated parts of the code, such as context menus of libpince reference widget. You'll see that some of the code serves as a placeholder that'll be +removed or replaced in the future. These are not marked as translatable as translating them would be a waste of time + +**ATTENTION:** Make sure you read this part even if you aren't a translator: +If you create or delete any Qt related string (for example, ui forms or translation constants in [tr.py](./tr/tr.py)), you must run [compile_ts.sh](./compile_ts.sh) so it updates the translations. +Not every string has to be translatable, if it's only printed on console, it can stay as is, in English. If it's shown to the user within a form, it should be translatable + +# Logo +All logo requests should be posted in `/media/logo/your_username`. Instead of opening a new issue, pull request your logo files to that folder. +Your PR must include at least one png file named pince_small, pince_medium or pince_big, according to its size. So, a minimal PR will look like this: + +`/media/logo/your_username/pince_big.png` + +pince_big is interchangeable with pince_medium and pince_small +A full PR will look like this: +``` +/media/logo/your_username/pince_big.png +/media/logo/your_username/pince_medium.png +/media/logo/your_username/pince_small.png +``` + +# Notes +Here are some notes that explains some of the caveats and hacks, they also include a timestamp. As we upgrade the libraries and the methods we are working with, +some of these notes might become obsolete. You are free to test and provide solutions to these tricks + +- 28/08/2018 - All QMessageBoxes that's called from outside of their classes(via parent() etc.) must use 'QApplication.focusWidget()' instead of 'self' in their first parameter. +Refer to issue #57 for more information + +- 23/11/2018 - Don't use get_current_item or get_current_row within currentItemChanged or currentChanged signals. +Qt doesn't update selected rows on first currentChanged or currentItemChanged calls + +- 22/05/2023 - For QTableWidget and QTableView, disabling wordWrap and using ScrollPerPixel as the horizontal scroll mode can help the user experience. +Consider doing these when creating a new QTableWidget or QTableView + +- 15/02/2024 - Don't always trust the "Adjust Size" button of the Qt Designer, it might expand widgets much more than needed, especially for smaller widgets. Consider the use cases +and adjust manually. This also helps functions like `guiutils.center_to_parent` work properly + +- 13/05/2024 - Monospace font and `utils.upper_hex` function greatly improve readability if the text area includes hex data, consider using those when creating new text areas. Memory Viewer is a good example for this + +- 2/9/2018 - Seek methods of all file handles that read directly from the memory(/proc/pid/mem etc.) should be wrapped in a try/except block that catches both +OSError and ValueError exceptions. For instance: +```python + try: + self.memory.seek(start_addr) + except (OSError, ValueError): + break +``` +OSError handles I/O related errors and ValueError handles the off_t limit error that prints "cannot fit 'int' into an offset-sized integer" + +- 12/09/2018 - All namedtuples must have the same field name with their variable names. This makes the namedtuple transferable via pickle. For instance: +```python + tuple_examine_expression = collections.namedtuple("tuple_examine_expression", "all address symbol") +``` +- 06/10/2016 - HexView section of MemoryViewerWindow.ui: Changed listWidget_HexView_Address to tableWidget_HexView_Address in order to prevent possible future visual bugs. +Logically, it should stay as a listwidget considering its functionality. But it doesn't play nice with the other neighboring tablewidgets in different pyqt versions, +forcing me to use magic numbers for adjusting, which is a bit hackish + +# Roadmap +So, after learning how to contribute, you are wondering where to start now. You can either search for `TODO` within the code or pick up any task from the roadmap below. +These tasks are ordered by importance but feel free to pick any of them. Further details can be discussed in the PINCE discord server +- Implement libpince engine +- Implement multi-line code injection, this will also help with previously dropped inject_with_advanced_injection +- Libpince support for Mono and Java (symbol recognition, calling functions, dissect obj tree etc.) +- Move GUI classes of PINCE.py to their own files +- Extend documentation to GUI parts. Libpince has 100% documentation coverage but GUI doesn't +- Use type hints(py 3.5) and variable annotations(py 3.6) when support drops for older systems +- Arrows for jump instructions based on disassembled output +- Flowcharts based on disassembled output +- Consider implementing a GUI for catchpoints. This is currently done via GDB Console +- Implement speedhack +- Implement unrandomizer +- Automatic function bypassing(make it return the desired value, hook specific parts etc.) +- Implement auto-ESP&aimbot +- Implement thread info widget +- Write at least one test for each function in libpince +- Refactorize memory write/read functions +- - ReferencedStringsWidgetForm refreshes the cache everytime the comboBox_ValueType changes, this creates serious performance issues if total results are more than 800k. + Only update the visible rows to prevent this(check ```disassemble_check_viewport``` for an example) +- - Implement same system for the TrackBreakpointWidgetForm if necessary. Do performance tests +- - Consider using a class instead of primitive return types to store the raw bytes. This class should also include a method to display None type as red '??' text for Qt +- - Provide an option to cut BOM bytes when writing to memory with the types UTF-16 and UTF-32 +- - Put a warning for users about replacement bytes for non UTF-8 types +- - Extend string types with LE and BE variants of UTF-16 and UTF-32 +- - Change comboBox_ValueType string order to be ... String_UTF-8 String_Others if necessary +- - Implement a custom combobox class for comboBox_ValueType and create a context menu for String_Others, if it gets implemented +- Implement "Investigate Registers" button to gather information about the addresses registers point to +- Add the ability to track down registers and addresses in tracer(unsure) +- Implement CE's Ultimap-like feature for tracing data, dissect code data and raw instruction list. +Search for calls and store their hit counts to filter out the functions that haven't or have executed specific number of times. +Implement a flexible input field for the execution count. For instance, 2^x only searches for hit counts 2, 4, 8 and so on, 3x only searches for 3, 6, 9 etc. +([CE#358](https://github.com/cheat-engine/cheat-engine/issues/358)) +- Extend search_referenced_strings with relative search +- Consider adding type guessing for the StackView +- Implement a psuedo-terminal for the inferior like edb does(idk if necessary, we don't usually target CLI games, up to debate) +- Try to optimize TrackBreakpoint and TrackWatchpoint return data structures further, adding an id field might simplify traversing of the tree, performance tests are required +- Implement extra MemoryViewerWindow tabs(not really critical right now, up to debate) +- ~~Consider removing the command file layer of IPC system for debugcore.send_command to speed up things~~ +[Update-29/04/2018 : Delaying this until GDB/MI implements a native multiline command feature or improves ```interpreter-exec``` command to cover every single multiline command type(including ```define``` commands)] +- Implement developer mode in settings. Developer mode will include features like dissection of GUI elements on events such as mouse-over +- Add ability to include non-absolute calls for dissect code feature(i.e call rax). Should be considered after the first version release. Might be useful for multi-breakpoint related features +- Provide information about absolute addresses in disassemble screen +- All tables that hold large amount of data should only update the visible rows(check ```disassemble_check_viewport``` for an example) diff --git a/GUI/AboutWidget.py b/GUI/AboutWidget.py index 4ff0ea25..a663c969 100644 --- a/GUI/AboutWidget.py +++ b/GUI/AboutWidget.py @@ -1,13 +1,13 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'aboutwidget.ui' +# Form implementation generated from reading ui file 'AboutWidget.ui' # -# Created: Wed Jun 29 16:34:26 2016 -# by: PyQt5 UI code generator 5.2.1 +# Created by: PyQt6 UI code generator 6.3.1 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets -from PyQt5 import QtCore, QtGui, QtWidgets class Ui_TabWidget(object): def setupUi(self, TabWidget): @@ -39,4 +39,3 @@ def retranslateUi(self, TabWidget): TabWidget.setWindowTitle(_translate("TabWidget", "About PINCE")) TabWidget.setTabText(TabWidget.indexOf(self.tab_Contributors), _translate("TabWidget", "Contributors")) TabWidget.setTabText(TabWidget.indexOf(self.tab_License), _translate("TabWidget", "License")) - diff --git a/tests/common_defs.py b/GUI/AbstractTableModels/AsciiModel.py similarity index 63% rename from tests/common_defs.py rename to GUI/AbstractTableModels/AsciiModel.py index 13b99f6c..b1f71978 100644 --- a/tests/common_defs.py +++ b/GUI/AbstractTableModels/AsciiModel.py @@ -14,4 +14,17 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ -# This source file will be used for things that are shared between test modules + +from GUI.AbstractTableModels.HexModel import QHexModel +from libpince import utils + + +class QAsciiModel(QHexModel): + def __init__(self, row_count, column_count, parent=None): + super().__init__(row_count, column_count, parent) + + def display_data(self, index): + return utils.aob_to_str(self.data_array[index]) + + def translate_data(self, data): + return utils.str_to_aob(data) diff --git a/GUI/AbstractTableModels/HexModel.py b/GUI/AbstractTableModels/HexModel.py new file mode 100644 index 00000000..70738269 --- /dev/null +++ b/GUI/AbstractTableModels/HexModel.py @@ -0,0 +1,96 @@ +""" +Copyright (C) 2016-2017 Korcan Karaokçu + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from PyQt6.QtCore import QAbstractTableModel, QModelIndex, Qt +from PyQt6.QtGui import QColor, QColorConstants +from libpince import utils, debugcore + + +class QHexModel(QAbstractTableModel): + def __init__(self, row_count, column_count, parent=None): + super().__init__(parent) + self.breakpoint_list = set() + self.row_count = row_count + self.column_count = column_count + self.current_address = 0 + offset = row_count * column_count + self.data_array = ["??"] * offset + self.cell_animation = [0] * offset + self.cell_change_color = QColor(QColorConstants.Red) + self.breakpoint_color = QColor(QColorConstants.Green) + self.breakpoint_color.setAlpha(96) + + def rowCount(self, QModelIndex_parent=None, *args, **kwargs): + return self.row_count + + def columnCount(self, QModelIndex_parent=None, *args, **kwargs): + return self.column_count + + def flags(self, index: QModelIndex): + return super().flags(index) | Qt.ItemFlag.ItemIsEditable + + def data(self, model_index: QModelIndex, int_role=None): + if self.data_array and model_index.isValid(): + index = model_index.row() * self.column_count + model_index.column() + if int_role == Qt.ItemDataRole.BackgroundRole: + address = self.current_address + index + if utils.modulo_address(address, debugcore.inferior_arch) in self.breakpoint_list: + return self.breakpoint_color + self.cell_change_color.setAlpha(20 * self.cell_animation[index]) + return self.cell_change_color + elif int_role == Qt.ItemDataRole.DisplayRole: + return self.display_data(index) + + def display_data(self, index): + return self.data_array[index] + + def translate_data(self, data): + return data + + def refresh(self, int_address, offset, data_array=None, breakpoint_info=None): + int_address = utils.modulo_address(int_address, debugcore.inferior_arch) + self.breakpoint_list.clear() + if data_array is None: + self.data_array = debugcore.hex_dump(int_address, offset) + else: + self.data_array = data_array + if breakpoint_info is None: + breakpoint_info = debugcore.get_breakpoint_info() + for bp in breakpoint_info: + breakpoint_address = int(bp.address, 16) + for i in range(bp.size): + self.breakpoint_list.add(utils.modulo_address(breakpoint_address + i, debugcore.inferior_arch)) + self.current_address = int_address + self.cell_animation = [0] * offset + self.layoutChanged.emit() + + def update_loop(self, updated_array): + for index, item in enumerate(self.cell_animation): + if item > 0: + self.cell_animation[index] = item - 1 + for index, item in enumerate(updated_array): + if item != self.data_array[index]: + self.cell_animation[index] = 6 + self.data_array = updated_array + self.layoutChanged.emit() + + def update_index(self, index: int, data: str): + data = self.translate_data(data) + if self.data_array[index] != data: + self.cell_animation[index] = 6 + self.data_array[index] = data + self.layoutChanged.emit() diff --git a/GUI/AddAddressManuallyDialog.py b/GUI/AddAddressManuallyDialog.py index 00ade6c1..a9c574af 100644 --- a/GUI/AddAddressManuallyDialog.py +++ b/GUI/AddAddressManuallyDialog.py @@ -1,126 +1,190 @@ -# -*- coding: utf-8 -*- - # Form implementation generated from reading ui file 'AddAddressManuallyDialog.ui' # -# Created by: PyQt5 UI code generator 5.5.1 +# Created by: PyQt6 UI code generator 6.4.2 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets -from PyQt5 import QtCore, QtGui, QtWidgets class Ui_Dialog(object): def setupUi(self, Dialog): Dialog.setObjectName("Dialog") - Dialog.resize(232, 248) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(Dialog.sizePolicy().hasHeightForWidth()) - Dialog.setSizePolicy(sizePolicy) - Dialog.setMaximumSize(QtCore.QSize(16777215, 248)) + Dialog.resize(262, 486) self.gridLayout = QtWidgets.QGridLayout(Dialog) self.gridLayout.setObjectName("gridLayout") - self.buttonBox = QtWidgets.QDialogButtonBox(Dialog) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) - self.buttonBox.setObjectName("buttonBox") - self.gridLayout.addWidget(self.buttonBox, 3, 0, 1, 1) - self.verticalLayout_2 = QtWidgets.QVBoxLayout() - self.verticalLayout_2.setObjectName("verticalLayout_2") - self.label = QtWidgets.QLabel(Dialog) - self.label.setObjectName("label") - self.verticalLayout_2.addWidget(self.label) - self.horizontalLayout = QtWidgets.QHBoxLayout() - self.horizontalLayout.setObjectName("horizontalLayout") - self.lineEdit_address = QtWidgets.QLineEdit(Dialog) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.lineEdit_address.sizePolicy().hasHeightForWidth()) - self.lineEdit_address.setSizePolicy(sizePolicy) - self.lineEdit_address.setMinimumSize(QtCore.QSize(100, 0)) - self.lineEdit_address.setText("") - self.lineEdit_address.setObjectName("lineEdit_address") - self.horizontalLayout.addWidget(self.lineEdit_address) - self.label_2 = QtWidgets.QLabel(Dialog) - self.label_2.setObjectName("label_2") - self.horizontalLayout.addWidget(self.label_2) - self.label_valueofaddress = QtWidgets.QLabel(Dialog) - self.label_valueofaddress.setText("") - self.label_valueofaddress.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse) - self.label_valueofaddress.setObjectName("label_valueofaddress") - self.horizontalLayout.addWidget(self.label_valueofaddress) - spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.horizontalLayout.addItem(spacerItem) - self.verticalLayout_2.addLayout(self.horizontalLayout) - self.gridLayout.addLayout(self.verticalLayout_2, 0, 0, 1, 1) - self.verticalLayout = QtWidgets.QVBoxLayout() - self.verticalLayout.setObjectName("verticalLayout") - self.label_4 = QtWidgets.QLabel(Dialog) - self.label_4.setObjectName("label_4") - self.verticalLayout.addWidget(self.label_4) - self.lineEdit_description = QtWidgets.QLineEdit(Dialog) - self.lineEdit_description.setText("") - self.lineEdit_description.setObjectName("lineEdit_description") - self.verticalLayout.addWidget(self.lineEdit_description) - self.gridLayout.addLayout(self.verticalLayout, 1, 0, 1, 1) self.verticalLayout_3 = QtWidgets.QVBoxLayout() self.verticalLayout_3.setObjectName("verticalLayout_3") self.horizontalLayout_3 = QtWidgets.QHBoxLayout() self.horizontalLayout_3.setObjectName("horizontalLayout_3") - self.label_5 = QtWidgets.QLabel(Dialog) + self.label_5 = QtWidgets.QLabel(parent=Dialog) self.label_5.setObjectName("label_5") self.horizontalLayout_3.addWidget(self.label_5) - spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.horizontalLayout_3.addItem(spacerItem1) - self.checkBox_zeroterminate = QtWidgets.QCheckBox(Dialog) - self.checkBox_zeroterminate.setChecked(True) - self.checkBox_zeroterminate.setObjectName("checkBox_zeroterminate") - self.horizontalLayout_3.addWidget(self.checkBox_zeroterminate) + self.comboBox_ValueType = QtWidgets.QComboBox(parent=Dialog) + self.comboBox_ValueType.setObjectName("comboBox_ValueType") + self.horizontalLayout_3.addWidget(self.comboBox_ValueType) + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) + self.horizontalLayout_3.addItem(spacerItem) self.verticalLayout_3.addLayout(self.horizontalLayout_3) - self.horizontalLayout_2 = QtWidgets.QHBoxLayout() + self.horizontalLayout_6 = QtWidgets.QHBoxLayout() + self.horizontalLayout_6.setObjectName("horizontalLayout_6") + self.label_3 = QtWidgets.QLabel(parent=Dialog) + self.label_3.setObjectName("label_3") + self.horizontalLayout_6.addWidget(self.label_3) + self.comboBox_Endianness = QtWidgets.QComboBox(parent=Dialog) + self.comboBox_Endianness.setObjectName("comboBox_Endianness") + self.horizontalLayout_6.addWidget(self.comboBox_Endianness) + spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) + self.horizontalLayout_6.addItem(spacerItem1) + self.verticalLayout_3.addLayout(self.horizontalLayout_6) + self.widget_Repr = QtWidgets.QWidget(parent=Dialog) + self.widget_Repr.setObjectName("widget_Repr") + self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.widget_Repr) + self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0) self.horizontalLayout_2.setObjectName("horizontalLayout_2") - self.comboBox_ValueType = QtWidgets.QComboBox(Dialog) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) + self.checkBox_Hex = QtWidgets.QCheckBox(parent=self.widget_Repr) + self.checkBox_Hex.setObjectName("checkBox_Hex") + self.horizontalLayout_2.addWidget(self.checkBox_Hex) + self.checkBox_Signed = QtWidgets.QCheckBox(parent=self.widget_Repr) + self.checkBox_Signed.setObjectName("checkBox_Signed") + self.horizontalLayout_2.addWidget(self.checkBox_Signed) + spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) + self.horizontalLayout_2.addItem(spacerItem2) + self.verticalLayout_3.addWidget(self.widget_Repr) + self.widget_Length = QtWidgets.QWidget(parent=Dialog) + self.widget_Length.setObjectName("widget_Length") + self.horizontalLayout_Length = QtWidgets.QHBoxLayout(self.widget_Length) + self.horizontalLayout_Length.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_Length.setObjectName("horizontalLayout_Length") + self.label_Length = QtWidgets.QLabel(parent=self.widget_Length) + self.label_Length.setObjectName("label_Length") + self.horizontalLayout_Length.addWidget(self.label_Length) + self.lineEdit_Length = QtWidgets.QLineEdit(parent=self.widget_Length) + self.lineEdit_Length.setMaximumSize(QtCore.QSize(60, 16777215)) + self.lineEdit_Length.setInputMask("") + self.lineEdit_Length.setText("10") + self.lineEdit_Length.setObjectName("lineEdit_Length") + self.horizontalLayout_Length.addWidget(self.lineEdit_Length) + self.checkBox_ZeroTerminate = QtWidgets.QCheckBox(parent=self.widget_Length) + self.checkBox_ZeroTerminate.setChecked(True) + self.checkBox_ZeroTerminate.setObjectName("checkBox_ZeroTerminate") + self.horizontalLayout_Length.addWidget(self.checkBox_ZeroTerminate) + spacerItem3 = QtWidgets.QSpacerItem(13, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) + self.horizontalLayout_Length.addItem(spacerItem3) + self.verticalLayout_3.addWidget(self.widget_Length) + self.gridLayout.addLayout(self.verticalLayout_3, 2, 0, 1, 1) + self.widget_Pointer = QtWidgets.QWidget(parent=Dialog) + self.widget_Pointer.setEnabled(True) + self.widget_Pointer.setMinimumSize(QtCore.QSize(0, 0)) + self.widget_Pointer.setObjectName("widget_Pointer") + self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.widget_Pointer) + self.verticalLayout_4.setContentsMargins(0, 0, 0, 0) + self.verticalLayout_4.setSpacing(0) + self.verticalLayout_4.setObjectName("verticalLayout_4") + self.verticalLayout_Pointers = QtWidgets.QVBoxLayout() + self.verticalLayout_Pointers.setObjectName("verticalLayout_Pointers") + self.label_BaseAddress = QtWidgets.QLabel(parent=self.widget_Pointer) + self.label_BaseAddress.setObjectName("label_BaseAddress") + self.verticalLayout_Pointers.addWidget(self.label_BaseAddress) + self.horizontalLayout_4 = QtWidgets.QHBoxLayout() + self.horizontalLayout_4.setObjectName("horizontalLayout_4") + self.lineEdit_PtrStartAddress = QtWidgets.QLineEdit(parent=self.widget_Pointer) + self.lineEdit_PtrStartAddress.setObjectName("lineEdit_PtrStartAddress") + self.horizontalLayout_4.addWidget(self.lineEdit_PtrStartAddress) + self.label_BaseAddressDeref = QtWidgets.QLabel(parent=self.widget_Pointer) + self.label_BaseAddressDeref.setText("-> ??") + self.label_BaseAddressDeref.setTextInteractionFlags(QtCore.Qt.TextInteractionFlag.LinksAccessibleByMouse|QtCore.Qt.TextInteractionFlag.TextSelectableByMouse) + self.label_BaseAddressDeref.setObjectName("label_BaseAddressDeref") + self.horizontalLayout_4.addWidget(self.label_BaseAddressDeref) + self.verticalLayout_Pointers.addLayout(self.horizontalLayout_4) + self.horizontalLayout_5 = QtWidgets.QHBoxLayout() + self.horizontalLayout_5.setObjectName("horizontalLayout_5") + self.pushButton_AddOffset = QtWidgets.QPushButton(parent=self.widget_Pointer) + self.pushButton_AddOffset.setObjectName("pushButton_AddOffset") + self.horizontalLayout_5.addWidget(self.pushButton_AddOffset) + self.pushButton_RemoveOffset = QtWidgets.QPushButton(parent=self.widget_Pointer) + self.pushButton_RemoveOffset.setObjectName("pushButton_RemoveOffset") + self.horizontalLayout_5.addWidget(self.pushButton_RemoveOffset) + spacerItem4 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) + self.horizontalLayout_5.addItem(spacerItem4) + self.verticalLayout_Pointers.addLayout(self.horizontalLayout_5) + self.verticalLayout_4.addLayout(self.verticalLayout_Pointers) + self.gridLayout.addWidget(self.widget_Pointer, 7, 0, 1, 1) + self.verticalLayout = QtWidgets.QVBoxLayout() + self.verticalLayout.setObjectName("verticalLayout") + self.label_4 = QtWidgets.QLabel(parent=Dialog) + self.label_4.setObjectName("label_4") + self.verticalLayout.addWidget(self.label_4) + self.lineEdit_Description = QtWidgets.QLineEdit(parent=Dialog) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, QtWidgets.QSizePolicy.Policy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.comboBox_ValueType.sizePolicy().hasHeightForWidth()) - self.comboBox_ValueType.setSizePolicy(sizePolicy) - self.comboBox_ValueType.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents) - self.comboBox_ValueType.setObjectName("comboBox_ValueType") - self.horizontalLayout_2.addWidget(self.comboBox_ValueType) - spacerItem2 = QtWidgets.QSpacerItem(13, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.horizontalLayout_2.addItem(spacerItem2) - self.label_length = QtWidgets.QLabel(Dialog) - self.label_length.setObjectName("label_length") - self.horizontalLayout_2.addWidget(self.label_length) - self.lineEdit_length = QtWidgets.QLineEdit(Dialog) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHeightForWidth(self.lineEdit_Description.sizePolicy().hasHeightForWidth()) + self.lineEdit_Description.setSizePolicy(sizePolicy) + self.lineEdit_Description.setText("") + self.lineEdit_Description.setObjectName("lineEdit_Description") + self.verticalLayout.addWidget(self.lineEdit_Description) + self.gridLayout.addLayout(self.verticalLayout, 1, 0, 1, 1) + self.buttonBox = QtWidgets.QDialogButtonBox(parent=Dialog) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.lineEdit_length.sizePolicy().hasHeightForWidth()) - self.lineEdit_length.setSizePolicy(sizePolicy) - self.lineEdit_length.setMaximumSize(QtCore.QSize(60, 16777215)) - self.lineEdit_length.setInputMask("") - self.lineEdit_length.setObjectName("lineEdit_length") - self.horizontalLayout_2.addWidget(self.lineEdit_length) - self.verticalLayout_3.addLayout(self.horizontalLayout_2) - self.gridLayout.addLayout(self.verticalLayout_3, 2, 0, 1, 1) + sizePolicy.setHeightForWidth(self.buttonBox.sizePolicy().hasHeightForWidth()) + self.buttonBox.setSizePolicy(sizePolicy) + self.buttonBox.setMaximumSize(QtCore.QSize(16777215, 35)) + self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.StandardButton.Cancel|QtWidgets.QDialogButtonBox.StandardButton.Ok) + self.buttonBox.setObjectName("buttonBox") + self.gridLayout.addWidget(self.buttonBox, 9, 0, 1, 1, QtCore.Qt.AlignmentFlag.AlignLeft) + spacerItem5 = QtWidgets.QSpacerItem(20, 20, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum) + self.gridLayout.addItem(spacerItem5, 8, 0, 1, 1) + self.verticalLayout_2 = QtWidgets.QVBoxLayout() + self.verticalLayout_2.setObjectName("verticalLayout_2") + self.label = QtWidgets.QLabel(parent=Dialog) + self.label.setObjectName("label") + self.verticalLayout_2.addWidget(self.label) + self.horizontalLayout = QtWidgets.QHBoxLayout() + self.horizontalLayout.setObjectName("horizontalLayout") + self.lineEdit_Address = QtWidgets.QLineEdit(parent=Dialog) + self.lineEdit_Address.setText("") + self.lineEdit_Address.setObjectName("lineEdit_Address") + self.horizontalLayout.addWidget(self.lineEdit_Address) + self.label_2 = QtWidgets.QLabel(parent=Dialog) + self.label_2.setText("=") + self.label_2.setObjectName("label_2") + self.horizontalLayout.addWidget(self.label_2) + self.label_Value = QtWidgets.QLabel(parent=Dialog) + self.label_Value.setText("") + self.label_Value.setTextInteractionFlags(QtCore.Qt.TextInteractionFlag.LinksAccessibleByMouse|QtCore.Qt.TextInteractionFlag.TextSelectableByMouse) + self.label_Value.setObjectName("label_Value") + self.horizontalLayout.addWidget(self.label_Value) + spacerItem6 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) + self.horizontalLayout.addItem(spacerItem6) + self.verticalLayout_2.addLayout(self.horizontalLayout) + self.gridLayout.addLayout(self.verticalLayout_2, 0, 0, 1, 1) + self.checkBox_IsPointer = QtWidgets.QCheckBox(parent=Dialog) + self.checkBox_IsPointer.setObjectName("checkBox_IsPointer") + self.gridLayout.addWidget(self.checkBox_IsPointer, 3, 0, 1, 1) self.retranslateUi(Dialog) - self.comboBox_ValueType.setCurrentIndex(-1) - self.buttonBox.accepted.connect(Dialog.accept) - self.buttonBox.rejected.connect(Dialog.reject) + self.buttonBox.accepted.connect(Dialog.accept) # type: ignore + self.buttonBox.rejected.connect(Dialog.reject) # type: ignore QtCore.QMetaObject.connectSlotsByName(Dialog) def retranslateUi(self, Dialog): _translate = QtCore.QCoreApplication.translate Dialog.setWindowTitle(_translate("Dialog", "Add Address Manually")) - self.label.setText(_translate("Dialog", "Address:")) - self.label_2.setText(_translate("Dialog", "=")) - self.label_4.setText(_translate("Dialog", "Description:")) self.label_5.setText(_translate("Dialog", "Type:")) - self.checkBox_zeroterminate.setText(_translate("Dialog", "Zero-Terminated")) - self.label_length.setText(_translate("Dialog", "Length")) - self.lineEdit_length.setText(_translate("Dialog", "10")) - + self.label_3.setText(_translate("Dialog", "Endianness:")) + self.checkBox_Hex.setText(_translate("Dialog", "Hex")) + self.checkBox_Signed.setText(_translate("Dialog", "Signed")) + self.label_Length.setText(_translate("Dialog", "Length:")) + self.checkBox_ZeroTerminate.setText(_translate("Dialog", "Zero-Terminated")) + self.label_BaseAddress.setText(_translate("Dialog", "Base Address:")) + self.pushButton_AddOffset.setText(_translate("Dialog", "Add Offset")) + self.pushButton_RemoveOffset.setText(_translate("Dialog", "Remove Offset")) + self.label_4.setText(_translate("Dialog", "Description:")) + self.label.setText(_translate("Dialog", "Address:")) + self.checkBox_IsPointer.setText(_translate("Dialog", "Pointer")) diff --git a/GUI/AddAddressManuallyDialog.ui b/GUI/AddAddressManuallyDialog.ui index 89610ce6..eb6109df 100644 --- a/GUI/AddAddressManuallyDialog.ui +++ b/GUI/AddAddressManuallyDialog.ui @@ -6,85 +6,57 @@ 0 0 - 232 - 248 + 262 + 486 - - - 0 - 0 - - - - - 16777215 - 248 - - Add Address Manually - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - Address: - - - + + - + - - - - 0 - 0 - - - - - 100 - 0 - - + - + Type: - - - = + + + + + + Qt::Horizontal - + + + 40 + 20 + + + + + + + - + - - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + Endianness: - + + + + Qt::Horizontal @@ -98,8 +70,206 @@ + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Hex + + + + + + + Signed + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Length: + + + + + + + + 60 + 16777215 + + + + + + + 10 + + + + + + + Zero-Terminated + + + true + + + + + + + Qt::Horizontal + + + + 13 + 20 + + + + + + + + + + + true + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Base Address: + + + + + + + + + + + + -> <font color=red>??</font> + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + + + + Add Offset + + + + + + + Remove Offset + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + @@ -110,7 +280,13 @@ - + + + + 0 + 0 + + @@ -118,106 +294,103 @@ - - + + + + + 0 + 0 + + + + + 16777215 + 35 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + Qt::Vertical + + + QSizePolicy::Minimum + + + + 20 + 20 + + + + + + - + + + Address: + + + + + - + - Type: + - - - Qt::Horizontal - - - - 40 - 20 - - - - - - + - Zero-Terminated - - - true + = - - - - - - - - 0 - 0 - - - - -1 + + + - - QComboBox::AdjustToContents + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - + Qt::Horizontal - 13 + 40 20 - - - - Length - - - - - - - - 0 - 0 - - - - - 60 - 16777215 - - - - - - - 10 - - - + + + + Pointer + + + @@ -229,8 +402,8 @@ accept() - 280 - 371 + 286 + 399 157 @@ -245,8 +418,8 @@ reject() - 280 - 371 + 286 + 399 283 diff --git a/GUI/BookmarkWidget.py b/GUI/BookmarkWidget.py index 72164235..62577ae3 100644 --- a/GUI/BookmarkWidget.py +++ b/GUI/BookmarkWidget.py @@ -1,13 +1,13 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'bookmarkwidget.ui' +# Form implementation generated from reading ui file 'BookmarkWidget.ui' # -# Created: Sat Jul 9 16:03:34 2016 -# by: PyQt5 UI code generator 5.2.1 +# Created by: PyQt6 UI code generator 6.4.2 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets -from PyQt5 import QtCore, QtGui, QtWidgets class Ui_Form(object): def setupUi(self, Form): @@ -19,30 +19,33 @@ def setupUi(self, Form): self.horizontalLayout.setObjectName("horizontalLayout") self.verticalLayout = QtWidgets.QVBoxLayout() self.verticalLayout.setObjectName("verticalLayout") - self.label = QtWidgets.QLabel(Form) + self.label = QtWidgets.QLabel(parent=Form) self.label.setObjectName("label") self.verticalLayout.addWidget(self.label) - self.listWidget = QtWidgets.QListWidget(Form) + self.listWidget = QtWidgets.QListWidget(parent=Form) + font = QtGui.QFont() + font.setFamily("Monospace") + self.listWidget.setFont(font) self.listWidget.setObjectName("listWidget") self.verticalLayout.addWidget(self.listWidget) self.horizontalLayout.addLayout(self.verticalLayout) self.verticalLayout_2 = QtWidgets.QVBoxLayout() self.verticalLayout_2.setObjectName("verticalLayout_2") - self.label_2 = QtWidgets.QLabel(Form) + self.label_2 = QtWidgets.QLabel(parent=Form) self.label_2.setObjectName("label_2") self.verticalLayout_2.addWidget(self.label_2) - self.lineEdit_Info = QtWidgets.QLineEdit(Form) + self.lineEdit_Info = QtWidgets.QLineEdit(parent=Form) self.lineEdit_Info.setReadOnly(True) self.lineEdit_Info.setObjectName("lineEdit_Info") self.verticalLayout_2.addWidget(self.lineEdit_Info) - self.label_3 = QtWidgets.QLabel(Form) + self.label_3 = QtWidgets.QLabel(parent=Form) self.label_3.setObjectName("label_3") self.verticalLayout_2.addWidget(self.label_3) - self.lineEdit_Comment = QtWidgets.QLineEdit(Form) + self.lineEdit_Comment = QtWidgets.QLineEdit(parent=Form) self.lineEdit_Comment.setReadOnly(True) self.lineEdit_Comment.setObjectName("lineEdit_Comment") self.verticalLayout_2.addWidget(self.lineEdit_Comment) - spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) self.verticalLayout_2.addItem(spacerItem) self.horizontalLayout.addLayout(self.verticalLayout_2) self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1) @@ -56,4 +59,3 @@ def retranslateUi(self, Form): self.label.setText(_translate("Form", "Bookmarked Addresses")) self.label_2.setText(_translate("Form", "Info")) self.label_3.setText(_translate("Form", "Comment")) - diff --git a/GUI/BookmarkWidget.ui b/GUI/BookmarkWidget.ui index a0b7a9be..09cbfecc 100644 --- a/GUI/BookmarkWidget.ui +++ b/GUI/BookmarkWidget.ui @@ -26,7 +26,13 @@ - + + + + Monospace + + + diff --git a/GUI/BreakpointInfoWidget.py b/GUI/BreakpointInfoWidget.py index 88b5f101..d634521e 100644 --- a/GUI/BreakpointInfoWidget.py +++ b/GUI/BreakpointInfoWidget.py @@ -1,12 +1,13 @@ -# -*- coding: utf-8 -*- - # Form implementation generated from reading ui file 'BreakpointInfoWidget.ui' # -# Created by: PyQt5 UI code generator 5.5.1 +# Created by: PyQt6 UI code generator 6.4.2 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets -from PyQt5 import QtCore, QtGui, QtWidgets class Ui_TabWidget(object): def setupUi(self, TabWidget): @@ -16,10 +17,15 @@ def setupUi(self, TabWidget): self.tab_BreakpointInfo.setObjectName("tab_BreakpointInfo") self.gridLayout_2 = QtWidgets.QGridLayout(self.tab_BreakpointInfo) self.gridLayout_2.setObjectName("gridLayout_2") - self.tableWidget_BreakpointInfo = QtWidgets.QTableWidget(self.tab_BreakpointInfo) - self.tableWidget_BreakpointInfo.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) - self.tableWidget_BreakpointInfo.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) - self.tableWidget_BreakpointInfo.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) + self.tableWidget_BreakpointInfo = QtWidgets.QTableWidget(parent=self.tab_BreakpointInfo) + font = QtGui.QFont() + font.setFamily("Monospace") + self.tableWidget_BreakpointInfo.setFont(font) + self.tableWidget_BreakpointInfo.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers) + self.tableWidget_BreakpointInfo.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.SingleSelection) + self.tableWidget_BreakpointInfo.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows) + self.tableWidget_BreakpointInfo.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollMode.ScrollPerPixel) + self.tableWidget_BreakpointInfo.setWordWrap(False) self.tableWidget_BreakpointInfo.setObjectName("tableWidget_BreakpointInfo") self.tableWidget_BreakpointInfo.setColumnCount(9) self.tableWidget_BreakpointInfo.setRowCount(0) @@ -51,7 +57,10 @@ def setupUi(self, TabWidget): self.tab_RawBreakpointInfo.setObjectName("tab_RawBreakpointInfo") self.gridLayout = QtWidgets.QGridLayout(self.tab_RawBreakpointInfo) self.gridLayout.setObjectName("gridLayout") - self.textBrowser_BreakpointInfo = QtWidgets.QTextBrowser(self.tab_RawBreakpointInfo) + self.textBrowser_BreakpointInfo = QtWidgets.QTextBrowser(parent=self.tab_RawBreakpointInfo) + font = QtGui.QFont() + font.setFamily("Monospace") + self.textBrowser_BreakpointInfo.setFont(font) self.textBrowser_BreakpointInfo.setObjectName("textBrowser_BreakpointInfo") self.gridLayout.addWidget(self.textBrowser_BreakpointInfo, 0, 0, 1, 1) TabWidget.addTab(self.tab_RawBreakpointInfo, "") @@ -83,4 +92,3 @@ def retranslateUi(self, TabWidget): item.setText(_translate("TabWidget", "Condition")) TabWidget.setTabText(TabWidget.indexOf(self.tab_BreakpointInfo), _translate("TabWidget", "Interactive")) TabWidget.setTabText(TabWidget.indexOf(self.tab_RawBreakpointInfo), _translate("TabWidget", "Raw")) - diff --git a/GUI/BreakpointInfoWidget.ui b/GUI/BreakpointInfoWidget.ui index 21109d8c..e8137ed3 100644 --- a/GUI/BreakpointInfoWidget.ui +++ b/GUI/BreakpointInfoWidget.ui @@ -23,6 +23,11 @@ + + + Monospace + + QAbstractItemView::NoEditTriggers @@ -32,16 +37,22 @@ QAbstractItemView::SelectRows + + QAbstractItemView::ScrollPerPixel + + + false + true false - + 16 - + 16 @@ -99,7 +110,13 @@ - + + + + Monospace + + + diff --git a/GUI/ConsoleWidget.py b/GUI/ConsoleWidget.py index 29c6e48e..31f5de53 100644 --- a/GUI/ConsoleWidget.py +++ b/GUI/ConsoleWidget.py @@ -1,19 +1,19 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'consolewidget.ui' +# Form implementation generated from reading ui file 'ConsoleWidget.ui' # -# Created: Thu Jul 21 18:11:36 2016 -# by: PyQt5 UI code generator 5.2.1 +# Created by: PyQt6 UI code generator 6.4.2 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets -from PyQt5 import QtCore, QtGui, QtWidgets class Ui_Form(object): def setupUi(self, Form): Form.setObjectName("Form") Form.resize(850, 500) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(Form.sizePolicy().hasHeightForWidth()) @@ -23,50 +23,55 @@ def setupUi(self, Form): self.verticalLayout_2.setObjectName("verticalLayout_2") self.verticalLayout = QtWidgets.QVBoxLayout() self.verticalLayout.setObjectName("verticalLayout") - self.textBrowser = QtWidgets.QTextBrowser(Form) + self.textBrowser = QtWidgets.QTextBrowser(parent=Form) palette = QtGui.QPalette() brush = QtGui.QBrush(QtGui.QColor(0, 255, 0)) - brush.setStyle(QtCore.Qt.SolidPattern) - palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Text, brush) + brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) + palette.setBrush(QtGui.QPalette.ColorGroup.Active, QtGui.QPalette.ColorRole.Text, brush) brush = QtGui.QBrush(QtGui.QColor(0, 0, 0)) - brush.setStyle(QtCore.Qt.SolidPattern) - palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Base, brush) + brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) + palette.setBrush(QtGui.QPalette.ColorGroup.Active, QtGui.QPalette.ColorRole.Base, brush) + brush = QtGui.QBrush(QtGui.QColor(0, 255, 0, 128)) + brush.setStyle(QtCore.Qt.BrushStyle.NoBrush) + palette.setBrush(QtGui.QPalette.ColorGroup.Active, QtGui.QPalette.ColorRole.PlaceholderText, brush) brush = QtGui.QBrush(QtGui.QColor(0, 255, 0)) - brush.setStyle(QtCore.Qt.SolidPattern) - palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Text, brush) + brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) + palette.setBrush(QtGui.QPalette.ColorGroup.Inactive, QtGui.QPalette.ColorRole.Text, brush) brush = QtGui.QBrush(QtGui.QColor(0, 0, 0)) - brush.setStyle(QtCore.Qt.SolidPattern) - palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Base, brush) + brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) + palette.setBrush(QtGui.QPalette.ColorGroup.Inactive, QtGui.QPalette.ColorRole.Base, brush) + brush = QtGui.QBrush(QtGui.QColor(0, 255, 0, 128)) + brush.setStyle(QtCore.Qt.BrushStyle.NoBrush) + palette.setBrush(QtGui.QPalette.ColorGroup.Inactive, QtGui.QPalette.ColorRole.PlaceholderText, brush) brush = QtGui.QBrush(QtGui.QColor(128, 128, 128)) - brush.setStyle(QtCore.Qt.SolidPattern) - palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Text, brush) + brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) + palette.setBrush(QtGui.QPalette.ColorGroup.Disabled, QtGui.QPalette.ColorRole.Text, brush) brush = QtGui.QBrush(QtGui.QColor(255, 255, 255)) - brush.setStyle(QtCore.Qt.SolidPattern) - palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Base, brush) + brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern) + palette.setBrush(QtGui.QPalette.ColorGroup.Disabled, QtGui.QPalette.ColorRole.Base, brush) + brush = QtGui.QBrush(QtGui.QColor(0, 255, 0, 128)) + brush.setStyle(QtCore.Qt.BrushStyle.NoBrush) + palette.setBrush(QtGui.QPalette.ColorGroup.Disabled, QtGui.QPalette.ColorRole.PlaceholderText, brush) self.textBrowser.setPalette(palette) font = QtGui.QFont() font.setFamily("Monospace") font.setBold(False) font.setItalic(False) - font.setWeight(50) self.textBrowser.setFont(font) self.textBrowser.setObjectName("textBrowser") self.verticalLayout.addWidget(self.textBrowser) self.horizontalLayout = QtWidgets.QHBoxLayout() self.horizontalLayout.setObjectName("horizontalLayout") - self.lineEdit = QtWidgets.QLineEdit(Form) + self.lineEdit = QtWidgets.QLineEdit(parent=Form) self.lineEdit.setObjectName("lineEdit") self.horizontalLayout.addWidget(self.lineEdit) - self.pushButton_Send = QtWidgets.QPushButton(Form) + self.pushButton_Send = QtWidgets.QPushButton(parent=Form) self.pushButton_Send.setObjectName("pushButton_Send") self.horizontalLayout.addWidget(self.pushButton_Send) - self.pushButton_SendCtrl = QtWidgets.QPushButton(Form) - self.pushButton_SendCtrl.setObjectName("pushButton_SendCtrl") - self.horizontalLayout.addWidget(self.pushButton_SendCtrl) - self.radioButton_CLI = QtWidgets.QRadioButton(Form) + self.radioButton_CLI = QtWidgets.QRadioButton(parent=Form) self.radioButton_CLI.setObjectName("radioButton_CLI") self.horizontalLayout.addWidget(self.radioButton_CLI) - self.radioButton_MI = QtWidgets.QRadioButton(Form) + self.radioButton_MI = QtWidgets.QRadioButton(parent=Form) self.radioButton_MI.setChecked(True) self.radioButton_MI.setObjectName("radioButton_MI") self.horizontalLayout.addWidget(self.radioButton_MI) @@ -79,13 +84,6 @@ def setupUi(self, Form): def retranslateUi(self, Form): _translate = QtCore.QCoreApplication.translate Form.setWindowTitle(_translate("Form", "GDB Console")) - self.textBrowser.setHtml(_translate("Form", "\n" -"\n" -"


")) self.pushButton_Send.setText(_translate("Form", "Send")) - self.pushButton_SendCtrl.setText(_translate("Form", "Send ctrl+c")) self.radioButton_CLI.setText(_translate("Form", "CLI")) self.radioButton_MI.setText(_translate("Form", "MI")) - diff --git a/GUI/ConsoleWidget.ui b/GUI/ConsoleWidget.ui index 51b3eba2..f4b589dd 100644 --- a/GUI/ConsoleWidget.ui +++ b/GUI/ConsoleWidget.ui @@ -51,6 +51,15 @@ + + + + 0 + 255 + 0 + + + @@ -71,6 +80,15 @@ + + + + 0 + 255 + 0 + + + @@ -91,24 +109,25 @@ + + + + 0 + 255 + 0 + + + Monospace - 50 false false - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Monospace'; font-size:9pt; font-weight:400; font-style:normal;"> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> -
@@ -123,13 +142,6 @@ p, li { white-space: pre-wrap; } - - - - Send ctrl+c - - - diff --git a/GUI/CustomAbstractTableModels/AsciiModel.py b/GUI/CustomAbstractTableModels/AsciiModel.py deleted file mode 100644 index b43750e9..00000000 --- a/GUI/CustomAbstractTableModels/AsciiModel.py +++ /dev/null @@ -1,40 +0,0 @@ -""" -Copyright (C) 2016-2017 Korcan Karaokçu - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -""" -from PyQt5.QtCore import QVariant, Qt -from PyQt5.QtGui import QColor -from GUI.CustomAbstractTableModels.HexModel import QHexModel - -from libpince import SysUtils, GDB_Engine - - -class QAsciiModel(QHexModel): - def __init__(self, row_count, column_count, parent=None): - super().__init__(row_count, column_count, parent) - - def data(self, QModelIndex, int_role=None): - if not QModelIndex.isValid(): - return QVariant() - if int_role == Qt.BackgroundColorRole: - address = self.current_address + QModelIndex.row() * self.column_count + QModelIndex.column() - if SysUtils.modulo_address(address, GDB_Engine.inferior_arch) in self.breakpoint_list: - return QVariant(QColor(Qt.red)) - elif int_role != Qt.DisplayRole: - return QVariant() - if self.data_array is None: - return QVariant() - return QVariant( - SysUtils.aob_to_str(self.data_array[QModelIndex.row() * self.column_count + QModelIndex.column()])) diff --git a/GUI/CustomAbstractTableModels/HexModel.py b/GUI/CustomAbstractTableModels/HexModel.py deleted file mode 100644 index dc2fcb3f..00000000 --- a/GUI/CustomAbstractTableModels/HexModel.py +++ /dev/null @@ -1,65 +0,0 @@ -""" -Copyright (C) 2016-2017 Korcan Karaokçu - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -""" -from PyQt5.QtCore import QAbstractTableModel, QVariant, Qt -from PyQt5.QtGui import QColor - -from libpince import SysUtils, GDB_Engine - - -class QHexModel(QAbstractTableModel): - def __init__(self, row_count, column_count, parent=None): - super().__init__(parent) - self.data_array = [] - self.breakpoint_list = set() - self.row_count = row_count - self.column_count = column_count - self.current_address = 0 - - def rowCount(self, QModelIndex_parent=None, *args, **kwargs): - return self.row_count - - def columnCount(self, QModelIndex_parent=None, *args, **kwargs): - return self.column_count - - def data(self, QModelIndex, int_role=None): - if not QModelIndex.isValid(): - return QVariant() - if int_role == Qt.BackgroundColorRole: - address = self.current_address + QModelIndex.row() * self.column_count + QModelIndex.column() - if SysUtils.modulo_address(address, GDB_Engine.inferior_arch) in self.breakpoint_list: - return QVariant(QColor(Qt.red)) - elif int_role != Qt.DisplayRole: - return QVariant() - if self.data_array is None: - return QVariant() - return QVariant(self.data_array[QModelIndex.row() * self.column_count + QModelIndex.column()]) - - def refresh(self, int_address, offset, data_array=None, breakpoint_info=None): - int_address = SysUtils.modulo_address(int_address, GDB_Engine.inferior_arch) - self.breakpoint_list.clear() - if data_array is None: - self.data_array = GDB_Engine.hex_dump(int_address, offset) - else: - self.data_array = data_array - if breakpoint_info is None: - breakpoint_info = GDB_Engine.get_breakpoint_info() - for breakpoint in breakpoint_info: - breakpoint_address = int(breakpoint.address, 16) - for i in range(breakpoint.size): - self.breakpoint_list.add(SysUtils.modulo_address(breakpoint_address + i, GDB_Engine.inferior_arch)) - self.current_address = int_address - self.layoutChanged.emit() diff --git a/GUI/CustomLabels/FlagRegisterLabel.py b/GUI/CustomLabels/FlagRegisterLabel.py deleted file mode 100644 index 5dbbb03b..00000000 --- a/GUI/CustomLabels/FlagRegisterLabel.py +++ /dev/null @@ -1,47 +0,0 @@ -""" -Copyright (C) 2016-2017 Korcan Karaokçu - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -""" -from PyQt5.QtWidgets import QLabel, QMessageBox -from PyQt5.QtGui import QCursor -from PyQt5.QtCore import Qt -from libpince import GDB_Engine -from PINCE import InputDialogForm - - -class QFlagRegisterLabel(QLabel): - def __init__(self, parent=None): - super().__init__(parent) - - def set_value(self, value): - new = value - old = self.text() - if old != new: - self.setStyleSheet("color: red") - else: - self.setStyleSheet("color: black") - self.setText(new) - - def enterEvent(self, QEvent): - self.setCursor(QCursor(Qt.PointingHandCursor)) - - def mouseDoubleClickEvent(self, QMouseEvent): - registers = GDB_Engine.read_registers() - current_flag = self.objectName().lower() - label_text = "Enter the new value of flag " + self.objectName() - register_dialog = InputDialogForm(item_list=[(label_text, ["0", "1", int(registers[current_flag])])]) - if register_dialog.exec_(): - GDB_Engine.set_register_flag(current_flag, register_dialog.get_values()) - self.set_value(GDB_Engine.read_registers()[current_flag]) diff --git a/GUI/CustomLabels/RegisterLabel.py b/GUI/CustomLabels/RegisterLabel.py deleted file mode 100644 index 8e8b04c9..00000000 --- a/GUI/CustomLabels/RegisterLabel.py +++ /dev/null @@ -1,67 +0,0 @@ -""" -Copyright (C) 2016-2017 Korcan Karaokçu - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -""" -from PyQt5.QtWidgets import QLabel, QMenu -from PyQt5.QtGui import QCursor -from PyQt5.QtCore import Qt -from libpince import GDB_Engine -from libpince import GuiUtils -from PINCE import InputDialogForm - - -class QRegisterLabel(QLabel): - def __init__(self, parent=None): - super().__init__(parent) - - def set_value(self, value): - new = self.objectName() + "=" + value - old = self.text() - if old != new: - self.setStyleSheet("color: red") - else: - self.setStyleSheet("color: black") - self.setText(new) - - def enterEvent(self, QEvent): - self.setCursor(QCursor(Qt.PointingHandCursor)) - - def mouseDoubleClickEvent(self, QMouseEvent): - registers = GDB_Engine.read_registers() - current_register = self.objectName().lower() - register_dialog = InputDialogForm( - item_list=[("Enter the new value of register " + self.objectName(), registers[current_register])]) - if register_dialog.exec_(): - GDB_Engine.set_convenience_variable(current_register, register_dialog.get_values()) - self.set_value(GDB_Engine.read_registers()[current_register]) - - def contextMenuEvent(self, QContextMenuEvent): - menu = QMenu() - show_in_hex_view = menu.addAction("Show in HexView") - show_in_disassembler = menu.addAction("Show in Disassembler") - font_size = self.font().pointSize() - menu.setStyleSheet("font-size: " + str(font_size) + "pt;") - action = menu.exec_(QContextMenuEvent.globalPos()) - if action == show_in_hex_view: - parent = GuiUtils.search_parents_by_function(self, "hex_dump_address") - if parent.objectName() == "MainWindow_MemoryView": - address = self.text().split("=")[-1] - address_int = int(address, 16) - parent.hex_dump_address(address_int) - elif action == show_in_disassembler: - parent = GuiUtils.search_parents_by_function(self, "disassemble_expression") - if parent.objectName() == "MainWindow_MemoryView": - address = self.text().split("=")[-1] - parent.disassemble_expression(address) diff --git a/GUI/CustomTableViews/HexView.py b/GUI/CustomTableViews/HexView.py deleted file mode 100644 index afcf1760..00000000 --- a/GUI/CustomTableViews/HexView.py +++ /dev/null @@ -1,48 +0,0 @@ -""" -Copyright (C) 2016-2017 Korcan Karaokçu - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -""" -from PyQt5.QtWidgets import QTableView, QAbstractItemView -from PyQt5.QtCore import Qt - -from libpince import SysUtils, GDB_Engine - - -class QHexView(QTableView): - def __init__(self, parent=None): - super().__init__(parent) - self.horizontalHeader().setVisible(False) - self.verticalHeader().setVisible(False) - self.verticalHeader().setDefaultSectionSize(self.verticalHeader().minimumSectionSize()) - self.horizontalHeader().setDefaultSectionSize(self.horizontalHeader().minimumSectionSize()) - self.setStyleSheet("QTableView {background-color: transparent;}") - self.setShowGrid(False) - self.setSelectionMode(QAbstractItemView.SingleSelection) - self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) - self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) - self.setAutoScroll(False) - - def wheelEvent(self, QWheelEvent): - QWheelEvent.ignore() - - def resize_to_contents(self): - size = self.columnWidth(0) * self.model().columnCount() - self.setMinimumWidth(size) - self.setMaximumWidth(size) - - def get_selected_address(self): - ci = self.currentIndex() - current_address = self.model().current_address + ci.row() * self.model().columnCount() + ci.column() - return SysUtils.modulo_address(current_address, GDB_Engine.inferior_arch) diff --git a/GUI/DissectCodeDialog.py b/GUI/DissectCodeDialog.py index 84cb236b..2c432f3f 100644 --- a/GUI/DissectCodeDialog.py +++ b/GUI/DissectCodeDialog.py @@ -1,12 +1,13 @@ -# -*- coding: utf-8 -*- - # Form implementation generated from reading ui file 'DissectCodeDialog.ui' # -# Created by: PyQt5 UI code generator 5.5.1 +# Created by: PyQt6 UI code generator 6.4.2 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets -from PyQt5 import QtCore, QtGui, QtWidgets class Ui_Dialog(object): def setupUi(self, Dialog): @@ -14,12 +15,16 @@ def setupUi(self, Dialog): Dialog.resize(799, 412) self.gridLayout = QtWidgets.QGridLayout(Dialog) self.gridLayout.setObjectName("gridLayout") - self.splitter = QtWidgets.QSplitter(Dialog) - self.splitter.setOrientation(QtCore.Qt.Horizontal) + self.splitter = QtWidgets.QSplitter(parent=Dialog) + self.splitter.setOrientation(QtCore.Qt.Orientation.Horizontal) self.splitter.setObjectName("splitter") - self.tableWidget_ExecutableMemoryRegions = QtWidgets.QTableWidget(self.splitter) - self.tableWidget_ExecutableMemoryRegions.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) - self.tableWidget_ExecutableMemoryRegions.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) + self.tableWidget_ExecutableMemoryRegions = QtWidgets.QTableWidget(parent=self.splitter) + font = QtGui.QFont() + font.setFamily("Monospace") + self.tableWidget_ExecutableMemoryRegions.setFont(font) + self.tableWidget_ExecutableMemoryRegions.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers) + self.tableWidget_ExecutableMemoryRegions.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows) + self.tableWidget_ExecutableMemoryRegions.setWordWrap(False) self.tableWidget_ExecutableMemoryRegions.setObjectName("tableWidget_ExecutableMemoryRegions") self.tableWidget_ExecutableMemoryRegions.setColumnCount(2) self.tableWidget_ExecutableMemoryRegions.setRowCount(0) @@ -31,91 +36,95 @@ def setupUi(self, Dialog): self.tableWidget_ExecutableMemoryRegions.verticalHeader().setVisible(False) self.tableWidget_ExecutableMemoryRegions.verticalHeader().setDefaultSectionSize(16) self.tableWidget_ExecutableMemoryRegions.verticalHeader().setMinimumSectionSize(16) - self.layoutWidget = QtWidgets.QWidget(self.splitter) + self.layoutWidget = QtWidgets.QWidget(parent=self.splitter) self.layoutWidget.setObjectName("layoutWidget") self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.layoutWidget) + self.verticalLayout_2.setContentsMargins(0, 0, 0, 0) self.verticalLayout_2.setObjectName("verticalLayout_2") self.verticalLayout = QtWidgets.QVBoxLayout() self.verticalLayout.setObjectName("verticalLayout") - self.label_ScanInfo = QtWidgets.QLabel(self.layoutWidget) - self.label_ScanInfo.setAlignment(QtCore.Qt.AlignCenter) - self.label_ScanInfo.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse) + self.label_ScanInfo = QtWidgets.QLabel(parent=self.layoutWidget) + self.label_ScanInfo.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + self.label_ScanInfo.setTextInteractionFlags(QtCore.Qt.TextInteractionFlag.LinksAccessibleByMouse|QtCore.Qt.TextInteractionFlag.TextSelectableByMouse) self.label_ScanInfo.setObjectName("label_ScanInfo") self.verticalLayout.addWidget(self.label_ScanInfo) - self.label_RegionInfo = QtWidgets.QLabel(self.layoutWidget) - self.label_RegionInfo.setAlignment(QtCore.Qt.AlignCenter) - self.label_RegionInfo.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse) + self.label_RegionInfo = QtWidgets.QLabel(parent=self.layoutWidget) + self.label_RegionInfo.setText("-") + self.label_RegionInfo.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + self.label_RegionInfo.setTextInteractionFlags(QtCore.Qt.TextInteractionFlag.LinksAccessibleByMouse|QtCore.Qt.TextInteractionFlag.TextSelectableByMouse) self.label_RegionInfo.setObjectName("label_RegionInfo") self.verticalLayout.addWidget(self.label_RegionInfo) - self.label_RegionCountInfo = QtWidgets.QLabel(self.layoutWidget) - self.label_RegionCountInfo.setAlignment(QtCore.Qt.AlignCenter) - self.label_RegionCountInfo.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse) + self.label_RegionCountInfo = QtWidgets.QLabel(parent=self.layoutWidget) + self.label_RegionCountInfo.setText("-") + self.label_RegionCountInfo.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + self.label_RegionCountInfo.setTextInteractionFlags(QtCore.Qt.TextInteractionFlag.LinksAccessibleByMouse|QtCore.Qt.TextInteractionFlag.TextSelectableByMouse) self.label_RegionCountInfo.setObjectName("label_RegionCountInfo") self.verticalLayout.addWidget(self.label_RegionCountInfo) - self.label_4 = QtWidgets.QLabel(self.layoutWidget) - self.label_4.setAlignment(QtCore.Qt.AlignCenter) - self.label_4.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse) + self.label_4 = QtWidgets.QLabel(parent=self.layoutWidget) + self.label_4.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + self.label_4.setTextInteractionFlags(QtCore.Qt.TextInteractionFlag.LinksAccessibleByMouse|QtCore.Qt.TextInteractionFlag.TextSelectableByMouse) self.label_4.setObjectName("label_4") self.verticalLayout.addWidget(self.label_4) - self.label_CurrentRange = QtWidgets.QLabel(self.layoutWidget) - self.label_CurrentRange.setAlignment(QtCore.Qt.AlignCenter) - self.label_CurrentRange.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse) + self.label_CurrentRange = QtWidgets.QLabel(parent=self.layoutWidget) + self.label_CurrentRange.setText("-") + self.label_CurrentRange.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + self.label_CurrentRange.setTextInteractionFlags(QtCore.Qt.TextInteractionFlag.LinksAccessibleByMouse|QtCore.Qt.TextInteractionFlag.TextSelectableByMouse) self.label_CurrentRange.setObjectName("label_CurrentRange") self.verticalLayout.addWidget(self.label_CurrentRange) self.verticalLayout_2.addLayout(self.verticalLayout) - self.line = QtWidgets.QFrame(self.layoutWidget) - self.line.setFrameShape(QtWidgets.QFrame.HLine) - self.line.setFrameShadow(QtWidgets.QFrame.Sunken) + self.line = QtWidgets.QFrame(parent=self.layoutWidget) + self.line.setFrameShape(QtWidgets.QFrame.Shape.HLine) + self.line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) self.line.setObjectName("line") self.verticalLayout_2.addWidget(self.line) - self.label = QtWidgets.QLabel(self.layoutWidget) - self.label.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse) + self.label = QtWidgets.QLabel(parent=self.layoutWidget) + self.label.setTextInteractionFlags(QtCore.Qt.TextInteractionFlag.LinksAccessibleByMouse|QtCore.Qt.TextInteractionFlag.TextSelectableByMouse) self.label.setObjectName("label") self.verticalLayout_2.addWidget(self.label) - self.label_StringReferenceCount = QtWidgets.QLabel(self.layoutWidget) + self.label_StringReferenceCount = QtWidgets.QLabel(parent=self.layoutWidget) self.label_StringReferenceCount.setText("") - self.label_StringReferenceCount.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse) + self.label_StringReferenceCount.setTextInteractionFlags(QtCore.Qt.TextInteractionFlag.LinksAccessibleByMouse|QtCore.Qt.TextInteractionFlag.TextSelectableByMouse) self.label_StringReferenceCount.setObjectName("label_StringReferenceCount") self.verticalLayout_2.addWidget(self.label_StringReferenceCount) - self.label_2 = QtWidgets.QLabel(self.layoutWidget) - self.label_2.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse) + self.label_2 = QtWidgets.QLabel(parent=self.layoutWidget) + self.label_2.setTextInteractionFlags(QtCore.Qt.TextInteractionFlag.LinksAccessibleByMouse|QtCore.Qt.TextInteractionFlag.TextSelectableByMouse) self.label_2.setObjectName("label_2") self.verticalLayout_2.addWidget(self.label_2) - self.label_JumpReferenceCount = QtWidgets.QLabel(self.layoutWidget) + self.label_JumpReferenceCount = QtWidgets.QLabel(parent=self.layoutWidget) self.label_JumpReferenceCount.setText("") - self.label_JumpReferenceCount.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse) + self.label_JumpReferenceCount.setTextInteractionFlags(QtCore.Qt.TextInteractionFlag.LinksAccessibleByMouse|QtCore.Qt.TextInteractionFlag.TextSelectableByMouse) self.label_JumpReferenceCount.setObjectName("label_JumpReferenceCount") self.verticalLayout_2.addWidget(self.label_JumpReferenceCount) - self.label_3 = QtWidgets.QLabel(self.layoutWidget) - self.label_3.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse) + self.label_3 = QtWidgets.QLabel(parent=self.layoutWidget) + self.label_3.setTextInteractionFlags(QtCore.Qt.TextInteractionFlag.LinksAccessibleByMouse|QtCore.Qt.TextInteractionFlag.TextSelectableByMouse) self.label_3.setObjectName("label_3") self.verticalLayout_2.addWidget(self.label_3) - self.label_CallReferenceCount = QtWidgets.QLabel(self.layoutWidget) + self.label_CallReferenceCount = QtWidgets.QLabel(parent=self.layoutWidget) self.label_CallReferenceCount.setText("") - self.label_CallReferenceCount.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse) + self.label_CallReferenceCount.setTextInteractionFlags(QtCore.Qt.TextInteractionFlag.LinksAccessibleByMouse|QtCore.Qt.TextInteractionFlag.TextSelectableByMouse) self.label_CallReferenceCount.setObjectName("label_CallReferenceCount") self.verticalLayout_2.addWidget(self.label_CallReferenceCount) - self.line_2 = QtWidgets.QFrame(self.layoutWidget) - self.line_2.setFrameShape(QtWidgets.QFrame.HLine) - self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken) + self.line_2 = QtWidgets.QFrame(parent=self.layoutWidget) + self.line_2.setFrameShape(QtWidgets.QFrame.Shape.HLine) + self.line_2.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) self.line_2.setObjectName("line_2") self.verticalLayout_2.addWidget(self.line_2) - self.checkBox_DiscardInvalidStrings = QtWidgets.QCheckBox(self.layoutWidget) + self.checkBox_DiscardInvalidStrings = QtWidgets.QCheckBox(parent=self.layoutWidget) self.checkBox_DiscardInvalidStrings.setChecked(True) self.checkBox_DiscardInvalidStrings.setObjectName("checkBox_DiscardInvalidStrings") self.verticalLayout_2.addWidget(self.checkBox_DiscardInvalidStrings) self.horizontalLayout = QtWidgets.QHBoxLayout() self.horizontalLayout.setObjectName("horizontalLayout") - spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) self.horizontalLayout.addItem(spacerItem) - self.pushButton_StartCancel = QtWidgets.QPushButton(self.layoutWidget) + self.pushButton_StartCancel = QtWidgets.QPushButton(parent=self.layoutWidget) self.pushButton_StartCancel.setText("") self.pushButton_StartCancel.setObjectName("pushButton_StartCancel") self.horizontalLayout.addWidget(self.pushButton_StartCancel) - spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) self.horizontalLayout.addItem(spacerItem1) self.verticalLayout_2.addLayout(self.horizontalLayout) - spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) self.verticalLayout_2.addItem(spacerItem2) self.gridLayout.addWidget(self.splitter, 0, 0, 1, 1) @@ -130,14 +139,10 @@ def retranslateUi(self, Dialog): item = self.tableWidget_ExecutableMemoryRegions.horizontalHeaderItem(1) item.setText(_translate("Dialog", "Path")) self.label_ScanInfo.setText(_translate("Dialog", "Selected regions will be scanned")) - self.label_RegionInfo.setText(_translate("Dialog", "-")) - self.label_RegionCountInfo.setText(_translate("Dialog", "-")) self.label_4.setText(_translate("Dialog", "Currently scanning range:")) - self.label_CurrentRange.setText(_translate("Dialog", "-")) self.label.setText(_translate("Dialog", "String references found:")) self.label_2.setText(_translate("Dialog", "Jumps found:")) self.label_3.setText(_translate("Dialog", "Calls found:")) self.checkBox_DiscardInvalidStrings.setToolTip(_translate("Dialog", "Entries that can\'t be decoded as utf-8 won\'t be included in referenced strings\n" "Unchecking it makes ReferencedStringsWidget load slower but allows you to examine non-string pointers on it")) self.checkBox_DiscardInvalidStrings.setText(_translate("Dialog", "Discard invalid strings")) - diff --git a/GUI/DissectCodeDialog.ui b/GUI/DissectCodeDialog.ui index 948db664..74cbe9cd 100644 --- a/GUI/DissectCodeDialog.ui +++ b/GUI/DissectCodeDialog.ui @@ -20,22 +20,30 @@ Qt::Horizontal + + + Monospace + + QAbstractItemView::NoEditTriggers QAbstractItemView::SelectRows + + false + true false - + 16 - + 16 @@ -69,7 +77,7 @@ - - + - Qt::AlignCenter @@ -82,7 +90,7 @@ - - + - Qt::AlignCenter @@ -108,7 +116,7 @@ - - + - Qt::AlignCenter diff --git a/GUI/EditInstructionDialog.py b/GUI/EditInstructionDialog.py new file mode 100644 index 00000000..a2024439 --- /dev/null +++ b/GUI/EditInstructionDialog.py @@ -0,0 +1,57 @@ +# Form implementation generated from reading ui file 'EditInstructionDialog.ui' +# +# Created by: PyQt6 UI code generator 6.4.0 +# +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets + + +class Ui_Dialog(object): + def setupUi(self, Dialog): + Dialog.setObjectName("Dialog") + Dialog.setEnabled(True) + Dialog.resize(500, 124) + Dialog.setMaximumSize(QtCore.QSize(16777215, 124)) + self.gridLayout = QtWidgets.QGridLayout(Dialog) + self.gridLayout.setObjectName("gridLayout") + self.lineEdit_Bytes = QtWidgets.QLineEdit(Dialog) + self.lineEdit_Bytes.setObjectName("lineEdit_Bytes") + self.gridLayout.addWidget(self.lineEdit_Bytes, 1, 0, 1, 1) + self.buttonBox = QtWidgets.QDialogButtonBox(Dialog) + self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.StandardButton.Cancel|QtWidgets.QDialogButtonBox.StandardButton.Ok) + self.buttonBox.setObjectName("buttonBox") + self.gridLayout.addWidget(self.buttonBox, 2, 0, 1, 1) + self.horizontalLayout = QtWidgets.QHBoxLayout() + self.horizontalLayout.setObjectName("horizontalLayout") + self.label = QtWidgets.QLabel(Dialog) + self.label.setObjectName("label") + self.horizontalLayout.addWidget(self.label) + self.lineEdit_Address = QtWidgets.QLineEdit(Dialog) + self.lineEdit_Address.setEnabled(False) + self.lineEdit_Address.setAutoFillBackground(False) + self.lineEdit_Address.setReadOnly(True) + self.lineEdit_Address.setObjectName("lineEdit_Address") + self.horizontalLayout.addWidget(self.lineEdit_Address) + self.label_2 = QtWidgets.QLabel(Dialog) + self.label_2.setObjectName("label_2") + self.horizontalLayout.addWidget(self.label_2) + self.lineEdit_Instruction = QtWidgets.QLineEdit(Dialog) + self.lineEdit_Instruction.setObjectName("lineEdit_Instruction") + self.horizontalLayout.addWidget(self.lineEdit_Instruction) + self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1) + + self.retranslateUi(Dialog) + self.buttonBox.accepted.connect(Dialog.accept) # type: ignore + self.buttonBox.rejected.connect(Dialog.reject) # type: ignore + QtCore.QMetaObject.connectSlotsByName(Dialog) + + def retranslateUi(self, Dialog): + _translate = QtCore.QCoreApplication.translate + Dialog.setWindowTitle(_translate("Dialog", "Edit Instruction")) + self.label.setText(_translate("Dialog", "Address:")) + self.label_2.setText(_translate("Dialog", "Instruction:")) + self.lineEdit_Instruction.setToolTip(_translate("Dialog", "Multiple entries are separated with ;")) diff --git a/GUI/EditInstructionDialog.ui b/GUI/EditInstructionDialog.ui new file mode 100644 index 00000000..a1376f0e --- /dev/null +++ b/GUI/EditInstructionDialog.ui @@ -0,0 +1,114 @@ + + + Dialog + + + true + + + + 0 + 0 + 500 + 124 + + + + + 16777215 + 124 + + + + Edit Instruction + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + Address: + + + + + + + false + + + false + + + true + + + + + + + Instruction: + + + + + + + Multiple entries are separated with ; + + + + + + + + + + + buttonBox + accepted() + Dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/GUI/EditTypeDialog.py b/GUI/EditTypeDialog.py index fc4d0a5d..ab428ac3 100644 --- a/GUI/EditTypeDialog.py +++ b/GUI/EditTypeDialog.py @@ -1,76 +1,90 @@ -# -*- coding: utf-8 -*- - # Form implementation generated from reading ui file 'EditTypeDialog.ui' # -# Created by: PyQt5 UI code generator 5.5.1 +# Created by: PyQt6 UI code generator 6.4.2 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets -from PyQt5 import QtCore, QtGui, QtWidgets class Ui_Dialog(object): def setupUi(self, Dialog): Dialog.setObjectName("Dialog") - Dialog.resize(345, 119) - Dialog.setMaximumSize(QtCore.QSize(345, 119)) + Dialog.resize(235, 163) self.gridLayout = QtWidgets.QGridLayout(Dialog) self.gridLayout.setObjectName("gridLayout") self.horizontalLayout_3 = QtWidgets.QHBoxLayout() self.horizontalLayout_3.setObjectName("horizontalLayout_3") self.verticalLayout = QtWidgets.QVBoxLayout() self.verticalLayout.setObjectName("verticalLayout") - self.label = QtWidgets.QLabel(Dialog) + self.label = QtWidgets.QLabel(parent=Dialog) self.label.setObjectName("label") self.verticalLayout.addWidget(self.label) - self.comboBox_ValueType = QtWidgets.QComboBox(Dialog) + self.comboBox_ValueType = QtWidgets.QComboBox(parent=Dialog) self.comboBox_ValueType.setObjectName("comboBox_ValueType") self.verticalLayout.addWidget(self.comboBox_ValueType) self.horizontalLayout_3.addLayout(self.verticalLayout) self.verticalLayout_3 = QtWidgets.QVBoxLayout() self.verticalLayout_3.setObjectName("verticalLayout_3") - self.label_Length = QtWidgets.QLabel(Dialog) - self.label_Length.setObjectName("label_Length") - self.verticalLayout_3.addWidget(self.label_Length) + self.label_2 = QtWidgets.QLabel(parent=Dialog) + self.label_2.setObjectName("label_2") + self.verticalLayout_3.addWidget(self.label_2) + self.comboBox_Endianness = QtWidgets.QComboBox(parent=Dialog) + self.comboBox_Endianness.setObjectName("comboBox_Endianness") + self.verticalLayout_3.addWidget(self.comboBox_Endianness) + self.horizontalLayout_3.addLayout(self.verticalLayout_3) + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) + self.horizontalLayout_3.addItem(spacerItem) + self.gridLayout.addLayout(self.horizontalLayout_3, 0, 0, 1, 1) self.horizontalLayout_2 = QtWidgets.QHBoxLayout() self.horizontalLayout_2.setObjectName("horizontalLayout_2") - self.lineEdit_Length = QtWidgets.QLineEdit(Dialog) + self.checkBox_Hex = QtWidgets.QCheckBox(parent=Dialog) + self.checkBox_Hex.setObjectName("checkBox_Hex") + self.horizontalLayout_2.addWidget(self.checkBox_Hex) + self.checkBox_Signed = QtWidgets.QCheckBox(parent=Dialog) + self.checkBox_Signed.setObjectName("checkBox_Signed") + self.horizontalLayout_2.addWidget(self.checkBox_Signed) + spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) + self.horizontalLayout_2.addItem(spacerItem1) + self.gridLayout.addLayout(self.horizontalLayout_2, 1, 0, 1, 1) + self.buttonBox = QtWidgets.QDialogButtonBox(parent=Dialog) + self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.StandardButton.Cancel|QtWidgets.QDialogButtonBox.StandardButton.Ok) + self.buttonBox.setObjectName("buttonBox") + self.gridLayout.addWidget(self.buttonBox, 3, 0, 1, 1, QtCore.Qt.AlignmentFlag.AlignLeft) + self.widget_Length = QtWidgets.QWidget(parent=Dialog) + self.widget_Length.setObjectName("widget_Length") + self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.widget_Length) + self.horizontalLayout_4.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_4.setObjectName("horizontalLayout_4") + self.label_Length = QtWidgets.QLabel(parent=self.widget_Length) + self.label_Length.setObjectName("label_Length") + self.horizontalLayout_4.addWidget(self.label_Length) + self.lineEdit_Length = QtWidgets.QLineEdit(parent=self.widget_Length) + self.lineEdit_Length.setText("10") self.lineEdit_Length.setObjectName("lineEdit_Length") - self.horizontalLayout_2.addWidget(self.lineEdit_Length) - spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.horizontalLayout_2.addItem(spacerItem) - self.verticalLayout_3.addLayout(self.horizontalLayout_2) - self.horizontalLayout_3.addLayout(self.verticalLayout_3) - self.verticalLayout_2 = QtWidgets.QVBoxLayout() - self.verticalLayout_2.setObjectName("verticalLayout_2") - spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.verticalLayout_2.addItem(spacerItem1) - self.checkBox_ZeroTerminate = QtWidgets.QCheckBox(Dialog) + self.horizontalLayout_4.addWidget(self.lineEdit_Length) + self.checkBox_ZeroTerminate = QtWidgets.QCheckBox(parent=self.widget_Length) self.checkBox_ZeroTerminate.setChecked(True) self.checkBox_ZeroTerminate.setObjectName("checkBox_ZeroTerminate") - self.verticalLayout_2.addWidget(self.checkBox_ZeroTerminate) - self.horizontalLayout_3.addLayout(self.verticalLayout_2) - self.gridLayout.addLayout(self.horizontalLayout_3, 0, 0, 1, 1) - self.horizontalLayout = QtWidgets.QHBoxLayout() - self.horizontalLayout.setObjectName("horizontalLayout") - self.buttonBox = QtWidgets.QDialogButtonBox(Dialog) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) - self.buttonBox.setObjectName("buttonBox") - self.horizontalLayout.addWidget(self.buttonBox) - spacerItem2 = QtWidgets.QSpacerItem(37, 17, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.horizontalLayout.addItem(spacerItem2) - self.gridLayout.addLayout(self.horizontalLayout, 1, 0, 1, 1) + self.horizontalLayout_4.addWidget(self.checkBox_ZeroTerminate) + spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) + self.horizontalLayout_4.addItem(spacerItem2) + self.gridLayout.addWidget(self.widget_Length, 2, 0, 1, 1) self.retranslateUi(Dialog) - self.buttonBox.accepted.connect(Dialog.accept) - self.buttonBox.rejected.connect(Dialog.reject) + self.buttonBox.rejected.connect(Dialog.reject) # type: ignore + self.buttonBox.accepted.connect(Dialog.accept) # type: ignore QtCore.QMetaObject.connectSlotsByName(Dialog) def retranslateUi(self, Dialog): _translate = QtCore.QCoreApplication.translate Dialog.setWindowTitle(_translate("Dialog", "Type")) - self.label.setText(_translate("Dialog", "Select the new type")) + self.label.setText(_translate("Dialog", "Type")) + self.label_2.setText(_translate("Dialog", "Endianness")) + self.checkBox_Hex.setText(_translate("Dialog", "Hex")) + self.checkBox_Signed.setText(_translate("Dialog", "Signed")) self.label_Length.setText(_translate("Dialog", "Length")) - self.lineEdit_Length.setText(_translate("Dialog", "10")) self.checkBox_ZeroTerminate.setText(_translate("Dialog", "Zero-Terminated")) - diff --git a/GUI/EditTypeDialog.ui b/GUI/EditTypeDialog.ui index 42b68050..5ee137c3 100644 --- a/GUI/EditTypeDialog.ui +++ b/GUI/EditTypeDialog.ui @@ -6,16 +6,10 @@ 0 0 - 345 - 119 + 235 + 163 - - - 345 - 119 - - Type @@ -27,7 +21,7 @@ - Select the new type + Type @@ -39,76 +33,45 @@ - + - Length + Endianness - - - - - 10 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - + - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Zero-Terminated - - - true - - - - + + + Qt::Horizontal + + + + 40 + 20 + + +
- + - - - Qt::Horizontal + + + Hex - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + Signed @@ -119,46 +82,111 @@ - 37 - 17 + 40 + 20
+ + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Length + + + + + + + 10 + + + + + + + Zero-Terminated + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + buttonBox - accepted() + rejected() Dialog - accept() + reject() - 248 - 254 + 316 + 260 - 157 + 286 274 buttonBox - rejected() + accepted() Dialog - reject() + accept() - 316 - 260 + 248 + 254 - 286 + 157 274 diff --git a/GUI/ExamineReferrersWidget.py b/GUI/ExamineReferrersWidget.py index c6c61691..833b0172 100644 --- a/GUI/ExamineReferrersWidget.py +++ b/GUI/ExamineReferrersWidget.py @@ -1,12 +1,13 @@ -# -*- coding: utf-8 -*- - # Form implementation generated from reading ui file 'ExamineReferrersWidget.ui' # -# Created by: PyQt5 UI code generator 5.5.1 +# Created by: PyQt6 UI code generator 6.4.2 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets -from PyQt5 import QtCore, QtGui, QtWidgets class Ui_Form(object): def setupUi(self, Form): @@ -15,34 +16,41 @@ def setupUi(self, Form): Form.setToolTip("") self.gridLayout = QtWidgets.QGridLayout(Form) self.gridLayout.setObjectName("gridLayout") - self.splitter = QtWidgets.QSplitter(Form) - self.splitter.setOrientation(QtCore.Qt.Horizontal) + self.splitter = QtWidgets.QSplitter(parent=Form) + self.splitter.setOrientation(QtCore.Qt.Orientation.Horizontal) self.splitter.setObjectName("splitter") - self.layoutWidget = QtWidgets.QWidget(self.splitter) + self.layoutWidget = QtWidgets.QWidget(parent=self.splitter) self.layoutWidget.setObjectName("layoutWidget") self.verticalLayout = QtWidgets.QVBoxLayout(self.layoutWidget) + self.verticalLayout.setContentsMargins(0, 0, 0, 0) self.verticalLayout.setObjectName("verticalLayout") self.horizontalLayout_2 = QtWidgets.QHBoxLayout() self.horizontalLayout_2.setObjectName("horizontalLayout_2") - self.lineEdit_Regex = QtWidgets.QLineEdit(self.layoutWidget) + self.lineEdit_Regex = QtWidgets.QLineEdit(parent=self.layoutWidget) self.lineEdit_Regex.setObjectName("lineEdit_Regex") self.horizontalLayout_2.addWidget(self.lineEdit_Regex) - self.checkBox_CaseSensitive = QtWidgets.QCheckBox(self.layoutWidget) + self.checkBox_CaseSensitive = QtWidgets.QCheckBox(parent=self.layoutWidget) self.checkBox_CaseSensitive.setObjectName("checkBox_CaseSensitive") self.horizontalLayout_2.addWidget(self.checkBox_CaseSensitive) - self.checkBox_Regex = QtWidgets.QCheckBox(self.layoutWidget) + self.checkBox_Regex = QtWidgets.QCheckBox(parent=self.layoutWidget) self.checkBox_Regex.setObjectName("checkBox_Regex") self.horizontalLayout_2.addWidget(self.checkBox_Regex) - self.pushButton_Search = QtWidgets.QPushButton(self.layoutWidget) + self.pushButton_Search = QtWidgets.QPushButton(parent=self.layoutWidget) self.pushButton_Search.setObjectName("pushButton_Search") self.horizontalLayout_2.addWidget(self.pushButton_Search) self.verticalLayout.addLayout(self.horizontalLayout_2) - self.listWidget_Referrers = QtWidgets.QListWidget(self.layoutWidget) - self.listWidget_Referrers.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) + self.listWidget_Referrers = QtWidgets.QListWidget(parent=self.layoutWidget) + font = QtGui.QFont() + font.setFamily("Monospace") + self.listWidget_Referrers.setFont(font) + self.listWidget_Referrers.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers) self.listWidget_Referrers.setObjectName("listWidget_Referrers") self.verticalLayout.addWidget(self.listWidget_Referrers) - self.textBrowser_DisasInfo = QtWidgets.QTextBrowser(self.splitter) - self.textBrowser_DisasInfo.setLineWrapMode(QtWidgets.QTextEdit.NoWrap) + self.textBrowser_DisasInfo = QtWidgets.QTextBrowser(parent=self.splitter) + font = QtGui.QFont() + font.setFamily("Monospace") + self.textBrowser_DisasInfo.setFont(font) + self.textBrowser_DisasInfo.setLineWrapMode(QtWidgets.QTextEdit.LineWrapMode.NoWrap) self.textBrowser_DisasInfo.setObjectName("textBrowser_DisasInfo") self.gridLayout.addWidget(self.splitter, 0, 0, 1, 1) @@ -59,4 +67,3 @@ def retranslateUi(self, Form): self.checkBox_Regex.setToolTip(_translate("Form", "Your string will be treated as a regex if checked")) self.checkBox_Regex.setText(_translate("Form", "Regex")) self.pushButton_Search.setText(_translate("Form", "Search(Enter)")) - diff --git a/GUI/ExamineReferrersWidget.ui b/GUI/ExamineReferrersWidget.ui index f44699eb..2af499d9 100644 --- a/GUI/ExamineReferrersWidget.ui +++ b/GUI/ExamineReferrersWidget.ui @@ -67,6 +67,11 @@ + + + Monospace + + QAbstractItemView::NoEditTriggers @@ -75,6 +80,11 @@ + + + Monospace + + QTextEdit::NoWrap diff --git a/GUI/FloatRegisterWidget.py b/GUI/FloatRegisterWidget.py index 35aa8a46..4f54389f 100644 --- a/GUI/FloatRegisterWidget.py +++ b/GUI/FloatRegisterWidget.py @@ -1,26 +1,27 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'floatregisterwidget.ui' +# Form implementation generated from reading ui file 'FloatRegisterWidget.ui' # -# Created: Tue Jul 19 01:12:53 2016 -# by: PyQt5 UI code generator 5.2.1 +# Created by: PyQt6 UI code generator 6.4.2 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets -from PyQt5 import QtCore, QtGui, QtWidgets class Ui_TabWidget(object): def setupUi(self, TabWidget): TabWidget.setObjectName("TabWidget") - TabWidget.resize(400, 300) + TabWidget.resize(400, 316) self.FPU = QtWidgets.QWidget() self.FPU.setObjectName("FPU") self.gridLayout = QtWidgets.QGridLayout(self.FPU) self.gridLayout.setObjectName("gridLayout") - self.tableWidget_FPU = QtWidgets.QTableWidget(self.FPU) - self.tableWidget_FPU.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) - self.tableWidget_FPU.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) - self.tableWidget_FPU.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) + self.tableWidget_FPU = QtWidgets.QTableWidget(parent=self.FPU) + self.tableWidget_FPU.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers) + self.tableWidget_FPU.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.SingleSelection) + self.tableWidget_FPU.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows) + self.tableWidget_FPU.setWordWrap(False) self.tableWidget_FPU.setObjectName("tableWidget_FPU") self.tableWidget_FPU.setColumnCount(2) self.tableWidget_FPU.setRowCount(0) @@ -31,15 +32,15 @@ def setupUi(self, TabWidget): self.tableWidget_FPU.horizontalHeader().setStretchLastSection(True) self.tableWidget_FPU.verticalHeader().setVisible(False) self.gridLayout.addWidget(self.tableWidget_FPU, 0, 0, 1, 1) - TabWidget.addTab(self.FPU, "") + TabWidget.addTab(self.FPU, "FPU") self.XMM = QtWidgets.QWidget() self.XMM.setObjectName("XMM") self.gridLayout_2 = QtWidgets.QGridLayout(self.XMM) self.gridLayout_2.setObjectName("gridLayout_2") - self.tableWidget_XMM = QtWidgets.QTableWidget(self.XMM) - self.tableWidget_XMM.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) - self.tableWidget_XMM.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) - self.tableWidget_XMM.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) + self.tableWidget_XMM = QtWidgets.QTableWidget(parent=self.XMM) + self.tableWidget_XMM.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers) + self.tableWidget_XMM.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.SingleSelection) + self.tableWidget_XMM.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows) self.tableWidget_XMM.setObjectName("tableWidget_XMM") self.tableWidget_XMM.setColumnCount(2) self.tableWidget_XMM.setRowCount(0) @@ -50,7 +51,7 @@ def setupUi(self, TabWidget): self.tableWidget_XMM.horizontalHeader().setStretchLastSection(True) self.tableWidget_XMM.verticalHeader().setVisible(False) self.gridLayout_2.addWidget(self.tableWidget_XMM, 0, 0, 1, 1) - TabWidget.addTab(self.XMM, "") + TabWidget.addTab(self.XMM, "XMM") self.retranslateUi(TabWidget) TabWidget.setCurrentIndex(0) @@ -63,10 +64,7 @@ def retranslateUi(self, TabWidget): item.setText(_translate("TabWidget", "Register")) item = self.tableWidget_FPU.horizontalHeaderItem(1) item.setText(_translate("TabWidget", "Value")) - TabWidget.setTabText(TabWidget.indexOf(self.FPU), _translate("TabWidget", "FPU")) item = self.tableWidget_XMM.horizontalHeaderItem(0) item.setText(_translate("TabWidget", "Register")) item = self.tableWidget_XMM.horizontalHeaderItem(1) item.setText(_translate("TabWidget", "Value")) - TabWidget.setTabText(TabWidget.indexOf(self.XMM), _translate("TabWidget", "XMM")) - diff --git a/GUI/FloatRegisterWidget.ui b/GUI/FloatRegisterWidget.ui index 3bd78037..3719368e 100644 --- a/GUI/FloatRegisterWidget.ui +++ b/GUI/FloatRegisterWidget.ui @@ -7,7 +7,7 @@ 0 0 400 - 300 + 316 @@ -18,7 +18,7 @@ - FPU + FPU @@ -32,6 +32,9 @@ QAbstractItemView::SelectRows + + false + true @@ -54,7 +57,7 @@ - XMM + XMM diff --git a/GUI/FunctionsInfoWidget.py b/GUI/FunctionsInfoWidget.py index e63e39a4..84ce319c 100644 --- a/GUI/FunctionsInfoWidget.py +++ b/GUI/FunctionsInfoWidget.py @@ -1,12 +1,13 @@ -# -*- coding: utf-8 -*- - # Form implementation generated from reading ui file 'FunctionsInfoWidget.ui' # -# Created by: PyQt5 UI code generator 5.5.1 +# Created by: PyQt6 UI code generator 6.4.2 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets -from PyQt5 import QtCore, QtGui, QtWidgets class Ui_Form(object): def setupUi(self, Form): @@ -14,10 +15,14 @@ def setupUi(self, Form): Form.resize(640, 555) self.gridLayout = QtWidgets.QGridLayout(Form) self.gridLayout.setObjectName("gridLayout") - self.tableWidget_SymbolInfo = QtWidgets.QTableWidget(Form) - self.tableWidget_SymbolInfo.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) - self.tableWidget_SymbolInfo.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) - self.tableWidget_SymbolInfo.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) + self.tableWidget_SymbolInfo = QtWidgets.QTableWidget(parent=Form) + font = QtGui.QFont() + font.setFamily("Monospace") + self.tableWidget_SymbolInfo.setFont(font) + self.tableWidget_SymbolInfo.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers) + self.tableWidget_SymbolInfo.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.SingleSelection) + self.tableWidget_SymbolInfo.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows) + self.tableWidget_SymbolInfo.setWordWrap(False) self.tableWidget_SymbolInfo.setObjectName("tableWidget_SymbolInfo") self.tableWidget_SymbolInfo.setColumnCount(2) self.tableWidget_SymbolInfo.setRowCount(0) @@ -32,21 +37,24 @@ def setupUi(self, Form): self.gridLayout.addWidget(self.tableWidget_SymbolInfo, 2, 0, 1, 1) self.horizontalLayout = QtWidgets.QHBoxLayout() self.horizontalLayout.setObjectName("horizontalLayout") - self.lineEdit_SearchInput = QtWidgets.QLineEdit(Form) + self.lineEdit_SearchInput = QtWidgets.QLineEdit(parent=Form) self.lineEdit_SearchInput.setObjectName("lineEdit_SearchInput") self.horizontalLayout.addWidget(self.lineEdit_SearchInput) - self.checkBox_CaseSensitive = QtWidgets.QCheckBox(Form) + self.checkBox_CaseSensitive = QtWidgets.QCheckBox(parent=Form) self.checkBox_CaseSensitive.setObjectName("checkBox_CaseSensitive") self.horizontalLayout.addWidget(self.checkBox_CaseSensitive) - self.pushButton_Search = QtWidgets.QPushButton(Form) + self.pushButton_Search = QtWidgets.QPushButton(parent=Form) self.pushButton_Search.setObjectName("pushButton_Search") self.horizontalLayout.addWidget(self.pushButton_Search) - self.pushButton_Help = QtWidgets.QPushButton(Form) + self.pushButton_Help = QtWidgets.QPushButton(parent=Form) self.pushButton_Help.setText("") self.pushButton_Help.setObjectName("pushButton_Help") self.horizontalLayout.addWidget(self.pushButton_Help) self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1) - self.textBrowser_AddressInfo = QtWidgets.QTextBrowser(Form) + self.textBrowser_AddressInfo = QtWidgets.QTextBrowser(parent=Form) + font = QtGui.QFont() + font.setFamily("Monospace") + self.textBrowser_AddressInfo.setFont(font) self.textBrowser_AddressInfo.setObjectName("textBrowser_AddressInfo") self.gridLayout.addWidget(self.textBrowser_AddressInfo, 1, 0, 1, 1) @@ -65,4 +73,3 @@ def retranslateUi(self, Form): self.checkBox_CaseSensitive.setToolTip(_translate("Form", "Ignore case if checked")) self.checkBox_CaseSensitive.setText(_translate("Form", "Case sensitive")) self.pushButton_Search.setText(_translate("Form", "Search(Enter)")) - diff --git a/GUI/FunctionsInfoWidget.ui b/GUI/FunctionsInfoWidget.ui index b295e1a7..3fb27c7e 100644 --- a/GUI/FunctionsInfoWidget.ui +++ b/GUI/FunctionsInfoWidget.ui @@ -16,6 +16,11 @@ + + + Monospace + + QAbstractItemView::NoEditTriggers @@ -28,16 +33,19 @@ true + + false + true false - + 20 - + 20 @@ -88,7 +96,13 @@ - + + + + Monospace + + + diff --git a/GUI/GenPyFromUI.sh b/GUI/GenPyFromUI.sh new file mode 100755 index 00000000..8fe7b4c0 --- /dev/null +++ b/GUI/GenPyFromUI.sh @@ -0,0 +1,5 @@ +for uifile in *.ui +do + outfile=$(echo $uifile | sed 's/\.ui/\.py/g') + pyuic6 $uifile -o $outfile +done \ No newline at end of file diff --git a/GUI/HandleSignalsDialog.py b/GUI/HandleSignalsDialog.py new file mode 100644 index 00000000..bfb9294c --- /dev/null +++ b/GUI/HandleSignalsDialog.py @@ -0,0 +1,55 @@ +# Form implementation generated from reading ui file 'HandleSignalsDialog.ui' +# +# Created by: PyQt6 UI code generator 6.4.2 +# +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets + + +class Ui_Dialog(object): + def setupUi(self, Dialog): + Dialog.setObjectName("Dialog") + Dialog.resize(363, 523) + self.gridLayout = QtWidgets.QGridLayout(Dialog) + self.gridLayout.setObjectName("gridLayout") + self.tableWidget_Signals = QtWidgets.QTableWidget(parent=Dialog) + self.tableWidget_Signals.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers) + self.tableWidget_Signals.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.SingleSelection) + self.tableWidget_Signals.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows) + self.tableWidget_Signals.setObjectName("tableWidget_Signals") + self.tableWidget_Signals.setColumnCount(3) + self.tableWidget_Signals.setRowCount(0) + item = QtWidgets.QTableWidgetItem() + self.tableWidget_Signals.setHorizontalHeaderItem(0, item) + item = QtWidgets.QTableWidgetItem() + self.tableWidget_Signals.setHorizontalHeaderItem(1, item) + item = QtWidgets.QTableWidgetItem() + self.tableWidget_Signals.setHorizontalHeaderItem(2, item) + self.tableWidget_Signals.horizontalHeader().setStretchLastSection(True) + self.tableWidget_Signals.verticalHeader().setVisible(False) + self.tableWidget_Signals.verticalHeader().setDefaultSectionSize(16) + self.tableWidget_Signals.verticalHeader().setMinimumSectionSize(16) + self.gridLayout.addWidget(self.tableWidget_Signals, 0, 0, 1, 1) + self.buttonBox = QtWidgets.QDialogButtonBox(parent=Dialog) + self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.StandardButton.Cancel|QtWidgets.QDialogButtonBox.StandardButton.Ok) + self.buttonBox.setObjectName("buttonBox") + self.gridLayout.addWidget(self.buttonBox, 1, 0, 1, 1) + + self.retranslateUi(Dialog) + self.buttonBox.rejected.connect(Dialog.reject) # type: ignore + self.buttonBox.accepted.connect(Dialog.accept) # type: ignore + QtCore.QMetaObject.connectSlotsByName(Dialog) + + def retranslateUi(self, Dialog): + _translate = QtCore.QCoreApplication.translate + Dialog.setWindowTitle(_translate("Dialog", "Handle Signals")) + item = self.tableWidget_Signals.horizontalHeaderItem(0) + item.setText(_translate("Dialog", "Signal")) + item = self.tableWidget_Signals.horizontalHeaderItem(1) + item.setText(_translate("Dialog", "Stop & Print")) + item = self.tableWidget_Signals.horizontalHeaderItem(2) + item.setText(_translate("Dialog", "Pass to Program")) diff --git a/GUI/HandleSignalsDialog.ui b/GUI/HandleSignalsDialog.ui new file mode 100644 index 00000000..a07fb787 --- /dev/null +++ b/GUI/HandleSignalsDialog.ui @@ -0,0 +1,104 @@ + + + Dialog + + + + 0 + 0 + 363 + 523 + + + + Handle Signals + + + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + true + + + false + + + 16 + + + 16 + + + + Signal + + + + + Stop & Print + + + + + Pass to Program + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + rejected() + Dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + buttonBox + accepted() + Dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + diff --git a/GUI/HexEditDialog.py b/GUI/HexEditDialog.py index 179ff306..305c866c 100644 --- a/GUI/HexEditDialog.py +++ b/GUI/HexEditDialog.py @@ -1,13 +1,13 @@ -# -*- coding: utf-8 -*- - # Form implementation generated from reading ui file 'HexEditDialog.ui' # -# Created: Fri Dec 9 21:31:02 2016 -# by: PyQt5 UI code generator 5.2.1 +# Created by: PyQt6 UI code generator 6.3.1 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets -from PyQt5 import QtCore, QtGui, QtWidgets class Ui_Dialog(object): def setupUi(self, Dialog): @@ -40,14 +40,14 @@ def setupUi(self, Dialog): self.lineEdit_HexView.setObjectName("lineEdit_HexView") self.gridLayout.addWidget(self.lineEdit_HexView, 2, 0, 1, 1) self.buttonBox = QtWidgets.QDialogButtonBox(Dialog) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) + self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.StandardButton.Cancel|QtWidgets.QDialogButtonBox.StandardButton.Ok) self.buttonBox.setObjectName("buttonBox") self.gridLayout.addWidget(self.buttonBox, 3, 0, 1, 1) self.retranslateUi(Dialog) - self.buttonBox.accepted.connect(Dialog.accept) - self.buttonBox.rejected.connect(Dialog.reject) + self.buttonBox.accepted.connect(Dialog.accept) # type: ignore + self.buttonBox.rejected.connect(Dialog.reject) # type: ignore QtCore.QMetaObject.connectSlotsByName(Dialog) def retranslateUi(self, Dialog): @@ -56,4 +56,3 @@ def retranslateUi(self, Dialog): self.label.setText(_translate("Dialog", "Address:")) self.label_2.setText(_translate("Dialog", "Length:")) self.pushButton_Refresh.setText(_translate("Dialog", "Refresh")) - diff --git a/GUI/InputDialog.py b/GUI/InputDialog.py index 5424a658..aa26a5a7 100644 --- a/GUI/InputDialog.py +++ b/GUI/InputDialog.py @@ -1,12 +1,13 @@ -# -*- coding: utf-8 -*- - # Form implementation generated from reading ui file 'InputDialog.ui' # -# Created by: PyQt5 UI code generator 5.5.1 +# Created by: PyQt6 UI code generator 6.3.1 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets -from PyQt5 import QtCore, QtGui, QtWidgets class Ui_Dialog(object): def setupUi(self, Dialog): @@ -18,18 +19,17 @@ def setupUi(self, Dialog): self.verticalLayout = QtWidgets.QVBoxLayout() self.verticalLayout.setObjectName("verticalLayout") self.buttonBox = QtWidgets.QDialogButtonBox(Dialog) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.NoButton) + self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.StandardButton.NoButton) self.buttonBox.setCenterButtons(True) self.buttonBox.setObjectName("buttonBox") self.verticalLayout.addWidget(self.buttonBox) self.horizontalLayout.addLayout(self.verticalLayout) self.retranslateUi(Dialog) - self.buttonBox.accepted.connect(Dialog.accept) - self.buttonBox.rejected.connect(Dialog.reject) + self.buttonBox.accepted.connect(Dialog.accept) # type: ignore + self.buttonBox.rejected.connect(Dialog.reject) # type: ignore QtCore.QMetaObject.connectSlotsByName(Dialog) def retranslateUi(self, Dialog): pass - diff --git a/GUI/ItemDelegates/HexDelegate.py b/GUI/ItemDelegates/HexDelegate.py new file mode 100644 index 00000000..904da3b8 --- /dev/null +++ b/GUI/ItemDelegates/HexDelegate.py @@ -0,0 +1,44 @@ +""" +Copyright (C) Korcan Karaokçu + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from PyQt6.QtWidgets import QStyledItemDelegate, QLineEdit, QWidget +from PyQt6.QtGui import QRegularExpressionValidator +from PyQt6.QtCore import QModelIndex, Qt, QRegularExpression + + +class QHexDelegate(QStyledItemDelegate): + def __init__(self, max_length: int = 2, regexp: str = "[0-9a-fA-F]+", parent=None) -> None: + super().__init__(parent) + self.max_length = max_length + self.regexp = regexp + + def createEditor(self, parent: QWidget, option, index: QModelIndex) -> QLineEdit: + self.editor = QLineEdit(parent) + self.editor.setMaxLength(self.max_length) + hex_validator = QRegularExpressionValidator(QRegularExpression(self.regexp), self.editor) + self.editor.setValidator(hex_validator) + self.editor.setText(index.model().data(index, Qt.ItemDataRole.DisplayRole)) + self.editor.textChanged.connect(self.check_text) + return self.editor + + def setEditorData(self, editor, index) -> None: + # Initial text was set in createEditor, this is a trick to dodge the textChanged signal + return + + def check_text(self) -> None: + if len(self.editor.text()) >= self.max_length: + self.closeEditor.emit(self.editor, QStyledItemDelegate.EndEditHint.EditNextItem) diff --git a/GUI/Labels/FlagRegisterLabel.py b/GUI/Labels/FlagRegisterLabel.py new file mode 100644 index 00000000..06ffd4da --- /dev/null +++ b/GUI/Labels/FlagRegisterLabel.py @@ -0,0 +1,60 @@ +""" +Copyright (C) 2016-2017 Korcan Karaokçu + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from PyQt6.QtWidgets import QLabel +from PyQt6.QtGui import QCursor, QMouseEvent, QEnterEvent +from PyQt6.QtCore import Qt +from libpince import debugcore, typedefs +from PINCE import InputDialogForm +from GUI.Utils import guiutils +from tr.tr import TranslationConstants as tr + + +class QFlagRegisterLabel(QLabel): + def __init__(self, parent=None): + super().__init__(parent) + + def set_value(self, value): + new = value + old = self.text() + if old != new: + self.setStyleSheet("color: red") + else: + self.setStyleSheet("") + self.setText(new) + + def enterEvent(self, event: QEnterEvent): + self.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) + super().enterEvent(event) + + def mouseDoubleClickEvent(self, event: QMouseEvent): + if ( + event.button() != Qt.MouseButton.LeftButton + or debugcore.currentpid == -1 + or debugcore.inferior_status == typedefs.INFERIOR_STATUS.RUNNING + ): + return + registers = debugcore.read_registers() + current_flag = self.objectName().lower() + label_text = tr.ENTER_FLAG_VALUE.format(self.objectName()) + parent = guiutils.search_parents_by_function(self, "set_debug_menu_shortcuts") + register_dialog = InputDialogForm(parent, [(label_text, ["0", "1", int(registers[current_flag])])]) + if register_dialog.exec(): + if debugcore.currentpid == -1 or debugcore.inferior_status == typedefs.INFERIOR_STATUS.RUNNING: + return + debugcore.set_register_flag(current_flag, register_dialog.get_values()) + self.set_value(debugcore.read_registers()[current_flag]) diff --git a/GUI/Labels/RegisterLabel.py b/GUI/Labels/RegisterLabel.py new file mode 100644 index 00000000..010f1335 --- /dev/null +++ b/GUI/Labels/RegisterLabel.py @@ -0,0 +1,80 @@ +""" +Copyright (C) 2016-2017 Korcan Karaokçu + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from PyQt6.QtWidgets import QLabel, QMenu, QApplication +from PyQt6.QtGui import QCursor, QMouseEvent, QEnterEvent, QContextMenuEvent +from PyQt6.QtCore import Qt +from libpince import debugcore, typedefs +from PINCE import InputDialogForm +from GUI.Utils import guiutils +from tr.tr import TranslationConstants as tr + + +class QRegisterLabel(QLabel): + def __init__(self, parent=None): + super().__init__(parent) + + def set_value(self, value): + new = self.objectName() + "=" + value + old = self.text() + if old != new: + self.setStyleSheet("color: red") + else: + self.setStyleSheet("") + self.setText(new) + + def enterEvent(self, event: QEnterEvent): + self.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) + super().enterEvent(event) + + def mouseDoubleClickEvent(self, event: QMouseEvent): + if ( + event.button() != Qt.MouseButton.LeftButton + or debugcore.currentpid == -1 + or debugcore.inferior_status == typedefs.INFERIOR_STATUS.RUNNING + ): + return + registers = debugcore.read_registers() + current_register = self.objectName().lower() + items = [(tr.ENTER_REGISTER_VALUE.format(self.objectName()), registers[current_register])] + memory_view = guiutils.search_parents_by_function(self, "set_debug_menu_shortcuts") + register_dialog = InputDialogForm(memory_view, items) + if register_dialog.exec(): + if debugcore.currentpid == -1 or debugcore.inferior_status == typedefs.INFERIOR_STATUS.RUNNING: + return + debugcore.set_convenience_variable(current_register, register_dialog.get_values()) + self.set_value(debugcore.read_registers()[current_register]) + + def contextMenuEvent(self, event: QContextMenuEvent): + menu = QMenu() + copy = menu.addAction(tr.COPY) + menu.addSeparator() + show_in_hex_view = menu.addAction(tr.SHOW_HEXVIEW) + show_in_disassembler = menu.addAction(tr.SHOW_DISASSEMBLER) + font_size = self.font().pointSize() + menu.setStyleSheet("font-size: " + str(font_size) + "pt;") + action = menu.exec(event.globalPos()) + memory_view = guiutils.search_parents_by_function(self, "set_debug_menu_shortcuts") + if action == show_in_hex_view: + address = self.text().split("=")[-1] + address_int = int(address, 16) + memory_view.hex_dump_address(address_int) + elif action == show_in_disassembler: + address = self.text().split("=")[-1] + memory_view.disassemble_expression(address) + elif action == copy: + QApplication.instance().clipboard().setText(self.text().split("=")[1]) diff --git a/GUI/LibpinceReferenceWidget.py b/GUI/LibpinceReferenceWidget.py deleted file mode 100644 index 686845f7..00000000 --- a/GUI/LibpinceReferenceWidget.py +++ /dev/null @@ -1,172 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'LibpinceReferenceWidget.ui' -# -# Created by: PyQt5 UI code generator 5.14.1 -# -# WARNING! All changes made in this file will be lost! - - -from PyQt5 import QtCore, QtGui, QtWidgets - - -class Ui_Form(object): - def setupUi(self, Form): - Form.setObjectName("Form") - Form.resize(887, 569) - self.gridLayout_2 = QtWidgets.QGridLayout(Form) - self.gridLayout_2.setContentsMargins(0, 0, 0, 0) - self.gridLayout_2.setSpacing(0) - self.gridLayout_2.setObjectName("gridLayout_2") - self.splitter = QtWidgets.QSplitter(Form) - self.splitter.setOrientation(QtCore.Qt.Horizontal) - self.splitter.setHandleWidth(10) - self.splitter.setObjectName("splitter") - self.widget_TypeDefs = QtWidgets.QWidget(self.splitter) - self.widget_TypeDefs.setObjectName("widget_TypeDefs") - self.gridLayout = QtWidgets.QGridLayout(self.widget_TypeDefs) - self.gridLayout.setContentsMargins(0, 0, 0, 0) - self.gridLayout.setObjectName("gridLayout") - self.horizontalLayout_4 = QtWidgets.QHBoxLayout() - self.horizontalLayout_4.setObjectName("horizontalLayout_4") - self.label_5 = QtWidgets.QLabel(self.widget_TypeDefs) - self.label_5.setObjectName("label_5") - self.horizontalLayout_4.addWidget(self.label_5) - self.line = QtWidgets.QFrame(self.widget_TypeDefs) - self.line.setFrameShape(QtWidgets.QFrame.VLine) - self.line.setFrameShadow(QtWidgets.QFrame.Sunken) - self.line.setObjectName("line") - self.horizontalLayout_4.addWidget(self.line) - self.label_3 = QtWidgets.QLabel(self.widget_TypeDefs) - self.label_3.setObjectName("label_3") - self.horizontalLayout_4.addWidget(self.label_3) - spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.horizontalLayout_4.addItem(spacerItem) - self.gridLayout.addLayout(self.horizontalLayout_4, 0, 0, 1, 1) - self.horizontalLayout_2 = QtWidgets.QHBoxLayout() - self.horizontalLayout_2.setObjectName("horizontalLayout_2") - self.lineEdit_SearchText = QtWidgets.QLineEdit(self.widget_TypeDefs) - self.lineEdit_SearchText.setObjectName("lineEdit_SearchText") - self.horizontalLayout_2.addWidget(self.lineEdit_SearchText) - self.pushButton_TextUp = QtWidgets.QPushButton(self.widget_TypeDefs) - self.pushButton_TextUp.setText("") - self.pushButton_TextUp.setObjectName("pushButton_TextUp") - self.horizontalLayout_2.addWidget(self.pushButton_TextUp) - self.pushButton_TextDown = QtWidgets.QPushButton(self.widget_TypeDefs) - self.pushButton_TextDown.setText("") - self.pushButton_TextDown.setObjectName("pushButton_TextDown") - self.horizontalLayout_2.addWidget(self.pushButton_TextDown) - self.label_FoundCount = QtWidgets.QLabel(self.widget_TypeDefs) - self.label_FoundCount.setObjectName("label_FoundCount") - self.horizontalLayout_2.addWidget(self.label_FoundCount) - self.gridLayout.addLayout(self.horizontalLayout_2, 1, 0, 1, 1) - self.textBrowser_TypeDefs = QtWidgets.QTextBrowser(self.widget_TypeDefs) - self.textBrowser_TypeDefs.setObjectName("textBrowser_TypeDefs") - self.gridLayout.addWidget(self.textBrowser_TypeDefs, 2, 0, 1, 1) - self.widget_Resources = QtWidgets.QWidget(self.splitter) - self.widget_Resources.setObjectName("widget_Resources") - self.gridLayout_3 = QtWidgets.QGridLayout(self.widget_Resources) - self.gridLayout_3.setContentsMargins(0, 0, 0, 0) - self.gridLayout_3.setObjectName("gridLayout_3") - self.stackedWidget_Resources = QtWidgets.QStackedWidget(self.widget_Resources) - self.stackedWidget_Resources.setObjectName("stackedWidget_Resources") - self.page = QtWidgets.QWidget() - self.page.setObjectName("page") - self.gridLayout_4 = QtWidgets.QGridLayout(self.page) - self.gridLayout_4.setContentsMargins(0, 0, 0, 0) - self.gridLayout_4.setSpacing(0) - self.gridLayout_4.setObjectName("gridLayout_4") - self.treeWidget_ResourceTree = QtWidgets.QTreeWidget(self.page) - self.treeWidget_ResourceTree.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) - self.treeWidget_ResourceTree.setObjectName("treeWidget_ResourceTree") - self.treeWidget_ResourceTree.headerItem().setText(0, "Item Name") - self.gridLayout_4.addWidget(self.treeWidget_ResourceTree, 0, 0, 1, 1) - self.stackedWidget_Resources.addWidget(self.page) - self.page_2 = QtWidgets.QWidget() - self.page_2.setObjectName("page_2") - self.gridLayout_5 = QtWidgets.QGridLayout(self.page_2) - self.gridLayout_5.setContentsMargins(0, 0, 0, 0) - self.gridLayout_5.setSpacing(0) - self.gridLayout_5.setObjectName("gridLayout_5") - self.tableWidget_ResourceTable = QtWidgets.QTableWidget(self.page_2) - self.tableWidget_ResourceTable.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) - self.tableWidget_ResourceTable.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) - self.tableWidget_ResourceTable.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) - self.tableWidget_ResourceTable.setObjectName("tableWidget_ResourceTable") - self.tableWidget_ResourceTable.setColumnCount(2) - self.tableWidget_ResourceTable.setRowCount(0) - item = QtWidgets.QTableWidgetItem() - self.tableWidget_ResourceTable.setHorizontalHeaderItem(0, item) - item = QtWidgets.QTableWidgetItem() - self.tableWidget_ResourceTable.setHorizontalHeaderItem(1, item) - self.tableWidget_ResourceTable.horizontalHeader().setStretchLastSection(True) - self.tableWidget_ResourceTable.verticalHeader().setVisible(False) - self.tableWidget_ResourceTable.verticalHeader().setDefaultSectionSize(16) - self.tableWidget_ResourceTable.verticalHeader().setMinimumSectionSize(16) - self.gridLayout_5.addWidget(self.tableWidget_ResourceTable, 0, 0, 1, 1) - self.stackedWidget_Resources.addWidget(self.page_2) - self.gridLayout_3.addWidget(self.stackedWidget_Resources, 2, 0, 1, 1) - self.horizontalLayout = QtWidgets.QHBoxLayout() - self.horizontalLayout.setObjectName("horizontalLayout") - self.verticalLayout_2 = QtWidgets.QVBoxLayout() - self.verticalLayout_2.setObjectName("verticalLayout_2") - self.label_4 = QtWidgets.QLabel(self.widget_Resources) - self.label_4.setObjectName("label_4") - self.verticalLayout_2.addWidget(self.label_4) - self.lineEdit_Search = QtWidgets.QLineEdit(self.widget_Resources) - self.lineEdit_Search.setObjectName("lineEdit_Search") - self.verticalLayout_2.addWidget(self.lineEdit_Search) - self.horizontalLayout.addLayout(self.verticalLayout_2) - self.verticalLayout = QtWidgets.QVBoxLayout() - self.verticalLayout.setObjectName("verticalLayout") - self.label = QtWidgets.QLabel(self.widget_Resources) - self.label.setObjectName("label") - self.verticalLayout.addWidget(self.label) - self.comboBox_SourceFile = QtWidgets.QComboBox(self.widget_Resources) - self.comboBox_SourceFile.setObjectName("comboBox_SourceFile") - self.verticalLayout.addWidget(self.comboBox_SourceFile) - self.horizontalLayout.addLayout(self.verticalLayout) - self.gridLayout_3.addLayout(self.horizontalLayout, 0, 0, 1, 1) - self.horizontalLayout_3 = QtWidgets.QHBoxLayout() - self.horizontalLayout_3.setObjectName("horizontalLayout_3") - self.label_2 = QtWidgets.QLabel(self.widget_Resources) - self.label_2.setObjectName("label_2") - self.horizontalLayout_3.addWidget(self.label_2) - spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.horizontalLayout_3.addItem(spacerItem1) - self.pushButton_ShowTypeDefs = QtWidgets.QPushButton(self.widget_Resources) - self.pushButton_ShowTypeDefs.setObjectName("pushButton_ShowTypeDefs") - self.horizontalLayout_3.addWidget(self.pushButton_ShowTypeDefs) - self.gridLayout_3.addLayout(self.horizontalLayout_3, 1, 0, 1, 1) - self.gridLayout_2.addWidget(self.splitter, 0, 0, 1, 1) - - self.retranslateUi(Form) - self.stackedWidget_Resources.setCurrentIndex(0) - QtCore.QMetaObject.connectSlotsByName(Form) - - def retranslateUi(self, Form): - _translate = QtCore.QCoreApplication.translate - Form.setWindowTitle(_translate("Form", "libpince Reference")) - self.label_5.setText(_translate("Form", "Search")) - self.label_3.setText(_translate("Form", "type_defs(Type Definitions)")) - self.label_FoundCount.setText(_translate("Form", "0/0")) - self.treeWidget_ResourceTree.headerItem().setText(1, _translate("Form", "Value")) - self.tableWidget_ResourceTable.setSortingEnabled(True) - item = self.tableWidget_ResourceTable.horizontalHeaderItem(0) - item.setText(_translate("Form", "Item Name")) - item = self.tableWidget_ResourceTable.horizontalHeaderItem(1) - item.setText(_translate("Form", "Value")) - self.label_4.setText(_translate("Form", "Search")) - self.label.setText(_translate("Form", "Source File")) - self.label_2.setText(_translate("Form", "Resources(Mouse-over items to see docstrings)")) - self.pushButton_ShowTypeDefs.setText(_translate("Form", "Hide type_defs")) - - -if __name__ == "__main__": - import sys - app = QtWidgets.QApplication(sys.argv) - Form = QtWidgets.QWidget() - ui = Ui_Form() - ui.setupUi(Form) - Form.show() - sys.exit(app.exec_()) diff --git a/GUI/LibpinceReferenceWidget.ui b/GUI/LibpinceReferenceWidget.ui deleted file mode 100644 index 5c97b3dd..00000000 --- a/GUI/LibpinceReferenceWidget.ui +++ /dev/null @@ -1,292 +0,0 @@ - - - Form - - - - 0 - 0 - 887 - 569 - - - - libpince Reference - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Horizontal - - - 10 - - - - - 0 - - - - - - - Search - - - - - - - Qt::Vertical - - - - - - - type_defs(Type Definitions) - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0/0 - - - - - - - - - - - - - - 0 - - - - - 0 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QAbstractItemView::NoEditTriggers - - - - Item Name - - - - - Value - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QAbstractItemView::NoEditTriggers - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - true - - - true - - - false - - - 16 - - - 16 - - - - Item Name - - - - - Value - - - - - - - - - - - - - - - - Search - - - - - - - - - - - - - - Source File - - - - - - - - - - - - - - - - Resources(Mouse-over items to see docstrings) - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Hide type_defs - - - - - - - - - - - - - - diff --git a/GUI/LoadingDialog.py b/GUI/LoadingDialog.py index 5efff741..f7066e80 100644 --- a/GUI/LoadingDialog.py +++ b/GUI/LoadingDialog.py @@ -1,26 +1,26 @@ -# -*- coding: utf-8 -*- - # Form implementation generated from reading ui file 'LoadingDialog.ui' # -# Created: Tue Feb 21 22:38:30 2017 -# by: PyQt5 UI code generator 5.2.1 +# Created by: PyQt6 UI code generator 6.4.2 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets -from PyQt5 import QtCore, QtGui, QtWidgets class Ui_Dialog(object): def setupUi(self, Dialog): Dialog.setObjectName("Dialog") - Dialog.resize(208, 89) - self.gridLayout_2 = QtWidgets.QGridLayout(Dialog) - self.gridLayout_2.setObjectName("gridLayout_2") + Dialog.resize(107, 71) + self.gridLayout = QtWidgets.QGridLayout(Dialog) + self.gridLayout.setObjectName("gridLayout") self.horizontalLayout = QtWidgets.QHBoxLayout() self.horizontalLayout.setObjectName("horizontalLayout") - spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) self.horizontalLayout.addItem(spacerItem) - self.label_Animated = QtWidgets.QLabel(Dialog) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) + self.label_Animated = QtWidgets.QLabel(parent=Dialog) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.label_Animated.sizePolicy().hasHeightForWidth()) @@ -29,32 +29,20 @@ def setupUi(self, Dialog): self.label_Animated.setScaledContents(False) self.label_Animated.setObjectName("label_Animated") self.horizontalLayout.addWidget(self.label_Animated) - self.label_StatusText = QtWidgets.QLabel(Dialog) + self.label_StatusText = QtWidgets.QLabel(parent=Dialog) self.label_StatusText.setObjectName("label_StatusText") self.horizontalLayout.addWidget(self.label_StatusText) - spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) self.horizontalLayout.addItem(spacerItem1) - self.gridLayout_2.addLayout(self.horizontalLayout, 0, 0, 1, 1) - self.widget_Cancel = QtWidgets.QWidget(Dialog) - self.widget_Cancel.setObjectName("widget_Cancel") - self.gridLayout = QtWidgets.QGridLayout(self.widget_Cancel) - self.gridLayout.setContentsMargins(0, 0, 0, 0) - self.gridLayout.setObjectName("gridLayout") - spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.gridLayout.addItem(spacerItem2, 0, 0, 1, 1) - self.pushButton_Cancel = QtWidgets.QPushButton(self.widget_Cancel) + self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1) + self.pushButton_Cancel = QtWidgets.QPushButton(parent=Dialog) self.pushButton_Cancel.setObjectName("pushButton_Cancel") - self.gridLayout.addWidget(self.pushButton_Cancel, 0, 1, 1, 1) - spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.gridLayout.addItem(spacerItem3, 0, 2, 1, 1) - self.gridLayout_2.addWidget(self.widget_Cancel, 1, 0, 1, 1) + self.gridLayout.addWidget(self.pushButton_Cancel, 1, 0, 1, 1) self.retranslateUi(Dialog) QtCore.QMetaObject.connectSlotsByName(Dialog) def retranslateUi(self, Dialog): _translate = QtCore.QCoreApplication.translate - Dialog.setWindowTitle(_translate("Dialog", "Dialog")) self.label_StatusText.setText(_translate("Dialog", "Processing")) self.pushButton_Cancel.setText(_translate("Dialog", "Cancel")) - diff --git a/GUI/LoadingDialog.ui b/GUI/LoadingDialog.ui index 0f6ad516..2c02b567 100644 --- a/GUI/LoadingDialog.ui +++ b/GUI/LoadingDialog.ui @@ -6,14 +6,11 @@ 0 0 - 208 - 89 + 107 + 71 - - Dialog - - + @@ -68,42 +65,10 @@ - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Cancel - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - + + + Cancel + diff --git a/GUI/LogFileWidget.py b/GUI/LogFileWidget.py index 8ed0d9da..1cf14c8a 100644 --- a/GUI/LogFileWidget.py +++ b/GUI/LogFileWidget.py @@ -1,12 +1,13 @@ -# -*- coding: utf-8 -*- - # Form implementation generated from reading ui file 'LogFileWidget.ui' # -# Created by: PyQt5 UI code generator 5.5.1 +# Created by: PyQt6 UI code generator 6.4.2 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets -from PyQt5 import QtCore, QtGui, QtWidgets class Ui_Form(object): def setupUi(self, Form): @@ -16,16 +17,19 @@ def setupUi(self, Form): self.gridLayout.setObjectName("gridLayout") self.horizontalLayout = QtWidgets.QHBoxLayout() self.horizontalLayout.setObjectName("horizontalLayout") - self.label_FilePath = QtWidgets.QLabel(Form) - self.label_FilePath.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse) + self.label_FilePath = QtWidgets.QLabel(parent=Form) + self.label_FilePath.setTextInteractionFlags(QtCore.Qt.TextInteractionFlag.LinksAccessibleByMouse|QtCore.Qt.TextInteractionFlag.TextSelectableByMouse) self.label_FilePath.setObjectName("label_FilePath") self.horizontalLayout.addWidget(self.label_FilePath) - self.label_LoggingStatus = QtWidgets.QLabel(Form) + self.label_LoggingStatus = QtWidgets.QLabel(parent=Form) self.label_LoggingStatus.setObjectName("label_LoggingStatus") self.horizontalLayout.addWidget(self.label_LoggingStatus) self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1) - self.textBrowser_LogContent = QtWidgets.QTextBrowser(Form) - self.textBrowser_LogContent.setLineWrapMode(QtWidgets.QTextEdit.NoWrap) + self.textBrowser_LogContent = QtWidgets.QTextBrowser(parent=Form) + font = QtGui.QFont() + font.setFamily("Monospace") + self.textBrowser_LogContent.setFont(font) + self.textBrowser_LogContent.setLineWrapMode(QtWidgets.QTextEdit.LineWrapMode.NoWrap) self.textBrowser_LogContent.setObjectName("textBrowser_LogContent") self.gridLayout.addWidget(self.textBrowser_LogContent, 1, 0, 1, 1) @@ -37,4 +41,3 @@ def retranslateUi(self, Form): Form.setWindowTitle(_translate("Form", "Form")) self.label_FilePath.setText(_translate("Form", "TextLabel")) self.label_LoggingStatus.setText(_translate("Form", "TextLabel")) - diff --git a/GUI/LogFileWidget.ui b/GUI/LogFileWidget.ui index 3eedf60f..b0aaf6ad 100644 --- a/GUI/LogFileWidget.ui +++ b/GUI/LogFileWidget.ui @@ -37,6 +37,11 @@ + + + Monospace + + QTextEdit::NoWrap diff --git a/GUI/MainWindow.py b/GUI/MainWindow.py index 6cdd8b57..27f18a18 100644 --- a/GUI/MainWindow.py +++ b/GUI/MainWindow.py @@ -1,64 +1,67 @@ -# -*- coding: utf-8 -*- - # Form implementation generated from reading ui file 'MainWindow.ui' # -# Created by: PyQt5 UI code generator 5.14.1 +# Created by: PyQt6 UI code generator 6.4.2 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. -from PyQt5 import QtCore, QtGui, QtWidgets +from PyQt6 import QtCore, QtGui, QtWidgets class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") - MainWindow.resize(768, 659) - self.centralwidget = QtWidgets.QWidget(MainWindow) + MainWindow.resize(678, 636) + MainWindow.setWindowTitle("PINCE") + self.centralwidget = QtWidgets.QWidget(parent=MainWindow) self.centralwidget.setObjectName("centralwidget") self.gridLayout = QtWidgets.QGridLayout(self.centralwidget) self.gridLayout.setObjectName("gridLayout") - self.treeWidget_AddressTable = QtWidgets.QTreeWidget(self.centralwidget) - self.treeWidget_AddressTable.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) - self.treeWidget_AddressTable.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop) - self.treeWidget_AddressTable.setDefaultDropAction(QtCore.Qt.MoveAction) - self.treeWidget_AddressTable.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) + self.treeWidget_AddressTable = QAddressTree(parent=self.centralwidget) + self.treeWidget_AddressTable.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers) + self.treeWidget_AddressTable.setDragDropMode(QtWidgets.QAbstractItemView.DragDropMode.DragDrop) + self.treeWidget_AddressTable.setDefaultDropAction(QtCore.Qt.DropAction.MoveAction) + self.treeWidget_AddressTable.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.ExtendedSelection) + self.treeWidget_AddressTable.setIndentation(12) self.treeWidget_AddressTable.setExpandsOnDoubleClick(False) self.treeWidget_AddressTable.setObjectName("treeWidget_AddressTable") self.gridLayout.addWidget(self.treeWidget_AddressTable, 3, 0, 1, 1) self.horizontalLayout_8 = QtWidgets.QHBoxLayout() - self.horizontalLayout_8.setSizeConstraint(QtWidgets.QLayout.SetMinAndMaxSize) + self.horizontalLayout_8.setSizeConstraint(QtWidgets.QLayout.SizeConstraint.SetMinAndMaxSize) self.horizontalLayout_8.setObjectName("horizontalLayout_8") - self.pushButton_MemoryView = QtWidgets.QPushButton(self.centralwidget) + self.pushButton_MemoryView = QtWidgets.QPushButton(parent=self.centralwidget) + self.pushButton_MemoryView.setEnabled(False) self.pushButton_MemoryView.setObjectName("pushButton_MemoryView") self.horizontalLayout_8.addWidget(self.pushButton_MemoryView) - spacerItem = QtWidgets.QSpacerItem(120, 20, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Minimum) + spacerItem = QtWidgets.QSpacerItem(120, 20, QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Minimum) self.horizontalLayout_8.addItem(spacerItem) - self.pushButton_CopyToAddressTable = QtWidgets.QPushButton(self.centralwidget) + self.pushButton_CopyToAddressTable = QtWidgets.QPushButton(parent=self.centralwidget) self.pushButton_CopyToAddressTable.setText("") self.pushButton_CopyToAddressTable.setObjectName("pushButton_CopyToAddressTable") self.horizontalLayout_8.addWidget(self.pushButton_CopyToAddressTable) - self.pushButton_CleanAddressTable = QtWidgets.QPushButton(self.centralwidget) + self.pushButton_CleanAddressTable = QtWidgets.QPushButton(parent=self.centralwidget) self.pushButton_CleanAddressTable.setText("") self.pushButton_CleanAddressTable.setObjectName("pushButton_CleanAddressTable") self.horizontalLayout_8.addWidget(self.pushButton_CleanAddressTable) - spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) self.horizontalLayout_8.addItem(spacerItem1) - self.pushButton_RefreshAdressTable = QtWidgets.QPushButton(self.centralwidget) + self.pushButton_RefreshAdressTable = QtWidgets.QPushButton(parent=self.centralwidget) self.pushButton_RefreshAdressTable.setText("") self.pushButton_RefreshAdressTable.setObjectName("pushButton_RefreshAdressTable") self.horizontalLayout_8.addWidget(self.pushButton_RefreshAdressTable) - spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) self.horizontalLayout_8.addItem(spacerItem2) - self.pushButton_AddAddressManually = QtWidgets.QPushButton(self.centralwidget) + self.pushButton_AddAddressManually = QtWidgets.QPushButton(parent=self.centralwidget) + self.pushButton_AddAddressManually.setEnabled(False) self.pushButton_AddAddressManually.setObjectName("pushButton_AddAddressManually") self.horizontalLayout_8.addWidget(self.pushButton_AddAddressManually) self.gridLayout.addLayout(self.horizontalLayout_8, 2, 0, 1, 1) self.horizontalLayout_5 = QtWidgets.QHBoxLayout() - self.horizontalLayout_5.setSizeConstraint(QtWidgets.QLayout.SetFixedSize) + self.horizontalLayout_5.setSizeConstraint(QtWidgets.QLayout.SizeConstraint.SetFixedSize) self.horizontalLayout_5.setObjectName("horizontalLayout_5") - self.pushButton_AttachProcess = QtWidgets.QPushButton(self.centralwidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) + self.pushButton_AttachProcess = QtWidgets.QPushButton(parent=self.centralwidget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.pushButton_AttachProcess.sizePolicy().hasHeightForWidth()) @@ -66,8 +69,8 @@ def setupUi(self, MainWindow): self.pushButton_AttachProcess.setText("") self.pushButton_AttachProcess.setObjectName("pushButton_AttachProcess") self.horizontalLayout_5.addWidget(self.pushButton_AttachProcess) - self.pushButton_Open = QtWidgets.QPushButton(self.centralwidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) + self.pushButton_Open = QtWidgets.QPushButton(parent=self.centralwidget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.pushButton_Open.sizePolicy().hasHeightForWidth()) @@ -75,8 +78,8 @@ def setupUi(self, MainWindow): self.pushButton_Open.setText("") self.pushButton_Open.setObjectName("pushButton_Open") self.horizontalLayout_5.addWidget(self.pushButton_Open) - self.pushButton_Save = QtWidgets.QPushButton(self.centralwidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) + self.pushButton_Save = QtWidgets.QPushButton(parent=self.centralwidget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.pushButton_Save.sizePolicy().hasHeightForWidth()) @@ -84,32 +87,32 @@ def setupUi(self, MainWindow): self.pushButton_Save.setText("") self.pushButton_Save.setObjectName("pushButton_Save") self.horizontalLayout_5.addWidget(self.pushButton_Save) - self.pushButton_Wiki = QtWidgets.QPushButton(self.centralwidget) + self.pushButton_Wiki = QtWidgets.QPushButton(parent=self.centralwidget) self.pushButton_Wiki.setText("") self.pushButton_Wiki.setObjectName("pushButton_Wiki") self.horizontalLayout_5.addWidget(self.pushButton_Wiki) - self.pushButton_About = QtWidgets.QPushButton(self.centralwidget) + self.pushButton_About = QtWidgets.QPushButton(parent=self.centralwidget) self.pushButton_About.setText("") self.pushButton_About.setObjectName("pushButton_About") self.horizontalLayout_5.addWidget(self.pushButton_About) - self.label_SelectedProcess = QtWidgets.QLabel(self.centralwidget) + self.label_SelectedProcess = QtWidgets.QLabel(parent=self.centralwidget) self.label_SelectedProcess.setObjectName("label_SelectedProcess") self.horizontalLayout_5.addWidget(self.label_SelectedProcess) - self.label_InferiorStatus = QtWidgets.QLabel(self.centralwidget) + self.label_InferiorStatus = QtWidgets.QLabel(parent=self.centralwidget) self.label_InferiorStatus.setText("") self.label_InferiorStatus.setObjectName("label_InferiorStatus") self.horizontalLayout_5.addWidget(self.label_InferiorStatus) - self.progressBar = QtWidgets.QProgressBar(self.centralwidget) + self.progressBar = QtWidgets.QProgressBar(parent=self.centralwidget) self.progressBar.setProperty("value", 0) - self.progressBar.setOrientation(QtCore.Qt.Horizontal) + self.progressBar.setOrientation(QtCore.Qt.Orientation.Horizontal) self.progressBar.setObjectName("progressBar") self.horizontalLayout_5.addWidget(self.progressBar) - self.pushButton_Console = QtWidgets.QPushButton(self.centralwidget) + self.pushButton_Console = QtWidgets.QPushButton(parent=self.centralwidget) self.pushButton_Console.setText("") self.pushButton_Console.setObjectName("pushButton_Console") self.horizontalLayout_5.addWidget(self.pushButton_Console) - self.pushButton_Settings = QtWidgets.QPushButton(self.centralwidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) + self.pushButton_Settings = QtWidgets.QPushButton(parent=self.centralwidget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.pushButton_Settings.sizePolicy().hasHeightForWidth()) @@ -122,18 +125,18 @@ def setupUi(self, MainWindow): self.horizontalLayout_9.setObjectName("horizontalLayout_9") self.verticalLayout_6 = QtWidgets.QVBoxLayout() self.verticalLayout_6.setObjectName("verticalLayout_6") - self.label_MatchCount = QtWidgets.QLabel(self.centralwidget) + self.label_MatchCount = QtWidgets.QLabel(parent=self.centralwidget) self.label_MatchCount.setObjectName("label_MatchCount") self.verticalLayout_6.addWidget(self.label_MatchCount) - self.tableWidget_valuesearchtable = QtWidgets.QTableWidget(self.centralwidget) + self.tableWidget_valuesearchtable = QtWidgets.QTableWidget(parent=self.centralwidget) self.tableWidget_valuesearchtable.setEnabled(True) self.tableWidget_valuesearchtable.setAutoFillBackground(False) - self.tableWidget_valuesearchtable.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) - self.tableWidget_valuesearchtable.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) + self.tableWidget_valuesearchtable.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOff) + self.tableWidget_valuesearchtable.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers) self.tableWidget_valuesearchtable.setAlternatingRowColors(True) - self.tableWidget_valuesearchtable.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) - self.tableWidget_valuesearchtable.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) + self.tableWidget_valuesearchtable.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows) self.tableWidget_valuesearchtable.setShowGrid(False) + self.tableWidget_valuesearchtable.setWordWrap(False) self.tableWidget_valuesearchtable.setObjectName("tableWidget_valuesearchtable") self.tableWidget_valuesearchtable.setColumnCount(3) self.tableWidget_valuesearchtable.setRowCount(0) @@ -150,139 +153,110 @@ def setupUi(self, MainWindow): self.tableWidget_valuesearchtable.verticalHeader().setMinimumSectionSize(20) self.verticalLayout_6.addWidget(self.tableWidget_valuesearchtable) self.horizontalLayout_9.addLayout(self.verticalLayout_6) - self.QWidget_Toolbox = QtWidgets.QWidget(self.centralwidget) - self.QWidget_Toolbox.setEnabled(True) + self.QWidget_Toolbox = QtWidgets.QWidget(parent=self.centralwidget) + self.QWidget_Toolbox.setEnabled(False) self.QWidget_Toolbox.setObjectName("QWidget_Toolbox") self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.QWidget_Toolbox) self.verticalLayout_5.setObjectName("verticalLayout_5") self.horizontalLayout_6 = QtWidgets.QHBoxLayout() self.horizontalLayout_6.setObjectName("horizontalLayout_6") - self.pushButton_NewFirstScan = QtWidgets.QPushButton(self.QWidget_Toolbox) + self.pushButton_NewFirstScan = QtWidgets.QPushButton(parent=self.QWidget_Toolbox) self.pushButton_NewFirstScan.setObjectName("pushButton_NewFirstScan") self.horizontalLayout_6.addWidget(self.pushButton_NewFirstScan) - self.pushButton_NextScan = QtWidgets.QPushButton(self.QWidget_Toolbox) + self.pushButton_NextScan = QtWidgets.QPushButton(parent=self.QWidget_Toolbox) self.pushButton_NextScan.setObjectName("pushButton_NextScan") self.horizontalLayout_6.addWidget(self.pushButton_NextScan) - spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) self.horizontalLayout_6.addItem(spacerItem3) - self.pushButton_UndoScan = QtWidgets.QPushButton(self.QWidget_Toolbox) + self.pushButton_UndoScan = QtWidgets.QPushButton(parent=self.QWidget_Toolbox) self.pushButton_UndoScan.setObjectName("pushButton_UndoScan") self.horizontalLayout_6.addWidget(self.pushButton_UndoScan) self.verticalLayout_5.addLayout(self.horizontalLayout_6) - self.horizontalLayout_7 = QtWidgets.QHBoxLayout() + self.widget_Scan = QtWidgets.QWidget(parent=self.QWidget_Toolbox) + self.widget_Scan.setObjectName("widget_Scan") + self.horizontalLayout_7 = QtWidgets.QHBoxLayout(self.widget_Scan) + self.horizontalLayout_7.setContentsMargins(0, -1, 0, 0) self.horizontalLayout_7.setObjectName("horizontalLayout_7") - self.widget = QtWidgets.QWidget(self.QWidget_Toolbox) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.widget.sizePolicy().hasHeightForWidth()) - self.widget.setSizePolicy(sizePolicy) - self.widget.setObjectName("widget") - self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.widget) - self.verticalLayout_2.setContentsMargins(0, 0, 0, 0) - self.verticalLayout_2.setSpacing(0) - self.verticalLayout_2.setObjectName("verticalLayout_2") - self.radioButton_Bits = QtWidgets.QRadioButton(self.widget) - self.radioButton_Bits.setObjectName("radioButton_Bits") - self.verticalLayout_2.addWidget(self.radioButton_Bits) - self.radioButton_Decimal = QtWidgets.QRadioButton(self.widget) - self.radioButton_Decimal.setObjectName("radioButton_Decimal") - self.verticalLayout_2.addWidget(self.radioButton_Decimal) - self.checkBox_Hex = QtWidgets.QCheckBox(self.widget) + self.checkBox_Hex = QtWidgets.QCheckBox(parent=self.widget_Scan) self.checkBox_Hex.setObjectName("checkBox_Hex") - self.verticalLayout_2.addWidget(self.checkBox_Hex) - self.horizontalLayout_7.addWidget(self.widget) - self.lineEdit_Scan = QtWidgets.QLineEdit(self.QWidget_Toolbox) + self.horizontalLayout_7.addWidget(self.checkBox_Hex) + self.lineEdit_Scan = QtWidgets.QLineEdit(parent=self.widget_Scan) self.lineEdit_Scan.setObjectName("lineEdit_Scan") self.horizontalLayout_7.addWidget(self.lineEdit_Scan) - self.label_Between = QtWidgets.QLabel(self.QWidget_Toolbox) + self.label_Between = QtWidgets.QLabel(parent=self.widget_Scan) + self.label_Between.setText("<->") self.label_Between.setObjectName("label_Between") self.horizontalLayout_7.addWidget(self.label_Between) - self.lineEdit_Scan2 = QtWidgets.QLineEdit(self.QWidget_Toolbox) + self.lineEdit_Scan2 = QtWidgets.QLineEdit(parent=self.widget_Scan) self.lineEdit_Scan2.setObjectName("lineEdit_Scan2") self.horizontalLayout_7.addWidget(self.lineEdit_Scan2) - self.verticalLayout_5.addLayout(self.horizontalLayout_7) + self.verticalLayout_5.addWidget(self.widget_Scan) self.horizontalLayout_4 = QtWidgets.QHBoxLayout() self.horizontalLayout_4.setObjectName("horizontalLayout_4") - self.widget1 = QtWidgets.QWidget(self.QWidget_Toolbox) - self.widget1.setObjectName("widget1") - self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.widget1) + self.widget = QtWidgets.QWidget(parent=self.QWidget_Toolbox) + self.widget.setObjectName("widget") + self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.widget) self.verticalLayout_4.setObjectName("verticalLayout_4") self.horizontalLayout_2 = QtWidgets.QHBoxLayout() self.horizontalLayout_2.setObjectName("horizontalLayout_2") - self.label = QtWidgets.QLabel(self.widget1) + self.label = QtWidgets.QLabel(parent=self.widget) self.label.setObjectName("label") self.horizontalLayout_2.addWidget(self.label) - self.comboBox_ScanType = QtWidgets.QComboBox(self.widget1) + self.comboBox_ScanType = QtWidgets.QComboBox(parent=self.widget) self.comboBox_ScanType.setObjectName("comboBox_ScanType") self.horizontalLayout_2.addWidget(self.comboBox_ScanType) self.verticalLayout_4.addLayout(self.horizontalLayout_2) self.horizontalLayout = QtWidgets.QHBoxLayout() self.horizontalLayout.setObjectName("horizontalLayout") - self.label_2 = QtWidgets.QLabel(self.widget1) + self.label_2 = QtWidgets.QLabel(parent=self.widget) self.label_2.setObjectName("label_2") self.horizontalLayout.addWidget(self.label_2) - self.comboBox_ValueType = QtWidgets.QComboBox(self.widget1) + self.comboBox_ValueType = QtWidgets.QComboBox(parent=self.widget) self.comboBox_ValueType.setObjectName("comboBox_ValueType") self.horizontalLayout.addWidget(self.comboBox_ValueType) self.verticalLayout_4.addLayout(self.horizontalLayout) self.horizontalLayout_10 = QtWidgets.QHBoxLayout() self.horizontalLayout_10.setObjectName("horizontalLayout_10") - self.label_ScanScope = QtWidgets.QLabel(self.widget1) + self.label_ScanScope = QtWidgets.QLabel(parent=self.widget) self.label_ScanScope.setObjectName("label_ScanScope") self.horizontalLayout_10.addWidget(self.label_ScanScope) - self.comboBox_ScanScope = QtWidgets.QComboBox(self.widget1) + self.comboBox_ScanScope = QtWidgets.QComboBox(parent=self.widget) self.comboBox_ScanScope.setObjectName("comboBox_ScanScope") self.horizontalLayout_10.addWidget(self.comboBox_ScanScope) self.verticalLayout_4.addLayout(self.horizontalLayout_10) - spacerItem4 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.horizontalLayout_3 = QtWidgets.QHBoxLayout() + self.horizontalLayout_3.setObjectName("horizontalLayout_3") + self.label_3 = QtWidgets.QLabel(parent=self.widget) + self.label_3.setObjectName("label_3") + self.horizontalLayout_3.addWidget(self.label_3) + self.comboBox_Endianness = QtWidgets.QComboBox(parent=self.widget) + self.comboBox_Endianness.setObjectName("comboBox_Endianness") + self.horizontalLayout_3.addWidget(self.comboBox_Endianness) + self.verticalLayout_4.addLayout(self.horizontalLayout_3) + self.pushButton_ScanRegions = QtWidgets.QPushButton(parent=self.widget) + self.pushButton_ScanRegions.setObjectName("pushButton_ScanRegions") + self.verticalLayout_4.addWidget(self.pushButton_ScanRegions) + spacerItem4 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) self.verticalLayout_4.addItem(spacerItem4) - self.horizontalLayout_4.addWidget(self.widget1) - self.widget_2 = QtWidgets.QWidget(self.QWidget_Toolbox) + self.horizontalLayout_4.addWidget(self.widget) + self.widget_2 = QtWidgets.QWidget(parent=self.QWidget_Toolbox) self.widget_2.setObjectName("widget_2") self.verticalLayout = QtWidgets.QVBoxLayout(self.widget_2) self.verticalLayout.setContentsMargins(0, 0, 0, 0) self.verticalLayout.setSpacing(0) self.verticalLayout.setObjectName("verticalLayout") - self.radioButton_RoundedDefault = QtWidgets.QRadioButton(self.widget_2) - self.radioButton_RoundedDefault.setEnabled(False) - self.radioButton_RoundedDefault.setObjectName("radioButton_RoundedDefault") - self.verticalLayout.addWidget(self.radioButton_RoundedDefault) - self.radioButton_RoundedExtreme = QtWidgets.QRadioButton(self.widget_2) - self.radioButton_RoundedExtreme.setEnabled(False) - self.radioButton_RoundedExtreme.setObjectName("radioButton_RoundedExtreme") - self.verticalLayout.addWidget(self.radioButton_RoundedExtreme) - self.radioButton_Truncated = QtWidgets.QRadioButton(self.widget_2) - self.radioButton_Truncated.setEnabled(False) - self.radioButton_Truncated.setObjectName("radioButton_Truncated") - self.verticalLayout.addWidget(self.radioButton_Truncated) - self.checkBox_Unicode = QtWidgets.QCheckBox(self.widget_2) - self.checkBox_Unicode.setEnabled(False) - self.checkBox_Unicode.setObjectName("checkBox_Unicode") - self.verticalLayout.addWidget(self.checkBox_Unicode) - self.checkBox_CaseSensitive = QtWidgets.QCheckBox(self.widget_2) - self.checkBox_CaseSensitive.setEnabled(False) - self.checkBox_CaseSensitive.setObjectName("checkBox_CaseSensitive") - self.verticalLayout.addWidget(self.checkBox_CaseSensitive) self.verticalLayout_3 = QtWidgets.QVBoxLayout() self.verticalLayout_3.setObjectName("verticalLayout_3") - spacerItem5 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + spacerItem5 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) self.verticalLayout_3.addItem(spacerItem5) - self.checkBox_Unrandomizer = QtWidgets.QCheckBox(self.widget_2) - self.checkBox_Unrandomizer.setEnabled(False) - self.checkBox_Unrandomizer.setObjectName("checkBox_Unrandomizer") - self.verticalLayout_3.addWidget(self.checkBox_Unrandomizer) self.verticalLayout.addLayout(self.verticalLayout_3) self.horizontalLayout_4.addWidget(self.widget_2) self.verticalLayout_5.addLayout(self.horizontalLayout_4) - self.horizontalLayout_3 = QtWidgets.QHBoxLayout() - self.horizontalLayout_3.setObjectName("horizontalLayout_3") - self.verticalLayout_5.addLayout(self.horizontalLayout_3) self.horizontalLayout_9.addWidget(self.QWidget_Toolbox) self.gridLayout.addLayout(self.horizontalLayout_9, 1, 0, 1, 1) MainWindow.setCentralWidget(self.centralwidget) - self.menubar = QtWidgets.QMenuBar(MainWindow) - self.menubar.setGeometry(QtCore.QRect(0, 0, 768, 30)) + self.menubar = QtWidgets.QMenuBar(parent=MainWindow) + self.menubar.setGeometry(QtCore.QRect(0, 0, 678, 23)) self.menubar.setObjectName("menubar") MainWindow.setMenuBar(self.menubar) @@ -291,7 +265,7 @@ def setupUi(self, MainWindow): def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate - MainWindow.setWindowTitle(_translate("MainWindow", "PINCE")) + self.treeWidget_AddressTable.setSortingEnabled(True) self.treeWidget_AddressTable.headerItem().setText(0, _translate("MainWindow", "Freeze")) self.treeWidget_AddressTable.headerItem().setToolTip(0, _translate("MainWindow", "Freeze the value")) self.treeWidget_AddressTable.headerItem().setText(1, _translate("MainWindow", "Description")) @@ -312,6 +286,7 @@ def retranslateUi(self, MainWindow): self.pushButton_Console.setToolTip(_translate("MainWindow", "Open a gdb console")) self.pushButton_Settings.setToolTip(_translate("MainWindow", "Configure options")) self.label_MatchCount.setText(_translate("MainWindow", "Match count: 0")) + self.tableWidget_valuesearchtable.setSortingEnabled(True) item = self.tableWidget_valuesearchtable.horizontalHeaderItem(0) item.setText(_translate("MainWindow", "Address")) item = self.tableWidget_valuesearchtable.horizontalHeaderItem(1) @@ -321,32 +296,10 @@ def retranslateUi(self, MainWindow): self.pushButton_NewFirstScan.setText(_translate("MainWindow", "First Scan")) self.pushButton_NextScan.setText(_translate("MainWindow", "Next Scan")) self.pushButton_UndoScan.setText(_translate("MainWindow", "Undo Scan")) - self.radioButton_Bits.setText(_translate("MainWindow", "B&its")) - self.radioButton_Decimal.setText(_translate("MainWindow", "&Decimal")) self.checkBox_Hex.setText(_translate("MainWindow", "Hex")) - self.label_Between.setText(_translate("MainWindow", "<->")) self.label.setText(_translate("MainWindow", "Scan Type:")) self.label_2.setText(_translate("MainWindow", "Value Type:")) self.label_ScanScope.setText(_translate("MainWindow", "Scan Scope:")) - self.radioButton_RoundedDefault.setToolTip(_translate("MainWindow", "Not currently supported")) - self.radioButton_RoundedDefault.setText(_translate("MainWindow", "Ro&unded (Default)")) - self.radioButton_RoundedExtreme.setToolTip(_translate("MainWindow", "Not currently supported")) - self.radioButton_RoundedExtreme.setText(_translate("MainWindow", "Rou&nded (Extreme)")) - self.radioButton_Truncated.setToolTip(_translate("MainWindow", "Not currently supported")) - self.radioButton_Truncated.setText(_translate("MainWindow", "Truncated")) - self.checkBox_Unicode.setToolTip(_translate("MainWindow", "Not currently supported")) - self.checkBox_Unicode.setText(_translate("MainWindow", "Unicode")) - self.checkBox_CaseSensitive.setToolTip(_translate("MainWindow", "Not currently supported")) - self.checkBox_CaseSensitive.setText(_translate("MainWindow", "Case Sensitive")) - self.checkBox_Unrandomizer.setToolTip(_translate("MainWindow", "Not currently supported")) - self.checkBox_Unrandomizer.setText(_translate("MainWindow", "Unrandomizer")) - - -if __name__ == "__main__": - import sys - app = QtWidgets.QApplication(sys.argv) - MainWindow = QtWidgets.QMainWindow() - ui = Ui_MainWindow() - ui.setupUi(MainWindow) - MainWindow.show() - sys.exit(app.exec_()) + self.label_3.setText(_translate("MainWindow", "Endianness:")) + self.pushButton_ScanRegions.setText(_translate("MainWindow", "Manage Scan Regions")) +from GUI.TreeWidgets.AddressTree import QAddressTree diff --git a/GUI/MainWindow.ui b/GUI/MainWindow.ui index 5da5736e..7197c4c4 100644 --- a/GUI/MainWindow.ui +++ b/GUI/MainWindow.ui @@ -6,17 +6,17 @@ 0 0 - 768 - 659 + 678 + 636 - PINCE + PINCE - + QAbstractItemView::NoEditTriggers @@ -29,6 +29,12 @@ QAbstractItemView::ExtendedSelection + + 12 + + + true + false @@ -69,6 +75,9 @@ + + false + Memory View @@ -148,6 +157,9 @@ + + false + Add Address Manually @@ -308,15 +320,18 @@ true - - QAbstractItemView::ExtendedSelection - QAbstractItemView::SelectRows false + + true + + + false + true @@ -354,7 +369,7 @@ - true + false @@ -396,69 +411,39 @@ - - - - - - 0 - 0 - - - - - 0 - - - 0 - - - 0 + + + + 0 + + + 0 + + + 0 + + + + + Hex - - 0 + + + + + + + + + <-> - - 0 - - - - - B&its - - - - - - - &Decimal - - - - - - - Hex - - - - - - - - - - - - - <-> - - - - - - - + + + + + + + @@ -507,6 +492,27 @@ + + + + + + Endianness: + + + + + + + + + + + + Manage Scan Regions + + + @@ -541,71 +547,6 @@ 0 - - - - false - - - Not currently supported - - - Ro&unded (Default) - - - - - - - false - - - Not currently supported - - - Rou&nded (Extreme) - - - - - - - false - - - Not currently supported - - - Truncated - - - - - - - false - - - Not currently supported - - - Unicode - - - - - - - false - - - Not currently supported - - - Case Sensitive - - - @@ -621,19 +562,6 @@ - - - - false - - - Not currently supported - - - Unrandomizer - - - @@ -641,9 +569,6 @@ - - - @@ -656,12 +581,19 @@ 0 0 - 768 - 30 + 678 + 23 + + + QAddressTree + QTreeWidget +
GUI.TreeWidgets.AddressTree
+
+
diff --git a/GUI/ManualAddressDialogUtils/PointerChainOffset.py b/GUI/ManualAddressDialogUtils/PointerChainOffset.py new file mode 100644 index 00000000..230b3ac4 --- /dev/null +++ b/GUI/ManualAddressDialogUtils/PointerChainOffset.py @@ -0,0 +1,60 @@ +from PyQt6.QtWidgets import QWidget, QFrame, QLabel, QLineEdit, QPushButton, QHBoxLayout, QSizePolicy, QSpacerItem +from PyQt6.QtCore import Qt, pyqtSignal +from operator import add as opAdd, sub as opSub + +from GUI.Utils import guiutils + + +# Only intended to be used by ManualAddressForm +class PointerChainOffset(QFrame): + offset_changed_signal = pyqtSignal(name="offsetChanged") + + def __init__(self, offset_index: int, parent: QWidget | None = None): + super().__init__(parent) + self.offset_index = offset_index + self.initUI() + + def initUI(self): + offsetLayout = QHBoxLayout(self) + offsetLayout.setContentsMargins(0, 3, 0, 3) + self.setLayout(offsetLayout) + buttonLeft = QPushButton("<", self) + buttonLeft.setFixedWidth(20) + offsetLayout.addWidget(buttonLeft) + self.offsetText = QLineEdit(self) + self.offsetText.setValidator(guiutils.validator_map["int_hex"]) + self.offsetText.setText(hex(0)) + self.offsetText.setFixedWidth(70) + self.offsetText.textChanged.connect(self.offset_changed) + offsetLayout.addWidget(self.offsetText) + buttonRight = QPushButton(">", self) + buttonRight.setFixedWidth(20) + offsetLayout.addWidget(buttonRight) + spacer = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding) + self.derefLabel = QLabel(self) + self.derefLabel.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse) + self.derefLabel.setText(" -> ??") + offsetLayout.addWidget(self.derefLabel) + offsetLayout.addItem(spacer) + buttonLeft.clicked.connect(lambda: self.on_offset_arrow_clicked(self.offsetText, opSub)) + buttonRight.clicked.connect(lambda: self.on_offset_arrow_clicked(self.offsetText, opAdd)) + + def get_offset_as_int(self): + return int(self.offsetText.text(), 16) + + def on_offset_arrow_clicked(self, offsetTextWidget, operator_func): + offsetText = offsetTextWidget.text() + try: + offsetValue = int(offsetText, 16) + except ValueError: + offsetValue = 0 + # first parent is the widget_pointer, second parent is the ManualAddressDialog + sizeVal = self.parent().parent().get_type_size() if hasattr(self.parent().parent(), "get_type_size") else 1 + offsetValue = operator_func(offsetValue, sizeVal) + offsetTextWidget.setText(hex(offsetValue)) + + def offset_changed(self): + self.offset_changed_signal.emit() + + def update_deref_label(self, text: str): + self.derefLabel.setText(text) diff --git a/GUI/MemoryRegionsWidget.py b/GUI/MemoryRegionsWidget.py index dc3ffb09..cbb9898f 100644 --- a/GUI/MemoryRegionsWidget.py +++ b/GUI/MemoryRegionsWidget.py @@ -1,13 +1,13 @@ -# -*- coding: utf-8 -*- - # Form implementation generated from reading ui file 'MemoryRegionsWidget.ui' # -# Created: Fri Feb 24 20:41:26 2017 -# by: PyQt5 UI code generator 5.2.1 +# Created by: PyQt6 UI code generator 6.4.2 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets -from PyQt5 import QtCore, QtGui, QtWidgets class Ui_Form(object): def setupUi(self, Form): @@ -15,12 +15,17 @@ def setupUi(self, Form): Form.resize(684, 539) self.gridLayout = QtWidgets.QGridLayout(Form) self.gridLayout.setObjectName("gridLayout") - self.tableWidget_MemoryRegions = QtWidgets.QTableWidget(Form) - self.tableWidget_MemoryRegions.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) - self.tableWidget_MemoryRegions.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) - self.tableWidget_MemoryRegions.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) + self.tableWidget_MemoryRegions = QtWidgets.QTableWidget(parent=Form) + font = QtGui.QFont() + font.setFamily("Monospace") + self.tableWidget_MemoryRegions.setFont(font) + self.tableWidget_MemoryRegions.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers) + self.tableWidget_MemoryRegions.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.SingleSelection) + self.tableWidget_MemoryRegions.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows) + self.tableWidget_MemoryRegions.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollMode.ScrollPerPixel) + self.tableWidget_MemoryRegions.setWordWrap(False) self.tableWidget_MemoryRegions.setObjectName("tableWidget_MemoryRegions") - self.tableWidget_MemoryRegions.setColumnCount(13) + self.tableWidget_MemoryRegions.setColumnCount(4) self.tableWidget_MemoryRegions.setRowCount(0) item = QtWidgets.QTableWidgetItem() self.tableWidget_MemoryRegions.setHorizontalHeaderItem(0, item) @@ -30,24 +35,6 @@ def setupUi(self, Form): self.tableWidget_MemoryRegions.setHorizontalHeaderItem(2, item) item = QtWidgets.QTableWidgetItem() self.tableWidget_MemoryRegions.setHorizontalHeaderItem(3, item) - item = QtWidgets.QTableWidgetItem() - self.tableWidget_MemoryRegions.setHorizontalHeaderItem(4, item) - item = QtWidgets.QTableWidgetItem() - self.tableWidget_MemoryRegions.setHorizontalHeaderItem(5, item) - item = QtWidgets.QTableWidgetItem() - self.tableWidget_MemoryRegions.setHorizontalHeaderItem(6, item) - item = QtWidgets.QTableWidgetItem() - self.tableWidget_MemoryRegions.setHorizontalHeaderItem(7, item) - item = QtWidgets.QTableWidgetItem() - self.tableWidget_MemoryRegions.setHorizontalHeaderItem(8, item) - item = QtWidgets.QTableWidgetItem() - self.tableWidget_MemoryRegions.setHorizontalHeaderItem(9, item) - item = QtWidgets.QTableWidgetItem() - self.tableWidget_MemoryRegions.setHorizontalHeaderItem(10, item) - item = QtWidgets.QTableWidgetItem() - self.tableWidget_MemoryRegions.setHorizontalHeaderItem(11, item) - item = QtWidgets.QTableWidgetItem() - self.tableWidget_MemoryRegions.setHorizontalHeaderItem(12, item) self.tableWidget_MemoryRegions.horizontalHeader().setStretchLastSection(True) self.tableWidget_MemoryRegions.verticalHeader().setVisible(False) self.tableWidget_MemoryRegions.verticalHeader().setDefaultSectionSize(16) @@ -66,25 +53,6 @@ def retranslateUi(self, Form): item = self.tableWidget_MemoryRegions.horizontalHeaderItem(1) item.setText(_translate("Form", "Perms")) item = self.tableWidget_MemoryRegions.horizontalHeaderItem(2) - item.setText(_translate("Form", "Size")) + item.setText(_translate("Form", "Offset")) item = self.tableWidget_MemoryRegions.horizontalHeaderItem(3) item.setText(_translate("Form", "Path")) - item = self.tableWidget_MemoryRegions.horizontalHeaderItem(4) - item.setText(_translate("Form", "RSS")) - item = self.tableWidget_MemoryRegions.horizontalHeaderItem(5) - item.setText(_translate("Form", "PSS")) - item = self.tableWidget_MemoryRegions.horizontalHeaderItem(6) - item.setText(_translate("Form", "Shared_Clean")) - item = self.tableWidget_MemoryRegions.horizontalHeaderItem(7) - item.setText(_translate("Form", "Shared_Dirty")) - item = self.tableWidget_MemoryRegions.horizontalHeaderItem(8) - item.setText(_translate("Form", "Private_Clean")) - item = self.tableWidget_MemoryRegions.horizontalHeaderItem(9) - item.setText(_translate("Form", "Private_Dirty")) - item = self.tableWidget_MemoryRegions.horizontalHeaderItem(10) - item.setText(_translate("Form", "Referenced")) - item = self.tableWidget_MemoryRegions.horizontalHeaderItem(11) - item.setText(_translate("Form", "Anonymous")) - item = self.tableWidget_MemoryRegions.horizontalHeaderItem(12) - item.setText(_translate("Form", "Swap")) - diff --git a/GUI/MemoryRegionsWidget.ui b/GUI/MemoryRegionsWidget.ui index 3fac6ba9..43f58942 100644 --- a/GUI/MemoryRegionsWidget.ui +++ b/GUI/MemoryRegionsWidget.ui @@ -16,6 +16,11 @@ + + + Monospace + + QAbstractItemView::NoEditTriggers @@ -25,19 +30,25 @@ QAbstractItemView::SelectRows + + QAbstractItemView::ScrollPerPixel + true + + false + true false - + 16 - + 16 @@ -52,7 +63,7 @@ - Size + Offset @@ -60,51 +71,6 @@ Path - - - RSS - - - - - PSS - - - - - Shared_Clean - - - - - Shared_Dirty - - - - - Private_Clean - - - - - Private_Dirty - - - - - Referenced - - - - - Anonymous - - - - - Swap - - diff --git a/GUI/MemoryViewerWindow.py b/GUI/MemoryViewerWindow.py index 476bbd8f..1e636318 100644 --- a/GUI/MemoryViewerWindow.py +++ b/GUI/MemoryViewerWindow.py @@ -1,53 +1,54 @@ -# -*- coding: utf-8 -*- - # Form implementation generated from reading ui file 'MemoryViewerWindow.ui' # -# Created by: PyQt5 UI code generator 5.14.1 +# Created by: PyQt6 UI code generator 6.4.2 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. -from PyQt5 import QtCore, QtGui, QtWidgets +from PyQt6 import QtCore, QtGui, QtWidgets class Ui_MainWindow_MemoryView(object): def setupUi(self, MainWindow_MemoryView): MainWindow_MemoryView.setObjectName("MainWindow_MemoryView") MainWindow_MemoryView.resize(800, 600) - self.centralwidget = QtWidgets.QWidget(MainWindow_MemoryView) + self.centralwidget = QtWidgets.QWidget(parent=MainWindow_MemoryView) self.centralwidget.setObjectName("centralwidget") self.gridLayout_5 = QtWidgets.QGridLayout(self.centralwidget) self.gridLayout_5.setContentsMargins(0, 0, 0, 0) self.gridLayout_5.setSpacing(0) self.gridLayout_5.setObjectName("gridLayout_5") - self.splitter_MainMiddle = QtWidgets.QSplitter(self.centralwidget) + self.splitter_MainMiddle = QtWidgets.QSplitter(parent=self.centralwidget) self.splitter_MainMiddle.setLineWidth(1) - self.splitter_MainMiddle.setOrientation(QtCore.Qt.Vertical) + self.splitter_MainMiddle.setOrientation(QtCore.Qt.Orientation.Vertical) self.splitter_MainMiddle.setOpaqueResize(True) self.splitter_MainMiddle.setHandleWidth(10) self.splitter_MainMiddle.setChildrenCollapsible(True) self.splitter_MainMiddle.setObjectName("splitter_MainMiddle") - self.splitter_Disassemble_Registers = QtWidgets.QSplitter(self.splitter_MainMiddle) - self.splitter_Disassemble_Registers.setOrientation(QtCore.Qt.Horizontal) + self.splitter_Disassemble_Registers = QtWidgets.QSplitter(parent=self.splitter_MainMiddle) + self.splitter_Disassemble_Registers.setOrientation(QtCore.Qt.Orientation.Horizontal) self.splitter_Disassemble_Registers.setHandleWidth(10) self.splitter_Disassemble_Registers.setObjectName("splitter_Disassemble_Registers") - self.widget_Disassemble = QtWidgets.QWidget(self.splitter_Disassemble_Registers) + self.widget_Disassemble = QtWidgets.QWidget(parent=self.splitter_Disassemble_Registers) self.widget_Disassemble.setObjectName("widget_Disassemble") self.gridLayout_2 = QtWidgets.QGridLayout(self.widget_Disassemble) self.gridLayout_2.setContentsMargins(0, 0, 0, 0) self.gridLayout_2.setSpacing(0) self.gridLayout_2.setObjectName("gridLayout_2") - self.tableWidget_Disassemble = QtWidgets.QTableWidget(self.widget_Disassemble) + self.tableWidget_Disassemble = QtWidgets.QTableWidget(parent=self.widget_Disassemble) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.tableWidget_Disassemble.setFont(font) - self.tableWidget_Disassemble.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + self.tableWidget_Disassemble.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOff) self.tableWidget_Disassemble.setAutoScroll(False) - self.tableWidget_Disassemble.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) + self.tableWidget_Disassemble.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers) self.tableWidget_Disassemble.setAlternatingRowColors(True) - self.tableWidget_Disassemble.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) - self.tableWidget_Disassemble.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) + self.tableWidget_Disassemble.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.SingleSelection) + self.tableWidget_Disassemble.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows) + self.tableWidget_Disassemble.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollMode.ScrollPerPixel) self.tableWidget_Disassemble.setShowGrid(False) + self.tableWidget_Disassemble.setWordWrap(False) self.tableWidget_Disassemble.setObjectName("tableWidget_Disassemble") self.tableWidget_Disassemble.setColumnCount(4) self.tableWidget_Disassemble.setRowCount(0) @@ -65,22 +66,24 @@ def setupUi(self, MainWindow_MemoryView): self.tableWidget_Disassemble.verticalHeader().setMinimumSectionSize(16) self.tableWidget_Disassemble.verticalHeader().setStretchLastSection(False) self.gridLayout_2.addWidget(self.tableWidget_Disassemble, 0, 0, 1, 1) - self.verticalScrollBar_Disassemble = QtWidgets.QScrollBar(self.widget_Disassemble) - self.verticalScrollBar_Disassemble.setOrientation(QtCore.Qt.Vertical) + self.verticalScrollBar_Disassemble = QtWidgets.QScrollBar(parent=self.widget_Disassemble) + self.verticalScrollBar_Disassemble.setOrientation(QtCore.Qt.Orientation.Vertical) self.verticalScrollBar_Disassemble.setObjectName("verticalScrollBar_Disassemble") self.gridLayout_2.addWidget(self.verticalScrollBar_Disassemble, 0, 1, 1, 1) - self.widget_Registers = QtWidgets.QWidget(self.splitter_Disassemble_Registers) - self.widget_Registers.setMinimumSize(QtCore.QSize(0, 0)) + self.widget_Registers = QtWidgets.QWidget(parent=self.splitter_Disassemble_Registers) + font = QtGui.QFont() + font.setFamily("Monospace") + self.widget_Registers.setFont(font) self.widget_Registers.setObjectName("widget_Registers") self.gridLayout_4 = QtWidgets.QGridLayout(self.widget_Registers) self.gridLayout_4.setContentsMargins(0, 0, 0, 0) self.gridLayout_4.setSpacing(0) self.gridLayout_4.setObjectName("gridLayout_4") - self.scrollArea_Registers = QtWidgets.QScrollArea(self.widget_Registers) + self.scrollArea_Registers = QtWidgets.QScrollArea(parent=self.widget_Registers) self.scrollArea_Registers.setWidgetResizable(True) self.scrollArea_Registers.setObjectName("scrollArea_Registers") self.scrollAreaWidgetContents_Registers = QtWidgets.QWidget() - self.scrollAreaWidgetContents_Registers.setGeometry(QtCore.QRect(0, 0, 331, 336)) + self.scrollAreaWidgetContents_Registers.setGeometry(QtCore.QRect(0, 0, 344, 343)) self.scrollAreaWidgetContents_Registers.setObjectName("scrollAreaWidgetContents_Registers") self.gridLayout_8 = QtWidgets.QGridLayout(self.scrollAreaWidgetContents_Registers) self.gridLayout_8.setContentsMargins(0, 0, 0, 0) @@ -92,19 +95,19 @@ def setupUi(self, MainWindow_MemoryView): self.verticalLayout_19 = QtWidgets.QVBoxLayout() self.verticalLayout_19.setSpacing(0) self.verticalLayout_19.setObjectName("verticalLayout_19") - self.label_3 = QtWidgets.QLabel(self.scrollAreaWidgetContents_Registers) + self.label_3 = QtWidgets.QLabel(parent=self.scrollAreaWidgetContents_Registers) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.label_3.setFont(font) self.label_3.setObjectName("label_3") self.verticalLayout_19.addWidget(self.label_3) - self.line = QtWidgets.QFrame(self.scrollAreaWidgetContents_Registers) - self.line.setFrameShape(QtWidgets.QFrame.HLine) - self.line.setFrameShadow(QtWidgets.QFrame.Sunken) + self.line = QtWidgets.QFrame(parent=self.scrollAreaWidgetContents_Registers) + self.line.setFrameShape(QtWidgets.QFrame.Shape.HLine) + self.line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) self.line.setObjectName("line") self.verticalLayout_19.addWidget(self.line) - self.stackedWidget = QtWidgets.QStackedWidget(self.scrollAreaWidgetContents_Registers) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + self.stackedWidget = QtWidgets.QStackedWidget(parent=self.scrollAreaWidgetContents_Registers) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.stackedWidget.sizePolicy().hasHeightForWidth()) @@ -127,106 +130,122 @@ def setupUi(self, MainWindow_MemoryView): self.verticalLayout_16 = QtWidgets.QVBoxLayout() self.verticalLayout_16.setSpacing(0) self.verticalLayout_16.setObjectName("verticalLayout_16") - self.RAX = QRegisterLabel(self.registers_64) + self.RAX = QRegisterLabel(parent=self.registers_64) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.RAX.setFont(font) + self.RAX.setText("RAX=") self.RAX.setObjectName("RAX") self.verticalLayout_16.addWidget(self.RAX) - self.RBX = QRegisterLabel(self.registers_64) + self.RBX = QRegisterLabel(parent=self.registers_64) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.RBX.setFont(font) + self.RBX.setText("RBX=") self.RBX.setObjectName("RBX") self.verticalLayout_16.addWidget(self.RBX) - self.RCX = QRegisterLabel(self.registers_64) + self.RCX = QRegisterLabel(parent=self.registers_64) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.RCX.setFont(font) + self.RCX.setText("RCX=") self.RCX.setObjectName("RCX") self.verticalLayout_16.addWidget(self.RCX) - self.RDX = QRegisterLabel(self.registers_64) + self.RDX = QRegisterLabel(parent=self.registers_64) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.RDX.setFont(font) + self.RDX.setText("RDX=") self.RDX.setObjectName("RDX") self.verticalLayout_16.addWidget(self.RDX) - self.RSI = QRegisterLabel(self.registers_64) + self.RSI = QRegisterLabel(parent=self.registers_64) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.RSI.setFont(font) + self.RSI.setText("RSI=") self.RSI.setObjectName("RSI") self.verticalLayout_16.addWidget(self.RSI) - self.RDI = QRegisterLabel(self.registers_64) + self.RDI = QRegisterLabel(parent=self.registers_64) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.RDI.setFont(font) + self.RDI.setText("RDI=") self.RDI.setObjectName("RDI") self.verticalLayout_16.addWidget(self.RDI) - self.RBP = QRegisterLabel(self.registers_64) + self.RBP = QRegisterLabel(parent=self.registers_64) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.RBP.setFont(font) + self.RBP.setText("RBP=") self.RBP.setObjectName("RBP") self.verticalLayout_16.addWidget(self.RBP) - self.RSP = QRegisterLabel(self.registers_64) + self.RSP = QRegisterLabel(parent=self.registers_64) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.RSP.setFont(font) + self.RSP.setText("RSP=") self.RSP.setObjectName("RSP") self.verticalLayout_16.addWidget(self.RSP) self.horizontalLayout.addLayout(self.verticalLayout_16) - spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) self.horizontalLayout.addItem(spacerItem) self.verticalLayout_3 = QtWidgets.QVBoxLayout() self.verticalLayout_3.setSpacing(0) self.verticalLayout_3.setObjectName("verticalLayout_3") - self.R8 = QRegisterLabel(self.registers_64) + self.R8 = QRegisterLabel(parent=self.registers_64) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.R8.setFont(font) + self.R8.setText("R8=") self.R8.setObjectName("R8") self.verticalLayout_3.addWidget(self.R8) - self.R9 = QRegisterLabel(self.registers_64) + self.R9 = QRegisterLabel(parent=self.registers_64) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.R9.setFont(font) + self.R9.setText("R9=") self.R9.setObjectName("R9") self.verticalLayout_3.addWidget(self.R9) - self.R10 = QRegisterLabel(self.registers_64) + self.R10 = QRegisterLabel(parent=self.registers_64) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.R10.setFont(font) + self.R10.setText("R10=") self.R10.setObjectName("R10") self.verticalLayout_3.addWidget(self.R10) - self.R11 = QRegisterLabel(self.registers_64) + self.R11 = QRegisterLabel(parent=self.registers_64) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.R11.setFont(font) + self.R11.setText("R11=") self.R11.setObjectName("R11") self.verticalLayout_3.addWidget(self.R11) - self.R12 = QRegisterLabel(self.registers_64) + self.R12 = QRegisterLabel(parent=self.registers_64) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.R12.setFont(font) + self.R12.setText("R12=") self.R12.setObjectName("R12") self.verticalLayout_3.addWidget(self.R12) - self.R13 = QRegisterLabel(self.registers_64) + self.R13 = QRegisterLabel(parent=self.registers_64) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.R13.setFont(font) + self.R13.setText("R13=") self.R13.setObjectName("R13") self.verticalLayout_3.addWidget(self.R13) - self.R14 = QRegisterLabel(self.registers_64) + self.R14 = QRegisterLabel(parent=self.registers_64) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.R14.setFont(font) + self.R14.setText("R14=") self.R14.setObjectName("R14") self.verticalLayout_3.addWidget(self.R14) - self.R15 = QRegisterLabel(self.registers_64) + self.R15 = QRegisterLabel(parent=self.registers_64) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.R15.setFont(font) + self.R15.setText("R15=") self.R15.setObjectName("R15") self.verticalLayout_3.addWidget(self.R15) self.horizontalLayout.addLayout(self.verticalLayout_3) @@ -234,16 +253,17 @@ def setupUi(self, MainWindow_MemoryView): self.horizontalLayout_18 = QtWidgets.QHBoxLayout() self.horizontalLayout_18.setSpacing(0) self.horizontalLayout_18.setObjectName("horizontalLayout_18") - self.RIP = QRegisterLabel(self.registers_64) + self.RIP = QRegisterLabel(parent=self.registers_64) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.RIP.setFont(font) + self.RIP.setText("RIP=") self.RIP.setObjectName("RIP") self.horizontalLayout_18.addWidget(self.RIP) - spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) self.horizontalLayout_18.addItem(spacerItem1) self.verticalLayout_17.addLayout(self.horizontalLayout_18) - spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) self.verticalLayout_17.addItem(spacerItem2) self.gridLayout_7.addLayout(self.verticalLayout_17, 0, 0, 1, 1) self.stackedWidget.addWidget(self.registers_64) @@ -262,76 +282,85 @@ def setupUi(self, MainWindow_MemoryView): self.verticalLayout_2 = QtWidgets.QVBoxLayout() self.verticalLayout_2.setSpacing(0) self.verticalLayout_2.setObjectName("verticalLayout_2") - self.EAX = QRegisterLabel(self.registers_32) + self.EAX = QRegisterLabel(parent=self.registers_32) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.EAX.setFont(font) + self.EAX.setText("EAX=") self.EAX.setObjectName("EAX") self.verticalLayout_2.addWidget(self.EAX) - self.EBX = QRegisterLabel(self.registers_32) + self.EBX = QRegisterLabel(parent=self.registers_32) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.EBX.setFont(font) + self.EBX.setText("EBX=") self.EBX.setObjectName("EBX") self.verticalLayout_2.addWidget(self.EBX) - self.ECX = QRegisterLabel(self.registers_32) + self.ECX = QRegisterLabel(parent=self.registers_32) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.ECX.setFont(font) + self.ECX.setText("ECX=") self.ECX.setObjectName("ECX") self.verticalLayout_2.addWidget(self.ECX) - self.EDX = QRegisterLabel(self.registers_32) + self.EDX = QRegisterLabel(parent=self.registers_32) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.EDX.setFont(font) + self.EDX.setText("EDX=") self.EDX.setObjectName("EDX") self.verticalLayout_2.addWidget(self.EDX) - self.ESI = QRegisterLabel(self.registers_32) + self.ESI = QRegisterLabel(parent=self.registers_32) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.ESI.setFont(font) + self.ESI.setText("ESI=") self.ESI.setObjectName("ESI") self.verticalLayout_2.addWidget(self.ESI) - self.EDI = QRegisterLabel(self.registers_32) + self.EDI = QRegisterLabel(parent=self.registers_32) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.EDI.setFont(font) + self.EDI.setText("EDI=") self.EDI.setObjectName("EDI") self.verticalLayout_2.addWidget(self.EDI) - self.EBP = QRegisterLabel(self.registers_32) + self.EBP = QRegisterLabel(parent=self.registers_32) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.EBP.setFont(font) + self.EBP.setText("EBP=") self.EBP.setObjectName("EBP") self.verticalLayout_2.addWidget(self.EBP) - self.ESP = QRegisterLabel(self.registers_32) + self.ESP = QRegisterLabel(parent=self.registers_32) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.ESP.setFont(font) + self.ESP.setText("ESP=") self.ESP.setObjectName("ESP") self.verticalLayout_2.addWidget(self.ESP) - self.EIP = QRegisterLabel(self.registers_32) + self.EIP = QRegisterLabel(parent=self.registers_32) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.EIP.setFont(font) + self.EIP.setText("EIP=") self.EIP.setObjectName("EIP") self.verticalLayout_2.addWidget(self.EIP) self.horizontalLayout_3.addLayout(self.verticalLayout_2) - spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) self.horizontalLayout_3.addItem(spacerItem3) self.verticalLayout_14.addLayout(self.horizontalLayout_3) self.gridLayout_6.addLayout(self.verticalLayout_14, 0, 0, 1, 1) self.stackedWidget.addWidget(self.registers_32) self.verticalLayout_19.addWidget(self.stackedWidget) - self.label_29 = QtWidgets.QLabel(self.scrollAreaWidgetContents_Registers) + self.label_29 = QtWidgets.QLabel(parent=self.scrollAreaWidgetContents_Registers) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.label_29.setFont(font) self.label_29.setObjectName("label_29") self.verticalLayout_19.addWidget(self.label_29) - self.line_2 = QtWidgets.QFrame(self.scrollAreaWidgetContents_Registers) - self.line_2.setFrameShape(QtWidgets.QFrame.HLine) - self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken) + self.line_2 = QtWidgets.QFrame(parent=self.scrollAreaWidgetContents_Registers) + self.line_2.setFrameShape(QtWidgets.QFrame.Shape.HLine) + self.line_2.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) self.line_2.setObjectName("line_2") self.verticalLayout_19.addWidget(self.line_2) self.horizontalLayout_21 = QtWidgets.QHBoxLayout() @@ -340,159 +369,177 @@ def setupUi(self, MainWindow_MemoryView): self.verticalLayout_5 = QtWidgets.QVBoxLayout() self.verticalLayout_5.setSpacing(0) self.verticalLayout_5.setObjectName("verticalLayout_5") - self.label_31 = QtWidgets.QLabel(self.scrollAreaWidgetContents_Registers) + self.label_31 = QtWidgets.QLabel(parent=self.scrollAreaWidgetContents_Registers) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.label_31.setFont(font) + self.label_31.setText("CF") self.label_31.setObjectName("label_31") self.verticalLayout_5.addWidget(self.label_31) - self.CF = QFlagRegisterLabel(self.scrollAreaWidgetContents_Registers) + self.CF = QFlagRegisterLabel(parent=self.scrollAreaWidgetContents_Registers) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.CF.setFont(font) + self.CF.setText("0") self.CF.setObjectName("CF") self.verticalLayout_5.addWidget(self.CF) self.horizontalLayout_21.addLayout(self.verticalLayout_5) self.verticalLayout_6 = QtWidgets.QVBoxLayout() self.verticalLayout_6.setSpacing(0) self.verticalLayout_6.setObjectName("verticalLayout_6") - self.label_35 = QtWidgets.QLabel(self.scrollAreaWidgetContents_Registers) + self.label_35 = QtWidgets.QLabel(parent=self.scrollAreaWidgetContents_Registers) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.label_35.setFont(font) + self.label_35.setText("PF") self.label_35.setObjectName("label_35") self.verticalLayout_6.addWidget(self.label_35) - self.PF = QFlagRegisterLabel(self.scrollAreaWidgetContents_Registers) + self.PF = QFlagRegisterLabel(parent=self.scrollAreaWidgetContents_Registers) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.PF.setFont(font) + self.PF.setText("0") self.PF.setObjectName("PF") self.verticalLayout_6.addWidget(self.PF) self.horizontalLayout_21.addLayout(self.verticalLayout_6) self.verticalLayout_7 = QtWidgets.QVBoxLayout() self.verticalLayout_7.setSpacing(0) self.verticalLayout_7.setObjectName("verticalLayout_7") - self.label_37 = QtWidgets.QLabel(self.scrollAreaWidgetContents_Registers) + self.label_37 = QtWidgets.QLabel(parent=self.scrollAreaWidgetContents_Registers) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.label_37.setFont(font) + self.label_37.setText("AF") self.label_37.setObjectName("label_37") self.verticalLayout_7.addWidget(self.label_37) - self.AF = QFlagRegisterLabel(self.scrollAreaWidgetContents_Registers) + self.AF = QFlagRegisterLabel(parent=self.scrollAreaWidgetContents_Registers) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.AF.setFont(font) + self.AF.setText("0") self.AF.setObjectName("AF") self.verticalLayout_7.addWidget(self.AF) self.horizontalLayout_21.addLayout(self.verticalLayout_7) self.verticalLayout_8 = QtWidgets.QVBoxLayout() self.verticalLayout_8.setSpacing(0) self.verticalLayout_8.setObjectName("verticalLayout_8") - self.label_39 = QtWidgets.QLabel(self.scrollAreaWidgetContents_Registers) + self.label_39 = QtWidgets.QLabel(parent=self.scrollAreaWidgetContents_Registers) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.label_39.setFont(font) + self.label_39.setText("ZF") self.label_39.setObjectName("label_39") self.verticalLayout_8.addWidget(self.label_39) - self.ZF = QFlagRegisterLabel(self.scrollAreaWidgetContents_Registers) + self.ZF = QFlagRegisterLabel(parent=self.scrollAreaWidgetContents_Registers) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.ZF.setFont(font) + self.ZF.setText("0") self.ZF.setObjectName("ZF") self.verticalLayout_8.addWidget(self.ZF) self.horizontalLayout_21.addLayout(self.verticalLayout_8) self.verticalLayout_9 = QtWidgets.QVBoxLayout() self.verticalLayout_9.setSpacing(0) self.verticalLayout_9.setObjectName("verticalLayout_9") - self.label_41 = QtWidgets.QLabel(self.scrollAreaWidgetContents_Registers) + self.label_41 = QtWidgets.QLabel(parent=self.scrollAreaWidgetContents_Registers) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.label_41.setFont(font) + self.label_41.setText("SF") self.label_41.setObjectName("label_41") self.verticalLayout_9.addWidget(self.label_41) - self.SF = QFlagRegisterLabel(self.scrollAreaWidgetContents_Registers) + self.SF = QFlagRegisterLabel(parent=self.scrollAreaWidgetContents_Registers) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.SF.setFont(font) + self.SF.setText("0") self.SF.setObjectName("SF") self.verticalLayout_9.addWidget(self.SF) self.horizontalLayout_21.addLayout(self.verticalLayout_9) self.verticalLayout_10 = QtWidgets.QVBoxLayout() self.verticalLayout_10.setSpacing(0) self.verticalLayout_10.setObjectName("verticalLayout_10") - self.label_43 = QtWidgets.QLabel(self.scrollAreaWidgetContents_Registers) + self.label_43 = QtWidgets.QLabel(parent=self.scrollAreaWidgetContents_Registers) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.label_43.setFont(font) + self.label_43.setText("TF") self.label_43.setObjectName("label_43") self.verticalLayout_10.addWidget(self.label_43) - self.TF = QFlagRegisterLabel(self.scrollAreaWidgetContents_Registers) + self.TF = QFlagRegisterLabel(parent=self.scrollAreaWidgetContents_Registers) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.TF.setFont(font) + self.TF.setText("0") self.TF.setObjectName("TF") self.verticalLayout_10.addWidget(self.TF) self.horizontalLayout_21.addLayout(self.verticalLayout_10) self.verticalLayout_11 = QtWidgets.QVBoxLayout() self.verticalLayout_11.setSpacing(0) self.verticalLayout_11.setObjectName("verticalLayout_11") - self.label_45 = QtWidgets.QLabel(self.scrollAreaWidgetContents_Registers) + self.label_45 = QtWidgets.QLabel(parent=self.scrollAreaWidgetContents_Registers) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.label_45.setFont(font) + self.label_45.setText("IF") self.label_45.setObjectName("label_45") self.verticalLayout_11.addWidget(self.label_45) - self.IF = QFlagRegisterLabel(self.scrollAreaWidgetContents_Registers) + self.IF = QFlagRegisterLabel(parent=self.scrollAreaWidgetContents_Registers) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.IF.setFont(font) + self.IF.setText("0") self.IF.setObjectName("IF") self.verticalLayout_11.addWidget(self.IF) self.horizontalLayout_21.addLayout(self.verticalLayout_11) self.verticalLayout_12 = QtWidgets.QVBoxLayout() self.verticalLayout_12.setSpacing(0) self.verticalLayout_12.setObjectName("verticalLayout_12") - self.label_47 = QtWidgets.QLabel(self.scrollAreaWidgetContents_Registers) + self.label_47 = QtWidgets.QLabel(parent=self.scrollAreaWidgetContents_Registers) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.label_47.setFont(font) + self.label_47.setText("DF") self.label_47.setObjectName("label_47") self.verticalLayout_12.addWidget(self.label_47) - self.DF = QFlagRegisterLabel(self.scrollAreaWidgetContents_Registers) + self.DF = QFlagRegisterLabel(parent=self.scrollAreaWidgetContents_Registers) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.DF.setFont(font) + self.DF.setText("0") self.DF.setObjectName("DF") self.verticalLayout_12.addWidget(self.DF) self.horizontalLayout_21.addLayout(self.verticalLayout_12) self.verticalLayout_13 = QtWidgets.QVBoxLayout() self.verticalLayout_13.setSpacing(0) self.verticalLayout_13.setObjectName("verticalLayout_13") - self.label_49 = QtWidgets.QLabel(self.scrollAreaWidgetContents_Registers) + self.label_49 = QtWidgets.QLabel(parent=self.scrollAreaWidgetContents_Registers) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.label_49.setFont(font) + self.label_49.setText("OF") self.label_49.setObjectName("label_49") self.verticalLayout_13.addWidget(self.label_49) - self.OF = QFlagRegisterLabel(self.scrollAreaWidgetContents_Registers) + self.OF = QFlagRegisterLabel(parent=self.scrollAreaWidgetContents_Registers) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.OF.setFont(font) + self.OF.setText("0") self.OF.setObjectName("OF") self.verticalLayout_13.addWidget(self.OF) self.horizontalLayout_21.addLayout(self.verticalLayout_13) self.verticalLayout_19.addLayout(self.horizontalLayout_21) - spacerItem4 = QtWidgets.QSpacerItem(20, 15, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) + spacerItem4 = QtWidgets.QSpacerItem(20, 15, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Fixed) self.verticalLayout_19.addItem(spacerItem4) - self.label_30 = QtWidgets.QLabel(self.scrollAreaWidgetContents_Registers) + self.label_30 = QtWidgets.QLabel(parent=self.scrollAreaWidgetContents_Registers) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.label_30.setFont(font) self.label_30.setObjectName("label_30") self.verticalLayout_19.addWidget(self.label_30) - self.line_3 = QtWidgets.QFrame(self.scrollAreaWidgetContents_Registers) - self.line_3.setFrameShape(QtWidgets.QFrame.HLine) - self.line_3.setFrameShadow(QtWidgets.QFrame.Sunken) + self.line_3 = QtWidgets.QFrame(parent=self.scrollAreaWidgetContents_Registers) + self.line_3.setFrameShape(QtWidgets.QFrame.Shape.HLine) + self.line_3.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) self.line_3.setObjectName("line_3") self.verticalLayout_19.addWidget(self.line_3) self.horizontalLayout_2 = QtWidgets.QHBoxLayout() @@ -501,86 +548,92 @@ def setupUi(self, MainWindow_MemoryView): self.verticalLayout_4 = QtWidgets.QVBoxLayout() self.verticalLayout_4.setSpacing(0) self.verticalLayout_4.setObjectName("verticalLayout_4") - self.CS = QRegisterLabel(self.scrollAreaWidgetContents_Registers) + self.CS = QRegisterLabel(parent=self.scrollAreaWidgetContents_Registers) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.CS.setFont(font) + self.CS.setText("CS=") self.CS.setObjectName("CS") self.verticalLayout_4.addWidget(self.CS) - self.ES = QRegisterLabel(self.scrollAreaWidgetContents_Registers) + self.ES = QRegisterLabel(parent=self.scrollAreaWidgetContents_Registers) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.ES.setFont(font) + self.ES.setText("ES=") self.ES.setObjectName("ES") self.verticalLayout_4.addWidget(self.ES) self.horizontalLayout_2.addLayout(self.verticalLayout_4) - spacerItem5 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + spacerItem5 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) self.horizontalLayout_2.addItem(spacerItem5) self.verticalLayout_15 = QtWidgets.QVBoxLayout() self.verticalLayout_15.setSpacing(0) self.verticalLayout_15.setObjectName("verticalLayout_15") - self.SS = QRegisterLabel(self.scrollAreaWidgetContents_Registers) + self.SS = QRegisterLabel(parent=self.scrollAreaWidgetContents_Registers) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.SS.setFont(font) + self.SS.setText("SS=") self.SS.setObjectName("SS") self.verticalLayout_15.addWidget(self.SS) - self.GS = QRegisterLabel(self.scrollAreaWidgetContents_Registers) + self.GS = QRegisterLabel(parent=self.scrollAreaWidgetContents_Registers) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.GS.setFont(font) + self.GS.setText("GS=") self.GS.setObjectName("GS") self.verticalLayout_15.addWidget(self.GS) self.horizontalLayout_2.addLayout(self.verticalLayout_15) - spacerItem6 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + spacerItem6 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) self.horizontalLayout_2.addItem(spacerItem6) self.verticalLayout_18 = QtWidgets.QVBoxLayout() self.verticalLayout_18.setSpacing(0) self.verticalLayout_18.setObjectName("verticalLayout_18") - self.DS = QRegisterLabel(self.scrollAreaWidgetContents_Registers) + self.DS = QRegisterLabel(parent=self.scrollAreaWidgetContents_Registers) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.DS.setFont(font) + self.DS.setText("DS=") self.DS.setObjectName("DS") self.verticalLayout_18.addWidget(self.DS) - self.FS = QRegisterLabel(self.scrollAreaWidgetContents_Registers) + self.FS = QRegisterLabel(parent=self.scrollAreaWidgetContents_Registers) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.FS.setFont(font) + self.FS.setText("FS=") self.FS.setObjectName("FS") self.verticalLayout_18.addWidget(self.FS) self.horizontalLayout_2.addLayout(self.verticalLayout_18) self.verticalLayout_19.addLayout(self.horizontalLayout_2) - self.pushButton_ShowFloatRegisters = QtWidgets.QPushButton(self.scrollAreaWidgetContents_Registers) + self.pushButton_ShowFloatRegisters = QtWidgets.QPushButton(parent=self.scrollAreaWidgetContents_Registers) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.pushButton_ShowFloatRegisters.setFont(font) self.pushButton_ShowFloatRegisters.setObjectName("pushButton_ShowFloatRegisters") self.verticalLayout_19.addWidget(self.pushButton_ShowFloatRegisters) - spacerItem7 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + spacerItem7 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) self.verticalLayout_19.addItem(spacerItem7) self.horizontalLayout_4.addLayout(self.verticalLayout_19) - spacerItem8 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + spacerItem8 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) self.horizontalLayout_4.addItem(spacerItem8) self.gridLayout_8.addLayout(self.horizontalLayout_4, 0, 0, 1, 1) self.scrollArea_Registers.setWidget(self.scrollAreaWidgetContents_Registers) self.gridLayout_4.addWidget(self.scrollArea_Registers, 0, 0, 1, 1) - self.splitter_HexView_StackView = QtWidgets.QSplitter(self.splitter_MainMiddle) - self.splitter_HexView_StackView.setOrientation(QtCore.Qt.Horizontal) + self.splitter_HexView_StackView = QtWidgets.QSplitter(parent=self.splitter_MainMiddle) + self.splitter_HexView_StackView.setOrientation(QtCore.Qt.Orientation.Horizontal) self.splitter_HexView_StackView.setHandleWidth(10) self.splitter_HexView_StackView.setObjectName("splitter_HexView_StackView") - self.widget_HexView = QtWidgets.QWidget(self.splitter_HexView_StackView) + self.widget_HexView = QtWidgets.QWidget(parent=self.splitter_HexView_StackView) self.widget_HexView.setObjectName("widget_HexView") self.gridLayout = QtWidgets.QGridLayout(self.widget_HexView) self.gridLayout.setContentsMargins(0, 0, 0, 0) self.gridLayout.setHorizontalSpacing(0) self.gridLayout.setVerticalSpacing(4) self.gridLayout.setObjectName("gridLayout") - self.scrollArea_Hex = QtWidgets.QScrollArea(self.widget_HexView) + self.scrollArea_Hex = QtWidgets.QScrollArea(parent=self.widget_HexView) self.scrollArea_Hex.setWidgetResizable(True) self.scrollArea_Hex.setObjectName("scrollArea_Hex") self.scrollAreaWidgetContents_2 = QtWidgets.QWidget() - self.scrollAreaWidgetContents_2.setGeometry(QtCore.QRect(0, 0, 546, 193)) + self.scrollAreaWidgetContents_2.setGeometry(QtCore.QRect(0, 0, 492, 197)) self.scrollAreaWidgetContents_2.setObjectName("scrollAreaWidgetContents_2") self.gridLayout_11 = QtWidgets.QGridLayout(self.scrollAreaWidgetContents_2) self.gridLayout_11.setContentsMargins(0, 0, 0, 0) @@ -589,11 +642,14 @@ def setupUi(self, MainWindow_MemoryView): self.horizontalLayout_5 = QtWidgets.QHBoxLayout() self.horizontalLayout_5.setSpacing(0) self.horizontalLayout_5.setObjectName("horizontalLayout_5") - self.tableWidget_HexView_Address = QtWidgets.QTableWidget(self.scrollAreaWidgetContents_2) - self.tableWidget_HexView_Address.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) - self.tableWidget_HexView_Address.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) - self.tableWidget_HexView_Address.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) + self.tableWidget_HexView_Address = QtWidgets.QTableWidget(parent=self.scrollAreaWidgetContents_2) + font = QtGui.QFont() + font.setFamily("Monospace") + self.tableWidget_HexView_Address.setFont(font) + self.tableWidget_HexView_Address.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers) + self.tableWidget_HexView_Address.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows) self.tableWidget_HexView_Address.setShowGrid(False) + self.tableWidget_HexView_Address.setWordWrap(False) self.tableWidget_HexView_Address.setObjectName("tableWidget_HexView_Address") self.tableWidget_HexView_Address.setColumnCount(1) self.tableWidget_HexView_Address.setRowCount(0) @@ -602,49 +658,57 @@ def setupUi(self, MainWindow_MemoryView): self.tableWidget_HexView_Address.horizontalHeader().setVisible(False) self.tableWidget_HexView_Address.verticalHeader().setVisible(False) self.horizontalLayout_5.addWidget(self.tableWidget_HexView_Address) - self.line_5 = QtWidgets.QFrame(self.scrollAreaWidgetContents_2) - self.line_5.setFrameShape(QtWidgets.QFrame.VLine) - self.line_5.setFrameShadow(QtWidgets.QFrame.Sunken) + self.line_5 = QtWidgets.QFrame(parent=self.scrollAreaWidgetContents_2) + self.line_5.setFrameShape(QtWidgets.QFrame.Shape.VLine) + self.line_5.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) self.line_5.setObjectName("line_5") self.horizontalLayout_5.addWidget(self.line_5) - self.tableView_HexView_Hex = QHexView(self.scrollAreaWidgetContents_2) + self.tableView_HexView_Hex = QHexView(parent=self.scrollAreaWidgetContents_2) + font = QtGui.QFont() + font.setFamily("Monospace") + self.tableView_HexView_Hex.setFont(font) + self.tableView_HexView_Hex.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.SizeAdjustPolicy.AdjustToContents) self.tableView_HexView_Hex.setObjectName("tableView_HexView_Hex") self.horizontalLayout_5.addWidget(self.tableView_HexView_Hex) - self.line_4 = QtWidgets.QFrame(self.scrollAreaWidgetContents_2) - self.line_4.setFrameShape(QtWidgets.QFrame.VLine) - self.line_4.setFrameShadow(QtWidgets.QFrame.Sunken) + self.line_4 = QtWidgets.QFrame(parent=self.scrollAreaWidgetContents_2) + self.line_4.setFrameShape(QtWidgets.QFrame.Shape.VLine) + self.line_4.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) self.line_4.setObjectName("line_4") self.horizontalLayout_5.addWidget(self.line_4) - self.tableView_HexView_Ascii = QAsciiView(self.scrollAreaWidgetContents_2) + self.tableView_HexView_Ascii = QAsciiView(parent=self.scrollAreaWidgetContents_2) + font = QtGui.QFont() + font.setFamily("Monospace") + self.tableView_HexView_Ascii.setFont(font) + self.tableView_HexView_Ascii.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.SizeAdjustPolicy.AdjustToContents) self.tableView_HexView_Ascii.setObjectName("tableView_HexView_Ascii") self.horizontalLayout_5.addWidget(self.tableView_HexView_Ascii) - spacerItem9 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + spacerItem9 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) self.horizontalLayout_5.addItem(spacerItem9) self.gridLayout_11.addLayout(self.horizontalLayout_5, 2, 0, 1, 1) - self.label_HexView_Information = QtWidgets.QLabel(self.scrollAreaWidgetContents_2) + self.label_HexView_Information = QtWidgets.QLabel(parent=self.scrollAreaWidgetContents_2) self.label_HexView_Information.setText("") self.label_HexView_Information.setObjectName("label_HexView_Information") self.gridLayout_11.addWidget(self.label_HexView_Information, 0, 0, 1, 1) - self.line_6 = QtWidgets.QFrame(self.scrollAreaWidgetContents_2) - self.line_6.setFrameShape(QtWidgets.QFrame.HLine) - self.line_6.setFrameShadow(QtWidgets.QFrame.Sunken) + self.line_6 = QtWidgets.QFrame(parent=self.scrollAreaWidgetContents_2) + self.line_6.setFrameShape(QtWidgets.QFrame.Shape.HLine) + self.line_6.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) self.line_6.setObjectName("line_6") self.gridLayout_11.addWidget(self.line_6, 1, 0, 1, 1) self.label_HexView_Information.raise_() self.line_6.raise_() self.scrollArea_Hex.setWidget(self.scrollAreaWidgetContents_2) self.gridLayout.addWidget(self.scrollArea_Hex, 0, 0, 1, 1) - self.verticalScrollBar_HexView = QtWidgets.QScrollBar(self.widget_HexView) - self.verticalScrollBar_HexView.setOrientation(QtCore.Qt.Vertical) + self.verticalScrollBar_HexView = QtWidgets.QScrollBar(parent=self.widget_HexView) + self.verticalScrollBar_HexView.setOrientation(QtCore.Qt.Orientation.Vertical) self.verticalScrollBar_HexView.setObjectName("verticalScrollBar_HexView") self.gridLayout.addWidget(self.verticalScrollBar_HexView, 0, 1, 1, 1) - self.widget_StackView = QtWidgets.QWidget(self.splitter_HexView_StackView) + self.widget_StackView = QtWidgets.QWidget(parent=self.splitter_HexView_StackView) self.widget_StackView.setObjectName("widget_StackView") self.gridLayout_3 = QtWidgets.QGridLayout(self.widget_StackView) self.gridLayout_3.setContentsMargins(0, 0, 0, 0) self.gridLayout_3.setSpacing(0) self.gridLayout_3.setObjectName("gridLayout_3") - self.stackedWidget_StackScreens = QtWidgets.QStackedWidget(self.widget_StackView) + self.stackedWidget_StackScreens = QtWidgets.QStackedWidget(parent=self.widget_StackView) self.stackedWidget_StackScreens.setObjectName("stackedWidget_StackScreens") self.StackTrace = QtWidgets.QWidget() self.StackTrace.setObjectName("StackTrace") @@ -652,13 +716,14 @@ def setupUi(self, MainWindow_MemoryView): self.gridLayout_9.setContentsMargins(0, 0, 0, 0) self.gridLayout_9.setSpacing(0) self.gridLayout_9.setObjectName("gridLayout_9") - self.tableWidget_StackTrace = QtWidgets.QTableWidget(self.StackTrace) + self.tableWidget_StackTrace = QtWidgets.QTableWidget(parent=self.StackTrace) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.tableWidget_StackTrace.setFont(font) - self.tableWidget_StackTrace.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) - self.tableWidget_StackTrace.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) - self.tableWidget_StackTrace.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) + self.tableWidget_StackTrace.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers) + self.tableWidget_StackTrace.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.SingleSelection) + self.tableWidget_StackTrace.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows) + self.tableWidget_StackTrace.setWordWrap(False) self.tableWidget_StackTrace.setObjectName("tableWidget_StackTrace") self.tableWidget_StackTrace.setColumnCount(2) self.tableWidget_StackTrace.setRowCount(0) @@ -678,13 +743,13 @@ def setupUi(self, MainWindow_MemoryView): self.gridLayout_10.setContentsMargins(0, 0, 0, 0) self.gridLayout_10.setSpacing(0) self.gridLayout_10.setObjectName("gridLayout_10") - self.tableWidget_Stack = QtWidgets.QTableWidget(self.Stack) + self.tableWidget_Stack = QtWidgets.QTableWidget(parent=self.Stack) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily("Monospace") self.tableWidget_Stack.setFont(font) - self.tableWidget_Stack.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) - self.tableWidget_Stack.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) - self.tableWidget_Stack.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) + self.tableWidget_Stack.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers) + self.tableWidget_Stack.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.SingleSelection) + self.tableWidget_Stack.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows) self.tableWidget_Stack.setObjectName("tableWidget_Stack") self.tableWidget_Stack.setColumnCount(3) self.tableWidget_Stack.setRowCount(0) @@ -703,73 +768,76 @@ def setupUi(self, MainWindow_MemoryView): self.gridLayout_3.addWidget(self.stackedWidget_StackScreens, 0, 0, 1, 1) self.gridLayout_5.addWidget(self.splitter_MainMiddle, 0, 0, 1, 1) MainWindow_MemoryView.setCentralWidget(self.centralwidget) - self.menubar = QtWidgets.QMenuBar(MainWindow_MemoryView) - self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 30)) + self.menubar = QtWidgets.QMenuBar(parent=MainWindow_MemoryView) + self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 23)) self.menubar.setObjectName("menubar") - self.menuView = QtWidgets.QMenu(self.menubar) + self.menuView = QtWidgets.QMenu(parent=self.menubar) self.menuView.setObjectName("menuView") - self.menuDebug = QtWidgets.QMenu(self.menubar) + self.menuDebug = QtWidgets.QMenu(parent=self.menubar) self.menuDebug.setObjectName("menuDebug") - self.menuTools = QtWidgets.QMenu(self.menubar) + self.menuTools = QtWidgets.QMenu(parent=self.menubar) self.menuTools.setObjectName("menuTools") - self.menuFile = QtWidgets.QMenu(self.menubar) + self.menuFile = QtWidgets.QMenu(parent=self.menubar) self.menuFile.setObjectName("menuFile") - self.menuHelp = QtWidgets.QMenu(self.menubar) + self.menuHelp = QtWidgets.QMenu(parent=self.menubar) self.menuHelp.setObjectName("menuHelp") MainWindow_MemoryView.setMenuBar(self.menubar) - self.statusbar = QtWidgets.QStatusBar(MainWindow_MemoryView) + self.statusbar = QtWidgets.QStatusBar(parent=MainWindow_MemoryView) self.statusbar.setObjectName("statusbar") MainWindow_MemoryView.setStatusBar(self.statusbar) - self.actionBookmarks = QtWidgets.QAction(MainWindow_MemoryView) + self.actionBookmarks = QtGui.QAction(parent=MainWindow_MemoryView) self.actionBookmarks.setObjectName("actionBookmarks") - self.actionStackTrace_Info = QtWidgets.QAction(MainWindow_MemoryView) + self.actionStackTrace_Info = QtGui.QAction(parent=MainWindow_MemoryView) self.actionStackTrace_Info.setObjectName("actionStackTrace_Info") - self.actionInject_so_file = QtWidgets.QAction(MainWindow_MemoryView) + self.actionInject_so_file = QtGui.QAction(parent=MainWindow_MemoryView) self.actionInject_so_file.setObjectName("actionInject_so_file") - self.actionRun = QtWidgets.QAction(MainWindow_MemoryView) + self.actionRun = QtGui.QAction(parent=MainWindow_MemoryView) self.actionRun.setObjectName("actionRun") - self.actionBreak = QtWidgets.QAction(MainWindow_MemoryView) + self.actionBreak = QtGui.QAction(parent=MainWindow_MemoryView) self.actionBreak.setObjectName("actionBreak") - self.actionStep = QtWidgets.QAction(MainWindow_MemoryView) + self.actionStep = QtGui.QAction(parent=MainWindow_MemoryView) self.actionStep.setObjectName("actionStep") - self.actionStep_Over = QtWidgets.QAction(MainWindow_MemoryView) + self.actionStep_Over = QtGui.QAction(parent=MainWindow_MemoryView) self.actionStep_Over.setObjectName("actionStep_Over") - self.actionExecute_Till_Return = QtWidgets.QAction(MainWindow_MemoryView) + self.actionExecute_Till_Return = QtGui.QAction(parent=MainWindow_MemoryView) self.actionExecute_Till_Return.setObjectName("actionExecute_Till_Return") - self.actionToggle_Breakpoint = QtWidgets.QAction(MainWindow_MemoryView) + self.actionToggle_Breakpoint = QtGui.QAction(parent=MainWindow_MemoryView) self.actionToggle_Breakpoint.setObjectName("actionToggle_Breakpoint") - self.actionBreakpoints = QtWidgets.QAction(MainWindow_MemoryView) + self.actionBreakpoints = QtGui.QAction(parent=MainWindow_MemoryView) self.actionBreakpoints.setObjectName("actionBreakpoints") - self.actionFunctions = QtWidgets.QAction(MainWindow_MemoryView) + self.actionFunctions = QtGui.QAction(parent=MainWindow_MemoryView) self.actionFunctions.setObjectName("actionFunctions") - self.actionSet_Address = QtWidgets.QAction(MainWindow_MemoryView) + self.actionSet_Address = QtGui.QAction(parent=MainWindow_MemoryView) self.actionSet_Address.setObjectName("actionSet_Address") - self.actionCall_Function = QtWidgets.QAction(MainWindow_MemoryView) + self.actionCall_Function = QtGui.QAction(parent=MainWindow_MemoryView) self.actionCall_Function.setObjectName("actionCall_Function") - self.actionLoad_Trace = QtWidgets.QAction(MainWindow_MemoryView) + self.actionLoad_Trace = QtGui.QAction(parent=MainWindow_MemoryView) self.actionLoad_Trace.setObjectName("actionLoad_Trace") - self.actionlibpince = QtWidgets.QAction(MainWindow_MemoryView) + self.actionlibpince = QtGui.QAction(parent=MainWindow_MemoryView) self.actionlibpince.setObjectName("actionlibpince") - self.actionGDB_Log_File = QtWidgets.QAction(MainWindow_MemoryView) + self.actionGDB_Log_File = QtGui.QAction(parent=MainWindow_MemoryView) self.actionGDB_Log_File.setObjectName("actionGDB_Log_File") - self.actionSearch_Opcode = QtWidgets.QAction(MainWindow_MemoryView) + self.actionSearch_Opcode = QtGui.QAction(parent=MainWindow_MemoryView) self.actionSearch_Opcode.setObjectName("actionSearch_Opcode") - self.actionMemory_Regions = QtWidgets.QAction(MainWindow_MemoryView) + self.actionMemory_Regions = QtGui.QAction(parent=MainWindow_MemoryView) self.actionMemory_Regions.setObjectName("actionMemory_Regions") - self.actionDissect_Code = QtWidgets.QAction(MainWindow_MemoryView) + self.actionDissect_Code = QtGui.QAction(parent=MainWindow_MemoryView) self.actionDissect_Code.setObjectName("actionDissect_Code") - self.actionReferenced_Strings = QtWidgets.QAction(MainWindow_MemoryView) + self.actionReferenced_Strings = QtGui.QAction(parent=MainWindow_MemoryView) self.actionReferenced_Strings.setObjectName("actionReferenced_Strings") - self.actionReferenced_Calls = QtWidgets.QAction(MainWindow_MemoryView) + self.actionReferenced_Calls = QtGui.QAction(parent=MainWindow_MemoryView) self.actionReferenced_Calls.setObjectName("actionReferenced_Calls") - self.actionToggle_Attach = QtWidgets.QAction(MainWindow_MemoryView) + self.actionToggle_Attach = QtGui.QAction(parent=MainWindow_MemoryView) self.actionToggle_Attach.setObjectName("actionToggle_Attach") + self.actionRestore_Instructions = QtGui.QAction(parent=MainWindow_MemoryView) + self.actionRestore_Instructions.setObjectName("actionRestore_Instructions") self.menuView.addAction(self.actionBookmarks) self.menuView.addAction(self.actionStackTrace_Info) self.menuView.addAction(self.actionBreakpoints) self.menuView.addAction(self.actionFunctions) self.menuView.addAction(self.actionGDB_Log_File) self.menuView.addAction(self.actionMemory_Regions) + self.menuView.addAction(self.actionRestore_Instructions) self.menuView.addSeparator() self.menuView.addAction(self.actionReferenced_Strings) self.menuView.addAction(self.actionReferenced_Calls) @@ -812,58 +880,8 @@ def retranslateUi(self, MainWindow_MemoryView): item = self.tableWidget_Disassemble.horizontalHeaderItem(3) item.setText(_translate("MainWindow_MemoryView", "Comment")) self.label_3.setText(_translate("MainWindow_MemoryView", "Registers")) - self.RAX.setText(_translate("MainWindow_MemoryView", "RAX=")) - self.RBX.setText(_translate("MainWindow_MemoryView", "RBX=")) - self.RCX.setText(_translate("MainWindow_MemoryView", "RCX=")) - self.RDX.setText(_translate("MainWindow_MemoryView", "RDX=")) - self.RSI.setText(_translate("MainWindow_MemoryView", "RSI=")) - self.RDI.setText(_translate("MainWindow_MemoryView", "RDI=")) - self.RBP.setText(_translate("MainWindow_MemoryView", "RBP=")) - self.RSP.setText(_translate("MainWindow_MemoryView", "RSP=")) - self.R8.setText(_translate("MainWindow_MemoryView", "R8=")) - self.R9.setText(_translate("MainWindow_MemoryView", "R9=")) - self.R10.setText(_translate("MainWindow_MemoryView", "R10=")) - self.R11.setText(_translate("MainWindow_MemoryView", "R11=")) - self.R12.setText(_translate("MainWindow_MemoryView", "R12=")) - self.R13.setText(_translate("MainWindow_MemoryView", "R13=")) - self.R14.setText(_translate("MainWindow_MemoryView", "R14=")) - self.R15.setText(_translate("MainWindow_MemoryView", "R15=")) - self.RIP.setText(_translate("MainWindow_MemoryView", "RIP=")) - self.EAX.setText(_translate("MainWindow_MemoryView", "EAX=")) - self.EBX.setText(_translate("MainWindow_MemoryView", "EBX=")) - self.ECX.setText(_translate("MainWindow_MemoryView", "ECX=")) - self.EDX.setText(_translate("MainWindow_MemoryView", "EDX=")) - self.ESI.setText(_translate("MainWindow_MemoryView", "ESI=")) - self.EDI.setText(_translate("MainWindow_MemoryView", "EDI=")) - self.EBP.setText(_translate("MainWindow_MemoryView", "EBP=")) - self.ESP.setText(_translate("MainWindow_MemoryView", "ESP=")) - self.EIP.setText(_translate("MainWindow_MemoryView", "EIP=")) self.label_29.setText(_translate("MainWindow_MemoryView", "Flags")) - self.label_31.setText(_translate("MainWindow_MemoryView", "CF")) - self.CF.setText(_translate("MainWindow_MemoryView", "0")) - self.label_35.setText(_translate("MainWindow_MemoryView", "PF")) - self.PF.setText(_translate("MainWindow_MemoryView", "0")) - self.label_37.setText(_translate("MainWindow_MemoryView", "AF")) - self.AF.setText(_translate("MainWindow_MemoryView", "0")) - self.label_39.setText(_translate("MainWindow_MemoryView", "ZF")) - self.ZF.setText(_translate("MainWindow_MemoryView", "0")) - self.label_41.setText(_translate("MainWindow_MemoryView", "SF")) - self.SF.setText(_translate("MainWindow_MemoryView", "0")) - self.label_43.setText(_translate("MainWindow_MemoryView", "TF")) - self.TF.setText(_translate("MainWindow_MemoryView", "0")) - self.label_45.setText(_translate("MainWindow_MemoryView", "IF")) - self.IF.setText(_translate("MainWindow_MemoryView", "0")) - self.label_47.setText(_translate("MainWindow_MemoryView", "DF")) - self.DF.setText(_translate("MainWindow_MemoryView", "0")) - self.label_49.setText(_translate("MainWindow_MemoryView", "OF")) - self.OF.setText(_translate("MainWindow_MemoryView", "0")) self.label_30.setText(_translate("MainWindow_MemoryView", "Segment Registers")) - self.CS.setText(_translate("MainWindow_MemoryView", "CS=")) - self.ES.setText(_translate("MainWindow_MemoryView", "ES=")) - self.SS.setText(_translate("MainWindow_MemoryView", "SS=")) - self.GS.setText(_translate("MainWindow_MemoryView", "GS=")) - self.DS.setText(_translate("MainWindow_MemoryView", "DS=")) - self.FS.setText(_translate("MainWindow_MemoryView", "FS=")) self.pushButton_ShowFloatRegisters.setText(_translate("MainWindow_MemoryView", "Show Float Registers")) item = self.tableWidget_HexView_Address.horizontalHeaderItem(0) item.setText(_translate("MainWindow_MemoryView", "Address")) @@ -904,17 +922,8 @@ def retranslateUi(self, MainWindow_MemoryView): self.actionReferenced_Strings.setText(_translate("MainWindow_MemoryView", "R&eferenced Strings")) self.actionReferenced_Calls.setText(_translate("MainWindow_MemoryView", "Referenced &Calls")) self.actionToggle_Attach.setText(_translate("MainWindow_MemoryView", "To&ggle Attach")) -from GUI.CustomLabels.FlagRegisterLabel import QFlagRegisterLabel -from GUI.CustomLabels.RegisterLabel import QRegisterLabel -from GUI.CustomTableViews.AsciiView import QAsciiView -from GUI.CustomTableViews.HexView import QHexView - - -if __name__ == "__main__": - import sys - app = QtWidgets.QApplication(sys.argv) - MainWindow_MemoryView = QtWidgets.QMainWindow() - ui = Ui_MainWindow_MemoryView() - ui.setupUi(MainWindow_MemoryView) - MainWindow_MemoryView.show() - sys.exit(app.exec_()) + self.actionRestore_Instructions.setText(_translate("MainWindow_MemoryView", "Restore Instructions")) +from GUI.Labels.FlagRegisterLabel import QFlagRegisterLabel +from GUI.Labels.RegisterLabel import QRegisterLabel +from GUI.TableViews.AsciiView import QAsciiView +from GUI.TableViews.HexView import QHexView diff --git a/GUI/MemoryViewerWindow.ui b/GUI/MemoryViewerWindow.ui index c0af65b4..6578615a 100644 --- a/GUI/MemoryViewerWindow.ui +++ b/GUI/MemoryViewerWindow.ui @@ -75,7 +75,7 @@ - 9 + Monospace @@ -96,9 +96,15 @@ QAbstractItemView::SelectRows + + QAbstractItemView::ScrollPerPixel + false + + false + true @@ -146,11 +152,10 @@ - - - 0 - 0 - + + + Monospace + @@ -178,8 +183,8 @@ 0 0 - 331 - 336 + 344 + 343 @@ -212,7 +217,7 @@ - 9 + Monospace @@ -286,11 +291,11 @@ - 9 + Monospace - RAX= + RAX= @@ -298,11 +303,11 @@ - 9 + Monospace - RBX= + RBX= @@ -310,11 +315,11 @@ - 9 + Monospace - RCX= + RCX= @@ -322,11 +327,11 @@ - 9 + Monospace - RDX= + RDX= @@ -334,11 +339,11 @@ - 9 + Monospace - RSI= + RSI= @@ -346,11 +351,11 @@ - 9 + Monospace - RDI= + RDI= @@ -358,11 +363,11 @@ - 9 + Monospace - RBP= + RBP= @@ -370,11 +375,11 @@ - 9 + Monospace - RSP= + RSP= @@ -402,11 +407,11 @@ - 9 + Monospace - R8= + R8= @@ -414,11 +419,11 @@ - 9 + Monospace - R9= + R9= @@ -426,11 +431,11 @@ - 9 + Monospace - R10= + R10= @@ -438,11 +443,11 @@ - 9 + Monospace - R11= + R11= @@ -450,11 +455,11 @@ - 9 + Monospace - R12= + R12= @@ -462,11 +467,11 @@ - 9 + Monospace - R13= + R13= @@ -474,11 +479,11 @@ - 9 + Monospace - R14= + R14= @@ -486,11 +491,11 @@ - 9 + Monospace - R15= + R15= @@ -507,11 +512,11 @@ - 9 + Monospace - RIP= + RIP= @@ -583,11 +588,11 @@ - 9 + Monospace - EAX= + EAX= @@ -595,11 +600,11 @@ - 9 + Monospace - EBX= + EBX= @@ -607,11 +612,11 @@ - 9 + Monospace - ECX= + ECX= @@ -619,11 +624,11 @@ - 9 + Monospace - EDX= + EDX= @@ -631,11 +636,11 @@ - 9 + Monospace - ESI= + ESI= @@ -643,11 +648,11 @@ - 9 + Monospace - EDI= + EDI= @@ -655,11 +660,11 @@ - 9 + Monospace - EBP= + EBP= @@ -667,11 +672,11 @@ - 9 + Monospace - ESP= + ESP= @@ -679,11 +684,11 @@ - 9 + Monospace - EIP= + EIP= @@ -714,7 +719,7 @@ - 9 + Monospace @@ -743,11 +748,11 @@ - 9 + Monospace - CF + CF @@ -755,11 +760,11 @@ - 9 + Monospace - 0 + 0 @@ -774,11 +779,11 @@ - 9 + Monospace - PF + PF @@ -786,11 +791,11 @@ - 9 + Monospace - 0 + 0 @@ -805,11 +810,11 @@ - 9 + Monospace - AF + AF @@ -817,11 +822,11 @@ - 9 + Monospace - 0 + 0 @@ -836,11 +841,11 @@ - 9 + Monospace - ZF + ZF @@ -848,11 +853,11 @@ - 9 + Monospace - 0 + 0 @@ -867,11 +872,11 @@ - 9 + Monospace - SF + SF @@ -879,11 +884,11 @@ - 9 + Monospace - 0 + 0 @@ -898,11 +903,11 @@ - 9 + Monospace - TF + TF @@ -910,11 +915,11 @@ - 9 + Monospace - 0 + 0 @@ -929,11 +934,11 @@ - 9 + Monospace - IF + IF @@ -941,11 +946,11 @@ - 9 + Monospace - 0 + 0 @@ -960,11 +965,11 @@ - 9 + Monospace - DF + DF @@ -972,11 +977,11 @@ - 9 + Monospace - 0 + 0 @@ -991,11 +996,11 @@ - 9 + Monospace - OF + OF @@ -1003,11 +1008,11 @@ - 9 + Monospace - 0 + 0 @@ -1035,7 +1040,7 @@ - 9 + Monospace @@ -1064,11 +1069,11 @@ - 9 + Monospace - CS= + CS= @@ -1076,11 +1081,11 @@ - 9 + Monospace - ES= + ES= @@ -1108,11 +1113,11 @@ - 9 + Monospace - SS= + SS= @@ -1120,11 +1125,11 @@ - 9 + Monospace - GS= + GS= @@ -1152,11 +1157,11 @@ - 9 + Monospace - DS= + DS= @@ -1164,11 +1169,11 @@ - 9 + Monospace - FS= + FS= @@ -1180,7 +1185,7 @@ - 9 + Monospace @@ -1262,8 +1267,8 @@ 0 0 - 546 - 193 + 492 + 197 @@ -1289,18 +1294,23 @@ + + + Monospace + + QAbstractItemView::NoEditTriggers - - QAbstractItemView::SingleSelection - QAbstractItemView::SelectRows false + + false + false @@ -1322,7 +1332,16 @@ - + + + + Monospace + + + + QAbstractScrollArea::AdjustToContents + + @@ -1332,7 +1351,16 @@ - + + + + Monospace + + + + QAbstractScrollArea::AdjustToContents + + @@ -1422,7 +1450,7 @@ - 9 + Monospace @@ -1434,6 +1462,9 @@ QAbstractItemView::SelectRows + + false + true @@ -1481,7 +1512,7 @@ - 9 + Monospace @@ -1539,7 +1570,7 @@ 0 0 800 - 30 + 23 @@ -1552,6 +1583,7 @@ + @@ -1709,27 +1741,32 @@ To&ggle Attach + + + Restore Instructions + + QRegisterLabel QLabel -
GUI.CustomLabels.RegisterLabel
+
GUI.Labels.RegisterLabel
QFlagRegisterLabel QLabel -
GUI.CustomLabels.FlagRegisterLabel
+
GUI.Labels.FlagRegisterLabel
QHexView QTableView -
GUI.CustomTableViews.HexView
+
GUI.TableViews.HexView
QAsciiView QTableView -
GUI.CustomTableViews.AsciiView
+
GUI.TableViews.AsciiView
diff --git a/GUI/Notes.txt b/GUI/Notes.txt deleted file mode 100644 index a8390a7e..00000000 --- a/GUI/Notes.txt +++ /dev/null @@ -1,3 +0,0 @@ -IMPORTANT: SettingsDialog.ui: Don't try to add a QKeySequenceEdit to the 2nd widget of stacked widget, pyuic5 gives error when QKeySequenceEdit is used. I had to implement it in PINCE.py because of this unfortunately. Check SettingsDialogForm class for the usage. - -6/10/2016 - HexView section of MemoryViewerWindow.ui: Changed listWidget_HexView_Address to tableWidget_HexView_Address in order to prevent possible future visual bugs. Logically, it should stay as a listwidget considering it's functionality. But it doesn't play nice with the other neighboring tablewidgets in different pyqt versions, forcing me to use magic numbers for adjusting, which is disgusting(I got the rhymes woohoo) \ No newline at end of file diff --git a/GUI/PointerScanFilterDialog.py b/GUI/PointerScanFilterDialog.py new file mode 100644 index 00000000..f3bbcfd8 --- /dev/null +++ b/GUI/PointerScanFilterDialog.py @@ -0,0 +1,68 @@ +# Form implementation generated from reading ui file 'PointerScanFilterDialog.ui' +# +# Created by: PyQt6 UI code generator 6.7.0 +# +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets + + +class Ui_Dialog(object): + def setupUi(self, Dialog): + Dialog.setObjectName("Dialog") + Dialog.resize(400, 129) + Dialog.setMinimumSize(QtCore.QSize(400, 129)) + Dialog.setMaximumSize(QtCore.QSize(400, 129)) + self.gridLayout = QtWidgets.QGridLayout(Dialog) + self.gridLayout.setObjectName("gridLayout") + self.verticalLayout = QtWidgets.QVBoxLayout() + self.verticalLayout.setContentsMargins(0, -1, -1, -1) + self.verticalLayout.setObjectName("verticalLayout") + self.horizontalLayout = QtWidgets.QHBoxLayout() + self.horizontalLayout.setObjectName("horizontalLayout") + self.label = QtWidgets.QLabel(parent=Dialog) + self.label.setScaledContents(False) + self.label.setObjectName("label") + self.horizontalLayout.addWidget(self.label) + self.lineEdit_File1Path = QtWidgets.QLineEdit(parent=Dialog) + self.lineEdit_File1Path.setReadOnly(True) + self.lineEdit_File1Path.setObjectName("lineEdit_File1Path") + self.horizontalLayout.addWidget(self.lineEdit_File1Path) + self.pushButton_File1Browse = QtWidgets.QPushButton(parent=Dialog) + self.pushButton_File1Browse.setObjectName("pushButton_File1Browse") + self.horizontalLayout.addWidget(self.pushButton_File1Browse) + self.verticalLayout.addLayout(self.horizontalLayout) + self.horizontalLayout_2 = QtWidgets.QHBoxLayout() + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.label_2 = QtWidgets.QLabel(parent=Dialog) + self.label_2.setObjectName("label_2") + self.horizontalLayout_2.addWidget(self.label_2) + self.lineEdit_File2Path = QtWidgets.QLineEdit(parent=Dialog) + self.lineEdit_File2Path.setReadOnly(True) + self.lineEdit_File2Path.setObjectName("lineEdit_File2Path") + self.horizontalLayout_2.addWidget(self.lineEdit_File2Path) + self.pushButton_File2Browse = QtWidgets.QPushButton(parent=Dialog) + self.pushButton_File2Browse.setObjectName("pushButton_File2Browse") + self.horizontalLayout_2.addWidget(self.pushButton_File2Browse) + self.verticalLayout.addLayout(self.horizontalLayout_2) + self.buttonBox = QtWidgets.QDialogButtonBox(parent=Dialog) + self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.StandardButton.Cancel) + self.buttonBox.setObjectName("buttonBox") + self.verticalLayout.addWidget(self.buttonBox) + self.gridLayout.addLayout(self.verticalLayout, 0, 0, 1, 1) + + self.retranslateUi(Dialog) + self.buttonBox.accepted.connect(Dialog.accept) # type: ignore + self.buttonBox.rejected.connect(Dialog.reject) # type: ignore + QtCore.QMetaObject.connectSlotsByName(Dialog) + + def retranslateUi(self, Dialog): + _translate = QtCore.QCoreApplication.translate + Dialog.setWindowTitle(_translate("Dialog", "Filter Pointers")) + self.label.setText(_translate("Dialog", "File 1:")) + self.pushButton_File1Browse.setText(_translate("Dialog", "Browse")) + self.label_2.setText(_translate("Dialog", "File 2:")) + self.pushButton_File2Browse.setText(_translate("Dialog", "Browse")) diff --git a/GUI/PointerScanFilterDialog.ui b/GUI/PointerScanFilterDialog.ui new file mode 100644 index 00000000..fb7dbc0a --- /dev/null +++ b/GUI/PointerScanFilterDialog.ui @@ -0,0 +1,136 @@ + + + Dialog + + + + 0 + 0 + 400 + 129 + + + + + 400 + 129 + + + + + 400 + 129 + + + + Filter Pointers + + + + + + 0 + + + + + + + File 1: + + + false + + + + + + + true + + + + + + + Browse + + + + + + + + + + + File 2: + + + + + + + true + + + + + + + Browse + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel + + + + + + + + + + + buttonBox + accepted() + Dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/GUI/PointerScanSearchDialog.py b/GUI/PointerScanSearchDialog.py new file mode 100644 index 00000000..ffe2e479 --- /dev/null +++ b/GUI/PointerScanSearchDialog.py @@ -0,0 +1,187 @@ +# Form implementation generated from reading ui file 'PointerScanSearchDialog.ui' +# +# Created by: PyQt6 UI code generator 6.7.0 +# +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets + + +class Ui_Dialog(object): + def setupUi(self, Dialog): + Dialog.setObjectName("Dialog") + Dialog.resize(404, 390) + Dialog.setMinimumSize(QtCore.QSize(404, 390)) + Dialog.setMaximumSize(QtCore.QSize(404, 390)) + self.gridLayout = QtWidgets.QGridLayout(Dialog) + self.gridLayout.setObjectName("gridLayout") + self.verticalLayout = QtWidgets.QVBoxLayout() + self.verticalLayout.setObjectName("verticalLayout") + self.horizontalLayout = QtWidgets.QHBoxLayout() + self.horizontalLayout.setObjectName("horizontalLayout") + self.label = QtWidgets.QLabel(parent=Dialog) + self.label.setObjectName("label") + self.horizontalLayout.addWidget(self.label) + self.lineEdit_Address = QtWidgets.QLineEdit(parent=Dialog) + self.lineEdit_Address.setObjectName("lineEdit_Address") + self.horizontalLayout.addWidget(self.lineEdit_Address) + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) + self.horizontalLayout.addItem(spacerItem) + self.label_2 = QtWidgets.QLabel(parent=Dialog) + self.label_2.setObjectName("label_2") + self.horizontalLayout.addWidget(self.label_2) + self.spinBox_Depth = QtWidgets.QSpinBox(parent=Dialog) + self.spinBox_Depth.setMinimum(1) + self.spinBox_Depth.setProperty("value", 3) + self.spinBox_Depth.setObjectName("spinBox_Depth") + self.horizontalLayout.addWidget(self.spinBox_Depth) + spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) + self.horizontalLayout.addItem(spacerItem1) + self.verticalLayout.addLayout(self.horizontalLayout) + self.horizontalLayout_2 = QtWidgets.QHBoxLayout() + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.label_3 = QtWidgets.QLabel(parent=Dialog) + self.label_3.setObjectName("label_3") + self.horizontalLayout_2.addWidget(self.label_3) + self.spinBox_ScanRangeStart = QtWidgets.QSpinBox(parent=Dialog) + self.spinBox_ScanRangeStart.setMaximum(16777215) + self.spinBox_ScanRangeStart.setDisplayIntegerBase(16) + self.spinBox_ScanRangeStart.setObjectName("spinBox_ScanRangeStart") + self.horizontalLayout_2.addWidget(self.spinBox_ScanRangeStart) + self.label_4 = QtWidgets.QLabel(parent=Dialog) + self.label_4.setObjectName("label_4") + self.horizontalLayout_2.addWidget(self.label_4) + self.spinBox_ScanRangeEnd = QtWidgets.QSpinBox(parent=Dialog) + self.spinBox_ScanRangeEnd.setSuffix("") + self.spinBox_ScanRangeEnd.setMaximum(16777215) + self.spinBox_ScanRangeEnd.setProperty("value", 1000) + self.spinBox_ScanRangeEnd.setDisplayIntegerBase(16) + self.spinBox_ScanRangeEnd.setObjectName("spinBox_ScanRangeEnd") + self.horizontalLayout_2.addWidget(self.spinBox_ScanRangeEnd) + spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) + self.horizontalLayout_2.addItem(spacerItem2) + self.verticalLayout.addLayout(self.horizontalLayout_2) + self.horizontalLayout_7 = QtWidgets.QHBoxLayout() + self.horizontalLayout_7.setObjectName("horizontalLayout_7") + self.label_10 = QtWidgets.QLabel(parent=Dialog) + self.label_10.setObjectName("label_10") + self.horizontalLayout_7.addWidget(self.label_10) + self.lineEdit_Path = QtWidgets.QLineEdit(parent=Dialog) + self.lineEdit_Path.setEnabled(True) + self.lineEdit_Path.setText("") + self.lineEdit_Path.setDragEnabled(False) + self.lineEdit_Path.setReadOnly(True) + self.lineEdit_Path.setClearButtonEnabled(False) + self.lineEdit_Path.setObjectName("lineEdit_Path") + self.horizontalLayout_7.addWidget(self.lineEdit_Path) + self.pushButton_PathBrowse = QtWidgets.QPushButton(parent=Dialog) + self.pushButton_PathBrowse.setEnabled(True) + self.pushButton_PathBrowse.setObjectName("pushButton_PathBrowse") + self.horizontalLayout_7.addWidget(self.pushButton_PathBrowse) + self.verticalLayout.addLayout(self.horizontalLayout_7) + self.horizontalLayout_8 = QtWidgets.QHBoxLayout() + self.horizontalLayout_8.setObjectName("horizontalLayout_8") + self.label_11 = QtWidgets.QLabel(parent=Dialog) + self.label_11.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + self.label_11.setObjectName("label_11") + self.horizontalLayout_8.addWidget(self.label_11) + self.verticalLayout.addLayout(self.horizontalLayout_8) + self.horizontalLayout_3 = QtWidgets.QHBoxLayout() + self.horizontalLayout_3.setObjectName("horizontalLayout_3") + self.label_5 = QtWidgets.QLabel(parent=Dialog) + self.label_5.setObjectName("label_5") + self.horizontalLayout_3.addWidget(self.label_5) + self.spinBox_ScanLRangeStart = QtWidgets.QSpinBox(parent=Dialog) + self.spinBox_ScanLRangeStart.setMaximum(16777215) + self.spinBox_ScanLRangeStart.setProperty("value", 0) + self.spinBox_ScanLRangeStart.setDisplayIntegerBase(16) + self.spinBox_ScanLRangeStart.setObjectName("spinBox_ScanLRangeStart") + self.horizontalLayout_3.addWidget(self.spinBox_ScanLRangeStart) + self.label_9 = QtWidgets.QLabel(parent=Dialog) + self.label_9.setObjectName("label_9") + self.horizontalLayout_3.addWidget(self.label_9) + self.spinBox_ScanLRangeEnd = QtWidgets.QSpinBox(parent=Dialog) + self.spinBox_ScanLRangeEnd.setMaximum(16777215) + self.spinBox_ScanLRangeEnd.setProperty("value", 0) + self.spinBox_ScanLRangeEnd.setDisplayIntegerBase(16) + self.spinBox_ScanLRangeEnd.setObjectName("spinBox_ScanLRangeEnd") + self.horizontalLayout_3.addWidget(self.spinBox_ScanLRangeEnd) + spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) + self.horizontalLayout_3.addItem(spacerItem3) + self.verticalLayout.addLayout(self.horizontalLayout_3) + self.horizontalLayout_4 = QtWidgets.QHBoxLayout() + self.horizontalLayout_4.setObjectName("horizontalLayout_4") + self.label_6 = QtWidgets.QLabel(parent=Dialog) + self.label_6.setObjectName("label_6") + self.horizontalLayout_4.addWidget(self.label_6) + self.spinBox_Node = QtWidgets.QSpinBox(parent=Dialog) + self.spinBox_Node.setMinimum(0) + self.spinBox_Node.setProperty("value", 0) + self.spinBox_Node.setObjectName("spinBox_Node") + self.horizontalLayout_4.addWidget(self.spinBox_Node) + spacerItem4 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) + self.horizontalLayout_4.addItem(spacerItem4) + self.verticalLayout.addLayout(self.horizontalLayout_4) + self.horizontalLayout_5 = QtWidgets.QHBoxLayout() + self.horizontalLayout_5.setObjectName("horizontalLayout_5") + self.label_7 = QtWidgets.QLabel(parent=Dialog) + self.label_7.setObjectName("label_7") + self.horizontalLayout_5.addWidget(self.label_7) + self.lineEdit_Last = QtWidgets.QLineEdit(parent=Dialog) + self.lineEdit_Last.setObjectName("lineEdit_Last") + self.horizontalLayout_5.addWidget(self.lineEdit_Last) + spacerItem5 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) + self.horizontalLayout_5.addItem(spacerItem5) + self.verticalLayout.addLayout(self.horizontalLayout_5) + self.horizontalLayout_6 = QtWidgets.QHBoxLayout() + self.horizontalLayout_6.setObjectName("horizontalLayout_6") + self.label_8 = QtWidgets.QLabel(parent=Dialog) + self.label_8.setObjectName("label_8") + self.horizontalLayout_6.addWidget(self.label_8) + self.spinBox_Max = QtWidgets.QSpinBox(parent=Dialog) + self.spinBox_Max.setMaximum(1000) + self.spinBox_Max.setProperty("value", 0) + self.spinBox_Max.setObjectName("spinBox_Max") + self.horizontalLayout_6.addWidget(self.spinBox_Max) + spacerItem6 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) + self.horizontalLayout_6.addItem(spacerItem6) + self.verticalLayout.addLayout(self.horizontalLayout_6) + self.checkBox_Cycle = QtWidgets.QCheckBox(parent=Dialog) + self.checkBox_Cycle.setObjectName("checkBox_Cycle") + self.verticalLayout.addWidget(self.checkBox_Cycle) + spacerItem7 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) + self.verticalLayout.addItem(spacerItem7) + self.buttonBox = QtWidgets.QDialogButtonBox(parent=Dialog) + self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.StandardButton.Cancel) + self.buttonBox.setObjectName("buttonBox") + self.verticalLayout.addWidget(self.buttonBox) + self.gridLayout.addLayout(self.verticalLayout, 0, 0, 1, 1) + + self.retranslateUi(Dialog) + self.buttonBox.accepted.connect(Dialog.accept) # type: ignore + self.buttonBox.rejected.connect(Dialog.reject) # type: ignore + QtCore.QMetaObject.connectSlotsByName(Dialog) + + def retranslateUi(self, Dialog): + _translate = QtCore.QCoreApplication.translate + Dialog.setWindowTitle(_translate("Dialog", "Scan for Pointers")) + self.label.setText(_translate("Dialog", "Address")) + self.label_2.setText(_translate("Dialog", "Depth")) + self.label_3.setText(_translate("Dialog", "Scan Range")) + self.spinBox_ScanRangeStart.setPrefix(_translate("Dialog", "0x")) + self.label_4.setText(_translate("Dialog", "<->")) + self.spinBox_ScanRangeEnd.setPrefix(_translate("Dialog", "0x")) + self.label_10.setText(_translate("Dialog", "File Path:")) + self.pushButton_PathBrowse.setText(_translate("Dialog", "Browse")) + self.label_11.setText(_translate("Dialog", "Optional Parameters (0 or empty will use defaults)")) + self.label_5.setText(_translate("Dialog", "Last Offset Scan Range")) + self.spinBox_ScanLRangeStart.setPrefix(_translate("Dialog", "0x")) + self.label_9.setText(_translate("Dialog", "<->")) + self.spinBox_ScanLRangeEnd.setPrefix(_translate("Dialog", "0x")) + self.label_6.setText(_translate("Dialog", "Minimum Chain Length")) + self.label_7.setText(_translate("Dialog", "Last Offset Value")) + self.label_8.setText(_translate("Dialog", "Max Results")) + self.checkBox_Cycle.setText(_translate("Dialog", "Solve Circular References")) diff --git a/GUI/PointerScanSearchDialog.ui b/GUI/PointerScanSearchDialog.ui new file mode 100644 index 00000000..41d36e4b --- /dev/null +++ b/GUI/PointerScanSearchDialog.ui @@ -0,0 +1,432 @@ + + + Dialog + + + + 0 + 0 + 404 + 390 + + + + + 404 + 390 + + + + + 404 + 390 + + + + Scan for Pointers + + + + + + + + + + Address + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + Depth + + + + + + + 1 + + + 3 + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Scan Range + + + + + + + 0x + + + 16777215 + + + 16 + + + + + + + <-> + + + + + + + + + + 0x + + + 16777215 + + + 1000 + + + 16 + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + + File Path: + + + + + + + true + + + + + + false + + + true + + + false + + + + + + + true + + + Browse + + + + + + + + + + + Optional Parameters (0 or empty will use defaults) + + + Qt::AlignmentFlag::AlignCenter + + + + + + + + + + + Last Offset Scan Range + + + + + + + 0x + + + 16777215 + + + 0 + + + 16 + + + + + + + <-> + + + + + + + 0x + + + 16777215 + + + 0 + + + 16 + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Minimum Chain Length + + + + + + + 0 + + + 0 + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Last Offset Value + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Max Results + + + + + + + 1000 + + + 0 + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + Solve Circular References + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + Qt::Orientation::Horizontal + + + QDialogButtonBox::StandardButton::Cancel + + + + + + + + + + + buttonBox + accepted() + Dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/GUI/PointerScanWindow.py b/GUI/PointerScanWindow.py new file mode 100644 index 00000000..e8f1bdb1 --- /dev/null +++ b/GUI/PointerScanWindow.py @@ -0,0 +1,90 @@ +# Form implementation generated from reading ui file 'PointerScanWindow.ui' +# +# Created by: PyQt6 UI code generator 6.7.0 +# +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets + + +class Ui_MainWindow(object): + def setupUi(self, MainWindow): + MainWindow.setObjectName("MainWindow") + MainWindow.resize(783, 691) + self.centralwidget = QtWidgets.QWidget(parent=MainWindow) + self.centralwidget.setObjectName("centralwidget") + self.gridLayout = QtWidgets.QGridLayout(self.centralwidget) + self.gridLayout.setObjectName("gridLayout") + self.tableWidget_ScanResult = QtWidgets.QTableWidget(parent=self.centralwidget) + font = QtGui.QFont() + font.setFamily("Monospace") + self.tableWidget_ScanResult.setFont(font) + self.tableWidget_ScanResult.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers) + self.tableWidget_ScanResult.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.SingleSelection) + self.tableWidget_ScanResult.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows) + self.tableWidget_ScanResult.setObjectName("tableWidget_ScanResult") + self.tableWidget_ScanResult.setColumnCount(0) + self.tableWidget_ScanResult.setRowCount(0) + self.tableWidget_ScanResult.horizontalHeader().setStretchLastSection(True) + self.tableWidget_ScanResult.verticalHeader().setVisible(False) + self.tableWidget_ScanResult.verticalHeader().setDefaultSectionSize(16) + self.tableWidget_ScanResult.verticalHeader().setMinimumSectionSize(16) + self.gridLayout.addWidget(self.tableWidget_ScanResult, 2, 0, 1, 1) + self.horizontalLayout = QtWidgets.QHBoxLayout() + self.horizontalLayout.setObjectName("horizontalLayout") + self.pushButton_Sort = QtWidgets.QPushButton(parent=self.centralwidget) + self.pushButton_Sort.setObjectName("pushButton_Sort") + self.horizontalLayout.addWidget(self.pushButton_Sort) + self.pushButton_Clear = QtWidgets.QPushButton(parent=self.centralwidget) + self.pushButton_Clear.setObjectName("pushButton_Clear") + self.horizontalLayout.addWidget(self.pushButton_Clear) + spacerItem = QtWidgets.QSpacerItem(684, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) + self.horizontalLayout.addItem(spacerItem) + self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1) + self.textEdit = QtWidgets.QTextEdit(parent=self.centralwidget) + self.textEdit.setObjectName("textEdit") + self.gridLayout.addWidget(self.textEdit, 1, 0, 1, 1) + MainWindow.setCentralWidget(self.centralwidget) + self.menubar = QtWidgets.QMenuBar(parent=MainWindow) + self.menubar.setGeometry(QtCore.QRect(0, 0, 783, 30)) + self.menubar.setObjectName("menubar") + self.menuFile = QtWidgets.QMenu(parent=self.menubar) + self.menuFile.setObjectName("menuFile") + self.menuActions = QtWidgets.QMenu(parent=self.menubar) + self.menuActions.setObjectName("menuActions") + MainWindow.setMenuBar(self.menubar) + self.statusbar = QtWidgets.QStatusBar(parent=MainWindow) + self.statusbar.setObjectName("statusbar") + MainWindow.setStatusBar(self.statusbar) + self.actionOpen = QtGui.QAction(parent=MainWindow) + self.actionOpen.setObjectName("actionOpen") + self.actionSaveAs = QtGui.QAction(parent=MainWindow) + self.actionSaveAs.setObjectName("actionSaveAs") + self.actionScan = QtGui.QAction(parent=MainWindow) + self.actionScan.setObjectName("actionScan") + self.actionFilter = QtGui.QAction(parent=MainWindow) + self.actionFilter.setObjectName("actionFilter") + self.menuFile.addAction(self.actionOpen) + self.menuFile.addAction(self.actionSaveAs) + self.menuActions.addAction(self.actionScan) + self.menuActions.addAction(self.actionFilter) + self.menubar.addAction(self.menuFile.menuAction()) + self.menubar.addAction(self.menuActions.menuAction()) + + self.retranslateUi(MainWindow) + QtCore.QMetaObject.connectSlotsByName(MainWindow) + + def retranslateUi(self, MainWindow): + _translate = QtCore.QCoreApplication.translate + MainWindow.setWindowTitle(_translate("MainWindow", "Pointer Scanner")) + self.tableWidget_ScanResult.setSortingEnabled(True) + self.pushButton_Sort.setText(_translate("MainWindow", "Sort")) + self.pushButton_Clear.setText(_translate("MainWindow", "Clear")) + self.menuFile.setTitle(_translate("MainWindow", "Fi&le")) + self.menuActions.setTitle(_translate("MainWindow", "Actio&ns")) + self.actionOpen.setText(_translate("MainWindow", "&Open")) + self.actionSaveAs.setText(_translate("MainWindow", "&Save As...")) + self.actionScan.setText(_translate("MainWindow", "&Scan")) + self.actionFilter.setText(_translate("MainWindow", "&Filter")) diff --git a/GUI/PointerScanWindow.ui b/GUI/PointerScanWindow.ui new file mode 100644 index 00000000..2e1bedee --- /dev/null +++ b/GUI/PointerScanWindow.ui @@ -0,0 +1,137 @@ + + + MainWindow + + + + 0 + 0 + 783 + 691 + + + + Pointer Scanner + + + + + + + + Monospace + + + + QAbstractItemView::EditTrigger::NoEditTriggers + + + QAbstractItemView::SelectionMode::SingleSelection + + + QAbstractItemView::SelectionBehavior::SelectRows + + + true + + + true + + + false + + + 16 + + + 16 + + + + + + + + + Sort + + + + + + + Clear + + + + + + + Qt::Orientation::Horizontal + + + + 684 + 20 + + + + + + + + + + + + + + + 0 + 0 + 783 + 30 + + + + + Fi&le + + + + + + + Actio&ns + + + + + + + + + + + &Open + + + + + &Save As... + + + + + &Scan + + + + + &Filter + + + + + + diff --git a/GUI/ReferencedCallsWidget.py b/GUI/ReferencedCallsWidget.py index bfb8539e..47b9fbb4 100644 --- a/GUI/ReferencedCallsWidget.py +++ b/GUI/ReferencedCallsWidget.py @@ -1,12 +1,13 @@ -# -*- coding: utf-8 -*- - # Form implementation generated from reading ui file 'ReferencedCallsWidget.ui' # -# Created by: PyQt5 UI code generator 5.5.1 +# Created by: PyQt6 UI code generator 6.4.2 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets -from PyQt5 import QtCore, QtGui, QtWidgets class Ui_Form(object): def setupUi(self, Form): @@ -15,32 +16,37 @@ def setupUi(self, Form): Form.setToolTip("") self.gridLayout = QtWidgets.QGridLayout(Form) self.gridLayout.setObjectName("gridLayout") - self.splitter = QtWidgets.QSplitter(Form) - self.splitter.setOrientation(QtCore.Qt.Horizontal) + self.splitter = QtWidgets.QSplitter(parent=Form) + self.splitter.setOrientation(QtCore.Qt.Orientation.Horizontal) self.splitter.setObjectName("splitter") - self.layoutWidget = QtWidgets.QWidget(self.splitter) + self.layoutWidget = QtWidgets.QWidget(parent=self.splitter) self.layoutWidget.setObjectName("layoutWidget") self.verticalLayout = QtWidgets.QVBoxLayout(self.layoutWidget) + self.verticalLayout.setContentsMargins(0, 0, 0, 0) self.verticalLayout.setObjectName("verticalLayout") self.horizontalLayout_2 = QtWidgets.QHBoxLayout() self.horizontalLayout_2.setObjectName("horizontalLayout_2") - self.lineEdit_Regex = QtWidgets.QLineEdit(self.layoutWidget) + self.lineEdit_Regex = QtWidgets.QLineEdit(parent=self.layoutWidget) self.lineEdit_Regex.setObjectName("lineEdit_Regex") self.horizontalLayout_2.addWidget(self.lineEdit_Regex) - self.checkBox_CaseSensitive = QtWidgets.QCheckBox(self.layoutWidget) + self.checkBox_CaseSensitive = QtWidgets.QCheckBox(parent=self.layoutWidget) self.checkBox_CaseSensitive.setObjectName("checkBox_CaseSensitive") self.horizontalLayout_2.addWidget(self.checkBox_CaseSensitive) - self.checkBox_Regex = QtWidgets.QCheckBox(self.layoutWidget) + self.checkBox_Regex = QtWidgets.QCheckBox(parent=self.layoutWidget) self.checkBox_Regex.setObjectName("checkBox_Regex") self.horizontalLayout_2.addWidget(self.checkBox_Regex) - self.pushButton_Search = QtWidgets.QPushButton(self.layoutWidget) + self.pushButton_Search = QtWidgets.QPushButton(parent=self.layoutWidget) self.pushButton_Search.setObjectName("pushButton_Search") self.horizontalLayout_2.addWidget(self.pushButton_Search) self.verticalLayout.addLayout(self.horizontalLayout_2) - self.tableWidget_References = QtWidgets.QTableWidget(self.layoutWidget) - self.tableWidget_References.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) - self.tableWidget_References.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) - self.tableWidget_References.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) + self.tableWidget_References = QtWidgets.QTableWidget(parent=self.layoutWidget) + font = QtGui.QFont() + font.setFamily("Monospace") + self.tableWidget_References.setFont(font) + self.tableWidget_References.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers) + self.tableWidget_References.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.SingleSelection) + self.tableWidget_References.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows) + self.tableWidget_References.setWordWrap(False) self.tableWidget_References.setObjectName("tableWidget_References") self.tableWidget_References.setColumnCount(2) self.tableWidget_References.setRowCount(0) @@ -53,8 +59,11 @@ def setupUi(self, Form): self.tableWidget_References.verticalHeader().setDefaultSectionSize(16) self.tableWidget_References.verticalHeader().setMinimumSectionSize(16) self.verticalLayout.addWidget(self.tableWidget_References) - self.listWidget_Referrers = QtWidgets.QListWidget(self.splitter) - self.listWidget_Referrers.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) + self.listWidget_Referrers = QtWidgets.QListWidget(parent=self.splitter) + font = QtGui.QFont() + font.setFamily("Monospace") + self.listWidget_Referrers.setFont(font) + self.listWidget_Referrers.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers) self.listWidget_Referrers.setObjectName("listWidget_Referrers") self.gridLayout.addWidget(self.splitter, 0, 0, 1, 1) @@ -75,4 +84,3 @@ def retranslateUi(self, Form): item.setText(_translate("Form", "Address")) item = self.tableWidget_References.horizontalHeaderItem(1) item.setText(_translate("Form", "Refcount")) - diff --git a/GUI/ReferencedCallsWidget.ui b/GUI/ReferencedCallsWidget.ui index dff3de79..b629b72c 100644 --- a/GUI/ReferencedCallsWidget.ui +++ b/GUI/ReferencedCallsWidget.ui @@ -64,6 +64,11 @@
+ + + Monospace + + QAbstractItemView::NoEditTriggers @@ -76,16 +81,19 @@ true + + false + true false - + 16 - + 16 @@ -103,6 +111,11 @@
+ + + Monospace + + QAbstractItemView::NoEditTriggers diff --git a/GUI/ReferencedStringsWidget.py b/GUI/ReferencedStringsWidget.py index 2aa8ba7e..4fc51879 100644 --- a/GUI/ReferencedStringsWidget.py +++ b/GUI/ReferencedStringsWidget.py @@ -1,12 +1,13 @@ -# -*- coding: utf-8 -*- - # Form implementation generated from reading ui file 'ReferencedStringsWidget.ui' # -# Created by: PyQt5 UI code generator 5.5.1 +# Created by: PyQt6 UI code generator 6.4.2 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets -from PyQt5 import QtCore, QtGui, QtWidgets class Ui_Form(object): def setupUi(self, Form): @@ -15,32 +16,37 @@ def setupUi(self, Form): Form.setToolTip("") self.gridLayout = QtWidgets.QGridLayout(Form) self.gridLayout.setObjectName("gridLayout") - self.splitter = QtWidgets.QSplitter(Form) - self.splitter.setOrientation(QtCore.Qt.Horizontal) + self.splitter = QtWidgets.QSplitter(parent=Form) + self.splitter.setOrientation(QtCore.Qt.Orientation.Horizontal) self.splitter.setObjectName("splitter") - self.layoutWidget = QtWidgets.QWidget(self.splitter) + self.layoutWidget = QtWidgets.QWidget(parent=self.splitter) self.layoutWidget.setObjectName("layoutWidget") self.verticalLayout = QtWidgets.QVBoxLayout(self.layoutWidget) + self.verticalLayout.setContentsMargins(0, 0, 0, 0) self.verticalLayout.setObjectName("verticalLayout") self.horizontalLayout_2 = QtWidgets.QHBoxLayout() self.horizontalLayout_2.setObjectName("horizontalLayout_2") - self.lineEdit_Regex = QtWidgets.QLineEdit(self.layoutWidget) + self.lineEdit_Regex = QtWidgets.QLineEdit(parent=self.layoutWidget) self.lineEdit_Regex.setObjectName("lineEdit_Regex") self.horizontalLayout_2.addWidget(self.lineEdit_Regex) - self.checkBox_CaseSensitive = QtWidgets.QCheckBox(self.layoutWidget) + self.checkBox_CaseSensitive = QtWidgets.QCheckBox(parent=self.layoutWidget) self.checkBox_CaseSensitive.setObjectName("checkBox_CaseSensitive") self.horizontalLayout_2.addWidget(self.checkBox_CaseSensitive) - self.checkBox_Regex = QtWidgets.QCheckBox(self.layoutWidget) + self.checkBox_Regex = QtWidgets.QCheckBox(parent=self.layoutWidget) self.checkBox_Regex.setObjectName("checkBox_Regex") self.horizontalLayout_2.addWidget(self.checkBox_Regex) - self.pushButton_Search = QtWidgets.QPushButton(self.layoutWidget) + self.pushButton_Search = QtWidgets.QPushButton(parent=self.layoutWidget) self.pushButton_Search.setObjectName("pushButton_Search") self.horizontalLayout_2.addWidget(self.pushButton_Search) self.verticalLayout.addLayout(self.horizontalLayout_2) - self.tableWidget_References = QtWidgets.QTableWidget(self.layoutWidget) - self.tableWidget_References.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) - self.tableWidget_References.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) - self.tableWidget_References.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) + self.tableWidget_References = QtWidgets.QTableWidget(parent=self.layoutWidget) + font = QtGui.QFont() + font.setFamily("Monospace") + self.tableWidget_References.setFont(font) + self.tableWidget_References.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers) + self.tableWidget_References.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.SingleSelection) + self.tableWidget_References.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows) + self.tableWidget_References.setWordWrap(False) self.tableWidget_References.setObjectName("tableWidget_References") self.tableWidget_References.setColumnCount(3) self.tableWidget_References.setRowCount(0) @@ -57,15 +63,18 @@ def setupUi(self, Form): self.verticalLayout.addWidget(self.tableWidget_References) self.horizontalLayout = QtWidgets.QHBoxLayout() self.horizontalLayout.setObjectName("horizontalLayout") - spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) self.horizontalLayout.addItem(spacerItem) - self.comboBox_ValueType = QtWidgets.QComboBox(self.layoutWidget) - self.comboBox_ValueType.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents) + self.comboBox_ValueType = QtWidgets.QComboBox(parent=self.layoutWidget) + self.comboBox_ValueType.setSizeAdjustPolicy(QtWidgets.QComboBox.SizeAdjustPolicy.AdjustToContents) self.comboBox_ValueType.setObjectName("comboBox_ValueType") self.horizontalLayout.addWidget(self.comboBox_ValueType) self.verticalLayout.addLayout(self.horizontalLayout) - self.listWidget_Referrers = QtWidgets.QListWidget(self.splitter) - self.listWidget_Referrers.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) + self.listWidget_Referrers = QtWidgets.QListWidget(parent=self.splitter) + font = QtGui.QFont() + font.setFamily("Monospace") + self.listWidget_Referrers.setFont(font) + self.listWidget_Referrers.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers) self.listWidget_Referrers.setObjectName("listWidget_Referrers") self.gridLayout.addWidget(self.splitter, 0, 0, 1, 1) @@ -89,4 +98,3 @@ def retranslateUi(self, Form): item.setText(_translate("Form", "Refcount")) item = self.tableWidget_References.horizontalHeaderItem(2) item.setText(_translate("Form", "Value")) - diff --git a/GUI/ReferencedStringsWidget.ui b/GUI/ReferencedStringsWidget.ui index 74554432..3384c90d 100644 --- a/GUI/ReferencedStringsWidget.ui +++ b/GUI/ReferencedStringsWidget.ui @@ -64,6 +64,11 @@ + + + Monospace + + QAbstractItemView::NoEditTriggers @@ -76,16 +81,19 @@ true + + false + true false - + 16 - + 16 @@ -135,6 +143,11 @@ + + + Monospace + + QAbstractItemView::NoEditTriggers diff --git a/GUI/RestoreInstructionsWidget.py b/GUI/RestoreInstructionsWidget.py new file mode 100644 index 00000000..c07a1369 --- /dev/null +++ b/GUI/RestoreInstructionsWidget.py @@ -0,0 +1,53 @@ +# Form implementation generated from reading ui file 'RestoreInstructionsWidget.ui' +# +# Created by: PyQt6 UI code generator 6.4.2 +# +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets + + +class Ui_Form(object): + def setupUi(self, Form): + Form.setObjectName("Form") + Form.resize(429, 420) + self.gridLayout = QtWidgets.QGridLayout(Form) + self.gridLayout.setObjectName("gridLayout") + self.tableWidget_Instructions = QtWidgets.QTableWidget(parent=Form) + font = QtGui.QFont() + font.setFamily("Monospace") + self.tableWidget_Instructions.setFont(font) + self.tableWidget_Instructions.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers) + self.tableWidget_Instructions.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.SingleSelection) + self.tableWidget_Instructions.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows) + self.tableWidget_Instructions.setWordWrap(False) + self.tableWidget_Instructions.setObjectName("tableWidget_Instructions") + self.tableWidget_Instructions.setColumnCount(3) + self.tableWidget_Instructions.setRowCount(0) + item = QtWidgets.QTableWidgetItem() + self.tableWidget_Instructions.setHorizontalHeaderItem(0, item) + item = QtWidgets.QTableWidgetItem() + self.tableWidget_Instructions.setHorizontalHeaderItem(1, item) + item = QtWidgets.QTableWidgetItem() + self.tableWidget_Instructions.setHorizontalHeaderItem(2, item) + self.tableWidget_Instructions.horizontalHeader().setStretchLastSection(True) + self.tableWidget_Instructions.verticalHeader().setVisible(False) + self.tableWidget_Instructions.verticalHeader().setDefaultSectionSize(16) + self.tableWidget_Instructions.verticalHeader().setMinimumSectionSize(16) + self.gridLayout.addWidget(self.tableWidget_Instructions, 0, 0, 1, 1) + + self.retranslateUi(Form) + QtCore.QMetaObject.connectSlotsByName(Form) + + def retranslateUi(self, Form): + _translate = QtCore.QCoreApplication.translate + Form.setWindowTitle(_translate("Form", "Restore Instructions")) + self.tableWidget_Instructions.setSortingEnabled(True) + item = self.tableWidget_Instructions.horizontalHeaderItem(0) + item.setText(_translate("Form", "Address")) + item = self.tableWidget_Instructions.horizontalHeaderItem(1) + item.setText(_translate("Form", "Original OpCode")) + item = self.tableWidget_Instructions.horizontalHeaderItem(2) + item.setText(_translate("Form", "Original Instruction")) diff --git a/GUI/RestoreInstructionsWidget.ui b/GUI/RestoreInstructionsWidget.ui new file mode 100644 index 00000000..7553c219 --- /dev/null +++ b/GUI/RestoreInstructionsWidget.ui @@ -0,0 +1,72 @@ + + + Form + + + + 0 + 0 + 429 + 420 + + + + Restore Instructions + + + + + + + Monospace + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + true + + + false + + + true + + + false + + + 16 + + + 16 + + + + Address + + + + + Original OpCode + + + + + Original Instruction + + + + + + + + + diff --git a/GUI/ScanRegionsDialog.py b/GUI/ScanRegionsDialog.py new file mode 100644 index 00000000..3f70fc72 --- /dev/null +++ b/GUI/ScanRegionsDialog.py @@ -0,0 +1,88 @@ +# Form implementation generated from reading ui file 'ScanRegionsDialog.ui' +# +# Created by: PyQt6 UI code generator 6.4.2 +# +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets + + +class Ui_Dialog(object): + def setupUi(self, Dialog): + Dialog.setObjectName("Dialog") + Dialog.resize(724, 568) + self.gridLayout = QtWidgets.QGridLayout(Dialog) + self.gridLayout.setObjectName("gridLayout") + self.horizontalLayout = QtWidgets.QHBoxLayout() + self.horizontalLayout.setObjectName("horizontalLayout") + self.label = QtWidgets.QLabel(parent=Dialog) + self.label.setObjectName("label") + self.horizontalLayout.addWidget(self.label) + self.pushButton_Invert = QtWidgets.QPushButton(parent=Dialog) + self.pushButton_Invert.setObjectName("pushButton_Invert") + self.horizontalLayout.addWidget(self.pushButton_Invert) + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) + self.horizontalLayout.addItem(spacerItem) + self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1) + self.tableWidget_Regions = QtWidgets.QTableWidget(parent=Dialog) + font = QtGui.QFont() + font.setFamily("Monospace") + self.tableWidget_Regions.setFont(font) + self.tableWidget_Regions.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers) + self.tableWidget_Regions.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows) + self.tableWidget_Regions.setWordWrap(False) + self.tableWidget_Regions.setObjectName("tableWidget_Regions") + self.tableWidget_Regions.setColumnCount(7) + self.tableWidget_Regions.setRowCount(0) + item = QtWidgets.QTableWidgetItem() + self.tableWidget_Regions.setHorizontalHeaderItem(0, item) + item = QtWidgets.QTableWidgetItem() + self.tableWidget_Regions.setHorizontalHeaderItem(1, item) + item = QtWidgets.QTableWidgetItem() + self.tableWidget_Regions.setHorizontalHeaderItem(2, item) + item = QtWidgets.QTableWidgetItem() + self.tableWidget_Regions.setHorizontalHeaderItem(3, item) + item = QtWidgets.QTableWidgetItem() + self.tableWidget_Regions.setHorizontalHeaderItem(4, item) + item = QtWidgets.QTableWidgetItem() + self.tableWidget_Regions.setHorizontalHeaderItem(5, item) + item = QtWidgets.QTableWidgetItem() + self.tableWidget_Regions.setHorizontalHeaderItem(6, item) + self.tableWidget_Regions.horizontalHeader().setStretchLastSection(True) + self.tableWidget_Regions.verticalHeader().setVisible(False) + self.tableWidget_Regions.verticalHeader().setDefaultSectionSize(16) + self.tableWidget_Regions.verticalHeader().setMinimumSectionSize(16) + self.gridLayout.addWidget(self.tableWidget_Regions, 1, 0, 1, 1) + self.buttonBox = QtWidgets.QDialogButtonBox(parent=Dialog) + self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.StandardButton.Cancel|QtWidgets.QDialogButtonBox.StandardButton.Ok) + self.buttonBox.setObjectName("buttonBox") + self.gridLayout.addWidget(self.buttonBox, 2, 0, 1, 1) + + self.retranslateUi(Dialog) + self.buttonBox.accepted.connect(Dialog.accept) # type: ignore + self.buttonBox.rejected.connect(Dialog.reject) # type: ignore + QtCore.QMetaObject.connectSlotsByName(Dialog) + + def retranslateUi(self, Dialog): + _translate = QtCore.QCoreApplication.translate + Dialog.setWindowTitle(_translate("Dialog", "Manage Scan Regions")) + self.label.setText(_translate("Dialog", "Selected regions will be deleted from the current scan")) + self.pushButton_Invert.setText(_translate("Dialog", "Invert Selection")) + self.tableWidget_Regions.setSortingEnabled(True) + item = self.tableWidget_Regions.horizontalHeaderItem(0) + item.setText(_translate("Dialog", "ID")) + item = self.tableWidget_Regions.horizontalHeaderItem(1) + item.setText(_translate("Dialog", "Start Address")) + item = self.tableWidget_Regions.horizontalHeaderItem(2) + item.setText(_translate("Dialog", "Size(bytes)")) + item = self.tableWidget_Regions.horizontalHeaderItem(3) + item.setText(_translate("Dialog", "Type")) + item = self.tableWidget_Regions.horizontalHeaderItem(4) + item.setText(_translate("Dialog", "Load Address")) + item = self.tableWidget_Regions.horizontalHeaderItem(5) + item.setText(_translate("Dialog", "Perms")) + item = self.tableWidget_Regions.horizontalHeaderItem(6) + item.setText(_translate("Dialog", "File")) diff --git a/GUI/ScanRegionsDialog.ui b/GUI/ScanRegionsDialog.ui new file mode 100644 index 00000000..0c71bcbf --- /dev/null +++ b/GUI/ScanRegionsDialog.ui @@ -0,0 +1,163 @@ + + + Dialog + + + + 0 + 0 + 724 + 568 + + + + Manage Scan Regions + + + + + + + + Selected regions will be deleted from the current scan + + + + + + + Invert Selection + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Monospace + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::SelectRows + + + true + + + false + + + true + + + false + + + 16 + + + 16 + + + + ID + + + + + Start Address + + + + + Size(bytes) + + + + + Type + + + + + Load Address + + + + + Perms + + + + + File + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + Dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/GUI/SearchOpcodeWidget.py b/GUI/SearchOpcodeWidget.py index 9a71efdd..5af0e3ef 100644 --- a/GUI/SearchOpcodeWidget.py +++ b/GUI/SearchOpcodeWidget.py @@ -1,12 +1,13 @@ -# -*- coding: utf-8 -*- - # Form implementation generated from reading ui file 'SearchOpcodeWidget.ui' # -# Created by: PyQt5 UI code generator 5.5.1 +# Created by: PyQt6 UI code generator 6.4.2 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets -from PyQt5 import QtCore, QtGui, QtWidgets class Ui_Form(object): def setupUi(self, Form): @@ -16,45 +17,49 @@ def setupUi(self, Form): self.gridLayout.setObjectName("gridLayout") self.horizontalLayout_2 = QtWidgets.QHBoxLayout() self.horizontalLayout_2.setObjectName("horizontalLayout_2") - self.label_3 = QtWidgets.QLabel(Form) + self.label_3 = QtWidgets.QLabel(parent=Form) self.label_3.setObjectName("label_3") self.horizontalLayout_2.addWidget(self.label_3) - self.lineEdit_Regex = QtWidgets.QLineEdit(Form) + self.lineEdit_Regex = QtWidgets.QLineEdit(parent=Form) self.lineEdit_Regex.setObjectName("lineEdit_Regex") self.horizontalLayout_2.addWidget(self.lineEdit_Regex) - self.checkBox_CaseSensitive = QtWidgets.QCheckBox(Form) + self.checkBox_CaseSensitive = QtWidgets.QCheckBox(parent=Form) self.checkBox_CaseSensitive.setObjectName("checkBox_CaseSensitive") self.horizontalLayout_2.addWidget(self.checkBox_CaseSensitive) - self.checkBox_Regex = QtWidgets.QCheckBox(Form) + self.checkBox_Regex = QtWidgets.QCheckBox(parent=Form) self.checkBox_Regex.setObjectName("checkBox_Regex") self.horizontalLayout_2.addWidget(self.checkBox_Regex) - self.pushButton_Search = QtWidgets.QPushButton(Form) + self.pushButton_Search = QtWidgets.QPushButton(parent=Form) self.pushButton_Search.setObjectName("pushButton_Search") self.horizontalLayout_2.addWidget(self.pushButton_Search) - self.pushButton_Help = QtWidgets.QPushButton(Form) + self.pushButton_Help = QtWidgets.QPushButton(parent=Form) self.pushButton_Help.setText("") self.pushButton_Help.setObjectName("pushButton_Help") self.horizontalLayout_2.addWidget(self.pushButton_Help) self.gridLayout.addLayout(self.horizontalLayout_2, 0, 0, 1, 1) self.horizontalLayout = QtWidgets.QHBoxLayout() self.horizontalLayout.setObjectName("horizontalLayout") - self.label = QtWidgets.QLabel(Form) + self.label = QtWidgets.QLabel(parent=Form) self.label.setObjectName("label") self.horizontalLayout.addWidget(self.label) - self.lineEdit_Start = QtWidgets.QLineEdit(Form) + self.lineEdit_Start = QtWidgets.QLineEdit(parent=Form) self.lineEdit_Start.setObjectName("lineEdit_Start") self.horizontalLayout.addWidget(self.lineEdit_Start) - self.label_2 = QtWidgets.QLabel(Form) + self.label_2 = QtWidgets.QLabel(parent=Form) self.label_2.setObjectName("label_2") self.horizontalLayout.addWidget(self.label_2) - self.lineEdit_End = QtWidgets.QLineEdit(Form) + self.lineEdit_End = QtWidgets.QLineEdit(parent=Form) self.lineEdit_End.setObjectName("lineEdit_End") self.horizontalLayout.addWidget(self.lineEdit_End) self.gridLayout.addLayout(self.horizontalLayout, 1, 0, 1, 1) - self.tableWidget_Opcodes = QtWidgets.QTableWidget(Form) - self.tableWidget_Opcodes.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) - self.tableWidget_Opcodes.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) - self.tableWidget_Opcodes.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) + self.tableWidget_Opcodes = QtWidgets.QTableWidget(parent=Form) + font = QtGui.QFont() + font.setFamily("Monospace") + self.tableWidget_Opcodes.setFont(font) + self.tableWidget_Opcodes.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers) + self.tableWidget_Opcodes.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.SingleSelection) + self.tableWidget_Opcodes.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows) + self.tableWidget_Opcodes.setWordWrap(False) self.tableWidget_Opcodes.setObjectName("tableWidget_Opcodes") self.tableWidget_Opcodes.setColumnCount(2) self.tableWidget_Opcodes.setRowCount(0) @@ -88,4 +93,3 @@ def retranslateUi(self, Form): item.setText(_translate("Form", "Address")) item = self.tableWidget_Opcodes.horizontalHeaderItem(1) item.setText(_translate("Form", "Opcodes")) - diff --git a/GUI/SearchOpcodeWidget.ui b/GUI/SearchOpcodeWidget.ui index cf12d575..593b27f2 100644 --- a/GUI/SearchOpcodeWidget.ui +++ b/GUI/SearchOpcodeWidget.ui @@ -92,6 +92,11 @@ + + + Monospace + + QAbstractItemView::NoEditTriggers @@ -104,16 +109,19 @@ true + + false + true false - + 16 - + 16 diff --git a/GUI/SelectProcess.py b/GUI/SelectProcess.py index 30f9f3fb..72cdda9b 100644 --- a/GUI/SelectProcess.py +++ b/GUI/SelectProcess.py @@ -1,17 +1,18 @@ -# -*- coding: utf-8 -*- - # Form implementation generated from reading ui file 'SelectProcess.ui' # -# Created by: PyQt5 UI code generator 5.5.1 +# Created by: PyQt6 UI code generator 6.4.0 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets -from PyQt5 import QtCore, QtGui, QtWidgets class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") - MainWindow.setWindowModality(QtCore.Qt.WindowModal) + MainWindow.setWindowModality(QtCore.Qt.WindowModality.WindowModal) MainWindow.resize(443, 420) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") @@ -27,16 +28,17 @@ def setupUi(self, MainWindow): self.horizontalLayout.addWidget(self.lineEdit_SearchProcess) self.verticalLayout.addLayout(self.horizontalLayout) self.tableWidget_ProcessTable = QtWidgets.QTableWidget(self.centralwidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.tableWidget_ProcessTable.sizePolicy().hasHeightForWidth()) self.tableWidget_ProcessTable.setSizePolicy(sizePolicy) self.tableWidget_ProcessTable.setAutoFillBackground(False) - self.tableWidget_ProcessTable.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustIgnored) - self.tableWidget_ProcessTable.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) - self.tableWidget_ProcessTable.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) - self.tableWidget_ProcessTable.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) + self.tableWidget_ProcessTable.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.SizeAdjustPolicy.AdjustIgnored) + self.tableWidget_ProcessTable.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers) + self.tableWidget_ProcessTable.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.SingleSelection) + self.tableWidget_ProcessTable.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows) + self.tableWidget_ProcessTable.setWordWrap(False) self.tableWidget_ProcessTable.setObjectName("tableWidget_ProcessTable") self.tableWidget_ProcessTable.setColumnCount(3) self.tableWidget_ProcessTable.setRowCount(0) @@ -45,7 +47,7 @@ def setupUi(self, MainWindow): item = QtWidgets.QTableWidgetItem() self.tableWidget_ProcessTable.setHorizontalHeaderItem(1, item) item = QtWidgets.QTableWidgetItem() - item.setTextAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignVCenter) + item.setTextAlignment(QtCore.Qt.AlignmentFlag.AlignLeading|QtCore.Qt.AlignmentFlag.AlignVCenter) self.tableWidget_ProcessTable.setHorizontalHeaderItem(2, item) self.tableWidget_ProcessTable.horizontalHeader().setDefaultSectionSize(70) self.tableWidget_ProcessTable.horizontalHeader().setStretchLastSection(True) @@ -54,7 +56,7 @@ def setupUi(self, MainWindow): self.verticalLayout.addWidget(self.tableWidget_ProcessTable) self.horizontalLayout_2 = QtWidgets.QHBoxLayout() self.horizontalLayout_2.setObjectName("horizontalLayout_2") - spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) self.horizontalLayout_2.addItem(spacerItem) self.pushButton_Open = QtWidgets.QPushButton(self.centralwidget) self.pushButton_Open.setObjectName("pushButton_Open") @@ -62,17 +64,17 @@ def setupUi(self, MainWindow): self.pushButton_Close = QtWidgets.QPushButton(self.centralwidget) self.pushButton_Close.setObjectName("pushButton_Close") self.horizontalLayout_2.addWidget(self.pushButton_Close) - spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) self.horizontalLayout_2.addItem(spacerItem1) self.verticalLayout.addLayout(self.horizontalLayout_2) self.horizontalLayout_3 = QtWidgets.QHBoxLayout() self.horizontalLayout_3.setObjectName("horizontalLayout_3") - spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) self.horizontalLayout_3.addItem(spacerItem2) self.pushButton_CreateProcess = QtWidgets.QPushButton(self.centralwidget) self.pushButton_CreateProcess.setObjectName("pushButton_CreateProcess") self.horizontalLayout_3.addWidget(self.pushButton_CreateProcess) - spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) self.horizontalLayout_3.addItem(spacerItem3) self.verticalLayout.addLayout(self.horizontalLayout_3) MainWindow.setCentralWidget(self.centralwidget) @@ -96,4 +98,3 @@ def retranslateUi(self, MainWindow): self.pushButton_Close.setText(_translate("MainWindow", "Cancel")) self.pushButton_CreateProcess.setToolTip(_translate("MainWindow", "Open an executable")) self.pushButton_CreateProcess.setText(_translate("MainWindow", "Create Process[F1]")) - diff --git a/GUI/SelectProcess.ui b/GUI/SelectProcess.ui index 37573700..65bb5ce2 100644 --- a/GUI/SelectProcess.ui +++ b/GUI/SelectProcess.ui @@ -58,6 +58,9 @@ true + + false + 70 diff --git a/GUI/Settings/hotkeys.py b/GUI/Settings/hotkeys.py new file mode 100644 index 00000000..bf9c6691 --- /dev/null +++ b/GUI/Settings/hotkeys.py @@ -0,0 +1,59 @@ +from keyboard import add_hotkey, remove_hotkey +from typing import Callable +from tr.tr import TranslationConstants as tr + + +class Hotkey: + def __init__(self, name="", desc="", default="", func=None, custom="", handle=None) -> None: + self.name = name + self.desc = desc + self.default = default + self.func = func + self.custom = custom + if default == "" or func is None: + self.handle = handle + else: + self.handle = add_hotkey(default, func) + + def change_key(self, custom: str) -> None: + if self.handle is not None: + remove_hotkey(self.handle) + self.handle = None + self.custom = custom + if custom == "": + return + self.handle = add_hotkey(custom.lower(), self.func) + + def change_func(self, func: Callable) -> None: + self.func = func + if self.handle is not None: + remove_hotkey(self.handle) + if self.custom != "": + self.handle = add_hotkey(self.custom, func) + elif self.default != "": + self.handle = add_hotkey(self.default, func) + + def get_active_key(self) -> str: + if self.custom == "": + return self.default + return self.custom + + +class Hotkeys: + def __init__(self) -> None: + self.pause_hotkey = Hotkey("pause_hotkey", tr.PAUSE_HOTKEY, "F1") + self.break_hotkey = Hotkey("break_hotkey", tr.BREAK_HOTKEY, "F2") + self.continue_hotkey = Hotkey("continue_hotkey", tr.CONTINUE_HOTKEY, "F3") + self.toggle_attach_hotkey = Hotkey("toggle_attach_hotkey", tr.TOGGLE_ATTACH_HOTKEY, "Shift+F10") + self.exact_scan_hotkey = Hotkey("exact_scan_hotkey", tr.EXACT_SCAN_HOTKEY, "") + self.increased_scan_hotkey = Hotkey("increased_scan_hotkey", tr.INC_SCAN_HOTKEY, "") + self.decreased_scan_hotkey = Hotkey("decreased_scan_hotkey", tr.DEC_SCAN_HOTKEY, "") + self.changed_scan_hotkey = Hotkey("changed_scan_hotkey", tr.CHANGED_SCAN_HOTKEY, "") + self.unchanged_scan_hotkey = Hotkey("unchanged_scan_hotkey", tr.UNCHANGED_SCAN_HOTKEY, "") + + def get_hotkeys(self) -> list[Hotkey]: + hotkey_list = [] + for _, value in vars(self).items(): + if isinstance(value, Hotkey): + hotkey_list.append(value) + return hotkey_list diff --git a/GUI/Settings/settings.py b/GUI/Settings/settings.py new file mode 100644 index 00000000..901cc076 --- /dev/null +++ b/GUI/Settings/settings.py @@ -0,0 +1,71 @@ +import libpince.typedefs as typedefs + +current_settings_version = "35" # Increase version by one if you change settings + +# Unused, will be re-added in the future +code_injection_method: int = 0 + +gdb_path: str = typedefs.PATHS.GDB +gdb_output_mode: tuple = (True, True, True) +locale: str = "en_US" +gdb_logging: bool = False +interrupt_signal: str +handle_signals: list = [] +# Due to community feedback, these signals are disabled by default: SIGUSR1, SIGUSR2, SIGPWR, SIGXCPU, SIGXFSZ, SIGSYS +default_signals = [ + ["SIGHUP", True, True], + ["SIGINT", True, False], + ["SIGQUIT", True, True], + ["SIGILL", True, True], + ["SIGTRAP", True, False], + ["SIGABRT", True, True], + ["SIGEMT", True, True], + ["SIGFPE", True, True], + ["SIGKILL", True, True], + ["SIGBUS", True, True], + ["SIGSEGV", True, True], + ["SIGSYS", False, True], + ["SIGPIPE", True, True], + ["SIGALRM", False, True], + ["SIGTERM", True, True], + ["SIGURG", False, True], + ["SIGSTOP", True, True], + ["SIGTSTP", True, True], + ["SIGCONT", True, True], + ["SIGCHLD", False, True], + ["SIGTTIN", True, True], + ["SIGTTOU", True, True], + ["SIGIO", False, True], + ["SIGXCPU", False, True], + ["SIGXFSZ", False, True], + ["SIGVTALRM", False, True], + ["SIGPROF", False, True], + ["SIGWINCH", False, True], + ["SIGLOST", True, True], + ["SIGUSR1", False, True], + ["SIGUSR2", False, True], + ["SIGPWR", False, True], + ["SIGPOLL", False, True], + ["SIGWIND", True, True], + ["SIGPHONE", True, True], + ["SIGWAITING", False, True], + ["SIGLWP", False, True], + ["SIGDANGER", True, True], + ["SIGGRANT", True, True], + ["SIGRETRACT", True, True], + ["SIGMSG", True, True], + ["SIGSOUND", True, True], + ["SIGSAK", True, True], + ["SIGPRIO", False, True], + ["SIGCANCEL", False, True], + ["SIGINFO", True, True], + ["EXC_BAD_ACCESS", True, True], + ["EXC_BAD_INSTRUCTION", True, True], + ["EXC_ARITHMETIC", True, True], + ["EXC_EMULATION", True, True], + ["EXC_SOFTWARE", True, True], + ["EXC_BREAKPOINT", True, True], + ["SIGLIBRT", False, True], +] +for x in range(32, 128): # Add signals SIG32-SIG127 + default_signals.append([f"SIG{x}", True, True]) diff --git a/GUI/Settings/themes.py b/GUI/Settings/themes.py new file mode 100644 index 00000000..6702d9f9 --- /dev/null +++ b/GUI/Settings/themes.py @@ -0,0 +1,222 @@ +from PyQt6.QtGui import QColor, QPalette + +theme_list = ["Dark", "Light", "System Default", "Wong (Colorblind Friendly)"] + +grp_dict = { + "ACTIVE": QPalette.ColorGroup.Active, + "INACTIVE": QPalette.ColorGroup.Inactive, + "DISABLED": QPalette.ColorGroup.Disabled, +} + +role_dict = { + "WINDOW_TEXT": QPalette.ColorRole.WindowText, + "BUTTON": QPalette.ColorRole.Button, + "LIGHT": QPalette.ColorRole.Light, + "MID_LIGHT": QPalette.ColorRole.Midlight, + "DARK": QPalette.ColorRole.Dark, + "MID": QPalette.ColorRole.Mid, + "TEXT": QPalette.ColorRole.Text, + "BRIGHT_TEXT": QPalette.ColorRole.BrightText, + "BUTTON_TEXT": QPalette.ColorRole.ButtonText, + "BASE": QPalette.ColorRole.Base, + "WINDOW": QPalette.ColorRole.Window, + "SHADOW": QPalette.ColorRole.Shadow, + "HIGHLIGHT": QPalette.ColorRole.Highlight, + "HIGHLIGHTED_TEXT": QPalette.ColorRole.HighlightedText, + "LINK": QPalette.ColorRole.Link, + "LINK_VISITED": QPalette.ColorRole.LinkVisited, + "ALTERNATE_BASE": QPalette.ColorRole.AlternateBase, + "TOOLTIP_BASE": QPalette.ColorRole.ToolTipBase, + "TOOLTIP_TEXT": QPalette.ColorRole.ToolTipText, + "PLACEHOLDER_TEXT": QPalette.ColorRole.PlaceholderText, +} + + +def get_theme(theme_name): + """Returns a customized theme based on the specified theme choice + + Args: + theme_name (str): Predefined theme chosen from theme_list + + Returns: + QPalette: Complete color palette swap for the app + """ + match theme_name: + case "Dark": + dup_dict = { + "WINDOW_TEXT": "#FFFFFF", + "BUTTON": "#241F31", + "LIGHT": "#80FFFFFF", + "MID_LIGHT": "#2D263D", + "DARK": "#80000000", + "MID": "#000000", + "TEXT": "#FFFFFF", + "BRIGHT_TEXT": "#FFFFFF", + "BUTTON_TEXT": "#FFFFFF", + "BASE": "#000000", + "WINDOW": "#241F31", + "SHADOW": "#000000", + "HIGHLIGHT": "#308CC6", + "HIGHLIGHTED_TEXT": "#FFFFFF", + "LINK": "#0000FF", + "LINK_VISITED": "#FF00FF", + "ALTERNATE_BASE": "#120F18", + "TOOLTIP_BASE": "#FFFFDC", + "TOOLTIP_TEXT": "#000000", + "PLACEHOLDER_TEXT": "#80FFFFFF", + } + + dark_dict = { + "ACTIVE": dup_dict, + "INACTIVE": dup_dict, + "DISABLED": { + "WINDOW_TEXT": "#80FFFFFF", + "BUTTON": "#241F31", + "LIGHT": "#362E49", + "MID_LIGHT": "#2D263D", + "DARK": "#120F18", + "MID": "#181521", + "TEXT": "#120F18", + "BRIGHT_TEXT": "#FFFFFF", + "BUTTON_TEXT": "#80FFFFFF", + "BASE": "#241F31", + "WINDOW": "#241F31", + "SHADOW": "#000000", + "HIGHLIGHT": "#919191", + "HIGHLIGHTED_TEXT": "#FFFFFF", + "LINK": "#0000FF", + "LINK_VISITED": "#FF00FF", + "ALTERNATE_BASE": "#241F31", + "TOOLTIP_BASE": "#FFFFDC", + "TOOLTIP_TEXT": "#000000", + "PLACEHOLDER_TEXT": "#80FFFFFF", + }, + } + return apply_palette(dark_dict) + case "Light": + dup_dict = { + "WINDOW_TEXT": "#000000", + "BUTTON": "#EFEFEF", + "LIGHT": "#FFFFFF", + "MID_LIGHT": "#CACACA", + "DARK": "#5E5C64", + "MID": "#B8B8B8", + "TEXT": "#000000", + "BRIGHT_TEXT": "#FFFFFF", + "BUTTON_TEXT": "#000000", + "BASE": "#FFFFFF", + "WINDOW": "#EFEFEF", + "SHADOW": "#767676", + "HIGHLIGHT": "#308CC6", + "HIGHLIGHTED_TEXT": "#FFFFFF", + "LINK": "#0000FF", + "LINK_VISITED": "#FF00FF", + "ALTERNATE_BASE": "#F7F7F7", + "TOOLTIP_BASE": "#FFFFDC", + "TOOLTIP_TEXT": "#000000", + "PLACEHOLDER_TEXT": "#80000000", + } + + light_dict = { + "ACTIVE": dup_dict, + "INACTIVE": dup_dict, + "DISABLED": { + "WINDOW_TEXT": "#BEBEBE", + "BUTTON": "#EFEFEF", + "LIGHT": "#FFFFFF", + "MID_LIGHT": "#CACACA", + "DARK": "#BEBEBE", + "MID": "#B8B8B8", + "TEXT": "#BEBEBE", + "BRIGHT_TEXT": "#FFFFFF", + "BUTTON_TEXT": "#BEBEBE", + "BASE": "#EFEFEF", + "WINDOW": "#EFEFEF", + "SHADOW": "#B1B1B1", + "HIGHLIGHT": "#919191", + "HIGHLIGHTED_TEXT": "#FFFFFF", + "LINK": "#0000FF", + "LINK_VISITED": "#FF00FF", + "ALTERNATE_BASE": "#F7F7F7", + "TOOLTIP_BASE": "#FFFFDC", + "TOOLTIP_TEXT": "#000000", + "PLACEHOLDER_TEXT": "#80000000", + }, + } + return apply_palette(light_dict) + case "System Default": + return QPalette() + case "Wong (Colorblind Friendly)": + dup_dict = { + "WINDOW_TEXT": "#000000", + "BUTTON": "#E69F00", + "LIGHT": "#FFFFFF", + "MID_LIGHT": "#000000", + "DARK": "#000000", + "MID": "#000000", + "TEXT": "#000000", + "BRIGHT_TEXT": "#FFFFFF", + "BUTTON_TEXT": "#000000", + "BASE": "#E69F00", + "WINDOW": "#009E73", + "SHADOW": "#009E73", + "HIGHLIGHT": "#0072B2", + "HIGHLIGHTED_TEXT": "#FFFFFF", + "LINK": "#56B4E9", + "LINK_VISITED": "#CC79A7", + "ALTERNATE_BASE": "#E69F00", + "TOOLTIP_BASE": "#FFFFDC", + "TOOLTIP_TEXT": "#000000", + "PLACEHOLDER_TEXT": "#80000000", + } + + wong_dict = { + "ACTIVE": dup_dict, + "INACTIVE": dup_dict, + "DISABLED": { + "WINDOW_TEXT": "#80000000", + "BUTTON": "#E69F00", + "LIGHT": "#FFFFFF", + "MID_LIGHT": "#FFFFFF", + "DARK": "#FFFFFF", + "MID": "#FFFFFF", + "TEXT": "#FFFFFF", + "BRIGHT_TEXT": "#000000", + "BUTTON_TEXT": "#80000000", + "BASE": "#E69F00", + "WINDOW": "#000000", + "SHADOW": "#F0E442", + "HIGHLIGHT": "#919191", + "HIGHLIGHTED_TEXT": "#000000", + "LINK": "#56b4E9", + "LINK_VISITED": "#CC79A7", + "ALTERNATE_BASE": "#919191", + "TOOLTIP_BASE": "#000000", + "TOOLTIP_TEXT": "#FFFFFF", + "PLACEHOLDER_TEXT": "#80000000", + }, + } + return apply_palette(wong_dict) + case _: + print("There was an error parsing themes") + + +def apply_palette(theme_dict): + """Creates a palette based on the given theme dictionary + + Args: + theme_dict (dict): See the usage in get_theme + + Return: + QPalette: Self-explanatory + """ + new_palette = QPalette() + + for group in theme_dict: + cur_grp = grp_dict[group] + + for color in theme_dict[group]: + cur_role = role_dict[color] + new_palette.setColor(cur_grp, cur_role, QColor(theme_dict[group][color])) + + return new_palette diff --git a/GUI/SettingsDialog.py b/GUI/SettingsDialog.py index b04b2b20..9060a85a 100644 --- a/GUI/SettingsDialog.py +++ b/GUI/SettingsDialog.py @@ -1,13 +1,12 @@ -# -*- coding: utf-8 -*- - # Form implementation generated from reading ui file 'SettingsDialog.ui' # -# Created by: PyQt5 UI code generator 5.14.1 +# Created by: PyQt6 UI code generator 6.4.2 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. -from PyQt5 import QtCore, QtGui, QtWidgets +from PyQt6 import QtCore, QtGui, QtWidgets class Ui_Dialog(object): @@ -20,8 +19,8 @@ def setupUi(self, Dialog): self.verticalLayout.setObjectName("verticalLayout") self.horizontalLayout_2 = QtWidgets.QHBoxLayout() self.horizontalLayout_2.setObjectName("horizontalLayout_2") - self.listWidget_Options = QtWidgets.QListWidget(Dialog) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding) + self.listWidget_Options = QtWidgets.QListWidget(parent=Dialog) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Expanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.listWidget_Options.sizePolicy().hasHeightForWidth()) @@ -38,8 +37,10 @@ def setupUi(self, Dialog): self.listWidget_Options.addItem(item) item = QtWidgets.QListWidgetItem() self.listWidget_Options.addItem(item) + item = QtWidgets.QListWidgetItem() + self.listWidget_Options.addItem(item) self.horizontalLayout_2.addWidget(self.listWidget_Options) - self.stackedWidget = QtWidgets.QStackedWidget(Dialog) + self.stackedWidget = QtWidgets.QStackedWidget(parent=Dialog) self.stackedWidget.setMinimumSize(QtCore.QSize(500, 500)) self.stackedWidget.setObjectName("stackedWidget") self.page = QtWidgets.QWidget() @@ -54,32 +55,37 @@ def setupUi(self, Dialog): self.verticalLayout_3.setObjectName("verticalLayout_3") self.verticalLayout_2 = QtWidgets.QVBoxLayout() self.verticalLayout_2.setObjectName("verticalLayout_2") - self.checkBox_AutoUpdateAddressTable = QtWidgets.QCheckBox(self.page) - self.checkBox_AutoUpdateAddressTable.setChecked(True) + self.checkBox_AutoUpdateAddressTable = QtWidgets.QCheckBox(parent=self.page) self.checkBox_AutoUpdateAddressTable.setObjectName("checkBox_AutoUpdateAddressTable") self.verticalLayout_2.addWidget(self.checkBox_AutoUpdateAddressTable) self.verticalLayout_3.addLayout(self.verticalLayout_2) - self.QWidget_UpdateInterval = QtWidgets.QWidget(self.page) + self.QWidget_UpdateInterval = QtWidgets.QWidget(parent=self.page) self.QWidget_UpdateInterval.setObjectName("QWidget_UpdateInterval") self.horizontalLayout_UpdateInterval = QtWidgets.QHBoxLayout(self.QWidget_UpdateInterval) + self.horizontalLayout_UpdateInterval.setContentsMargins(-1, 0, -1, -1) self.horizontalLayout_UpdateInterval.setObjectName("horizontalLayout_UpdateInterval") - self.label = QtWidgets.QLabel(self.QWidget_UpdateInterval) + self.label = QtWidgets.QLabel(parent=self.QWidget_UpdateInterval) self.label.setMinimumSize(QtCore.QSize(102, 0)) self.label.setObjectName("label") self.horizontalLayout_UpdateInterval.addWidget(self.label) - self.lineEdit_UpdateInterval = QtWidgets.QLineEdit(self.QWidget_UpdateInterval) - self.lineEdit_UpdateInterval.setObjectName("lineEdit_UpdateInterval") - self.horizontalLayout_UpdateInterval.addWidget(self.lineEdit_UpdateInterval) - self.label_2 = QtWidgets.QLabel(self.QWidget_UpdateInterval) + self.spinBox_UpdateInterval = QtWidgets.QSpinBox(parent=self.QWidget_UpdateInterval) + self.spinBox_UpdateInterval.setMinimum(100) + self.spinBox_UpdateInterval.setMaximum(10000) + self.spinBox_UpdateInterval.setSingleStep(100) + self.spinBox_UpdateInterval.setObjectName("spinBox_UpdateInterval") + self.horizontalLayout_UpdateInterval.addWidget(self.spinBox_UpdateInterval) + self.label_2 = QtWidgets.QLabel(parent=self.QWidget_UpdateInterval) + self.label_2.setText("ms") self.label_2.setObjectName("label_2") self.horizontalLayout_UpdateInterval.addWidget(self.label_2) self.verticalLayout_3.addWidget(self.QWidget_UpdateInterval) - self.LockInterval = QtWidgets.QWidget(self.page) + self.LockInterval = QtWidgets.QWidget(parent=self.page) self.LockInterval.setObjectName("LockInterval") self.horizontalLayout_14 = QtWidgets.QHBoxLayout(self.LockInterval) + self.horizontalLayout_14.setContentsMargins(-1, 0, -1, -1) self.horizontalLayout_14.setObjectName("horizontalLayout_14") - self.label_12 = QtWidgets.QLabel(self.LockInterval) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) + self.label_12 = QtWidgets.QLabel(parent=self.LockInterval) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.label_12.sizePolicy().hasHeightForWidth()) @@ -88,85 +94,85 @@ def setupUi(self, Dialog): self.label_12.setBaseSize(QtCore.QSize(0, 0)) self.label_12.setObjectName("label_12") self.horizontalLayout_14.addWidget(self.label_12) - self.lineEdit_FreezeInterval = QtWidgets.QLineEdit(self.LockInterval) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.lineEdit_FreezeInterval.sizePolicy().hasHeightForWidth()) - self.lineEdit_FreezeInterval.setSizePolicy(sizePolicy) - self.lineEdit_FreezeInterval.setBaseSize(QtCore.QSize(20, 0)) - self.lineEdit_FreezeInterval.setObjectName("lineEdit_FreezeInterval") - self.horizontalLayout_14.addWidget(self.lineEdit_FreezeInterval) - self.label_10 = QtWidgets.QLabel(self.LockInterval) + self.spinBox_FreezeInterval = QtWidgets.QSpinBox(parent=self.LockInterval) + self.spinBox_FreezeInterval.setMinimum(100) + self.spinBox_FreezeInterval.setMaximum(10000) + self.spinBox_FreezeInterval.setSingleStep(100) + self.spinBox_FreezeInterval.setObjectName("spinBox_FreezeInterval") + self.horizontalLayout_14.addWidget(self.spinBox_FreezeInterval) + self.label_10 = QtWidgets.QLabel(parent=self.LockInterval) + self.label_10.setText("ms") self.label_10.setObjectName("label_10") self.horizontalLayout_14.addWidget(self.label_10) self.verticalLayout_3.addWidget(self.LockInterval) self.horizontalLayout_7.addLayout(self.verticalLayout_3) - spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) self.horizontalLayout_7.addItem(spacerItem) self.verticalLayout_5.addLayout(self.horizontalLayout_7) - self.horizontalLayout_13 = QtWidgets.QHBoxLayout() - self.horizontalLayout_13.setObjectName("horizontalLayout_13") - spacerItem1 = QtWidgets.QSpacerItem(10, 20, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Maximum) - self.horizontalLayout_13.addItem(spacerItem1) - self.verticalLayout_5.addLayout(self.horizontalLayout_13) - self.checkBox_MessageBoxOnException = QtWidgets.QCheckBox(self.page) - self.checkBox_MessageBoxOnException.setChecked(True) - self.checkBox_MessageBoxOnException.setObjectName("checkBox_MessageBoxOnException") - self.verticalLayout_5.addWidget(self.checkBox_MessageBoxOnException) - self.verticalLayout_12 = QtWidgets.QVBoxLayout() - self.verticalLayout_12.setObjectName("verticalLayout_12") - self.verticalLayout_5.addLayout(self.verticalLayout_12) - self.checkBox_MessageBoxOnToggleAttach = QtWidgets.QCheckBox(self.page) - self.checkBox_MessageBoxOnToggleAttach.setChecked(True) - self.checkBox_MessageBoxOnToggleAttach.setObjectName("checkBox_MessageBoxOnToggleAttach") - self.verticalLayout_5.addWidget(self.checkBox_MessageBoxOnToggleAttach) self.horizontalLayout_3 = QtWidgets.QHBoxLayout() self.horizontalLayout_3.setObjectName("horizontalLayout_3") - self.label_8 = QtWidgets.QLabel(self.page) + self.label_8 = QtWidgets.QLabel(parent=self.page) self.label_8.setObjectName("label_8") self.horizontalLayout_3.addWidget(self.label_8) - self.checkBox_OutputModeAsync = QtWidgets.QCheckBox(self.page) - self.checkBox_OutputModeAsync.setChecked(True) + self.checkBox_OutputModeAsync = QtWidgets.QCheckBox(parent=self.page) self.checkBox_OutputModeAsync.setObjectName("checkBox_OutputModeAsync") self.horizontalLayout_3.addWidget(self.checkBox_OutputModeAsync) - self.checkBox_OutputModeCommand = QtWidgets.QCheckBox(self.page) - self.checkBox_OutputModeCommand.setChecked(True) + self.checkBox_OutputModeCommand = QtWidgets.QCheckBox(parent=self.page) self.checkBox_OutputModeCommand.setObjectName("checkBox_OutputModeCommand") self.horizontalLayout_3.addWidget(self.checkBox_OutputModeCommand) - self.checkBox_OutputModeCommandInfo = QtWidgets.QCheckBox(self.page) - self.checkBox_OutputModeCommandInfo.setChecked(True) + self.checkBox_OutputModeCommandInfo = QtWidgets.QCheckBox(parent=self.page) self.checkBox_OutputModeCommandInfo.setObjectName("checkBox_OutputModeCommandInfo") self.horizontalLayout_3.addWidget(self.checkBox_OutputModeCommandInfo) - spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.horizontalLayout_3.addItem(spacerItem2) + spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) + self.horizontalLayout_3.addItem(spacerItem1) self.verticalLayout_5.addLayout(self.horizontalLayout_3) self.horizontalLayout_12 = QtWidgets.QHBoxLayout() - self.horizontalLayout_12.setContentsMargins(-1, 0, -1, -1) self.horizontalLayout_12.setObjectName("horizontalLayout_12") - self.label_9 = QtWidgets.QLabel(self.page) + self.label_9 = QtWidgets.QLabel(parent=self.page) self.label_9.setObjectName("label_9") self.horizontalLayout_12.addWidget(self.label_9) - self.lineEdit_AutoAttachList = QtWidgets.QLineEdit(self.page) - self.lineEdit_AutoAttachList.setObjectName("lineEdit_AutoAttachList") - self.horizontalLayout_12.addWidget(self.lineEdit_AutoAttachList) - self.checkBox_AutoAttachRegex = QtWidgets.QCheckBox(self.page) + self.lineEdit_AutoAttach = QtWidgets.QLineEdit(parent=self.page) + self.lineEdit_AutoAttach.setObjectName("lineEdit_AutoAttach") + self.horizontalLayout_12.addWidget(self.lineEdit_AutoAttach) + self.checkBox_AutoAttachRegex = QtWidgets.QCheckBox(parent=self.page) self.checkBox_AutoAttachRegex.setObjectName("checkBox_AutoAttachRegex") self.horizontalLayout_12.addWidget(self.checkBox_AutoAttachRegex) self.verticalLayout_5.addLayout(self.horizontalLayout_12) + self.horizontalLayout_13 = QtWidgets.QHBoxLayout() + self.horizontalLayout_13.setObjectName("horizontalLayout_13") + self.label_13 = QtWidgets.QLabel(parent=self.page) + self.label_13.setObjectName("label_13") + self.horizontalLayout_13.addWidget(self.label_13) + self.comboBox_Language = QtWidgets.QComboBox(parent=self.page) + self.comboBox_Language.setObjectName("comboBox_Language") + self.horizontalLayout_13.addWidget(self.comboBox_Language) + spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) + self.horizontalLayout_13.addItem(spacerItem2) + self.verticalLayout_5.addLayout(self.horizontalLayout_13) self.horizontalLayout_15 = QtWidgets.QHBoxLayout() self.horizontalLayout_15.setObjectName("horizontalLayout_15") - self.label_11 = QtWidgets.QLabel(self.page) + self.label_11 = QtWidgets.QLabel(parent=self.page) self.label_11.setObjectName("label_11") self.horizontalLayout_15.addWidget(self.label_11) - self.comboBox_Logo = QtWidgets.QComboBox(self.page) + self.comboBox_Logo = QtWidgets.QComboBox(parent=self.page) self.comboBox_Logo.setObjectName("comboBox_Logo") self.horizontalLayout_15.addWidget(self.comboBox_Logo) - spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) self.horizontalLayout_15.addItem(spacerItem3) self.verticalLayout_5.addLayout(self.horizontalLayout_15) - spacerItem4 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.verticalLayout_5.addItem(spacerItem4) + self.horizontalLayout_17 = QtWidgets.QHBoxLayout() + self.horizontalLayout_17.setObjectName("horizontalLayout_17") + self.label_14 = QtWidgets.QLabel(parent=self.page) + self.label_14.setObjectName("label_14") + self.horizontalLayout_17.addWidget(self.label_14) + self.comboBox_Theme = QtWidgets.QComboBox(parent=self.page) + self.comboBox_Theme.setObjectName("comboBox_Theme") + self.horizontalLayout_17.addWidget(self.comboBox_Theme) + spacerItem4 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) + self.horizontalLayout_17.addItem(spacerItem4) + self.verticalLayout_5.addLayout(self.horizontalLayout_17) + spacerItem5 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) + self.verticalLayout_5.addItem(spacerItem5) self.gridLayout_2.addLayout(self.verticalLayout_5, 0, 0, 1, 1) self.stackedWidget.addWidget(self.page) self.page_2 = QtWidgets.QWidget() @@ -177,10 +183,10 @@ def setupUi(self, Dialog): self.horizontalLayout_5.setObjectName("horizontalLayout_5") self.verticalLayout_4 = QtWidgets.QVBoxLayout() self.verticalLayout_4.setObjectName("verticalLayout_4") - self.label_3 = QtWidgets.QLabel(self.page_2) + self.label_3 = QtWidgets.QLabel(parent=self.page_2) self.label_3.setObjectName("label_3") self.verticalLayout_4.addWidget(self.label_3) - self.listWidget_Functions = QtWidgets.QListWidget(self.page_2) + self.listWidget_Functions = QtWidgets.QListWidget(parent=self.page_2) self.listWidget_Functions.setObjectName("listWidget_Functions") self.verticalLayout_4.addWidget(self.listWidget_Functions) self.horizontalLayout_5.addLayout(self.verticalLayout_4) @@ -188,20 +194,24 @@ def setupUi(self, Dialog): self.verticalLayout_6.setObjectName("verticalLayout_6") self.verticalLayout_Hotkey = QtWidgets.QVBoxLayout() self.verticalLayout_Hotkey.setObjectName("verticalLayout_Hotkey") - self.label_4 = QtWidgets.QLabel(self.page_2) + self.label_4 = QtWidgets.QLabel(parent=self.page_2) self.label_4.setObjectName("label_4") self.verticalLayout_Hotkey.addWidget(self.label_4) + self.lineEdit_Hotkey = QtWidgets.QLineEdit(parent=self.page_2) + self.lineEdit_Hotkey.setReadOnly(True) + self.lineEdit_Hotkey.setObjectName("lineEdit_Hotkey") + self.verticalLayout_Hotkey.addWidget(self.lineEdit_Hotkey) self.verticalLayout_6.addLayout(self.verticalLayout_Hotkey) self.horizontalLayout_4 = QtWidgets.QHBoxLayout() self.horizontalLayout_4.setObjectName("horizontalLayout_4") - spacerItem5 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.horizontalLayout_4.addItem(spacerItem5) - self.pushButton_ClearHotkey = QtWidgets.QPushButton(self.page_2) + spacerItem6 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) + self.horizontalLayout_4.addItem(spacerItem6) + self.pushButton_ClearHotkey = QtWidgets.QPushButton(parent=self.page_2) self.pushButton_ClearHotkey.setObjectName("pushButton_ClearHotkey") self.horizontalLayout_4.addWidget(self.pushButton_ClearHotkey) self.verticalLayout_6.addLayout(self.horizontalLayout_4) - spacerItem6 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.verticalLayout_6.addItem(spacerItem6) + spacerItem7 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) + self.verticalLayout_6.addItem(spacerItem7) self.horizontalLayout_5.addLayout(self.verticalLayout_6) self.gridLayout_3.addLayout(self.horizontalLayout_5, 0, 0, 1, 1) self.stackedWidget.addWidget(self.page_2) @@ -215,30 +225,27 @@ def setupUi(self, Dialog): self.horizontalLayout_8.setObjectName("horizontalLayout_8") self.verticalLayout_8 = QtWidgets.QVBoxLayout() self.verticalLayout_8.setObjectName("verticalLayout_8") - self.label_5 = QtWidgets.QLabel(self.page_3) + self.label_5 = QtWidgets.QLabel(parent=self.page_3) self.label_5.setObjectName("label_5") self.verticalLayout_8.addWidget(self.label_5) self.horizontalLayout_6 = QtWidgets.QHBoxLayout() self.horizontalLayout_6.setObjectName("horizontalLayout_6") - spacerItem7 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.horizontalLayout_6.addItem(spacerItem7) self.verticalLayout_7 = QtWidgets.QVBoxLayout() self.verticalLayout_7.setObjectName("verticalLayout_7") - self.radioButton_SimpleDLopenCall = QtWidgets.QRadioButton(self.page_3) - self.radioButton_SimpleDLopenCall.setChecked(True) + self.radioButton_SimpleDLopenCall = QtWidgets.QRadioButton(parent=self.page_3) self.radioButton_SimpleDLopenCall.setObjectName("radioButton_SimpleDLopenCall") self.verticalLayout_7.addWidget(self.radioButton_SimpleDLopenCall) - self.radioButton_AdvancedInjection = QtWidgets.QRadioButton(self.page_3) + self.radioButton_AdvancedInjection = QtWidgets.QRadioButton(parent=self.page_3) self.radioButton_AdvancedInjection.setEnabled(False) self.radioButton_AdvancedInjection.setObjectName("radioButton_AdvancedInjection") self.verticalLayout_7.addWidget(self.radioButton_AdvancedInjection) self.horizontalLayout_6.addLayout(self.verticalLayout_7) self.verticalLayout_8.addLayout(self.horizontalLayout_6) self.horizontalLayout_8.addLayout(self.verticalLayout_8) - spacerItem8 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + spacerItem8 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) self.horizontalLayout_8.addItem(spacerItem8) self.verticalLayout_9.addLayout(self.horizontalLayout_8) - spacerItem9 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + spacerItem9 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) self.verticalLayout_9.addItem(spacerItem9) self.gridLayout_4.addLayout(self.verticalLayout_9, 0, 0, 1, 1) self.stackedWidget.addWidget(self.page_3) @@ -248,26 +255,37 @@ def setupUi(self, Dialog): self.gridLayout_5.setObjectName("gridLayout_5") self.verticalLayout_10 = QtWidgets.QVBoxLayout() self.verticalLayout_10.setObjectName("verticalLayout_10") - self.checkBox_BringDisassembleToFront = QtWidgets.QCheckBox(self.page_4) - self.checkBox_BringDisassembleToFront.setChecked(True) - self.checkBox_BringDisassembleToFront.setObjectName("checkBox_BringDisassembleToFront") - self.verticalLayout_10.addWidget(self.checkBox_BringDisassembleToFront) + self.checkBox_ShowMemoryViewOnStop = QtWidgets.QCheckBox(parent=self.page_4) + self.checkBox_ShowMemoryViewOnStop.setObjectName("checkBox_ShowMemoryViewOnStop") + self.verticalLayout_10.addWidget(self.checkBox_ShowMemoryViewOnStop) self.horizontalLayout_10 = QtWidgets.QHBoxLayout() self.horizontalLayout_10.setObjectName("horizontalLayout_10") - self.horizontalLayout_9 = QtWidgets.QHBoxLayout() - self.horizontalLayout_9.setObjectName("horizontalLayout_9") - self.label_6 = QtWidgets.QLabel(self.page_4) + self.label_6 = QtWidgets.QLabel(parent=self.page_4) self.label_6.setObjectName("label_6") - self.horizontalLayout_9.addWidget(self.label_6) - self.lineEdit_InstructionsPerScroll = QtWidgets.QLineEdit(self.page_4) - self.lineEdit_InstructionsPerScroll.setObjectName("lineEdit_InstructionsPerScroll") - self.horizontalLayout_9.addWidget(self.lineEdit_InstructionsPerScroll) - self.horizontalLayout_10.addLayout(self.horizontalLayout_9) - spacerItem10 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout_10.addWidget(self.label_6) + self.spinBox_InstructionsPerScroll = QtWidgets.QSpinBox(parent=self.page_4) + self.spinBox_InstructionsPerScroll.setObjectName("spinBox_InstructionsPerScroll") + self.horizontalLayout_10.addWidget(self.spinBox_InstructionsPerScroll) + spacerItem10 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) self.horizontalLayout_10.addItem(spacerItem10) self.verticalLayout_10.addLayout(self.horizontalLayout_10) - spacerItem11 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.verticalLayout_10.addItem(spacerItem11) + self.horizontalLayout_19 = QtWidgets.QHBoxLayout() + self.horizontalLayout_19.setObjectName("horizontalLayout_19") + self.label_16 = QtWidgets.QLabel(parent=self.page_4) + self.label_16.setObjectName("label_16") + self.horizontalLayout_19.addWidget(self.label_16) + self.spinBox_BytesPerScroll = QtWidgets.QSpinBox(parent=self.page_4) + self.spinBox_BytesPerScroll.setPrefix("0x") + self.spinBox_BytesPerScroll.setMaximum(4096) + self.spinBox_BytesPerScroll.setSingleStep(16) + self.spinBox_BytesPerScroll.setDisplayIntegerBase(16) + self.spinBox_BytesPerScroll.setObjectName("spinBox_BytesPerScroll") + self.horizontalLayout_19.addWidget(self.spinBox_BytesPerScroll) + spacerItem11 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) + self.horizontalLayout_19.addItem(spacerItem11) + self.verticalLayout_10.addLayout(self.horizontalLayout_19) + spacerItem12 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) + self.verticalLayout_10.addItem(spacerItem12) self.gridLayout_5.addLayout(self.verticalLayout_10, 0, 0, 1, 1) self.stackedWidget.addWidget(self.page_4) self.page_5 = QtWidgets.QWidget() @@ -280,40 +298,65 @@ def setupUi(self, Dialog): self.horizontalLayout_11.setObjectName("horizontalLayout_11") self.horizontalLayout_101 = QtWidgets.QHBoxLayout() self.horizontalLayout_101.setObjectName("horizontalLayout_101") - self.label_7 = QtWidgets.QLabel(self.page_5) - self.label_7.setObjectName("label_7") - self.horizontalLayout_101.addWidget(self.label_7) - self.lineEdit_GDBPath = QtWidgets.QLineEdit(self.page_5) + self.label_GDBPath = QtWidgets.QLabel(parent=self.page_5) + self.label_GDBPath.setObjectName("label_GDBPath") + self.horizontalLayout_101.addWidget(self.label_GDBPath) + self.lineEdit_GDBPath = QtWidgets.QLineEdit(parent=self.page_5) self.lineEdit_GDBPath.setObjectName("lineEdit_GDBPath") self.horizontalLayout_101.addWidget(self.lineEdit_GDBPath) self.horizontalLayout_11.addLayout(self.horizontalLayout_101) - self.pushButton_GDBPath = QtWidgets.QPushButton(self.page_5) - self.pushButton_GDBPath.setText("") + self.pushButton_GDBPath = QtWidgets.QPushButton(parent=self.page_5) self.pushButton_GDBPath.setObjectName("pushButton_GDBPath") self.horizontalLayout_11.addWidget(self.pushButton_GDBPath) self.verticalLayout_11.addLayout(self.horizontalLayout_11) - self.checkBox_GDBLogging = QtWidgets.QCheckBox(self.page_5) + self.checkBox_GDBLogging = QtWidgets.QCheckBox(parent=self.page_5) self.checkBox_GDBLogging.setObjectName("checkBox_GDBLogging") self.verticalLayout_11.addWidget(self.checkBox_GDBLogging) - self.checkBox_IgnoreSegfault = QtWidgets.QCheckBox(self.page_5) - self.checkBox_IgnoreSegfault.setObjectName("checkBox_IgnoreSegfault") - self.verticalLayout_11.addWidget(self.checkBox_IgnoreSegfault) - spacerItem12 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.verticalLayout_11.addItem(spacerItem12) + self.horizontalLayout_18 = QtWidgets.QHBoxLayout() + self.horizontalLayout_18.setObjectName("horizontalLayout_18") + self.label_15 = QtWidgets.QLabel(parent=self.page_5) + self.label_15.setObjectName("label_15") + self.horizontalLayout_18.addWidget(self.label_15) + self.comboBox_InterruptSignal = QtWidgets.QComboBox(parent=self.page_5) + self.comboBox_InterruptSignal.setObjectName("comboBox_InterruptSignal") + self.horizontalLayout_18.addWidget(self.comboBox_InterruptSignal) + spacerItem13 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) + self.horizontalLayout_18.addItem(spacerItem13) + self.verticalLayout_11.addLayout(self.horizontalLayout_18) + self.horizontalLayout_16 = QtWidgets.QHBoxLayout() + self.horizontalLayout_16.setObjectName("horizontalLayout_16") + self.pushButton_HandleSignals = QtWidgets.QPushButton(parent=self.page_5) + self.pushButton_HandleSignals.setObjectName("pushButton_HandleSignals") + self.horizontalLayout_16.addWidget(self.pushButton_HandleSignals) + spacerItem14 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) + self.horizontalLayout_16.addItem(spacerItem14) + self.verticalLayout_11.addLayout(self.horizontalLayout_16) + spacerItem15 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) + self.verticalLayout_11.addItem(spacerItem15) self.gridLayout_6.addLayout(self.verticalLayout_11, 0, 0, 1, 1) self.stackedWidget.addWidget(self.page_5) + self.page_6 = QtWidgets.QWidget() + self.page_6.setObjectName("page_6") + self.gridLayout_7 = QtWidgets.QGridLayout(self.page_6) + self.gridLayout_7.setObjectName("gridLayout_7") + self.checkBox_JavaSegfault = QtWidgets.QCheckBox(parent=self.page_6) + self.checkBox_JavaSegfault.setObjectName("checkBox_JavaSegfault") + self.gridLayout_7.addWidget(self.checkBox_JavaSegfault, 0, 0, 1, 1) + spacerItem16 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) + self.gridLayout_7.addItem(spacerItem16, 1, 0, 1, 1) + self.stackedWidget.addWidget(self.page_6) self.horizontalLayout_2.addWidget(self.stackedWidget) self.verticalLayout.addLayout(self.horizontalLayout_2) self.horizontalLayout = QtWidgets.QHBoxLayout() self.horizontalLayout.setObjectName("horizontalLayout") - self.pushButton_ResetSettings = QtWidgets.QPushButton(Dialog) + self.pushButton_ResetSettings = QtWidgets.QPushButton(parent=Dialog) self.pushButton_ResetSettings.setObjectName("pushButton_ResetSettings") self.horizontalLayout.addWidget(self.pushButton_ResetSettings) - spacerItem13 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.horizontalLayout.addItem(spacerItem13) - self.buttonBox = QtWidgets.QDialogButtonBox(Dialog) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) + spacerItem17 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) + self.horizontalLayout.addItem(spacerItem17) + self.buttonBox = QtWidgets.QDialogButtonBox(parent=Dialog) + self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.StandardButton.Cancel|QtWidgets.QDialogButtonBox.StandardButton.Ok) self.buttonBox.setObjectName("buttonBox") self.horizontalLayout.addWidget(self.buttonBox) self.verticalLayout.addLayout(self.horizontalLayout) @@ -322,8 +365,8 @@ def setupUi(self, Dialog): self.retranslateUi(Dialog) self.stackedWidget.setCurrentIndex(0) self.listWidget_Functions.setCurrentRow(-1) - self.buttonBox.accepted.connect(Dialog.accept) - self.buttonBox.rejected.connect(Dialog.reject) + self.buttonBox.accepted.connect(Dialog.accept) # type: ignore + self.buttonBox.rejected.connect(Dialog.reject) # type: ignore QtCore.QMetaObject.connectSlotsByName(Dialog) def retranslateUi(self, Dialog): @@ -338,47 +381,39 @@ def retranslateUi(self, Dialog): item = self.listWidget_Options.item(2) item.setText(_translate("Dialog", "Code Injection")) item = self.listWidget_Options.item(3) - item.setText(_translate("Dialog", "Disassemble")) + item.setText(_translate("Dialog", "Memory View")) item = self.listWidget_Options.item(4) item.setText(_translate("Dialog", "Debug")) + item = self.listWidget_Options.item(5) + item.setText(_translate("Dialog", "Java")) self.listWidget_Options.setSortingEnabled(__sortingEnabled) self.checkBox_AutoUpdateAddressTable.setText(_translate("Dialog", "Auto-update address table")) self.label.setText(_translate("Dialog", "Update Interval")) - self.lineEdit_UpdateInterval.setText(_translate("Dialog", "500")) - self.label_2.setText(_translate("Dialog", "ms")) self.label_12.setText(_translate("Dialog", "Freeze Interval")) - self.lineEdit_FreezeInterval.setText(_translate("Dialog", "100")) - self.label_10.setText(_translate("Dialog", "ms")) - self.checkBox_MessageBoxOnException.setText(_translate("Dialog", "Show a MessageBox on InferiorRunning and GDBInitialize exceptions")) - self.checkBox_MessageBoxOnToggleAttach.setText(_translate("Dialog", "Show a MessageBox on Toggle Attach")) self.label_8.setText(_translate("Dialog", "GDB output:")) self.checkBox_OutputModeAsync.setText(_translate("Dialog", "Async")) self.checkBox_OutputModeCommand.setText(_translate("Dialog", "Command")) self.checkBox_OutputModeCommandInfo.setText(_translate("Dialog", "Command info")) self.label_9.setToolTip(_translate("Dialog", "On start, automatically attach to processes with name matching one of the entries\n" "Patterns at former positions have higher priority if regex is off")) - self.label_9.setText(_translate("Dialog", "Auto-attach on start")) + self.label_9.setText(_translate("Dialog", "Auto-attach to processes named")) self.checkBox_AutoAttachRegex.setText(_translate("Dialog", "Regex")) + self.label_13.setText(_translate("Dialog", "Language")) self.label_11.setText(_translate("Dialog", "Logo")) + self.label_14.setText(_translate("Dialog", "Theme")) self.label_3.setText(_translate("Dialog", "Functions")) self.label_4.setText(_translate("Dialog", "Hotkey")) + self.lineEdit_Hotkey.setPlaceholderText(_translate("Dialog", "Press shortcut")) self.pushButton_ClearHotkey.setText(_translate("Dialog", "Clear")) self.label_5.setText(_translate("Dialog", "Code injection method:")) self.radioButton_SimpleDLopenCall.setText(_translate("Dialog", "Simp&le dlopen call")) self.radioButton_AdvancedInjection.setText(_translate("Dialog", "Advanced In&jection")) - self.checkBox_BringDisassembleToFront.setText(_translate("Dialog", "Bring disassemble screen to front when the inferior is stopped")) - self.label_6.setText(_translate("Dialog", "Instructions shown per scroll")) - self.label_7.setText(_translate("Dialog", "GDB Path")) + self.checkBox_ShowMemoryViewOnStop.setText(_translate("Dialog", "Bring Memory View to front when the inferior is stopped")) + self.label_6.setText(_translate("Dialog", "Instructions shown per scroll in Disassembly View")) + self.label_16.setText(_translate("Dialog", "Bytes shown per scroll in Hex View")) + self.label_GDBPath.setText(_translate("Dialog", "GDB Path")) self.checkBox_GDBLogging.setText(_translate("Dialog", "GDB Logging")) - self.checkBox_IgnoreSegfault.setText(_translate("Dialog", "Ignore segfault signal")) + self.label_15.setText(_translate("Dialog", "Interruption signal")) + self.pushButton_HandleSignals.setText(_translate("Dialog", "Handle Signals")) + self.checkBox_JavaSegfault.setText(_translate("Dialog", "Ignore SIGSEGV for Java processes (overrides signal settings if enabled)")) self.pushButton_ResetSettings.setText(_translate("Dialog", "Reset Settings")) - - -if __name__ == "__main__": - import sys - app = QtWidgets.QApplication(sys.argv) - Dialog = QtWidgets.QDialog() - ui = Ui_Dialog() - ui.setupUi(Dialog) - Dialog.show() - sys.exit(app.exec_()) diff --git a/GUI/SettingsDialog.ui b/GUI/SettingsDialog.ui index a29aff97..2b067680 100644 --- a/GUI/SettingsDialog.ui +++ b/GUI/SettingsDialog.ui @@ -49,7 +49,7 @@ - Disassemble + Memory View @@ -57,6 +57,11 @@ Debug + + + Java + + @@ -85,9 +90,6 @@ Auto-update address table - - true - @@ -95,6 +97,9 @@ + + 0 + @@ -109,16 +114,22 @@ - - - 500 + + + 100 + + + 10000 + + + 100 - ms + ms @@ -128,6 +139,9 @@ + + 0 + @@ -154,28 +168,22 @@ - - - - 0 - 0 - + + + 100 - - - 20 - 0 - + + 10000 - - 100 + + 100 - ms + ms @@ -199,49 +207,6 @@ - - - - - - Qt::Vertical - - - QSizePolicy::Maximum - - - - 10 - 20 - - - - - - - - - - Show a MessageBox on InferiorRunning and GDBInitialize exceptions - - - true - - - - - - - - - - Show a MessageBox on Toggle Attach - - - true - - - @@ -256,9 +221,6 @@ Async - - true - @@ -266,9 +228,6 @@ Command - - true - @@ -276,9 +235,6 @@ Command info - - true - @@ -298,9 +254,6 @@ - - 0 - @@ -308,12 +261,12 @@ Patterns at former positions have higher priority if regex is off - Auto-attach on start + Auto-attach to processes named - + @@ -324,6 +277,33 @@ Patterns at former positions have higher priority if regex is off + + + + + + Language + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + @@ -351,6 +331,33 @@ Patterns at former positions have higher priority if regex is off + + + + + + Theme + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + @@ -401,6 +408,16 @@ Patterns at former positions have higher priority if regex is off + + + + true + + + Press shortcut + + + @@ -463,19 +480,6 @@ Patterns at former positions have higher priority if regex is off - - - - Qt::Horizontal - - - - 40 - 20 - - - - @@ -483,9 +487,6 @@ Patterns at former positions have higher priority if regex is off Simp&le dlopen call - - true - @@ -541,30 +542,23 @@ Patterns at former positions have higher priority if regex is off - + - Bring disassemble screen to front when the inferior is stopped - - - true + Bring Memory View to front when the inferior is stopped - - - - - Instructions shown per scroll - - - - - - - + + + Instructions shown per scroll in Disassembly View + + + + + @@ -581,6 +575,46 @@ Patterns at former positions have higher priority if regex is off + + + + + + Bytes shown per scroll in Hex View + + + + + + + 0x + + + 4096 + + + 16 + + + 16 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + @@ -607,7 +641,7 @@ Patterns at former positions have higher priority if regex is off - + GDB Path @@ -619,11 +653,7 @@ Patterns at former positions have higher priority if regex is off - - - - - + @@ -635,11 +665,55 @@ Patterns at former positions have higher priority if regex is off - - - Ignore segfault signal - - + + + + + Interruption signal + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Handle Signals + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + @@ -658,6 +732,30 @@ Patterns at former positions have higher priority if regex is off + + + + + + Ignore SIGSEGV for Java processes (overrides signal settings if enabled) + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + diff --git a/GUI/StackTraceInfoWidget.py b/GUI/StackTraceInfoWidget.py index e9877bcc..eb8ff40b 100644 --- a/GUI/StackTraceInfoWidget.py +++ b/GUI/StackTraceInfoWidget.py @@ -1,13 +1,13 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'stacktraceinfowidget.ui' +# Form implementation generated from reading ui file 'StackTraceInfoWidget.ui' # -# Created: Tue Jul 26 21:56:27 2016 -# by: PyQt5 UI code generator 5.2.1 +# Created by: PyQt6 UI code generator 6.4.2 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets -from PyQt5 import QtCore, QtGui, QtWidgets class Ui_Form(object): def setupUi(self, Form): @@ -15,29 +15,35 @@ def setupUi(self, Form): Form.resize(768, 440) self.gridLayout = QtWidgets.QGridLayout(Form) self.gridLayout.setObjectName("gridLayout") - self.splitter = QtWidgets.QSplitter(Form) - self.splitter.setOrientation(QtCore.Qt.Horizontal) + self.splitter = QtWidgets.QSplitter(parent=Form) + self.splitter.setOrientation(QtCore.Qt.Orientation.Horizontal) self.splitter.setObjectName("splitter") - self.widget = QtWidgets.QWidget(self.splitter) - self.widget.setObjectName("widget") - self.verticalLayout = QtWidgets.QVBoxLayout(self.widget) + self.layoutWidget = QtWidgets.QWidget(parent=self.splitter) + self.layoutWidget.setObjectName("layoutWidget") + self.verticalLayout = QtWidgets.QVBoxLayout(self.layoutWidget) self.verticalLayout.setContentsMargins(0, 0, 0, 0) self.verticalLayout.setObjectName("verticalLayout") - self.label = QtWidgets.QLabel(self.widget) + self.label = QtWidgets.QLabel(parent=self.layoutWidget) self.label.setObjectName("label") self.verticalLayout.addWidget(self.label) - self.listWidget_ReturnAddresses = QtWidgets.QListWidget(self.widget) + self.listWidget_ReturnAddresses = QtWidgets.QListWidget(parent=self.layoutWidget) + font = QtGui.QFont() + font.setFamily("Monospace") + self.listWidget_ReturnAddresses.setFont(font) self.listWidget_ReturnAddresses.setObjectName("listWidget_ReturnAddresses") self.verticalLayout.addWidget(self.listWidget_ReturnAddresses) - self.widget1 = QtWidgets.QWidget(self.splitter) - self.widget1.setObjectName("widget1") - self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.widget1) + self.layoutWidget1 = QtWidgets.QWidget(parent=self.splitter) + self.layoutWidget1.setObjectName("layoutWidget1") + self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.layoutWidget1) self.verticalLayout_2.setContentsMargins(0, 0, 0, 0) self.verticalLayout_2.setObjectName("verticalLayout_2") - self.label_2 = QtWidgets.QLabel(self.widget1) + self.label_2 = QtWidgets.QLabel(parent=self.layoutWidget1) self.label_2.setObjectName("label_2") self.verticalLayout_2.addWidget(self.label_2) - self.textBrowser_Info = QtWidgets.QTextBrowser(self.widget1) + self.textBrowser_Info = QtWidgets.QTextBrowser(parent=self.layoutWidget1) + font = QtGui.QFont() + font.setFamily("Monospace") + self.textBrowser_Info.setFont(font) self.textBrowser_Info.setObjectName("textBrowser_Info") self.verticalLayout_2.addWidget(self.textBrowser_Info) self.gridLayout.addWidget(self.splitter, 0, 0, 1, 1) @@ -50,4 +56,3 @@ def retranslateUi(self, Form): Form.setWindowTitle(_translate("Form", "StackTrace Information")) self.label.setText(_translate("Form", "Return Address")) self.label_2.setText(_translate("Form", "Info")) - diff --git a/GUI/StackTraceInfoWidget.ui b/GUI/StackTraceInfoWidget.ui index 857ef1ba..c8092fc3 100644 --- a/GUI/StackTraceInfoWidget.ui +++ b/GUI/StackTraceInfoWidget.ui @@ -19,7 +19,7 @@ Qt::Horizontal - + @@ -29,11 +29,17 @@ - + + + + Monospace + + + - + @@ -43,7 +49,13 @@ - + + + + Monospace + + + diff --git a/GUI/CustomTableViews/AsciiView.py b/GUI/TableViews/AsciiView.py similarity index 69% rename from GUI/CustomTableViews/AsciiView.py rename to GUI/TableViews/AsciiView.py index 466a0ffb..fd572c13 100644 --- a/GUI/CustomTableViews/AsciiView.py +++ b/GUI/TableViews/AsciiView.py @@ -14,11 +14,16 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ -from GUI.CustomTableViews.HexView import QHexView + +from GUI.TableViews.HexView import QHexView +from GUI.ItemDelegates.HexDelegate import QHexDelegate +from libpince import typedefs class QAsciiView(QHexView): - # data_array is returned from GDB_Engine.hex_dump() def __init__(self, parent=None): super().__init__(parent) - self.horizontalHeader().setDefaultSectionSize(15) + self.write_type = typedefs.VALUE_INDEX.STRING_UTF8 + self.delegate = QHexDelegate(1, ".+") + self.delegate.closeEditor.connect(self.on_editor_close) + self.setItemDelegate(self.delegate) diff --git a/GUI/TableViews/HexView.py b/GUI/TableViews/HexView.py new file mode 100644 index 00000000..fedc9ec9 --- /dev/null +++ b/GUI/TableViews/HexView.py @@ -0,0 +1,86 @@ +""" +Copyright (C) 2016-2017 Korcan Karaokçu + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from PyQt6.QtGui import QKeyEvent, QWheelEvent +from PyQt6.QtWidgets import QTableView, QAbstractItemView +from PyQt6.QtCore import QItemSelectionModel, QModelIndex, Qt +from GUI.ItemDelegates.HexDelegate import QHexDelegate +from GUI.AbstractTableModels.HexModel import QHexModel +from libpince import utils, debugcore, typedefs + + +class QHexView(QTableView): + def __init__(self, parent=None): + super().__init__(parent) + self.setWordWrap(False) + self.horizontalHeader().setVisible(False) + self.verticalHeader().setVisible(False) + self.setStyleSheet("QTableView {background-color: transparent;}") + self.setShowGrid(False) + self.setEditTriggers(QAbstractItemView.EditTrigger.DoubleClicked) + self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) + self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) + self.setAutoScroll(False) + self.write_type = typedefs.VALUE_INDEX.AOB + self.delegate = QHexDelegate() + self.delegate.closeEditor.connect(self.on_editor_close) + self.setItemDelegate(self.delegate) + + def adjust_cell_size(self, char_count: int): + font_metrics = self.fontMetrics() + col_width = font_metrics.horizontalAdvance("F" * char_count) + 4 * char_count + row_height = font_metrics.height() + self.horizontalHeader().setMinimumSectionSize(col_width) + self.horizontalHeader().setDefaultSectionSize(col_width) + self.horizontalHeader().setMaximumSectionSize(col_width) + self.verticalHeader().setMinimumSectionSize(row_height) + self.verticalHeader().setDefaultSectionSize(row_height) + self.verticalHeader().setMaximumSectionSize(row_height) + + def wheelEvent(self, event: QWheelEvent): + event.ignore() + + def keyPressEvent(self, event: QKeyEvent): + if event.key() == Qt.Key.Key_Return and self.state() != QAbstractItemView.State.EditingState: + self.edit(self.currentIndex()) + else: + return super().keyPressEvent(event) + + def selectionCommand(self, index: QModelIndex, event: QKeyEvent): + if event.modifiers() == Qt.KeyboardModifier.ControlModifier: + # Disable multi-selection when Ctrl key is pressed + return QItemSelectionModel.SelectionFlag.ClearAndSelect + else: + return super().selectionCommand(index, event) + + def resize_to_contents(self): + size = self.columnWidth(0) * self.model().columnCount() + self.setMinimumWidth(size) + self.setMaximumWidth(size) + + def on_editor_close(self): + if not self.delegate.editor.isModified(): + return + model: QHexModel = self.model() + cell = self.currentIndex() + index = cell.row() * model.columnCount() + cell.column() + address = utils.modulo_address(model.current_address + index, debugcore.inferior_arch) + data = self.delegate.editor.text() + if self.write_type == typedefs.VALUE_INDEX.AOB: + data = data.upper() + debugcore.write_memory(address, self.write_type, data, False) + model.update_index(index, data) diff --git a/GUI/TextEditDialog.py b/GUI/TextEditDialog.py index 332de62c..32741a5d 100644 --- a/GUI/TextEditDialog.py +++ b/GUI/TextEditDialog.py @@ -1,12 +1,13 @@ -# -*- coding: utf-8 -*- - # Form implementation generated from reading ui file 'TextEditDialog.ui' # -# Created by: PyQt5 UI code generator 5.5.1 +# Created by: PyQt6 UI code generator 6.3.1 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets -from PyQt5 import QtCore, QtGui, QtWidgets class Ui_Dialog(object): def setupUi(self, Dialog): @@ -18,18 +19,17 @@ def setupUi(self, Dialog): self.textEdit.setObjectName("textEdit") self.gridLayout.addWidget(self.textEdit, 0, 0, 1, 1) self.buttonBox = QtWidgets.QDialogButtonBox(Dialog) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) + self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.StandardButton.Cancel|QtWidgets.QDialogButtonBox.StandardButton.Ok) self.buttonBox.setObjectName("buttonBox") self.gridLayout.addWidget(self.buttonBox, 1, 0, 1, 1) self.retranslateUi(Dialog) - self.buttonBox.accepted.connect(Dialog.accept) - self.buttonBox.rejected.connect(Dialog.reject) + self.buttonBox.accepted.connect(Dialog.accept) # type: ignore + self.buttonBox.rejected.connect(Dialog.reject) # type: ignore QtCore.QMetaObject.connectSlotsByName(Dialog) def retranslateUi(self, Dialog): _translate = QtCore.QCoreApplication.translate Dialog.setWindowTitle(_translate("Dialog", "Dialog")) self.textEdit.setPlaceholderText(_translate("Dialog", "Hit Esc to cancel and Ctrl+Enter to accept")) - diff --git a/GUI/TraceInstructionsPromptDialog.py b/GUI/TraceInstructionsPromptDialog.py index 4d0dd8d6..3dfec4c4 100644 --- a/GUI/TraceInstructionsPromptDialog.py +++ b/GUI/TraceInstructionsPromptDialog.py @@ -1,72 +1,61 @@ -# -*- coding: utf-8 -*- - # Form implementation generated from reading ui file 'TraceInstructionsPromptDialog.ui' # -# Created: Tue Jan 10 18:45:42 2017 -# by: PyQt5 UI code generator 5.2.1 +# Created by: PyQt6 UI code generator 6.4.2 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets -from PyQt5 import QtCore, QtGui, QtWidgets class Ui_Dialog(object): def setupUi(self, Dialog): Dialog.setObjectName("Dialog") - Dialog.resize(306, 381) + Dialog.resize(269, 294) self.gridLayout = QtWidgets.QGridLayout(Dialog) self.gridLayout.setObjectName("gridLayout") self.verticalLayout = QtWidgets.QVBoxLayout() self.verticalLayout.setObjectName("verticalLayout") - self.label = QtWidgets.QLabel(Dialog) + self.label = QtWidgets.QLabel(parent=Dialog) self.label.setObjectName("label") self.verticalLayout.addWidget(self.label) - self.lineEdit_MaxTraceCount = QtWidgets.QLineEdit(Dialog) + self.lineEdit_MaxTraceCount = QtWidgets.QLineEdit(parent=Dialog) + self.lineEdit_MaxTraceCount.setText("1000") self.lineEdit_MaxTraceCount.setObjectName("lineEdit_MaxTraceCount") self.verticalLayout.addWidget(self.lineEdit_MaxTraceCount) - self.label_3 = QtWidgets.QLabel(Dialog) + self.label_3 = QtWidgets.QLabel(parent=Dialog) self.label_3.setObjectName("label_3") self.verticalLayout.addWidget(self.label_3) - self.lineEdit_TriggerCondition = QtWidgets.QLineEdit(Dialog) + self.lineEdit_TriggerCondition = QtWidgets.QLineEdit(parent=Dialog) self.lineEdit_TriggerCondition.setObjectName("lineEdit_TriggerCondition") self.verticalLayout.addWidget(self.lineEdit_TriggerCondition) - self.label_2 = QtWidgets.QLabel(Dialog) + self.label_2 = QtWidgets.QLabel(parent=Dialog) self.label_2.setObjectName("label_2") self.verticalLayout.addWidget(self.label_2) - self.lineEdit_StopCondition = QtWidgets.QLineEdit(Dialog) + self.lineEdit_StopCondition = QtWidgets.QLineEdit(parent=Dialog) self.lineEdit_StopCondition.setObjectName("lineEdit_StopCondition") self.verticalLayout.addWidget(self.lineEdit_StopCondition) - self.checkBox_StepOver = QtWidgets.QCheckBox(Dialog) + self.checkBox_StepOver = QtWidgets.QCheckBox(parent=Dialog) self.checkBox_StepOver.setObjectName("checkBox_StepOver") self.verticalLayout.addWidget(self.checkBox_StepOver) - self.checkBox_StopAfterTrace = QtWidgets.QCheckBox(Dialog) + self.checkBox_StopAfterTrace = QtWidgets.QCheckBox(parent=Dialog) self.checkBox_StopAfterTrace.setObjectName("checkBox_StopAfterTrace") self.verticalLayout.addWidget(self.checkBox_StopAfterTrace) - self.checkBox_GeneralRegisters = QtWidgets.QCheckBox(Dialog) - self.checkBox_GeneralRegisters.setChecked(True) - self.checkBox_GeneralRegisters.setObjectName("checkBox_GeneralRegisters") - self.verticalLayout.addWidget(self.checkBox_GeneralRegisters) - self.checkBox_FlagRegisters = QtWidgets.QCheckBox(Dialog) - self.checkBox_FlagRegisters.setChecked(True) - self.checkBox_FlagRegisters.setObjectName("checkBox_FlagRegisters") - self.verticalLayout.addWidget(self.checkBox_FlagRegisters) - self.checkBox_SegmentRegisters = QtWidgets.QCheckBox(Dialog) - self.checkBox_SegmentRegisters.setChecked(True) - self.checkBox_SegmentRegisters.setObjectName("checkBox_SegmentRegisters") - self.verticalLayout.addWidget(self.checkBox_SegmentRegisters) - self.checkBox_FloatRegisters = QtWidgets.QCheckBox(Dialog) - self.checkBox_FloatRegisters.setChecked(True) - self.checkBox_FloatRegisters.setObjectName("checkBox_FloatRegisters") - self.verticalLayout.addWidget(self.checkBox_FloatRegisters) - self.buttonBox = QtWidgets.QDialogButtonBox(Dialog) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) + self.checkBox_CollectRegisters = QtWidgets.QCheckBox(parent=Dialog) + self.checkBox_CollectRegisters.setChecked(True) + self.checkBox_CollectRegisters.setObjectName("checkBox_CollectRegisters") + self.verticalLayout.addWidget(self.checkBox_CollectRegisters) + self.buttonBox = QtWidgets.QDialogButtonBox(parent=Dialog) + self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.StandardButton.Cancel|QtWidgets.QDialogButtonBox.StandardButton.Ok) self.buttonBox.setObjectName("buttonBox") self.verticalLayout.addWidget(self.buttonBox) self.gridLayout.addLayout(self.verticalLayout, 0, 0, 1, 1) self.retranslateUi(Dialog) - self.buttonBox.accepted.connect(Dialog.accept) - self.buttonBox.rejected.connect(Dialog.reject) + self.buttonBox.accepted.connect(Dialog.accept) # type: ignore + self.buttonBox.rejected.connect(Dialog.reject) # type: ignore QtCore.QMetaObject.connectSlotsByName(Dialog) def retranslateUi(self, Dialog): @@ -74,15 +63,10 @@ def retranslateUi(self, Dialog): Dialog.setWindowTitle(_translate("Dialog", "Parameters for tracing")) self.label.setToolTip(_translate("Dialog", "Number of the instructions that\'ll be traced")) self.label.setText(_translate("Dialog", "Max trace count(1 or greater):")) - self.lineEdit_MaxTraceCount.setText(_translate("Dialog", "1000")) self.label_3.setToolTip(_translate("Dialog", "Tracing will start if this condition is met")) self.label_3.setText(_translate("Dialog", "Trigger condition(Optional, gdb expression):")) self.label_2.setToolTip(_translate("Dialog", "Tracing will stop whenever this condition is met")) self.label_2.setText(_translate("Dialog", "Stop condition(Optional, gdb expression):")) self.checkBox_StepOver.setText(_translate("Dialog", "Step over instead of single step")) self.checkBox_StopAfterTrace.setText(_translate("Dialog", "Stop when tracing ends")) - self.checkBox_GeneralRegisters.setText(_translate("Dialog", "Collect general registers")) - self.checkBox_FlagRegisters.setText(_translate("Dialog", "Collect flag registers")) - self.checkBox_SegmentRegisters.setText(_translate("Dialog", "Collect segment registers")) - self.checkBox_FloatRegisters.setText(_translate("Dialog", "Collect float registers")) - + self.checkBox_CollectRegisters.setText(_translate("Dialog", "Collect registers")) diff --git a/GUI/TraceInstructionsPromptDialog.ui b/GUI/TraceInstructionsPromptDialog.ui index 57c6e2c1..57d4de78 100644 --- a/GUI/TraceInstructionsPromptDialog.ui +++ b/GUI/TraceInstructionsPromptDialog.ui @@ -6,8 +6,8 @@ 0 0 - 306 - 381 + 269 + 294 @@ -29,7 +29,7 @@ - 1000 + 1000 @@ -74,39 +74,9 @@ - + - Collect general registers - - - true - - - - - - - Collect flag registers - - - true - - - - - - - Collect segment registers - - - true - - - - - - - Collect float registers + Collect registers true diff --git a/GUI/TraceInstructionsWaitWidget.py b/GUI/TraceInstructionsWaitWidget.py index c3dfc3cf..d379c367 100644 --- a/GUI/TraceInstructionsWaitWidget.py +++ b/GUI/TraceInstructionsWaitWidget.py @@ -1,39 +1,38 @@ -# -*- coding: utf-8 -*- - # Form implementation generated from reading ui file 'TraceInstructionsWaitWidget.ui' # -# Created: Sun Dec 25 15:05:53 2016 -# by: PyQt5 UI code generator 5.2.1 +# Created by: PyQt6 UI code generator 6.4.2 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets -from PyQt5 import QtCore, QtGui, QtWidgets class Ui_Form(object): def setupUi(self, Form): Form.setObjectName("Form") - Form.resize(194, 91) + Form.resize(194, 93) self.gridLayout = QtWidgets.QGridLayout(Form) self.gridLayout.setObjectName("gridLayout") self.verticalLayout = QtWidgets.QVBoxLayout() self.verticalLayout.setObjectName("verticalLayout") - self.label_Animated = QtWidgets.QLabel(Form) + self.label_Animated = QtWidgets.QLabel(parent=Form) self.label_Animated.setText("") self.label_Animated.setObjectName("label_Animated") self.verticalLayout.addWidget(self.label_Animated) - self.label_StatusText = QtWidgets.QLabel(Form) + self.label_StatusText = QtWidgets.QLabel(parent=Form) self.label_StatusText.setText("") - self.label_StatusText.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse) self.label_StatusText.setObjectName("label_StatusText") self.verticalLayout.addWidget(self.label_StatusText) self.horizontalLayout = QtWidgets.QHBoxLayout() self.horizontalLayout.setObjectName("horizontalLayout") - spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) self.horizontalLayout.addItem(spacerItem) - self.pushButton_Cancel = QtWidgets.QPushButton(Form) + self.pushButton_Cancel = QtWidgets.QPushButton(parent=Form) self.pushButton_Cancel.setObjectName("pushButton_Cancel") self.horizontalLayout.addWidget(self.pushButton_Cancel) - spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) self.horizontalLayout.addItem(spacerItem1) self.verticalLayout.addLayout(self.horizontalLayout) self.gridLayout.addLayout(self.verticalLayout, 0, 0, 1, 1) @@ -43,6 +42,5 @@ def setupUi(self, Form): def retranslateUi(self, Form): _translate = QtCore.QCoreApplication.translate - Form.setWindowTitle(_translate("Form", "Form")) + Form.setWindowTitle(_translate("Form", "Tracer Status")) self.pushButton_Cancel.setText(_translate("Form", "Cancel")) - diff --git a/GUI/TraceInstructionsWaitWidget.ui b/GUI/TraceInstructionsWaitWidget.ui index 775bd110..c07e5474 100644 --- a/GUI/TraceInstructionsWaitWidget.ui +++ b/GUI/TraceInstructionsWaitWidget.ui @@ -7,11 +7,11 @@ 0 0 194 - 91 + 93 - Form + Tracer Status @@ -28,9 +28,6 @@ - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - diff --git a/GUI/TraceInstructionsWindow.py b/GUI/TraceInstructionsWindow.py index 59ac231d..c5b26dad 100644 --- a/GUI/TraceInstructionsWindow.py +++ b/GUI/TraceInstructionsWindow.py @@ -1,48 +1,54 @@ -# -*- coding: utf-8 -*- - # Form implementation generated from reading ui file 'TraceInstructionsWindow.ui' # -# Created: Mon Jan 16 19:23:48 2017 -# by: PyQt5 UI code generator 5.2.1 +# Created by: PyQt6 UI code generator 6.4.2 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets -from PyQt5 import QtCore, QtGui, QtWidgets class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(704, 545) - self.centralwidget = QtWidgets.QWidget(MainWindow) + self.centralwidget = QtWidgets.QWidget(parent=MainWindow) self.centralwidget.setObjectName("centralwidget") self.gridLayout = QtWidgets.QGridLayout(self.centralwidget) self.gridLayout.setObjectName("gridLayout") - self.splitter = QtWidgets.QSplitter(self.centralwidget) - self.splitter.setOrientation(QtCore.Qt.Horizontal) + self.splitter = QtWidgets.QSplitter(parent=self.centralwidget) + self.splitter.setOrientation(QtCore.Qt.Orientation.Horizontal) self.splitter.setObjectName("splitter") - self.treeWidget_InstructionInfo = QtWidgets.QTreeWidget(self.splitter) - self.treeWidget_InstructionInfo.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) + self.treeWidget_InstructionInfo = QtWidgets.QTreeWidget(parent=self.splitter) + font = QtGui.QFont() + font.setFamily("Monospace") + self.treeWidget_InstructionInfo.setFont(font) + self.treeWidget_InstructionInfo.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers) self.treeWidget_InstructionInfo.setObjectName("treeWidget_InstructionInfo") self.treeWidget_InstructionInfo.headerItem().setText(0, "1") self.treeWidget_InstructionInfo.header().setVisible(False) - self.textBrowser_RegisterInfo = QtWidgets.QTextBrowser(self.splitter) + self.textBrowser_RegisterInfo = QtWidgets.QTextBrowser(parent=self.splitter) + font = QtGui.QFont() + font.setFamily("Monospace") + self.textBrowser_RegisterInfo.setFont(font) self.textBrowser_RegisterInfo.setObjectName("textBrowser_RegisterInfo") self.gridLayout.addWidget(self.splitter, 0, 0, 1, 1) MainWindow.setCentralWidget(self.centralwidget) - self.menubar = QtWidgets.QMenuBar(MainWindow) + self.menubar = QtWidgets.QMenuBar(parent=MainWindow) self.menubar.setGeometry(QtCore.QRect(0, 0, 704, 22)) self.menubar.setObjectName("menubar") - self.menuFile = QtWidgets.QMenu(self.menubar) + self.menuFile = QtWidgets.QMenu(parent=self.menubar) self.menuFile.setObjectName("menuFile") MainWindow.setMenuBar(self.menubar) - self.statusbar = QtWidgets.QStatusBar(MainWindow) + self.statusbar = QtWidgets.QStatusBar(parent=MainWindow) self.statusbar.setObjectName("statusbar") MainWindow.setStatusBar(self.statusbar) - self.actionOpen = QtWidgets.QAction(MainWindow) + self.actionOpen = QtGui.QAction(parent=MainWindow) self.actionOpen.setObjectName("actionOpen") - self.actionSave = QtWidgets.QAction(MainWindow) + self.actionSave = QtGui.QAction(parent=MainWindow) self.actionSave.setObjectName("actionSave") - self.actionSave_as_a_text_file = QtWidgets.QAction(MainWindow) + self.actionSave_as_a_text_file = QtGui.QAction(parent=MainWindow) self.actionSave_as_a_text_file.setObjectName("actionSave_as_a_text_file") self.menuFile.addAction(self.actionOpen) self.menuFile.addAction(self.actionSave) @@ -58,4 +64,3 @@ def retranslateUi(self, MainWindow): self.actionOpen.setText(_translate("MainWindow", "Open")) self.actionSave.setText(_translate("MainWindow", "Save")) self.actionSave_as_a_text_file.setText(_translate("MainWindow", "Save as a text file")) - diff --git a/GUI/TraceInstructionsWindow.ui b/GUI/TraceInstructionsWindow.ui index 6d6092bb..76fddbdf 100644 --- a/GUI/TraceInstructionsWindow.ui +++ b/GUI/TraceInstructionsWindow.ui @@ -21,6 +21,11 @@ Qt::Horizontal + + + Monospace + + QAbstractItemView::NoEditTriggers @@ -33,7 +38,13 @@ - + + + + Monospace + + + diff --git a/GUI/TrackBreakpointWidget.py b/GUI/TrackBreakpointWidget.py index 26dd6cd1..1d27a685 100644 --- a/GUI/TrackBreakpointWidget.py +++ b/GUI/TrackBreakpointWidget.py @@ -1,12 +1,13 @@ -# -*- coding: utf-8 -*- - # Form implementation generated from reading ui file 'TrackBreakpointWidget.ui' # -# Created by: PyQt5 UI code generator 5.5.1 +# Created by: PyQt6 UI code generator 6.4.2 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets -from PyQt5 import QtCore, QtGui, QtWidgets class Ui_Form(object): def setupUi(self, Form): @@ -14,23 +15,12 @@ def setupUi(self, Form): Form.resize(549, 437) self.gridLayout = QtWidgets.QGridLayout(Form) self.gridLayout.setObjectName("gridLayout") - self.horizontalLayout = QtWidgets.QHBoxLayout() - self.horizontalLayout.setObjectName("horizontalLayout") - self.pushButton_Stop = QtWidgets.QPushButton(Form) - self.pushButton_Stop.setObjectName("pushButton_Stop") - self.horizontalLayout.addWidget(self.pushButton_Stop) - spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.horizontalLayout.addItem(spacerItem) - self.comboBox_ValueType = QtWidgets.QComboBox(Form) - self.comboBox_ValueType.setToolTip("") - self.comboBox_ValueType.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents) - self.comboBox_ValueType.setObjectName("comboBox_ValueType") - self.horizontalLayout.addWidget(self.comboBox_ValueType) - self.gridLayout.addLayout(self.horizontalLayout, 3, 0, 1, 1) - self.tableWidget_TrackInfo = QtWidgets.QTableWidget(Form) - self.tableWidget_TrackInfo.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) - self.tableWidget_TrackInfo.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) - self.tableWidget_TrackInfo.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) + self.tableWidget_TrackInfo = QtWidgets.QTableWidget(parent=Form) + self.tableWidget_TrackInfo.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers) + self.tableWidget_TrackInfo.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.SingleSelection) + self.tableWidget_TrackInfo.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows) + self.tableWidget_TrackInfo.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollMode.ScrollPerPixel) + self.tableWidget_TrackInfo.setWordWrap(False) self.tableWidget_TrackInfo.setObjectName("tableWidget_TrackInfo") self.tableWidget_TrackInfo.setColumnCount(4) self.tableWidget_TrackInfo.setRowCount(0) @@ -47,15 +37,19 @@ def setupUi(self, Form): self.tableWidget_TrackInfo.verticalHeader().setDefaultSectionSize(16) self.tableWidget_TrackInfo.verticalHeader().setMinimumSectionSize(16) self.gridLayout.addWidget(self.tableWidget_TrackInfo, 0, 0, 1, 1) - self.label_Info = QtWidgets.QLabel(Form) - self.label_Info.setText("") - self.label_Info.setAlignment(QtCore.Qt.AlignCenter) - self.label_Info.setObjectName("label_Info") - self.gridLayout.addWidget(self.label_Info, 1, 0, 1, 1) - self.label_AdditionalInfo = QtWidgets.QLabel(Form) - self.label_AdditionalInfo.setAlignment(QtCore.Qt.AlignCenter) - self.label_AdditionalInfo.setObjectName("label_AdditionalInfo") - self.gridLayout.addWidget(self.label_AdditionalInfo, 2, 0, 1, 1) + self.horizontalLayout = QtWidgets.QHBoxLayout() + self.horizontalLayout.setObjectName("horizontalLayout") + self.pushButton_Stop = QtWidgets.QPushButton(parent=Form) + self.pushButton_Stop.setObjectName("pushButton_Stop") + self.horizontalLayout.addWidget(self.pushButton_Stop) + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) + self.horizontalLayout.addItem(spacerItem) + self.comboBox_ValueType = QtWidgets.QComboBox(parent=Form) + self.comboBox_ValueType.setToolTip("") + self.comboBox_ValueType.setSizeAdjustPolicy(QtWidgets.QComboBox.SizeAdjustPolicy.AdjustToContents) + self.comboBox_ValueType.setObjectName("comboBox_ValueType") + self.horizontalLayout.addWidget(self.comboBox_ValueType) + self.gridLayout.addLayout(self.horizontalLayout, 1, 0, 1, 1) self.retranslateUi(Form) self.comboBox_ValueType.setCurrentIndex(-1) @@ -64,7 +58,6 @@ def setupUi(self, Form): def retranslateUi(self, Form): _translate = QtCore.QCoreApplication.translate Form.setWindowTitle(_translate("Form", "Form")) - self.pushButton_Stop.setText(_translate("Form", "Stop")) item = self.tableWidget_TrackInfo.horizontalHeaderItem(0) item.setText(_translate("Form", "Count")) item = self.tableWidget_TrackInfo.horizontalHeaderItem(1) @@ -73,5 +66,4 @@ def retranslateUi(self, Form): item.setText(_translate("Form", "Value")) item = self.tableWidget_TrackInfo.horizontalHeaderItem(3) item.setText(_translate("Form", "Source")) - self.label_AdditionalInfo.setText(_translate("Form", "Try changing combobox index if the \'Value\' part of table still isn\'t updated")) - + self.pushButton_Stop.setText(_translate("Form", "Stop")) diff --git a/GUI/TrackBreakpointWidget.ui b/GUI/TrackBreakpointWidget.ui index 17401600..c525bf20 100644 --- a/GUI/TrackBreakpointWidget.ui +++ b/GUI/TrackBreakpointWidget.ui @@ -14,43 +14,6 @@ Form - - - - - - Stop - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - -1 - - - QComboBox::AdjustToContents - - - - - @@ -62,16 +25,22 @@ QAbstractItemView::SelectRows + + QAbstractItemView::ScrollPerPixel + + + false + true false - + 16 - + 16 @@ -97,24 +66,41 @@ - - - - - - Qt::AlignCenter - - - - - - - Try changing combobox index if the 'Value' part of table still isn't updated - - - Qt::AlignCenter - - + + + + + Stop + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + -1 + + + QComboBox::AdjustToContents + + + + diff --git a/GUI/TrackSelectorDialog.py b/GUI/TrackSelectorDialog.py new file mode 100644 index 00000000..56e13bc7 --- /dev/null +++ b/GUI/TrackSelectorDialog.py @@ -0,0 +1,39 @@ +# Form implementation generated from reading ui file 'TrackSelectorDialog.ui' +# +# Created by: PyQt6 UI code generator 6.4.0 +# +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets + + +class Ui_Dialog(object): + def setupUi(self, Dialog): + Dialog.setObjectName("Dialog") + Dialog.resize(165, 99) + Dialog.setWindowTitle("") + self.gridLayout = QtWidgets.QGridLayout(Dialog) + self.gridLayout.setObjectName("gridLayout") + self.verticalLayout = QtWidgets.QVBoxLayout() + self.verticalLayout.setObjectName("verticalLayout") + self.label = QtWidgets.QLabel(Dialog) + self.label.setObjectName("label") + self.verticalLayout.addWidget(self.label) + self.pushButton_Pointer = QtWidgets.QPushButton(Dialog) + self.pushButton_Pointer.setObjectName("pushButton_Pointer") + self.verticalLayout.addWidget(self.pushButton_Pointer) + self.pushButton_Pointed = QtWidgets.QPushButton(Dialog) + self.pushButton_Pointed.setObjectName("pushButton_Pointed") + self.verticalLayout.addWidget(self.pushButton_Pointed) + self.gridLayout.addLayout(self.verticalLayout, 0, 0, 1, 1) + + self.retranslateUi(Dialog) + QtCore.QMetaObject.connectSlotsByName(Dialog) + + def retranslateUi(self, Dialog): + _translate = QtCore.QCoreApplication.translate + self.label.setText(_translate("Dialog", "Select an address to track")) + self.pushButton_Pointer.setText(_translate("Dialog", "Pointer")) + self.pushButton_Pointed.setText(_translate("Dialog", "Pointed Address")) diff --git a/GUI/TrackSelectorDialog.ui b/GUI/TrackSelectorDialog.ui new file mode 100644 index 00000000..a0067453 --- /dev/null +++ b/GUI/TrackSelectorDialog.ui @@ -0,0 +1,46 @@ + + + Dialog + + + + 0 + 0 + 165 + 99 + + + + + + + + + + + + Select an address to track + + + + + + + Pointer + + + + + + + Pointed Address + + + + + + + + + + diff --git a/GUI/TrackWatchpointWidget.py b/GUI/TrackWatchpointWidget.py index 7791008d..8f6f03f3 100644 --- a/GUI/TrackWatchpointWidget.py +++ b/GUI/TrackWatchpointWidget.py @@ -1,13 +1,13 @@ -# -*- coding: utf-8 -*- - # Form implementation generated from reading ui file 'TrackWatchpointWidget.ui' # -# Created: Fri Dec 2 21:30:54 2016 -# by: PyQt5 UI code generator 5.2.1 +# Created by: PyQt6 UI code generator 6.4.2 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets -from PyQt5 import QtCore, QtGui, QtWidgets class Ui_Form(object): def setupUi(self, Form): @@ -15,21 +15,25 @@ def setupUi(self, Form): Form.resize(530, 493) self.gridLayout = QtWidgets.QGridLayout(Form) self.gridLayout.setObjectName("gridLayout") - self.splitter_2 = QtWidgets.QSplitter(Form) - self.splitter_2.setOrientation(QtCore.Qt.Vertical) + self.splitter_2 = QtWidgets.QSplitter(parent=Form) + self.splitter_2.setOrientation(QtCore.Qt.Orientation.Vertical) self.splitter_2.setObjectName("splitter_2") - self.splitter = QtWidgets.QSplitter(self.splitter_2) - self.splitter.setOrientation(QtCore.Qt.Horizontal) + self.splitter = QtWidgets.QSplitter(parent=self.splitter_2) + self.splitter.setOrientation(QtCore.Qt.Orientation.Horizontal) self.splitter.setObjectName("splitter") - self.layoutWidget = QtWidgets.QWidget(self.splitter) + self.layoutWidget = QtWidgets.QWidget(parent=self.splitter) self.layoutWidget.setObjectName("layoutWidget") self.verticalLayout = QtWidgets.QVBoxLayout(self.layoutWidget) self.verticalLayout.setContentsMargins(0, 0, 0, 0) self.verticalLayout.setObjectName("verticalLayout") - self.tableWidget_Opcodes = QtWidgets.QTableWidget(self.layoutWidget) - self.tableWidget_Opcodes.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) - self.tableWidget_Opcodes.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) - self.tableWidget_Opcodes.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) + self.tableWidget_Opcodes = QtWidgets.QTableWidget(parent=self.layoutWidget) + font = QtGui.QFont() + font.setFamily("Monospace") + self.tableWidget_Opcodes.setFont(font) + self.tableWidget_Opcodes.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers) + self.tableWidget_Opcodes.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.SingleSelection) + self.tableWidget_Opcodes.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows) + self.tableWidget_Opcodes.setWordWrap(False) self.tableWidget_Opcodes.setObjectName("tableWidget_Opcodes") self.tableWidget_Opcodes.setColumnCount(2) self.tableWidget_Opcodes.setRowCount(0) @@ -44,16 +48,22 @@ def setupUi(self, Form): self.verticalLayout.addWidget(self.tableWidget_Opcodes) self.horizontalLayout = QtWidgets.QHBoxLayout() self.horizontalLayout.setObjectName("horizontalLayout") - self.pushButton_Refresh = QtWidgets.QPushButton(self.layoutWidget) + self.pushButton_Refresh = QtWidgets.QPushButton(parent=self.layoutWidget) self.pushButton_Refresh.setObjectName("pushButton_Refresh") self.horizontalLayout.addWidget(self.pushButton_Refresh) - self.pushButton_Stop = QtWidgets.QPushButton(self.layoutWidget) + self.pushButton_Stop = QtWidgets.QPushButton(parent=self.layoutWidget) self.pushButton_Stop.setObjectName("pushButton_Stop") self.horizontalLayout.addWidget(self.pushButton_Stop) self.verticalLayout.addLayout(self.horizontalLayout) - self.textBrowser_Info = QtWidgets.QTextBrowser(self.splitter) + self.textBrowser_Info = QtWidgets.QTextBrowser(parent=self.splitter) + font = QtGui.QFont() + font.setFamily("Monospace") + self.textBrowser_Info.setFont(font) self.textBrowser_Info.setObjectName("textBrowser_Info") - self.textBrowser_Disassemble = QtWidgets.QTextBrowser(self.splitter_2) + self.textBrowser_Disassemble = QtWidgets.QTextBrowser(parent=self.splitter_2) + font = QtGui.QFont() + font.setFamily("Monospace") + self.textBrowser_Disassemble.setFont(font) self.textBrowser_Disassemble.setObjectName("textBrowser_Disassemble") self.gridLayout.addWidget(self.splitter_2, 0, 0, 1, 1) @@ -69,4 +79,3 @@ def retranslateUi(self, Form): item.setText(_translate("Form", "Address")) self.pushButton_Refresh.setText(_translate("Form", "Refresh")) self.pushButton_Stop.setText(_translate("Form", "Stop")) - diff --git a/GUI/TrackWatchpointWidget.ui b/GUI/TrackWatchpointWidget.ui index 49e8ad96..d8b2a8a3 100644 --- a/GUI/TrackWatchpointWidget.ui +++ b/GUI/TrackWatchpointWidget.ui @@ -27,6 +27,11 @@ + + + Monospace + + QAbstractItemView::NoEditTriggers @@ -36,16 +41,19 @@ QAbstractItemView::SelectRows + + false + true false - + 16 - + 16 @@ -80,9 +88,21 @@ - + + + + Monospace + + + + + + + + Monospace + + - diff --git a/GUI/TreeWidgets/AddressTree.py b/GUI/TreeWidgets/AddressTree.py new file mode 100644 index 00000000..c355507d --- /dev/null +++ b/GUI/TreeWidgets/AddressTree.py @@ -0,0 +1,29 @@ +""" +Copyright (C) Korcan Karaokçu + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from PyQt6.QtWidgets import QTreeWidget + + +class QAddressTree(QTreeWidget): + def __init__(self, parent=None): + super().__init__(parent) + + # TODO: If the auto-update is enabled, address table will be updated with a delay after a drop event + # This probably happens because of the QTimers. It's not critical but a fix would be nice + def dropEvent(self, event): + self.parent().parent().update_address_table() + super().dropEvent(event) diff --git a/GUI/Utils/__init__.py b/GUI/Utils/__init__.py new file mode 100644 index 00000000..9bd5e10d --- /dev/null +++ b/GUI/Utils/__init__.py @@ -0,0 +1,5 @@ +import keyboard +from . import keyboard_hack + +# replace keyboard.parse_hotkey() with fix for literal '+' in hotkey strings +keyboard.parse_hotkey = keyboard_hack.parse_hotkey diff --git a/GUI/Utils/guiutils.py b/GUI/Utils/guiutils.py new file mode 100644 index 00000000..3ea5b8a2 --- /dev/null +++ b/GUI/Utils/guiutils.py @@ -0,0 +1,268 @@ +# -*- coding: utf-8 -*- +""" +Copyright (C) Korcan Karaokçu + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" +from PyQt6.QtWidgets import ( + QWidget, + QScrollBar, + QTableWidget, + QTableWidgetItem, + QTreeWidget, + QTreeWidgetItem, + QListWidget, + QListWidgetItem, + QComboBox, + QMenu, + QLayout, +) +from PyQt6.QtCore import QObject, QRegularExpression +from PyQt6.QtGui import QShortcut, QRegularExpressionValidator +from libpince import utils, typedefs, regexes +from tr.tr import TranslationConstants as tr +from typing import overload + +validator_map: dict[str, QRegularExpressionValidator | None] = { + "int": QRegularExpressionValidator(QRegularExpression(regexes.decimal_number.pattern)), # integers + "int_hex": QRegularExpressionValidator(QRegularExpression(regexes.hex_number_gui.pattern)), # hexadecimals + "float": QRegularExpressionValidator(QRegularExpression(regexes.float_number.pattern)), # floats + "bytearray": QRegularExpressionValidator(QRegularExpression(regexes.bytearray_input.pattern)), # array of bytes + "string": None, +} + + +def get_icons_directory(): + """Gets the directory of the icons + + Returns: + str: Path to the icons directory + """ + return utils.get_script_directory() + "/media/icons" + + +def center(window: QWidget): + """Center the given window to desktop + + Args: + window (QWidget): The window that'll be centered to desktop + """ + window.frameGeometry().moveCenter(window.screen().availableGeometry().center()) + + +def center_to_parent(window: QWidget): + """Center the given window to its parent + + Args: + window (QWidget): The window that'll be centered to its parent + """ + parent: QWidget = window.parent() + window.move(parent.frameGeometry().center() - window.rect().center()) + + +def center_scroll_bar(scrollbar: QScrollBar): + """Center the given scrollbar + + Args: + scrollbar (QScrollbar): Self-explanatory + """ + maximum = scrollbar.maximum() + minimum = scrollbar.minimum() + scrollbar.setValue((maximum + minimum) // 2) + + +def resize_to_contents(tablewidget: QTableWidget): + """Resizes the columns of the given QTableWidget to its contents + This also fixes the stretch problem of the last column + + Args: + tablewidget (QTableWidget): Self-explanatory + """ + tablewidget.resizeColumnsToContents() + default_size = tablewidget.horizontalHeader().defaultSectionSize() + tablewidget.horizontalHeader().resizeSection(tablewidget.columnCount() - 1, default_size) + + +def fill_value_combobox(combobox: QComboBox, current_index: int = typedefs.VALUE_INDEX.INT32): + """Fills the given QComboBox with value_index strings + + Args: + combobox (QComboBox): The combobox that'll be filled + current_index (int): Can be a member of typedefs.VALUE_INDEX + """ + for key in typedefs.index_to_text_dict: + combobox.addItem(typedefs.index_to_text_dict[key]) + combobox.setCurrentIndex(current_index) + + +def fill_endianness_combobox(combobox: QComboBox, current_index: int = typedefs.ENDIANNESS.HOST): + """Fills the given QComboBox with endianness strings + + Args: + combobox (QComboBox): The combobox that'll be filled + current_index (int): Can be a member of typedefs.ENDIANNESS + """ + endianness_text = [ + (typedefs.ENDIANNESS.HOST, tr.HOST), + (typedefs.ENDIANNESS.LITTLE, tr.LITTLE), + (typedefs.ENDIANNESS.BIG, tr.BIG), + ] + for endian, text in endianness_text: + combobox.addItem(text, endian) + combobox.setCurrentIndex(current_index) + + +def get_current_row(tablewidget: QTableWidget): + r"""Returns the currently selected row index for the given QTableWidget + If you try to use only selectionModel().currentIndex().row() for this purpose, you'll get the last selected row even + if it was unselected afterwards. This is why this function exists, it checks the selection state before returning + the selected row + + Args: + tablewidget (QTableWidget): Self-explanatory + + Returns: + int: Currently selected row. Returns -1 if nothing is selected + + Note: + This function doesn't work properly when used within signals such as currentItemChanged, currentIndexChanged, + currentChanged and currentRowChanged. Use the row, item, QModelIndex or whatever the signal provides instead. + This bug occurs because those signals only update the changed row, not the selectionModel. This causes + selectionModel().selectedRows() to return None and this function to behave improperly + + For developers: You can use the regex \.current.*\.connect to search signals if a cleanup is needed + """ + if tablewidget.selectionModel().selectedRows(): + return tablewidget.selectionModel().currentIndex().row() + return -1 + + +@overload +def get_current_item(listwidget: QListWidget) -> QListWidgetItem | None: ... + + +@overload +def get_current_item(tablewidget: QTableWidget) -> QTableWidgetItem | None: ... + + +@overload +def get_current_item(treewidget: QTreeWidget) -> QTreeWidgetItem | None: ... + + +def get_current_item(widget: QListWidget | QTableWidget | QTreeWidget): + r"""Returns the currently selected item for the given widget + If you try to use only selectionModel().currentItem() for this purpose, you'll get the last selected item even + if it was unselected afterwards. This is why this function exists, it checks the selection state before returning + the selected item. Unlike get_current_row, this function can be used with QTreeWidget + + Args: + widget (QListWidget | QTableWidget | QTreeWidget): Self-explanatory + + Returns: + Any: Currently selected item. Returns None if nothing is selected + + Note: + This function doesn't work properly when used within signals such as currentItemChanged, currentIndexChanged, + currentChanged and currentRowChanged. Use the row, item, QModelIndex or whatever the signal provides instead. + This bug occurs because those signals only update the changed row, not the selectionModel. This causes + selectionModel().selectedRows() to return None and this function to behave improperly + + For developers: You can use the regex \.current.*\.connect to search signals if a cleanup is needed + """ + if widget.selectionModel().selectedRows(): + return widget.currentItem() + + +def delete_menu_entries(menu: QMenu, QAction_list: list): + """Deletes given QActions from the QMenu recursively and cleans up the remaining redundant separators and menus + Doesn't support menus that includes types other than actions, separators and menus + + Args: + menu (QMenu): Self-explanatory + QAction_list (list): List of QActions. Leave blank if you just want to clean the redundant separators up + """ + + def remove_entries(menu: QMenu): + for action in menu.actions(): + try: + QAction_list.index(action) + except ValueError: + pass + else: + menu.removeAction(action) + + def clean_entries(menu: QMenu): + for action in menu.actions(): + if action.isSeparator(): + actions = menu.actions() + current_index = actions.index(action) + if ( + len(actions) == 1 + or (current_index == 0 and actions[1].isSeparator()) + or (current_index == -1 and actions[-2].isSeparator()) + or (actions[current_index - 1].isSeparator() and actions[current_index + 1].isSeparator()) + ): + menu.removeAction(action) + + remove_entries(menu) + clean_entries(menu) + + +# TODO: This is a really bad design pattern, remove this function after moving classes to their own files +def search_parents_by_function(qt_object: QObject, func_name: str): + """Search for func_name in the parents of given QObject. Once function is found, parent that possesses func_name + is returned + + Args: + qt_object (QObject): The object that'll be searched for it's parents + func_name (str): The name of the function that'll be searched + """ + while qt_object is not None: + qt_object = qt_object.parent() + if func_name in dir(qt_object): + return qt_object + + +def get_layout_widgets(layout: QLayout): + """Returns the widgets of a QLayout as a list + + Args: + layout: Self-explanatory + + Returns: + list: A list that contains the widgets of the given layout + """ + return [layout.itemAt(x).widget() for x in range(layout.count())] + + +def contains_reference_mark(string: str): + """Checks if given string contains the reference mark + + Args: + string (str): String that'll be checked for the reference mark + + Returns: + bool: True if given string contains the reference mark, False otherwise + """ + return True if regexes.reference_mark.search(string) else False + + +def append_shortcut_to_tooltip(qt_object: QObject, shortcut: QShortcut): + """Appends key string of the given QShortcut to the toolTip of the given QObject + + Args: + qt_object (QObject): Self-explanatory + shortcut (QShortcut): Self-explanatory + """ + qt_object.setToolTip(qt_object.toolTip() + "[" + shortcut.key().toString() + "]") diff --git a/GUI/Utils/keyboard_hack.py b/GUI/Utils/keyboard_hack.py new file mode 100644 index 00000000..0dbee1a4 --- /dev/null +++ b/GUI/Utils/keyboard_hack.py @@ -0,0 +1,43 @@ +from keyboard import key_to_scan_codes +import re as _re + +# copied from keyboard.__init__.py +_is_str = lambda x: isinstance(x, str) +_is_number = lambda x: isinstance(x, int) +_is_list = lambda x: isinstance(x, (list, tuple)) + + +def parse_hotkey(hotkey): + ## function to replace keyboard.parse_hotkey() with fix for literal '+' in hotkey strings + """ + Parses a user-provided hotkey into nested tuples representing the + parsed structure, with the bottom values being lists of scan codes. + Also accepts raw scan codes, which are then wrapped in the required + number of nestings. + + Example: + + parse_hotkey("alt+shift+a, alt+b, c") + # Keys: ^~^ ^~~~^ ^ ^~^ ^ ^ + # Steps: ^~~~~~~~~~^ ^~~~^ ^ + + # ((alt_codes, shift_codes, a_codes), (alt_codes, b_codes), (c_codes,)) + """ + if _is_number(hotkey) or len(hotkey) == 1: + scan_codes = key_to_scan_codes(hotkey) + step = (scan_codes,) + steps = (step,) + return steps + elif _is_list(hotkey): + if not any(map(_is_list, hotkey)): + step = tuple(key_to_scan_codes(k) for k in hotkey) + steps = (step,) + return steps + return hotkey + + steps = [] + # since we dont have spaces in hotkey strings, we can ignore whitespace in the regex + for step in _re.split(r"(? self.max_limit: - return QValidator.Invalid, p_str, p_int - return QValidator.Acceptable, p_str, p_int + return QValidator.State.Invalid, p_str, p_int + return QValidator.State.Acceptable, p_str, p_int diff --git a/Notes.txt b/Notes.txt deleted file mode 100644 index 79170a71..00000000 --- a/Notes.txt +++ /dev/null @@ -1,54 +0,0 @@ -Code Relocations: - --libpince - GuiUtils.py is used to contain GUI related utility functions - SysUtils.py is used to contain all useful but non-GDB related libraries such as psutil - GDB_Engine.py is used to contain all GDB related functions - type_defs.py is used to contain all static&shared variable definitions - All codes that'll be injected to the inferior go to the folder "Injection" - /gdb_python_script/ contains all the python scripts that'll invoked from the gdb - PINCE.py is used to interract with GUI and other utility libraries - /media/ contains all the images, icons, logos n' stuff -**Don't touch anything in GUI folder, it contains auto-generated codes created by pyuic - -27/3/2017 - All GUI classes that will be instanced multiple times must contain these code blocks to prevent getting removed by garbage collector: - - global instances - instances.append(self) - - def closeEvent(self, QCloseEvent): - global instances - instances.remove(self) - -If you need to only create one instance of a GUI class, use this instead to create the instance: - - try: - self.window.show() - except AttributeError: - self.window = WindowForm(self) # self parameter is optional - self.window.show() - self.window.activateWindow() - -If you need to pass self as a parameter, please don't use 'super().__init__(parent=parent)' in the child class, it makes Qt hide the child window. Use this in the child instead: - super().__init__() - self.parent = lambda: parent # A quick hack to make other functions see the correct parent(). But Qt won't see it, so there'll be no bugs - -28/8/2018 - All QMessageBoxes that's called from outside of their classes(via parent() etc.) must use 'QApplication.focusWidget()' instead of 'self' in their first parameter. Refer to issue #57 for more information - -14/11/2018 - All logo requests should be posted in media/logo/your_username. Instead of opening a new issue, PR your logo files to that folder. Your PR must include at least one png file named pince_small, pince_medium or pince_big, according to its size. Optionally, you can also post your design files -So, a minimal PR will look like this: - -/media/logo/your_username/pince_big.png - -pince_big is interchangeable with pince_medium and pince_small -A full PR will look like this: - -/media/logo/your_username/pince_big.png -/media/logo/your_username/pince_medium.png -/media/logo/your_username/pince_small.png -/media/logo/your_username/pince_big.ai -/media/logo/your_username/pince_medium.ai -/media/logo/your_username/pince_small.ai - -It doesn't have to be ai extension, I just picked Adobe Illustrator for the example above - -23/11/2018 - Don't use get_current_item or get_current_row within currentItemChanged or currentChanged signals. Qt doesn't update selected rows on first currentChanged or currentItemChanged calls \ No newline at end of file diff --git a/PINCE.py b/PINCE.py index b7e3ae87..ff3916d0 100755 --- a/PINCE.py +++ b/PINCE.py @@ -18,31 +18,103 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ -from PyQt5.QtGui import QIcon, QMovie, QPixmap, QCursor, QKeySequence, QColor, QTextCharFormat, QBrush, QTextCursor, \ - QKeyEvent, QRegExpValidator -from PyQt5.QtWidgets import QApplication, QMainWindow, QTableWidgetItem, QMessageBox, QDialog, QWidget, \ - QShortcut, QKeySequenceEdit, QTabWidget, QMenu, QFileDialog, QAbstractItemView, QTreeWidgetItem, \ - QTreeWidgetItemIterator, QCompleter, QLabel, QLineEdit, QComboBox, QDialogButtonBox -from PyQt5.QtCore import Qt, QThread, pyqtSignal, QSize, QByteArray, QSettings, QEvent, \ - QItemSelectionModel, QTimer, QModelIndex, QStringListModel, QRegExp, QRunnable, QThreadPool, pyqtSlot +import gi + +import GUI.Settings.settings as settings +from GUI.Settings.hotkeys import Hotkeys + +# This fixes GTK version mismatch issues and crashes on gnome +# See #153 and #159 for more information +# This line can be deleted when GTK 4.0 properly runs on all supported systems +gi.require_version("Gtk", "3.0") + +from tr.tr import TranslationConstants as tr +from tr.tr import language_list, get_locale + +from PyQt6.QtGui import ( + QIcon, + QMovie, + QPixmap, + QCursor, + QKeySequence, + QColor, + QContextMenuEvent, + QBrush, + QTextCursor, + QShortcut, + QColorConstants, + QStandardItemModel, + QStandardItem, + QCloseEvent, + QKeyEvent, + QMouseEvent, +) +from PyQt6.QtWidgets import ( + QApplication, + QMainWindow, + QTableWidgetItem, + QMessageBox, + QDialog, + QWidget, + QTabWidget, + QMenu, + QFileDialog, + QAbstractItemView, + QTreeWidgetItem, + QTreeWidgetItemIterator, + QCompleter, + QLabel, + QLineEdit, + QComboBox, + QDialogButtonBox, + QCheckBox, + QHBoxLayout, + QPushButton, +) +from PyQt6.QtCore import ( + Qt, + QThread, + pyqtSignal, + QSize, + QByteArray, + QSettings, + QEvent, + QKeyCombination, + QTranslator, + QItemSelectionModel, + QTimer, + QStringListModel, + QRunnable, + QObject, + QThreadPool, + QLocale, + QSignalBlocker, + QItemSelection, +) from time import sleep, time -import os, sys, traceback, signal, re, copy, io, queue, collections, ast, psutil, pexpect +import os, sys, traceback, signal, re, copy, io, queue, collections, ast, json, select -from libpince import GuiUtils, SysUtils, GDB_Engine, type_defs +from libpince import utils, debugcore, typedefs from libpince.libscanmem.scanmem import Scanmem +from libpince.libptrscan.ptrscan import PointerScan, FFIRange, FFIParam +from GUI.Settings.themes import get_theme +from GUI.Settings.themes import theme_list +from GUI.Utils import guiutils from GUI.MainWindow import Ui_MainWindow as MainWindow from GUI.SelectProcess import Ui_MainWindow as ProcessWindow from GUI.AddAddressManuallyDialog import Ui_Dialog as ManualAddressDialog from GUI.EditTypeDialog import Ui_Dialog as EditTypeDialog +from GUI.TrackSelectorDialog import Ui_Dialog as TrackSelectorDialog from GUI.LoadingDialog import Ui_Dialog as LoadingDialog from GUI.InputDialog import Ui_Dialog as InputDialog from GUI.TextEditDialog import Ui_Dialog as TextEditDialog from GUI.SettingsDialog import Ui_Dialog as SettingsDialog +from GUI.HandleSignalsDialog import Ui_Dialog as HandleSignalsDialog from GUI.ConsoleWidget import Ui_Form as ConsoleWidget from GUI.AboutWidget import Ui_TabWidget as AboutWidget -# If you are going to change the name "Ui_MainWindow_MemoryView", review GUI/CustomLabels/RegisterLabel.py as well +# If you are going to change the name "Ui_MainWindow_MemoryView", review GUI/Labels/RegisterLabel.py as well from GUI.MemoryViewerWindow import Ui_MainWindow_MemoryView as MemoryViewWindow from GUI.BookmarkWidget import Ui_Form as BookmarkWidget from GUI.FloatRegisterWidget import Ui_TabWidget as FloatRegisterWidget @@ -55,7 +127,7 @@ from GUI.TraceInstructionsWindow import Ui_MainWindow as TraceInstructionsWindow from GUI.FunctionsInfoWidget import Ui_Form as FunctionsInfoWidget from GUI.HexEditDialog import Ui_Dialog as HexEditDialog -from GUI.LibpinceReferenceWidget import Ui_Form as LibpinceReferenceWidget +from GUI.EditInstructionDialog import Ui_Dialog as EditInstructionDialog from GUI.LogFileWidget import Ui_Form as LogFileWidget from GUI.SearchOpcodeWidget import Ui_Form as SearchOpcodeWidget from GUI.MemoryRegionsWidget import Ui_Form as MemoryRegionsWidget @@ -63,51 +135,49 @@ from GUI.ReferencedStringsWidget import Ui_Form as ReferencedStringsWidget from GUI.ReferencedCallsWidget import Ui_Form as ReferencedCallsWidget from GUI.ExamineReferrersWidget import Ui_Form as ExamineReferrersWidget +from GUI.RestoreInstructionsWidget import Ui_Form as RestoreInstructionsWidget +from GUI.PointerScanSearchDialog import Ui_Dialog as PointerScanSearchDialog +from GUI.PointerScanFilterDialog import Ui_Dialog as PointerScanFilterDialog +from GUI.PointerScanWindow import Ui_MainWindow as PointerScanWindow +from GUI.ScanRegionsDialog import Ui_Dialog as ScanRegionsDialog -from GUI.CustomAbstractTableModels.HexModel import QHexModel -from GUI.CustomAbstractTableModels.AsciiModel import QAsciiModel -from GUI.CustomValidators.HexValidator import QHexValidator - -instances = [] # Holds temporary instances that will be deleted later on - -# settings -current_settings_version = "master-20" # Increase version by one if you change settings. Format: branch_name-version -update_table = bool -table_update_interval = int -FreezeInterval = int -show_messagebox_on_exception = bool -show_messagebox_on_toggle_attach = bool -gdb_output_mode = tuple -auto_attach_list = str -auto_attach_regex = bool -logo_path = str - - -class Hotkeys: - class Hotkey: - def __init__(self, name="", desc="", default="", value="", context=Qt.ApplicationShortcut): - self.name = name - self.desc = desc - self.default = default - self.value = value - self.context = context - - pause_hotkey = Hotkey("pause_hotkey", "Pause the process", "F1") - break_hotkey = Hotkey("break_hotkey", "Break the process", "F2") - continue_hotkey = Hotkey("continue_hotkey", "Continue the process", "F3") - toggle_attach_hotkey = Hotkey("toggle_attach_hotkey", "Toggle attach/detach", "Shift+F10") - - @staticmethod - def get_hotkeys(): - return Hotkeys.pause_hotkey, Hotkeys.break_hotkey, Hotkeys.continue_hotkey, Hotkeys.toggle_attach_hotkey - - -code_injection_method = int -bring_disassemble_to_front = bool -instructions_per_scroll = int -gdb_path = str -gdb_logging = bool -ignore_sigsegv = bool +from GUI.AbstractTableModels.HexModel import QHexModel +from GUI.AbstractTableModels.AsciiModel import QAsciiModel +from GUI.Validators.HexValidator import QHexValidator +from GUI.ManualAddressDialogUtils.PointerChainOffset import PointerChainOffset + +from keyboard import KeyboardEvent, _pressed_events +from keyboard._nixkeyboard import to_name + +if __name__ == "__main__": + app = QApplication([]) + app.setOrganizationName("PINCE") + app.setOrganizationDomain("github.com/korcankaraokcu/PINCE") + app.setApplicationName("PINCE") + QSettings.setPath( + QSettings.Format.NativeFormat, QSettings.Scope.UserScope, utils.get_user_path(typedefs.USER_PATHS.CONFIG) + ) + settings_instance = QSettings() + translator = QTranslator() + try: + locale = settings_instance.value("General/locale", type=str) + except SystemError: + # We're reading the settings for the first time here + # If there's an error due to python objects, clear settings + settings_instance.clear() + locale = None + if not locale: + locale = get_locale() + locale_file = utils.get_script_directory() + f"/i18n/qm/{locale}.qm" + translator.load(locale_file) + app.installTranslator(translator) + tr.translate() + hotkeys = Hotkeys() # Create the instance after translations to ensure hotkeys are translated + +# represents the index of columns in instructions restore table +INSTR_ADDR_COL = 0 +INSTR_AOB_COL = 1 +INSTR_NAME_COL = 2 # represents the index of columns in breakpoint table BREAK_NUM_COL = 0 @@ -120,12 +190,11 @@ def get_hotkeys(): BREAK_HIT_COUNT_COL = 7 BREAK_COND_COL = 8 -# row colours for disassemble qtablewidget -PC_COLOUR = Qt.blue -BOOKMARK_COLOUR = Qt.cyan -DEFAULT_COLOUR = Qt.white -BREAKPOINT_COLOUR = Qt.red -REF_COLOUR = Qt.lightGray +# row colors for disassemble qtablewidget +PC_COLOR = QColorConstants.Blue +BOOKMARK_COLOR = QColorConstants.Cyan +BREAKPOINT_COLOR = QColorConstants.Red +REF_COLOR = QColorConstants.LightGray # represents the index of columns in address table FROZEN_COL = 0 # Frozen @@ -134,6 +203,11 @@ def get_hotkeys(): TYPE_COL = 3 # Type VALUE_COL = 4 # Value +# represents the index of columns in search results table +SEARCH_TABLE_ADDRESS_COL = 0 +SEARCH_TABLE_VALUE_COL = 1 +SEARCH_TABLE_PREVIOUS_COL = 2 + # represents the index of columns in disassemble table DISAS_ADDR_COL = 0 DISAS_BYTES_COL = 1 @@ -182,17 +256,8 @@ def get_hotkeys(): # represents the index of columns in memory regions table MEMORY_REGIONS_ADDR_COL = 0 MEMORY_REGIONS_PERM_COL = 1 -MEMORY_REGIONS_SIZE_COL = 2 +MEMORY_REGIONS_OFFSET_COL = 2 MEMORY_REGIONS_PATH_COL = 3 -MEMORY_REGIONS_RSS_COL = 4 -MEMORY_REGIONS_PSS_COL = 5 -MEMORY_REGIONS_SHRCLN_COL = 6 -MEMORY_REGIONS_SHRDRTY_COL = 7 -MEMORY_REGIONS_PRIVCLN_COL = 8 -MEMORY_REGIONS_PRIVDRTY_COL = 9 -MEMORY_REGIONS_REF_COL = 10 -MEMORY_REGIONS_ANON_COL = 11 -MEMORY_REGIONS_SWAP_COL = 12 # represents the index of columns in dissect code table DISSECT_CODE_ADDR_COL = 0 @@ -207,46 +272,71 @@ def get_hotkeys(): REF_CALL_ADDR_COL = 0 REF_CALL_COUNT_COL = 1 -# used for automatically updating the values in the saved address tree widget -# see UpdateAddressTableThread -saved_addresses_changed_list = list() +# GDB expression cache +# TODO: Try to find a fast and non-gdb way to calculate symbols so we don't need this +# This is one of the few tricks we do to minimize examine_expression calls +# This solution might bring problems if the symbols are changing frequently +# Pressing the refresh button in the address table or attaching to a new process will clear this cache +# Currently only used in address_table_loop +exp_cache = {} -# vars for communication/storage with the non blocking threads -# see treeWidget_AddressTable_item_clicked -Exiting = 0 -FreezeVars = {} -FreezeStop = 0 -ProgressRun = 0 +# vars for communication with the non blocking threads +exiting = 0 + +scanmem = Scanmem(os.path.join(utils.get_libpince_directory(), "libscanmem", "libscanmem.so")) +ptrscan = PointerScan(os.path.join(utils.get_libpince_directory(), "libptrscan", "libptrscan.so")) +ptrscan.set_pointer_offset_symbol("->") threadpool = QThreadPool() # Placeholder number, may have to be changed in the future threadpool.setMaxThreadCount(10) +class ProcessSignals(QObject): + attach = pyqtSignal() + exit = pyqtSignal() + + +process_signals = ProcessSignals() + + +class WorkerSignals(QObject): + finished = pyqtSignal() + + class Worker(QRunnable): def __init__(self, fn, *args, **kwargs): - super(Worker, self).__init__() + super().__init__() + self.fn = fn + self.args = args + self.kwargs = kwargs + self.signals = WorkerSignals() + + def run(self): + self.fn(*self.args, **self.kwargs) + self.signals.finished.emit() + +class InterruptableWorker(QThread): + def __init__(self, fn, *args, **kwargs): + super().__init__() self.fn = fn self.args = args self.kwargs = kwargs + self.signals = WorkerSignals() - @pyqtSlot() def run(self): self.fn(*self.args, **self.kwargs) + self.signals.finished.emit() + + def stop(self): + self.terminate() def except_hook(exception_type, value, tb): - if show_messagebox_on_exception: - focused_widget = app.focusWidget() - if focused_widget: - if exception_type == type_defs.GDBInitializeException: - QMessageBox.information(focused_widget, "Error", "GDB isn't initialized yet") - elif exception_type == type_defs.InferiorRunningException: - error_dialog = InputDialogForm(item_list=[( - "Process is running" + "\nPress " + Hotkeys.break_hotkey.value + " to stop process" + - "\n\nGo to Settings->General to disable this dialog",)], buttons=[QDialogButtonBox.Ok]) - error_dialog.exec_() + focused_widget = app.focusWidget() + if focused_widget and exception_type == typedefs.GDBInitializeException: + QMessageBox.information(focused_widget, tr.ERROR, tr.GDB_INIT) traceback.print_exception(exception_type, value, tb) @@ -254,10 +344,35 @@ def except_hook(exception_type, value, tb): # So, we must override sys.excepthook to avoid calling of qFatal() sys.excepthook = except_hook +quit_prompt_active = False + def signal_handler(signal, frame): - GDB_Engine.cancel_last_command() - raise KeyboardInterrupt + global quit_prompt_active + with QSignalBlocker(app): + if debugcore.lock_send_command.locked(): + print("\nCancelling the last GDB command") + debugcore.cancel_last_command() + else: + if quit_prompt_active: + print() # Prints a newline so the terminal looks nicer when we quit + debugcore.detach() + quit() + quit_prompt_active = True + print("\nNo GDB command to cancel, quit PINCE? (y/n)", end="", flush=True) + while True: + # Using select() instead of input() because it causes the bug below + # QBackingStore::endPaint() called with active painter + rlist, _, _ = select.select([sys.stdin], [], [], 0.1) + if rlist: + user_input = sys.stdin.readline().strip().lower() + break + if user_input.startswith("y"): + debugcore.detach() + quit() + else: + print("Quit aborted") + quit_prompt_active = False signal.signal(signal.SIGINT, signal_handler) @@ -269,8 +384,8 @@ class AwaitProcessExit(QThread): def run(self): while True: - with GDB_Engine.process_exited_condition: - GDB_Engine.process_exited_condition.wait() + with debugcore.process_exited_condition: + debugcore.process_exited_condition.wait() self.process_exited.emit() @@ -279,11 +394,11 @@ class AwaitAsyncOutput(QThread): async_output_ready = pyqtSignal(str) def __init__(self): - super(AwaitAsyncOutput, self).__init__() + super().__init__() self.queue_active = True def run(self): - async_output_queue = GDB_Engine.gdb_async_output.register_queue() + async_output_queue = debugcore.gdb_async_output.register_queue() while self.queue_active: try: async_output = async_output_queue.get(timeout=5) @@ -291,7 +406,7 @@ def run(self): pass else: self.async_output_ready.emit(async_output) - GDB_Engine.gdb_async_output.delete_queue(async_output_queue) + debugcore.gdb_async_output.delete_queue(async_output_queue) def stop(self): self.queue_active = False @@ -303,78 +418,74 @@ class CheckInferiorStatus(QThread): def run(self): while True: - with GDB_Engine.status_changed_condition: - GDB_Engine.status_changed_condition.wait() - if GDB_Engine.inferior_status == type_defs.INFERIOR_STATUS.INFERIOR_STOPPED and GDB_Engine.temporary_execution_bool != True: - print("execute condition: " + str(GDB_Engine.temporary_execution_bool)) + with debugcore.status_changed_condition: + debugcore.status_changed_condition.wait() + if debugcore.inferior_status == typedefs.INFERIOR_STATUS.STOPPED: self.process_stopped.emit() - elif GDB_Engine.inferior_status == type_defs.INFERIOR_STATUS.INFERIOR_RUNNING and GDB_Engine.temporary_execution_bool != True: + elif debugcore.inferior_status == typedefs.INFERIOR_STATUS.RUNNING: self.process_running.emit() -# TODO undo scan, we would probably need to make some data structure we -# could pass to scanmem which then would set the current matches -# the mainwindow class MainForm(QMainWindow, MainWindow): + table_update_interval: int = 500 + freeze_interval: int = 100 + update_table: bool = True + auto_attach: str = "" + auto_attach_regex: bool = False + def __init__(self): super().__init__() self.setupUi(self) - self.hotkey_to_shortcut = {} # Dict[str:QShortcut]-->Dict[Hotkey.name:QShortcut(QKeySequence(Hotkey.value)] + self.hotkey_to_shortcut = {} hotkey_to_func = { - Hotkeys.pause_hotkey: self.pause_hotkey_pressed, - Hotkeys.break_hotkey: self.break_hotkey_pressed, - Hotkeys.continue_hotkey: self.continue_hotkey_pressed, - Hotkeys.toggle_attach_hotkey: self.toggle_attach_hotkey_pressed + hotkeys.pause_hotkey: self.pause_hotkey_pressed, + hotkeys.break_hotkey: self.break_hotkey_pressed, + hotkeys.continue_hotkey: self.continue_hotkey_pressed, + hotkeys.toggle_attach_hotkey: self.toggle_attach_hotkey_pressed, + hotkeys.exact_scan_hotkey: lambda: self.nextscan_hotkey_pressed(typedefs.SCAN_TYPE.EXACT), + hotkeys.increased_scan_hotkey: lambda: self.nextscan_hotkey_pressed(typedefs.SCAN_TYPE.INCREASED), + hotkeys.decreased_scan_hotkey: lambda: self.nextscan_hotkey_pressed(typedefs.SCAN_TYPE.DECREASED), + hotkeys.changed_scan_hotkey: lambda: self.nextscan_hotkey_pressed(typedefs.SCAN_TYPE.CHANGED), + hotkeys.unchanged_scan_hotkey: lambda: self.nextscan_hotkey_pressed(typedefs.SCAN_TYPE.UNCHANGED), } for hotkey, func in hotkey_to_func.items(): - current_shortcut = QShortcut(QKeySequence(hotkey.value), self) - current_shortcut.activated.connect(func) - current_shortcut.setContext(hotkey.context) - self.hotkey_to_shortcut[hotkey.name] = current_shortcut - GuiUtils.center(self) + hotkey.change_func(func) self.treeWidget_AddressTable.setColumnWidth(FROZEN_COL, 50) self.treeWidget_AddressTable.setColumnWidth(DESC_COL, 150) self.treeWidget_AddressTable.setColumnWidth(ADDR_COL, 150) self.treeWidget_AddressTable.setColumnWidth(TYPE_COL, 150) - app.setOrganizationName("PINCE") - app.setOrganizationDomain("github.com/korcankaraokcu/PINCE") - app.setApplicationName("PINCE") - QSettings.setPath(QSettings.NativeFormat, QSettings.UserScope, - SysUtils.get_user_path(type_defs.USER_PATHS.CONFIG_PATH)) + self.tableWidget_valuesearchtable.setColumnWidth(SEARCH_TABLE_ADDRESS_COL, 120) + self.tableWidget_valuesearchtable.setColumnWidth(SEARCH_TABLE_VALUE_COL, 80) + self.tableWidget_valuesearchtable.horizontalHeader().setSortIndicatorClearable(True) self.settings = QSettings() - if not SysUtils.is_path_valid(self.settings.fileName()): + self.memory_view_window = MemoryViewWindowForm(self) + self.await_exit_thread = AwaitProcessExit() + self.auto_attach_timer = QTimer(timeout=self.auto_attach_loop) + + if not os.path.exists(self.settings.fileName()): self.set_default_settings() try: settings_version = self.settings.value("Misc/version", type=str) except Exception as e: print("An exception occurred while reading settings version\n", e) settings_version = None - if settings_version != current_settings_version: + if settings_version != settings.current_settings_version: print("Settings version mismatch, rolling back to the default configuration") self.settings.clear() self.set_default_settings() try: self.apply_settings() except Exception as e: - print("An exception occurred while trying to load settings, rolling back to the default configuration\n", e) + print("An exception occurred while loading settings, rolling back to the default configuration\n", e) self.settings.clear() self.set_default_settings() - try: - GDB_Engine.init_gdb(gdb_path, ignore_sigsegv) - except pexpect.EOF: - text = "Unable to initialize GDB\n" \ - "You might want to reinstall GDB or use the system GDB\n" \ - "To change the current GDB path, check Settings->Debug" - InputDialogForm(item_list=[(text, None)], buttons=[QDialogButtonBox.Ok]).exec_() + gdb_path = settings.gdb_path + if os.environ.get("APPDIR"): + gdb_path = utils.get_default_gdb_path() + if debugcore.init_gdb(gdb_path): + self.apply_after_init() else: - GDB_Engine.set_logging(gdb_logging) - # this should be changed, only works if you use the current directory, fails if you for example install it to some place like bin - libscanmem_path = os.path.join(os.getcwd(), "libpince", "libscanmem", "libscanmem.so") - self.backend = Scanmem(libscanmem_path) - self.backend.send_command("option noptrace 1") - self.memory_view_window = MemoryViewWindowForm(self) - self.about_widget = AboutWidgetForm() - self.await_exit_thread = AwaitProcessExit() + InputDialogForm(self, [(tr.GDB_INIT_ERROR, None)], buttons=[QDialogButtonBox.StandardButton.Ok]).exec() self.await_exit_thread.process_exited.connect(self.on_inferior_exit) self.await_exit_thread.start() self.check_status_thread = CheckInferiorStatus() @@ -383,16 +494,24 @@ def __init__(self): self.check_status_thread.process_stopped.connect(self.memory_view_window.process_stopped) self.check_status_thread.process_running.connect(self.memory_view_window.process_running) self.check_status_thread.start() - self.update_address_table_thread = Worker(self.update_address_table_loop) - global threadpool - threadpool.start(self.update_address_table_thread) + self.address_table_timer = QTimer(timeout=self.address_table_loop, singleShot=True) + self.address_table_timer.start() + self.search_table_timer = QTimer(timeout=self.search_table_loop, singleShot=True) + self.search_table_timer.start() + self.freeze_timer = QTimer(timeout=self.freeze_loop, singleShot=True) + self.freeze_timer.start() self.shortcut_open_file = QShortcut(QKeySequence("Ctrl+O"), self) self.shortcut_open_file.activated.connect(self.pushButton_Open_clicked) - GuiUtils.append_shortcut_to_tooltip(self.pushButton_Open, self.shortcut_open_file) + guiutils.append_shortcut_to_tooltip(self.pushButton_Open, self.shortcut_open_file) self.shortcut_save_file = QShortcut(QKeySequence("Ctrl+S"), self) self.shortcut_save_file.activated.connect(self.pushButton_Save_clicked) - GuiUtils.append_shortcut_to_tooltip(self.pushButton_Save, self.shortcut_save_file) + guiutils.append_shortcut_to_tooltip(self.pushButton_Save, self.shortcut_save_file) + # Saving the original function because super() doesn't work when we override functions like this + self.treeWidget_AddressTable.mousePressEvent_original = self.treeWidget_AddressTable.mousePressEvent + self.treeWidget_AddressTable.mousePressEvent = self.treeWidget_AddressTable_mouse_press_event + self.treeWidget_AddressTable.mouseReleaseEvent_original = self.treeWidget_AddressTable.mouseReleaseEvent + self.treeWidget_AddressTable.mouseReleaseEvent = self.treeWidget_AddressTable_mouse_release_event self.treeWidget_AddressTable.keyPressEvent_original = self.treeWidget_AddressTable.keyPressEvent self.treeWidget_AddressTable.keyPressEvent = self.treeWidget_AddressTable_key_press_event self.treeWidget_AddressTable.contextMenuEvent = self.treeWidget_AddressTable_context_menu_event @@ -400,15 +519,22 @@ def __init__(self): self.pushButton_Open.clicked.connect(self.pushButton_Open_clicked) self.pushButton_Save.clicked.connect(self.pushButton_Save_clicked) self.pushButton_NewFirstScan.clicked.connect(self.pushButton_NewFirstScan_clicked) + self.pushButton_UndoScan.clicked.connect(self.pushButton_UndoScan_clicked) self.pushButton_NextScan.clicked.connect(self.pushButton_NextScan_clicked) - self.scan_mode = type_defs.SCAN_MODE.ONGOING + self.pushButton_ScanRegions.clicked.connect(self.pushButton_ScanRegions_clicked) + self.scan_mode = typedefs.SCAN_MODE.NEW self.pushButton_NewFirstScan_clicked() self.comboBox_ScanScope_init() self.comboBox_ValueType_init() + guiutils.fill_endianness_combobox(self.comboBox_Endianness) self.checkBox_Hex.stateChanged.connect(self.checkBox_Hex_stateChanged) self.comboBox_ValueType.currentIndexChanged.connect(self.comboBox_ValueType_current_index_changed) - self.lineEdit_Scan.setValidator(QRegExpValidator(QRegExp("-?[0-9]*"), parent=self.lineEdit_Scan)) - self.lineEdit_Scan2.setValidator(QRegExpValidator(QRegExp("-?[0-9]*"), parent=self.lineEdit_Scan2)) + self.lineEdit_Scan.setValidator(guiutils.validator_map.get("int")) + self.lineEdit_Scan2.setValidator(guiutils.validator_map.get("int")) + self.lineEdit_Scan.keyPressEvent_original = self.lineEdit_Scan.keyPressEvent + self.lineEdit_Scan2.keyPressEvent_original = self.lineEdit_Scan2.keyPressEvent + self.lineEdit_Scan.keyPressEvent = self.lineEdit_Scan_on_key_press_event + self.lineEdit_Scan2.keyPressEvent = self.lineEdit_Scan2_on_key_press_event self.comboBox_ScanType.currentIndexChanged.connect(self.comboBox_ScanType_current_index_changed) self.comboBox_ScanType_current_index_changed() self.pushButton_Settings.clicked.connect(self.pushButton_Settings_clicked) @@ -417,17 +543,21 @@ def __init__(self): self.pushButton_About.clicked.connect(self.pushButton_About_clicked) self.pushButton_AddAddressManually.clicked.connect(self.pushButton_AddAddressManually_clicked) self.pushButton_MemoryView.clicked.connect(self.pushButton_MemoryView_clicked) - self.pushButton_RefreshAdressTable.clicked.connect(self.update_address_table) + self.pushButton_RefreshAdressTable.clicked.connect(self.pushButton_RefreshAdressTable_clicked) self.pushButton_CopyToAddressTable.clicked.connect(self.copy_to_address_table) - self.pushButton_CleanAddressTable.clicked.connect(self.delete_address_table_contents) + self.pushButton_CleanAddressTable.clicked.connect(self.clear_address_table) self.tableWidget_valuesearchtable.cellDoubleClicked.connect( - self.tableWidget_valuesearchtable_cell_double_clicked) - self.treeWidget_AddressTable.itemClicked.connect(self.treeWidget_AddressTable_item_clicked) + self.tableWidget_valuesearchtable_cell_double_clicked + ) + self.tableWidget_valuesearchtable.keyPressEvent_original = self.tableWidget_valuesearchtable.keyPressEvent + self.tableWidget_valuesearchtable.keyPressEvent = self.tableWidget_valuesearchtable_key_press_event + self.tableWidget_valuesearchtable.contextMenuEvent = self.tableWidget_valuesearchtable_context_menu_event self.treeWidget_AddressTable.itemDoubleClicked.connect(self.treeWidget_AddressTable_item_double_clicked) self.treeWidget_AddressTable.expanded.connect(self.resize_address_table) self.treeWidget_AddressTable.collapsed.connect(self.resize_address_table) - icons_directory = GuiUtils.get_icons_directory() - current_dir = SysUtils.get_current_script_directory() + self.treeWidget_AddressTable.header().setSortIndicatorClearable(True) + self.treeWidget_AddressTable.header().setSortIndicator(-1, Qt.SortOrder.AscendingOrder) # Clear sort indicator + icons_directory = guiutils.get_icons_directory() self.pushButton_AttachProcess.setIcon(QIcon(QPixmap(icons_directory + "/monitor.png"))) self.pushButton_Open.setIcon(QIcon(QPixmap(icons_directory + "/folder.png"))) self.pushButton_Save.setIcon(QIcon(QPixmap(icons_directory + "/disk.png"))) @@ -438,257 +568,408 @@ def __init__(self): self.pushButton_Console.setIcon(QIcon(QPixmap(icons_directory + "/application_xp_terminal.png"))) self.pushButton_Wiki.setIcon(QIcon(QPixmap(icons_directory + "/book_open.png"))) self.pushButton_About.setIcon(QIcon(QPixmap(icons_directory + "/information.png"))) - self.auto_attach() - + self.pushButton_NextScan.setEnabled(False) + self.pushButton_UndoScan.setEnabled(False) + self.flashAttachButton = True + self.flashAttachButtonTimer = QTimer() + self.flashAttachButtonTimer.timeout.connect(self.flash_attach_button) + self.flashAttachButton_gradiantState = 0 + self.flashAttachButtonTimer.start(100) + guiutils.center(self) + + # Please refrain from using python specific objects in settings, use json-compatible ones instead + # Using python objects causes issues when filenames change def set_default_settings(self): self.settings.beginGroup("General") - self.settings.setValue("auto_update_address_table", True) - self.settings.setValue("address_table_update_interval", 500) - self.settings.setValue("freeze_interval", 100) - self.settings.setValue("show_messagebox_on_exception", True) - self.settings.setValue("show_messagebox_on_toggle_attach", True) - self.settings.setValue("gdb_output_mode", type_defs.gdb_output_mode(True, True, True)) - self.settings.setValue("auto_attach_list", "") + self.settings.setValue("auto_update_address_table", MainForm.update_table) + self.settings.setValue("address_table_update_interval", MainForm.table_update_interval) + self.settings.setValue("freeze_interval", MainForm.freeze_interval) + self.settings.setValue("gdb_output_mode", json.dumps([True, True, True])) + self.settings.setValue("auto_attach", MainForm.auto_attach) + self.settings.setValue("auto_attach_regex", MainForm.auto_attach_regex) + self.settings.setValue("locale", get_locale()) self.settings.setValue("logo_path", "ozgurozbek/pince_small_transparent.png") - self.settings.setValue("auto_attach_regex", False) + self.settings.setValue("theme", "System Default") self.settings.endGroup() self.settings.beginGroup("Hotkeys") - for hotkey in Hotkeys.get_hotkeys(): + for hotkey in hotkeys.get_hotkeys(): self.settings.setValue(hotkey.name, hotkey.default) self.settings.endGroup() self.settings.beginGroup("CodeInjection") - self.settings.setValue("code_injection_method", type_defs.INJECTION_METHOD.SIMPLE_DLOPEN_CALL) + self.settings.setValue("code_injection_method", typedefs.INJECTION_METHOD.DLOPEN) self.settings.endGroup() - self.settings.beginGroup("Disassemble") - self.settings.setValue("bring_disassemble_to_front", False) - self.settings.setValue("instructions_per_scroll", 2) + self.settings.beginGroup("MemoryView") + self.settings.setValue("show_memory_view_on_stop", False) + self.settings.setValue("instructions_per_scroll", MemoryViewWindowForm.instructions_per_scroll) + self.settings.setValue("bytes_per_scroll", MemoryViewWindowForm.bytes_per_scroll) self.settings.endGroup() self.settings.beginGroup("Debug") - self.settings.setValue("gdb_path", type_defs.PATHS.GDB_PATH) + self.settings.setValue("gdb_path", typedefs.PATHS.GDB) self.settings.setValue("gdb_logging", False) - self.settings.setValue("ignore_sigsegv", False) + self.settings.setValue("interrupt_signal", "SIGINT") + self.settings.setValue("handle_signals", json.dumps(settings.default_signals)) + self.settings.endGroup() + self.settings.beginGroup("Java") + self.settings.setValue("ignore_segfault", True) self.settings.endGroup() self.settings.beginGroup("Misc") - self.settings.setValue("version", current_settings_version) + self.settings.setValue("version", settings.current_settings_version) self.settings.endGroup() self.apply_settings() + def apply_after_init(self): + global exp_cache + + exp_cache.clear() + settings.gdb_logging = self.settings.value("Debug/gdb_logging", type=bool) + settings.interrupt_signal = self.settings.value("Debug/interrupt_signal", type=str) + settings.handle_signals = json.loads(self.settings.value("Debug/handle_signals", type=str)) + java_ignore_segfault = self.settings.value("Java/ignore_segfault", type=bool) + debugcore.set_logging(settings.gdb_logging) + + # Don't handle signals if a process isn't present, a small optimization to gain time on launch and detach + if debugcore.currentpid != -1: + debugcore.handle_signals(settings.handle_signals) + # Not a great method but okayish until the implementation of the libpince engine and the java dissector + # "jps" command could be used instead if we ever need to install openjdk + if java_ignore_segfault and utils.get_process_name(debugcore.currentpid).startswith("java"): + debugcore.handle_signal("SIGSEGV", False, True) + debugcore.set_interrupt_signal(settings.interrupt_signal) # Needs to be called after handle_signals + def apply_settings(self): - global update_table - global table_update_interval - global show_messagebox_on_exception - global show_messagebox_on_toggle_attach - global gdb_output_mode - global auto_attach_list - global logo_path - global auto_attach_regex - global code_injection_method - global bring_disassemble_to_front - global instructions_per_scroll - global gdb_path - global gdb_logging - global ignore_sigsegv - global FreezeInterval - - update_table = self.settings.value("General/auto_update_address_table", type=bool) - table_update_interval = self.settings.value("General/address_table_update_interval", type=int) - FreezeInterval = self.settings.value("General/freeze_interval", type=int) - show_messagebox_on_exception = self.settings.value("General/show_messagebox_on_exception", type=bool) - show_messagebox_on_toggle_attach = self.settings.value("General/show_messagebox_on_toggle_attach", type=bool) - gdb_output_mode = self.settings.value("General/gdb_output_mode", type=tuple) - auto_attach_list = self.settings.value("General/auto_attach_list", type=str) - logo_path = self.settings.value("General/logo_path", type=str) - app.setWindowIcon(QIcon(os.path.join(SysUtils.get_logo_directory(), logo_path))) - auto_attach_regex = self.settings.value("General/auto_attach_regex", type=bool) - GDB_Engine.set_gdb_output_mode(gdb_output_mode) - for hotkey in Hotkeys.get_hotkeys(): - hotkey.value = self.settings.value("Hotkeys/" + hotkey.name) - self.hotkey_to_shortcut[hotkey.name].setKey(QKeySequence(hotkey.value)) + self.update_table = self.settings.value("General/auto_update_address_table", type=bool) + self.table_update_interval = self.settings.value("General/address_table_update_interval", type=int) + self.freeze_interval = self.settings.value("General/freeze_interval", type=int) + settings.gdb_output_mode = json.loads(self.settings.value("General/gdb_output_mode", type=str)) + settings.gdb_output_mode = typedefs.gdb_output_mode(*settings.gdb_output_mode) + self.auto_attach = self.settings.value("General/auto_attach", type=str) + self.auto_attach_regex = self.settings.value("General/auto_attach_regex", type=bool) + if self.auto_attach: + self.auto_attach_timer.start(100) + else: + self.auto_attach_timer.stop() + settings.locale = self.settings.value("General/locale", type=str) + app.setWindowIcon( + QIcon(os.path.join(utils.get_logo_directory(), self.settings.value("General/logo_path", type=str))) + ) + app.setPalette(get_theme(self.settings.value("General/theme", type=str))) + debugcore.set_gdb_output_mode(settings.gdb_output_mode) + for hotkey in hotkeys.get_hotkeys(): + try: + hotkey.change_key(self.settings.value("Hotkeys/" + hotkey.name)) + except: + # if the hotkey cannot be applied for whatever reason, reset it to the default + self.settings.setValue("Hotkeys/" + hotkey.name, hotkey.default) + hotkey.change_key(hotkey.default) + try: self.memory_view_window.set_dynamic_debug_hotkeys() except AttributeError: pass - code_injection_method = self.settings.value("CodeInjection/code_injection_method", type=int) - bring_disassemble_to_front = self.settings.value("Disassemble/bring_disassemble_to_front", type=bool) - instructions_per_scroll = self.settings.value("Disassemble/instructions_per_scroll", type=int) - gdb_path = self.settings.value("Debug/gdb_path", type=str) - gdb_logging = self.settings.value("Debug/gdb_logging", type=bool) - ignore_sigsegv = self.settings.value("Debug/ignore_sigsegv", type=bool) - if GDB_Engine.gdb_initialized: - GDB_Engine.set_logging(gdb_logging) - GDB_Engine.ignore_segfault(ignore_sigsegv) + settings.code_injection_method = self.settings.value("CodeInjection/code_injection_method", type=int) + self.memory_view_window.show_memory_view_on_stop = self.settings.value( + "MemoryView/show_memory_view_on_stop", type=bool + ) + self.memory_view_window.instructions_per_scroll = self.settings.value( + "MemoryView/instructions_per_scroll", type=int + ) + self.memory_view_window.bytes_per_scroll = self.settings.value("MemoryView/bytes_per_scroll", type=int) + settings.gdb_path = self.settings.value("Debug/gdb_path", type=str) + if debugcore.gdb_initialized: + self.apply_after_init() # Check if any process should be attached to automatically # Patterns at former positions have higher priority if regex is off - def auto_attach(self): - if not auto_attach_list: + def auto_attach_loop(self): + if debugcore.currentpid != -1: return - if auto_attach_regex: + if self.auto_attach_regex: try: - compiled_re = re.compile(auto_attach_list) + compiled_re = re.compile(self.auto_attach) except: - print("Auto-attach failed: " + auto_attach_list + " isn't a valid regex") + print(f"Auto-attach failed: {self.auto_attach} isn't a valid regex") return - for process in SysUtils.iterate_processes(): - try: - name = process.name() - except psutil.NoSuchProcess: - continue + for pid, _, name in utils.get_process_list(): if compiled_re.search(name): - self.attach_to_pid(process.pid) + self.attach_to_pid(int(pid)) return else: - for target in auto_attach_list.split(";"): - for process in SysUtils.iterate_processes(): - try: - name = process.name() - except psutil.NoSuchProcess: - continue + for target in self.auto_attach.split(";"): + for pid, _, name in utils.get_process_list(): if name.find(target) != -1: - self.attach_to_pid(process.pid) + self.attach_to_pid(int(pid)) return + # Keyboard package has an issue with exceptions, any trigger function that throws an exception stops the event loop + # Writing a custom event loop instead of ignoring exceptions could work as well but honestly, this looks cleaner + # Keyboard package does not play well with Qt, do not use anything Qt related with hotkeys + # Instead of using Qt functions, try to use their signals to prevent crashes + @utils.ignore_exceptions def pause_hotkey_pressed(self): - GDB_Engine.interrupt_inferior(type_defs.STOP_REASON.PAUSE) + if not debugcore.active_trace: + debugcore.interrupt_inferior(typedefs.STOP_REASON.PAUSE) + @utils.ignore_exceptions def break_hotkey_pressed(self): - GDB_Engine.interrupt_inferior() + if not debugcore.active_trace: + debugcore.interrupt_inferior() + @utils.ignore_exceptions def continue_hotkey_pressed(self): - GDB_Engine.continue_inferior() - + if not ( + debugcore.currentpid == -1 + or debugcore.inferior_status == typedefs.INFERIOR_STATUS.RUNNING + or debugcore.active_trace + ): + debugcore.continue_inferior() + + @utils.ignore_exceptions def toggle_attach_hotkey_pressed(self): - result = GDB_Engine.toggle_attach() + result = debugcore.toggle_attach() if not result: - dialog_text = "Unable to toggle attach" - elif result == type_defs.TOGGLE_ATTACH.DETACHED: + print("Unable to toggle attach") + elif result == typedefs.TOGGLE_ATTACH.DETACHED: self.on_status_detached() - dialog_text = "GDB is detached from the process" else: - dialog_text = "GDB is attached back to the process" - if show_messagebox_on_toggle_attach: - dialog = InputDialogForm(item_list=[( - dialog_text + "\n\nGo to Settings->General to disable this dialog",)], buttons=[QDialogButtonBox.Ok]) - dialog.exec_() + # Attaching back doesn't update the status if the process is already stopped before detachment + with debugcore.status_changed_condition: + debugcore.status_changed_condition.notify_all() + + @utils.ignore_exceptions + def nextscan_hotkey_pressed(self, index): + if self.scan_mode == typedefs.SCAN_MODE.NEW: + return + self.comboBox_ScanType.setCurrentIndex(index) + self.pushButton_NextScan.clicked.emit() def treeWidget_AddressTable_context_menu_event(self, event): - current_row = GuiUtils.get_current_item(self.treeWidget_AddressTable) + current_row = guiutils.get_current_item(self.treeWidget_AddressTable) + current_address = current_row.text(ADDR_COL) if current_row else None + header = self.treeWidget_AddressTable.headerItem() menu = QMenu() - edit_menu = menu.addMenu("Edit") - edit_desc = edit_menu.addAction("Description[Ctrl+Enter]") - edit_address = edit_menu.addAction("Address[Ctrl+Alt+Enter]") - edit_type = edit_menu.addAction("Type[Alt+Enter]") - edit_value = edit_menu.addAction("Value[Enter]") - show_hex = menu.addAction("Show as hexadecimal") - toggle_record = menu.addAction("Toggle selected records[Space]") + delete_record = menu.addAction(f"{tr.DELETE}[Del]") + edit_menu = menu.addMenu(tr.EDIT) + edit_desc = edit_menu.addAction(f"{header.text(DESC_COL)}[Ctrl+Enter]") + edit_address = edit_menu.addAction(f"{header.text(ADDR_COL)}[Ctrl+Alt+Enter]") + edit_type = edit_menu.addAction(f"{header.text(TYPE_COL)}[Alt+Enter]") + edit_value = edit_menu.addAction(f"{header.text(VALUE_COL)}[Enter]") + edit_offset = edit_menu.addAction("Offset[Shift+Enter]") + + show_hex = menu.addAction(tr.SHOW_HEX) + show_dec = menu.addAction(tr.SHOW_DEC) + show_unsigned = menu.addAction(tr.SHOW_UNSIGNED) + show_signed = menu.addAction(tr.SHOW_SIGNED) + toggle_record = menu.addAction(f"{tr.TOGGLE}[Space]") + toggle_children = menu.addAction(f"{tr.TOGGLE_CHILDREN}[Ctrl+Space]") + freeze_menu = menu.addMenu(tr.FREEZE) + freeze_default = freeze_menu.addAction(tr.DEFAULT) + freeze_inc = freeze_menu.addAction(tr.INCREMENTAL) + freeze_dec = freeze_menu.addAction(tr.DECREMENTAL) + menu.addSeparator() + browse_region = menu.addAction(f"{tr.BROWSE_MEMORY_REGION}[Ctrl+B]") + disassemble = menu.addAction(f"{tr.DISASSEMBLE_ADDRESS}[Ctrl+D]") menu.addSeparator() - browse_region = menu.addAction("Browse this memory region[Ctrl+B]") - disassemble = menu.addAction("Disassemble this address[Ctrl+D]") + pointer_scanner = menu.addAction(tr.POINTER_SCANNER) + pointer_scan = menu.addAction(tr.POINTER_SCAN) menu.addSeparator() - cut_record = menu.addAction("Cut selected records[Ctrl+X]") - copy_record = menu.addAction("Copy selected records[Ctrl+C]") - cut_record_recursively = menu.addAction("Cut selected records (recursive)[X]") - copy_record_recursively = menu.addAction("Copy selected records (recursive)[C]") - paste_record_before = menu.addAction("Paste selected records before[Ctrl+V]") - paste_record_after = menu.addAction("Paste selected records after[V]") - paste_record_inside = menu.addAction("Paste selected records inside[I]") - delete_record = menu.addAction("Delete selected records[Del]") + what_writes = menu.addAction(tr.WHAT_WRITES) + what_reads = menu.addAction(tr.WHAT_READS) + what_accesses = menu.addAction(tr.WHAT_ACCESSES) menu.addSeparator() - what_writes = menu.addAction("Find out what writes to this address") - what_reads = menu.addAction("Find out what reads this address") - what_accesses = menu.addAction("Find out what accesses this address") + cut_record = menu.addAction(f"{tr.CUT}[Ctrl+X]") + copy_record = menu.addAction(f"{tr.COPY}[Ctrl+C]") + paste_record = menu.addAction(f"{tr.PASTE}[Ctrl+V]") + paste_inside = menu.addAction(f"{tr.PASTE_INSIDE}[V]") + menu.addSeparator() + add_group = menu.addAction(tr.ADD_GROUP) + create_group = menu.addAction(tr.CREATE_GROUP) if current_row is None: - deletion_list = [edit_menu.menuAction(), show_hex, toggle_record, browse_region, disassemble, what_writes, - what_reads, what_accesses] - GuiUtils.delete_menu_entries(menu, deletion_list) + deletion_list = [ + edit_menu.menuAction(), + show_hex, + show_dec, + show_unsigned, + show_signed, + toggle_record, + toggle_children, + freeze_menu.menuAction(), + browse_region, + disassemble, + pointer_scan, + what_writes, + what_reads, + what_accesses, + cut_record, + copy_record, + paste_inside, + delete_record, + add_group, + ] + guiutils.delete_menu_entries(menu, deletion_list) else: - current_text = current_row.text(TYPE_COL) - index, length, zero_terminate, byte_len, hex_repr = GuiUtils.text_to_valuetype(current_text) - if type_defs.VALUE_INDEX.has_length(index): - GuiUtils.delete_menu_entries(menu, [show_hex]) - elif hex_repr: - show_hex.setText("Show as decimal") + value_type = current_row.data(TYPE_COL, Qt.ItemDataRole.UserRole) + if typedefs.VALUE_INDEX.is_integer(value_type.value_index): + if value_type.value_repr is typedefs.VALUE_REPR.HEX: + guiutils.delete_menu_entries(menu, [show_unsigned, show_signed, show_hex]) + elif value_type.value_repr is typedefs.VALUE_REPR.UNSIGNED: + guiutils.delete_menu_entries(menu, [show_unsigned, show_dec]) + elif value_type.value_repr is typedefs.VALUE_REPR.SIGNED: + guiutils.delete_menu_entries(menu, [show_signed, show_dec]) + if current_row.checkState(FROZEN_COL) == Qt.CheckState.Unchecked: + guiutils.delete_menu_entries(menu, [freeze_menu.menuAction()]) + else: + guiutils.delete_menu_entries( + menu, [show_hex, show_dec, show_unsigned, show_signed, freeze_menu.menuAction()] + ) + if current_row.childCount() == 0: + guiutils.delete_menu_entries(menu, [toggle_children]) + guiutils.delete_menu_entries(menu, [pointer_scanner]) + if debugcore.currentpid == -1: + browse_region.setEnabled(False) + disassemble.setEnabled(False) + pointer_scan.setEnabled(False) + if not debugcore.is_attached(): + what_writes.setEnabled(False) + what_reads.setEnabled(False) + what_accesses.setEnabled(False) font_size = self.treeWidget_AddressTable.font().pointSize() menu.setStyleSheet("font-size: " + str(font_size) + "pt;") - action = menu.exec_(event.globalPos()) + action = menu.exec(event.globalPos()) actions = { + delete_record: self.delete_records, edit_desc: self.treeWidget_AddressTable_edit_desc, edit_address: self.treeWidget_AddressTable_edit_address, edit_type: self.treeWidget_AddressTable_edit_type, edit_value: self.treeWidget_AddressTable_edit_value, - show_hex: self.treeWidget_AddressTable_show_hex, - toggle_record: self.toggle_selected_records, - browse_region: self.browse_region_for_selected_row, - disassemble: self.disassemble_selected_row, - cut_record: self.cut_selected_records, - copy_record: self.copy_selected_records, - cut_record_recursively: self.cut_selected_records_recursively, - copy_record_recursively: self.copy_selected_records_recursively, - paste_record_before: lambda: self.paste_records(insert_after=False), - paste_record_after: lambda: self.paste_records(insert_after=True), - paste_record_inside: lambda: self.paste_records(insert_inside=True), - delete_record: self.delete_selected_records, - what_writes: lambda: self.exec_track_watchpoint_widget(type_defs.WATCHPOINT_TYPE.WRITE_ONLY), - what_reads: lambda: self.exec_track_watchpoint_widget(type_defs.WATCHPOINT_TYPE.READ_ONLY), - what_accesses: lambda: self.exec_track_watchpoint_widget(type_defs.WATCHPOINT_TYPE.BOTH) + edit_offset: self.treeWidget_AddressTable_edit_offset, + show_hex: lambda: self.treeWidget_AddressTable_change_repr(typedefs.VALUE_REPR.HEX), + show_dec: lambda: self.treeWidget_AddressTable_change_repr(typedefs.VALUE_REPR.UNSIGNED), + show_unsigned: lambda: self.treeWidget_AddressTable_change_repr(typedefs.VALUE_REPR.UNSIGNED), + show_signed: lambda: self.treeWidget_AddressTable_change_repr(typedefs.VALUE_REPR.SIGNED), + toggle_record: self.toggle_records, + toggle_children: lambda: self.toggle_records(True), + freeze_default: lambda: self.change_freeze_type(typedefs.FREEZE_TYPE.DEFAULT), + freeze_inc: lambda: self.change_freeze_type(typedefs.FREEZE_TYPE.INCREMENT), + freeze_dec: lambda: self.change_freeze_type(typedefs.FREEZE_TYPE.DECREMENT), + browse_region: lambda: self.browse_region_for_address(current_address), + disassemble: lambda: self.disassemble_for_address(current_address), + pointer_scanner: self.exec_pointer_scanner, + pointer_scan: self.exec_pointer_scan, + what_writes: lambda: self.exec_track_watchpoint_widget(typedefs.WATCHPOINT_TYPE.WRITE_ONLY), + what_reads: lambda: self.exec_track_watchpoint_widget(typedefs.WATCHPOINT_TYPE.READ_ONLY), + what_accesses: lambda: self.exec_track_watchpoint_widget(typedefs.WATCHPOINT_TYPE.BOTH), + cut_record: self.cut_records, + copy_record: self.copy_records, + paste_record: self.paste_records, + paste_inside: lambda: self.paste_records(True), + add_group: self.group_records, + create_group: self.create_group, } try: actions[action]() except KeyError: pass - @GDB_Engine.execute_with_temporary_interruption + def exec_pointer_scanner(self): + pointer_window = PointerScanWindowForm(self) + pointer_window.show() + + def exec_pointer_scan(self): + selected_row = guiutils.get_current_item(self.treeWidget_AddressTable) + if not selected_row: + return + address = selected_row.text(ADDR_COL).strip("P->") + pointer_window = PointerScanWindowForm(self) + pointer_window.show() + dialog = PointerScanSearchDialogForm(pointer_window, address) + dialog.exec() + def exec_track_watchpoint_widget(self, watchpoint_type): - selected_row = GuiUtils.get_current_item(self.treeWidget_AddressTable) + selected_row = guiutils.get_current_item(self.treeWidget_AddressTable) if not selected_row: return - address = selected_row.text(ADDR_COL) - value_type_text = selected_row.text(TYPE_COL) - index, length, zero_terminate, byte_len, hex_repr = GuiUtils.text_to_valuetype(value_type_text) - if byte_len == -1: + address = selected_row.text(ADDR_COL).strip("P->") # @todo Maybe rework address grabbing logic in the future + address_data = selected_row.data(ADDR_COL, Qt.ItemDataRole.UserRole) + if isinstance(address_data, typedefs.PointerChainRequest): + selection_dialog = TrackSelectorDialogForm(self) + selection_dialog.exec() + if not selection_dialog.selection: + return + if selection_dialog.selection == "pointer": + address = address_data.get_base_address_as_str() + value_type = selected_row.data(TYPE_COL, Qt.ItemDataRole.UserRole) + if typedefs.VALUE_INDEX.is_string(value_type.value_index): value_text = selected_row.text(VALUE_COL) - encoding, option = type_defs.string_index_to_encoding_dict[index] + encoding, option = typedefs.string_index_to_encoding_dict[value_type.value_index] byte_len = len(value_text.encode(encoding, option)) - TrackWatchpointWidgetForm(address, byte_len, watchpoint_type, self).show() + elif value_type.value_index == typedefs.VALUE_INDEX.AOB: + byte_len = value_type.length + else: + byte_len = typedefs.index_to_valuetype_dict[value_type.value_index][0] + TrackWatchpointWidgetForm(self, address, byte_len, watchpoint_type) - def browse_region_for_selected_row(self): - row = GuiUtils.get_current_item(self.treeWidget_AddressTable) - if row: - self.memory_view_window.hex_dump_address(int(row.text(ADDR_COL), 16)) + def browse_region_for_address(self, address: str): + if address: + self.memory_view_window.hex_dump_address(int(address.strip("P->"), 16)) self.memory_view_window.show() self.memory_view_window.activateWindow() - def disassemble_selected_row(self): - row = GuiUtils.get_current_item(self.treeWidget_AddressTable) - if row: - if self.memory_view_window.disassemble_expression(row.text(ADDR_COL), append_to_travel_history=True): - self.memory_view_window.show() - self.memory_view_window.activateWindow() + def disassemble_for_address(self, address: str): + if address and self.memory_view_window.disassemble_expression(address.strip("P->"), append_history=True): + self.memory_view_window.show() + self.memory_view_window.activateWindow() - def toggle_selected_records(self): - row = GuiUtils.get_current_item(self.treeWidget_AddressTable) - if row: + def change_freeze_type(self, freeze_type: int | None = None, row: QTreeWidgetItem | None = None) -> None: + if freeze_type == None: + # No type has been specified, iterate through the freeze types + # This usually happens if user clicks the freeze type text instead of the checkbox + frozen: typedefs.Frozen = row.data(FROZEN_COL, Qt.ItemDataRole.UserRole) + if frozen.freeze_type == typedefs.FREEZE_TYPE.DECREMENT: + # Decrement is the last freeze type + freeze_type = typedefs.FREEZE_TYPE.DEFAULT + else: + freeze_type = frozen.freeze_type + 1 + rows = [row] if row else self.treeWidget_AddressTable.selectedItems() + for row in rows: + frozen: typedefs.Frozen = row.data(FROZEN_COL, Qt.ItemDataRole.UserRole) + if row.checkState(FROZEN_COL) == Qt.CheckState.Checked: + frozen.freeze_type = freeze_type + if freeze_type == typedefs.FREEZE_TYPE.DEFAULT: + row.setText(FROZEN_COL, "") + row.setForeground(FROZEN_COL, QBrush()) + elif freeze_type == typedefs.FREEZE_TYPE.INCREMENT: + row.setText(FROZEN_COL, "▲") + row.setForeground(FROZEN_COL, QBrush(QColor(0, 255, 0))) + elif freeze_type == typedefs.FREEZE_TYPE.DECREMENT: + row.setText(FROZEN_COL, "▼") + row.setForeground(FROZEN_COL, QBrush(QColor(255, 0, 0))) + else: + frozen.freeze_type = typedefs.FREEZE_TYPE.DEFAULT + row.setText(FROZEN_COL, "") + row.setForeground(FROZEN_COL, QBrush()) + + def toggle_records(self, toggle_children=False): + row = guiutils.get_current_item(self.treeWidget_AddressTable) + selected_items = self.treeWidget_AddressTable.selectedItems() + # If only one item is selected and then clicked while ctrl is being held + # There'll be no selected rows even with a current row present + if row and selected_items: + if not row.isSelected(): + row = selected_items[0] check_state = row.checkState(FROZEN_COL) - new_check_state = Qt.Checked if check_state == Qt.Unchecked else Qt.Unchecked - for row in self.treeWidget_AddressTable.selectedItems(): - row.setCheckState(FROZEN_COL, new_check_state) - - def cut_selected_records(self): - # Flat cut, does not preserve structure - self.copy_selected_records() - self.delete_selected_records() - - def copy_selected_records(self): - # Flat copy, does not preserve structure - app.clipboard().setText(repr([self.read_address_table_entries(selected_row) + ((),) for selected_row in - self.treeWidget_AddressTable.selectedItems()])) - # each element in the list has no children - - def cut_selected_records_recursively(self): - self.copy_selected_records_recursively() - self.delete_selected_records() - - def copy_selected_records_recursively(self): + new_state = Qt.CheckState.Checked if check_state == Qt.CheckState.Unchecked else Qt.CheckState.Unchecked + for row in selected_items: + self.handle_freeze_change(row, new_state) + if toggle_children: + for index in range(row.childCount()): + child = row.child(index) + self.handle_freeze_change(child, new_state) + + def cut_records(self): + self.copy_records() + self.delete_records() + + def copy_records(self): # Recursive copy items = self.treeWidget_AddressTable.selectedItems() @@ -714,7 +995,7 @@ def index_of(item): items = [] last_index = [-1] # any invalid list of indices are fine for index, item in index_items: - if index[:len(last_index)] == last_index: + if index[: len(last_index)] == last_index: continue # this item is a descendant of the last item items.append(item) last_index = index @@ -729,21 +1010,31 @@ def insert_records(self, records, parent_row, insert_index): rows = [] for rec in records: row = QTreeWidgetItem() - row.setCheckState(FROZEN_COL, Qt.Unchecked) - self.change_address_table_entries(row, *rec[:-1]) + row.setCheckState(FROZEN_COL, Qt.CheckState.Unchecked) + frozen = typedefs.Frozen("", typedefs.FREEZE_TYPE.DEFAULT) + row.setData(FROZEN_COL, Qt.ItemDataRole.UserRole, frozen) + + # Deserialize the address_expr & value_type param + if type(rec[1]) in [list, tuple]: + address_expr = typedefs.PointerChainRequest(*rec[1]) + else: + address_expr = rec[1] + value_type = typedefs.ValueType(*rec[2]) + self.change_address_table_entries(row, rec[0], address_expr, value_type) self.insert_records(rec[-1], row, 0) rows.append(row) parent_row.insertChildren(insert_index, rows) + parent_row.setExpanded(True) - def paste_records(self, insert_after=None, insert_inside=False): + def paste_records(self, insert_inside=False): try: records = ast.literal_eval(app.clipboard().text()) except (SyntaxError, ValueError): - QMessageBox.information(self, "Error", "Invalid clipboard content") + QMessageBox.information(self, tr.ERROR, tr.INVALID_CLIPBOARD) return - insert_row = GuiUtils.get_current_item(self.treeWidget_AddressTable) + insert_row = guiutils.get_current_item(self.treeWidget_AddressTable) root = self.treeWidget_AddressTable.invisibleRootItem() if not insert_row: # this is common when the treeWidget_AddressTable is empty self.insert_records(records, root, self.treeWidget_AddressTable.topLevelItemCount()) @@ -751,152 +1042,322 @@ def paste_records(self, insert_after=None, insert_inside=False): self.insert_records(records, insert_row, 0) else: parent = insert_row.parent() or root - self.insert_records(records, parent, parent.indexOfChild(insert_row) + insert_after) + self.insert_records(records, parent, parent.indexOfChild(insert_row) + 1) self.update_address_table() - def check_if_address_exists(self, address): - root = self.treeWidget_AddressTable.invisibleRootItem() - child_count = root.childCount() - for i in range(child_count): - item = root.child(i) - if item.text(ADDR_COL) == address: - return True + def group_records(self): + selected_items = self.treeWidget_AddressTable.selectedItems() + if self.create_group(): + item_count = self.treeWidget_AddressTable.topLevelItemCount() + last_item = self.treeWidget_AddressTable.topLevelItem(item_count - 1) + for item in selected_items: + parent = item.parent() + if parent: + parent.removeChild(item) + else: + index = self.treeWidget_AddressTable.indexOfTopLevelItem(item) + self.treeWidget_AddressTable.takeTopLevelItem(index) + last_item.addChild(item) + self.treeWidget_AddressTable.setCurrentItem(last_item) + last_item.setExpanded(True) + + def create_group(self): + dialog = InputDialogForm(self, [(tr.ENTER_DESCRIPTION, tr.GROUP)]) + if dialog.exec(): + desc = dialog.get_values() + self.add_entry_to_addresstable(desc, "0x0") + return True return False - def delete_selected_records(self): + def delete_records(self): root = self.treeWidget_AddressTable.invisibleRootItem() for item in self.treeWidget_AddressTable.selectedItems(): (item.parent() or root).removeChild(item) - if not self.check_if_address_exists(item.text(ADDR_COL)): - del FreezeVars[item.text(ADDR_COL)] - - def treeWidget_AddressTable_key_press_event(self, event): - actions = type_defs.KeyboardModifiersTupleDict([ - ((Qt.NoModifier, Qt.Key_Delete), self.delete_selected_records), - ((Qt.ControlModifier, Qt.Key_B), self.browse_region_for_selected_row), - ((Qt.ControlModifier, Qt.Key_D), self.disassemble_selected_row), - ((Qt.NoModifier, Qt.Key_R), self.update_address_table), - ((Qt.NoModifier, Qt.Key_Space), self.toggle_selected_records), - ((Qt.ControlModifier, Qt.Key_X), self.cut_selected_records), - ((Qt.ControlModifier, Qt.Key_C), self.copy_selected_records), - ((Qt.NoModifier, Qt.Key_X), self.cut_selected_records_recursively), - ((Qt.NoModifier, Qt.Key_C), self.copy_selected_records_recursively), - ((Qt.ControlModifier, Qt.Key_V), lambda: self.paste_records(insert_after=False)), - ((Qt.NoModifier, Qt.Key_V), lambda: self.paste_records(insert_after=True)), - ((Qt.NoModifier, Qt.Key_I), lambda: self.paste_records(insert_inside=True)), - ((Qt.NoModifier, Qt.Key_Return), self.treeWidget_AddressTable_edit_value), - ((Qt.ControlModifier, Qt.Key_Return), self.treeWidget_AddressTable_edit_desc), - ((Qt.ControlModifier | Qt.AltModifier, Qt.Key_Return), self.treeWidget_AddressTable_edit_address), - ((Qt.AltModifier, Qt.Key_Return), self.treeWidget_AddressTable_edit_type) - ]) + + def treeWidget_AddressTable_mouse_press_event(self, event: QMouseEvent) -> None: + self.treeWidget_AddressTable.mousePressEvent_original(event) + item = self.treeWidget_AddressTable.itemAt(event.pos()) + column = self.treeWidget_AddressTable.columnAt(event.pos().x()) + # Qt doesn't select rows when checkboxes are clicked + # Ensure that the row is selected when frozen col is clicked + if item and column == FROZEN_COL: + item.setSelected(True) + + def treeWidget_AddressTable_mouse_release_event(self, event: QMouseEvent) -> None: + item = self.treeWidget_AddressTable.itemAt(event.pos()) + column = self.treeWidget_AddressTable.columnAt(event.pos().x()) + if item and column == FROZEN_COL: + old_state = item.checkState(FROZEN_COL) + self.treeWidget_AddressTable.mouseReleaseEvent_original(event) + new_state = item.checkState(FROZEN_COL) + item.setSelected(True) + box_clicked = old_state != new_state + current_item = self.treeWidget_AddressTable.currentItem() + if not box_clicked and new_state == Qt.CheckState.Checked: + self.change_freeze_type(row=current_item) + frozen: typedefs.Frozen = current_item.data(FROZEN_COL, Qt.ItemDataRole.UserRole) + freeze_type = frozen.freeze_type + for selected_item in self.treeWidget_AddressTable.selectedItems(): + if box_clicked: + self.handle_freeze_change(selected_item, new_state) + elif new_state == Qt.CheckState.Checked: + self.change_freeze_type(freeze_type, selected_item) + else: + self.treeWidget_AddressTable.mouseReleaseEvent_original(event) + + def treeWidget_AddressTable_key_press_event(self, event: QKeyEvent): + current_row = guiutils.get_current_item(self.treeWidget_AddressTable) + current_address = current_row.text(ADDR_COL) if current_row else None + actions = typedefs.KeyboardModifiersTupleDict( + [ + (QKeyCombination(Qt.KeyboardModifier.NoModifier, Qt.Key.Key_Delete), self.delete_records), + ( + QKeyCombination(Qt.KeyboardModifier.ControlModifier, Qt.Key.Key_B), + lambda: self.browse_region_for_address(current_address), + ), + ( + QKeyCombination(Qt.KeyboardModifier.ControlModifier, Qt.Key.Key_D), + lambda: self.disassemble_for_address(current_address), + ), + ( + QKeyCombination(Qt.KeyboardModifier.NoModifier, Qt.Key.Key_R), + self.pushButton_RefreshAdressTable_clicked, + ), + (QKeyCombination(Qt.KeyboardModifier.NoModifier, Qt.Key.Key_Space), self.toggle_records), + (QKeyCombination(Qt.KeyboardModifier.ShiftModifier, Qt.Key.Key_Space), self.toggle_records), + ( + QKeyCombination(Qt.KeyboardModifier.ControlModifier, Qt.Key.Key_Space), + lambda: self.toggle_records(True), + ), + (QKeyCombination(Qt.KeyboardModifier.ControlModifier, Qt.Key.Key_X), self.cut_records), + (QKeyCombination(Qt.KeyboardModifier.ControlModifier, Qt.Key.Key_C), self.copy_records), + (QKeyCombination(Qt.KeyboardModifier.ControlModifier, Qt.Key.Key_V), self.paste_records), + (QKeyCombination(Qt.KeyboardModifier.NoModifier, Qt.Key.Key_V), lambda: self.paste_records(True)), + ( + QKeyCombination(Qt.KeyboardModifier.NoModifier, Qt.Key.Key_Return), + self.treeWidget_AddressTable_edit_value, + ), + ( + QKeyCombination(Qt.KeyboardModifier.KeypadModifier, Qt.Key.Key_Enter), + self.treeWidget_AddressTable_edit_value, + ), + ( + QKeyCombination(Qt.KeyboardModifier.ShiftModifier, Qt.Key.Key_Return), + self.treeWidget_AddressTable_edit_offset, + ), + ( + QKeyCombination(Qt.KeyboardModifier.ShiftModifier, Qt.Key.Key_Enter), + self.treeWidget_AddressTable_edit_offset, + ), + ( + QKeyCombination(Qt.KeyboardModifier.ControlModifier, Qt.Key.Key_Return), + self.treeWidget_AddressTable_edit_desc, + ), + ( + QKeyCombination( + Qt.KeyboardModifier.ControlModifier | Qt.KeyboardModifier.AltModifier, Qt.Key.Key_Return + ), + self.treeWidget_AddressTable_edit_address, + ), + ( + QKeyCombination(Qt.KeyboardModifier.AltModifier, Qt.Key.Key_Return), + self.treeWidget_AddressTable_edit_type, + ), + ] + ) try: - actions[event.modifiers(), event.key()]() + actions[QKeyCombination(event.modifiers(), Qt.Key(event.key()))]() except KeyError: self.treeWidget_AddressTable.keyPressEvent_original(event) def update_address_table(self): - if GDB_Engine.currentpid == -1 or self.treeWidget_AddressTable.topLevelItemCount() == 0: + global exp_cache + if debugcore.currentpid == -1 or self.treeWidget_AddressTable.topLevelItemCount() == 0: return it = QTreeWidgetItemIterator(self.treeWidget_AddressTable) - table_contents = [] - address_expr_list = [] - value_type_list = [] - hex_repr_list = [] - rows = [] + mem_handle = debugcore.memory_handle() + basic_math_exp = re.compile(r"^[0-9a-fA-F][/*+\-0-9a-fA-FxX]+$") while True: row = it.value() if not row: break it += 1 - address_expr_list.append(row.data(ADDR_COL, Qt.UserRole)) - value_type_list.append(row.text(TYPE_COL)) - rows.append(row) - try: - address_list = [item.address for item in GDB_Engine.examine_expressions(address_expr_list)] - except type_defs.InferiorRunningException: - address_list = address_expr_list - for address, value_type in zip(address_list, value_type_list): - index, length, zero_terminate, byte_len, hex_repr = GuiUtils.text_to_valuetype(value_type) - table_contents.append((address, index, length, zero_terminate)) - hex_repr_list.append(hex_repr) - new_table_contents = GDB_Engine.read_memory_multiple(table_contents) - for row, address, address_expr, value, hex_repr in zip(rows, address_list, address_expr_list, - new_table_contents, hex_repr_list): - row.setText(ADDR_COL, address or address_expr) - if value is None: - value = "" - elif hex_repr: - value = hex(value) + address_data = row.data(ADDR_COL, Qt.ItemDataRole.UserRole) + if isinstance(address_data, typedefs.PointerChainRequest): + expression = address_data.base_address + else: + expression = address_data + parent = row.parent() + if parent and expression.startswith(("+", "-")): + expression = parent.data(ADDR_COL, Qt.ItemDataRole.UserRole + 1) + expression + if expression in exp_cache: + address = exp_cache[expression] + elif expression.startswith(("+", "-")): # If parent has an empty address + address = expression + elif basic_math_exp.match(expression.replace(" ", "")): + try: + address = hex(eval(expression)) + except: + address = debugcore.examine_expression(expression).address + exp_cache[expression] = address + else: + address = debugcore.examine_expression(expression).address + exp_cache[expression] = address + vt = row.data(TYPE_COL, Qt.ItemDataRole.UserRole) + if isinstance(address_data, typedefs.PointerChainRequest): + # The original base could be a symbol so we have to save it + # This little hack avoids the unnecessary examine_expression call + # TODO: Consider implementing exp_cache inside libpince so we don't need this hack + pointer_chain_req = address_data + if address: + old_base = pointer_chain_req.base_address # save the old base + pointer_chain_req.base_address = address + pointer_chain_result = debugcore.read_pointer_chain(pointer_chain_req) + if pointer_chain_result and pointer_chain_result.get_final_address(): + address = pointer_chain_result.get_final_address_as_hex() + else: + address = None + address_data.base_address = old_base # then set it back + if address: + row.setText(ADDR_COL, f"P->{address}") + else: + row.setText(ADDR_COL, "P->??") + else: + row.setText(ADDR_COL, "P->??") else: - value = str(value) + row.setText(ADDR_COL, address or address_data) + address = "" if not address else address + row.setData(ADDR_COL, Qt.ItemDataRole.UserRole + 1, address) + value = debugcore.read_memory( + address, vt.value_index, vt.length, vt.zero_terminate, vt.value_repr, vt.endian, mem_handle=mem_handle + ) + value = "" if value is None else str(value) row.setText(VALUE_COL, value) + def scan_values(self): + global threadpool + if debugcore.currentpid == -1: + return + search_for = self.validate_search(self.lineEdit_Scan.text(), self.lineEdit_Scan2.text()) + self.QWidget_Toolbox.setEnabled(False) + self.progressBar.setValue(0) + self.progress_bar_timer = QTimer(timeout=self.update_progress_bar) + self.progress_bar_timer.start(100) + scan_thread = Worker(scanmem.send_command, search_for) + scan_thread.signals.finished.connect(self.scan_callback) + threadpool.start(scan_thread) + def resize_address_table(self): self.treeWidget_AddressTable.resizeColumnToContents(FROZEN_COL) # gets the information from the dialog then adds it to addresstable def pushButton_AddAddressManually_clicked(self): - manual_address_dialog = ManualAddressDialogForm() - if manual_address_dialog.exec_(): - desc, address_expr, address_type, length, zero_terminate, hex_repr = manual_address_dialog.get_values() - self.add_entry_to_addresstable(desc, address_expr, address_type, length, zero_terminate) + manual_address_dialog = ManualAddressDialogForm(self) + if manual_address_dialog.exec(): + desc, address_expr, vt = manual_address_dialog.get_values() + self.add_entry_to_addresstable(desc, address_expr, vt) + self.update_address_table() + + def pushButton_RefreshAdressTable_clicked(self): + global exp_cache + exp_cache.clear() + self.update_address_table() def pushButton_MemoryView_clicked(self): self.memory_view_window.showMaximized() self.memory_view_window.activateWindow() def pushButton_Wiki_clicked(self): - SysUtils.execute_shell_command_as_user('python3 -m webbrowser "https://github.com/korcankaraokcu/PINCE/wiki"') + utils.execute_command_as_user('python3 -m webbrowser "https://github.com/korcankaraokcu/PINCE/wiki"') def pushButton_About_clicked(self): - self.about_widget.show() - self.about_widget.activateWindow() + about_widget = AboutWidgetForm(self) + about_widget.show() + about_widget.activateWindow() def pushButton_Settings_clicked(self): - settings_dialog = SettingsDialogForm(self.set_default_settings) - if settings_dialog.exec_(): + settings_dialog = SettingsDialogForm(self, self.set_default_settings) + if settings_dialog.exec(): self.apply_settings() def pushButton_Console_clicked(self): - console_widget = ConsoleWidgetForm() + console_widget = ConsoleWidgetForm(self) console_widget.showMaximized() def checkBox_Hex_stateChanged(self, state): - if state == Qt.Checked: + if Qt.CheckState(state) == Qt.CheckState.Checked: # allows only things that are hex, can also start with 0x - self.lineEdit_Scan.setValidator(QRegExpValidator(QRegExp("(0x)?[A-Fa-f0-9]*$"), parent=self.lineEdit_Scan)) - self.lineEdit_Scan2.setValidator( - QRegExpValidator(QRegExp("(0x)?[A-Fa-f0-9]*$"), parent=self.lineEdit_Scan2)) + self.lineEdit_Scan.setValidator(guiutils.validator_map.get("int_hex")) + self.lineEdit_Scan2.setValidator(guiutils.validator_map.get("int_hex")) else: # sets it back to integers only - self.lineEdit_Scan.setValidator(QRegExpValidator(QRegExp("-?[0-9]*"), parent=self.lineEdit_Scan)) - self.lineEdit_Scan2.setValidator(QRegExpValidator(QRegExp("-?[0-9]*"), parent=self.lineEdit_Scan2)) + self.lineEdit_Scan.setValidator(guiutils.validator_map.get("int")) + self.lineEdit_Scan2.setValidator(guiutils.validator_map.get("int")) - # TODO add a damn keybind for this... def pushButton_NewFirstScan_clicked(self): - if self.scan_mode == type_defs.SCAN_MODE.ONGOING: - self.scan_mode = type_defs.SCAN_MODE.NEW - self.pushButton_NewFirstScan.setText("First Scan") - self.backend.send_command("reset") - self.tableWidget_valuesearchtable.setRowCount(0) - self.comboBox_ValueType.setEnabled(True) - self.pushButton_NextScan.setEnabled(False) - self.comboBox_ScanScope.setEnabled(True) - self.progressBar.setValue(0) + if debugcore.currentpid == -1: self.comboBox_ScanType_init() + return + if self.scan_mode == typedefs.SCAN_MODE.ONGOING: + self.reset_scan() else: - self.scan_mode = type_defs.SCAN_MODE.ONGOING - self.pushButton_NewFirstScan.setText("New Scan") + self.scan_mode = typedefs.SCAN_MODE.ONGOING + self.pushButton_NewFirstScan.setText(tr.NEW_SCAN) self.comboBox_ValueType.setEnabled(False) self.pushButton_NextScan.setEnabled(True) - search_scope = self.comboBox_ScanScope.currentData(Qt.UserRole) - self.backend.send_command("option region_scan_level " + str(search_scope)) + search_scope = self.comboBox_ScanScope.currentData(Qt.ItemDataRole.UserRole) + endian = self.comboBox_Endianness.currentData(Qt.ItemDataRole.UserRole) + scanmem.send_command(f"option region_scan_level {search_scope}") + scanmem.send_command(f"option endianness {endian}") + scanmem.reset() self.comboBox_ScanScope.setEnabled(False) - self.pushButton_NextScan_clicked() # makes code a little simpler to just implement everything in nextscan - self.comboBox_ScanType_init() + self.comboBox_Endianness.setEnabled(False) + self.scan_values() + self.comboBox_ScanType_init() + + def handle_line_edit_scan_key_press_event(self, event): + valid_keys = [Qt.Key.Key_Return, Qt.Key.Key_Enter] + if event.key() in valid_keys and Qt.KeyboardModifier.ControlModifier in event.modifiers(): + self.pushButton_NewFirstScan_clicked() + return + + if event.key() in valid_keys: + if self.scan_mode == typedefs.SCAN_MODE.ONGOING: + self.pushButton_NextScan_clicked() + else: + self.pushButton_NewFirstScan_clicked() + return + + def lineEdit_Scan_on_key_press_event(self, event): + self.handle_line_edit_scan_key_press_event(event) + self.lineEdit_Scan.keyPressEvent_original(event) + + def lineEdit_Scan2_on_key_press_event(self, event): + self.handle_line_edit_scan_key_press_event(event) + self.lineEdit_Scan2.keyPressEvent_original(event) + + def pushButton_UndoScan_clicked(self): + global threadpool + if debugcore.currentpid == -1: + return + undo_thread = Worker(scanmem.undo_scan) + undo_thread.signals.finished.connect(self.scan_callback) + threadpool.start(undo_thread) + self.pushButton_UndoScan.setEnabled(False) # we can undo once so set it to false and re-enable at next scan def comboBox_ScanType_current_index_changed(self): - if self.comboBox_ScanType.currentData(Qt.UserRole) == type_defs.SCAN_TYPE.BETWEEN: + hidden_types = [ + typedefs.SCAN_TYPE.INCREASED, + typedefs.SCAN_TYPE.DECREASED, + typedefs.SCAN_TYPE.CHANGED, + typedefs.SCAN_TYPE.UNCHANGED, + typedefs.SCAN_TYPE.UNKNOWN, + ] + if self.comboBox_ScanType.currentData(Qt.ItemDataRole.UserRole) in hidden_types: + self.widget_Scan.setEnabled(False) + else: + self.widget_Scan.setEnabled(True) + if self.comboBox_ScanType.currentData(Qt.ItemDataRole.UserRole) == typedefs.SCAN_TYPE.BETWEEN: self.label_Between.setVisible(True) self.lineEdit_Scan2.setVisible(True) else: @@ -904,268 +1365,427 @@ def comboBox_ScanType_current_index_changed(self): self.lineEdit_Scan2.setVisible(False) def comboBox_ScanType_init(self): - current_type = self.comboBox_ScanType.currentData(Qt.UserRole) + scan_type_text = { + typedefs.SCAN_TYPE.EXACT: tr.EXACT, + typedefs.SCAN_TYPE.NOT: tr.NOT, + typedefs.SCAN_TYPE.INCREASED: tr.INCREASED, + typedefs.SCAN_TYPE.INCREASED_BY: tr.INCREASED_BY, + typedefs.SCAN_TYPE.DECREASED: tr.DECREASED, + typedefs.SCAN_TYPE.DECREASED_BY: tr.DECREASED_BY, + typedefs.SCAN_TYPE.LESS: tr.LESS_THAN, + typedefs.SCAN_TYPE.MORE: tr.MORE_THAN, + typedefs.SCAN_TYPE.BETWEEN: tr.BETWEEN, + typedefs.SCAN_TYPE.CHANGED: tr.CHANGED, + typedefs.SCAN_TYPE.UNCHANGED: tr.UNCHANGED, + typedefs.SCAN_TYPE.UNKNOWN: tr.UNKNOWN_VALUE, + } + current_type = self.comboBox_ScanType.currentData(Qt.ItemDataRole.UserRole) + value_type = self.comboBox_ValueType.currentData(Qt.ItemDataRole.UserRole) self.comboBox_ScanType.clear() - if self.scan_mode == type_defs.SCAN_MODE.NEW: - items = [type_defs.SCAN_TYPE.EXACT, type_defs.SCAN_TYPE.LESS, type_defs.SCAN_TYPE.MORE, - type_defs.SCAN_TYPE.BETWEEN, type_defs.SCAN_TYPE.UNKNOWN] - else: - items = [type_defs.SCAN_TYPE.EXACT, type_defs.SCAN_TYPE.INCREASED, type_defs.SCAN_TYPE.DECREASED, - type_defs.SCAN_TYPE.LESS, type_defs.SCAN_TYPE.MORE, type_defs.SCAN_TYPE.BETWEEN, - type_defs.SCAN_TYPE.CHANGED, type_defs.SCAN_TYPE.UNCHANGED] + items = typedefs.SCAN_TYPE.get_list(self.scan_mode, value_type) old_index = 0 for index, type_index in enumerate(items): if current_type == type_index: old_index = index - self.comboBox_ScanType.addItem(type_defs.scan_type_to_text_dict[type_index], type_index) + self.comboBox_ScanType.addItem(scan_type_text[type_index], type_index) self.comboBox_ScanType.setCurrentIndex(old_index) def comboBox_ScanScope_init(self): - items = [type_defs.SCAN_SCOPE.BASIC, type_defs.SCAN_SCOPE.NORMAL, type_defs.SCAN_SCOPE.FULL] - for scope in items: - self.comboBox_ScanScope.addItem(type_defs.scan_scope_to_text_dict[scope], scope) - self.comboBox_ScanScope.setCurrentIndex(1) # type_defs.SCAN_SCOPE.NORMAL + scan_scope_text = [ + (typedefs.SCAN_SCOPE.BASIC, tr.BASIC), + (typedefs.SCAN_SCOPE.NORMAL, tr.NORMAL), + (typedefs.SCAN_SCOPE.FULL_RW, tr.RW), + (typedefs.SCAN_SCOPE.FULL, tr.FULL), + ] + for scope, text in scan_scope_text: + self.comboBox_ScanScope.addItem(text, scope) + self.comboBox_ScanScope.setCurrentIndex(1) # typedefs.SCAN_SCOPE.NORMAL def comboBox_ValueType_init(self): self.comboBox_ValueType.clear() - items = [type_defs.VALUE_INDEX.INDEX_BYTE, type_defs.VALUE_INDEX.INDEX_2BYTES, - type_defs.VALUE_INDEX.INDEX_4BYTES, type_defs.VALUE_INDEX.INDEX_8BYTES, - type_defs.VALUE_INDEX.INDEX_FLOAT, type_defs.VALUE_INDEX.INDEX_DOUBLE, - type_defs.VALUE_INDEX.INDEX_STRING_UTF8, type_defs.VALUE_INDEX.INDEX_AOB] - for type_index in items: - self.comboBox_ValueType.addItem(type_defs.index_to_text_dict[type_index], type_index) - self.comboBox_ValueType.setCurrentIndex(type_defs.VALUE_INDEX.INDEX_4BYTES) - - # :doc: + for value_index, value_text in typedefs.scan_index_to_text_dict.items(): + self.comboBox_ValueType.addItem(value_text, value_index) + self.comboBox_ValueType.setCurrentIndex(typedefs.SCAN_INDEX.INT32) + self.comboBox_ValueType_current_index_changed() + # adds things like 0x when searching for etc, basically just makes the line valid for scanmem # this should cover most things, more things might be added later if need be - def validate_search(self, search_for, search_for2): - type_index = self.comboBox_ScanType.currentData(Qt.UserRole) + def validate_search(self, search_for: str, search_for2: str): + type_index = self.comboBox_ScanType.currentData(Qt.ItemDataRole.UserRole) symbol_map = { - type_defs.SCAN_TYPE.INCREASED: "+", - type_defs.SCAN_TYPE.DECREASED: "-", - type_defs.SCAN_TYPE.CHANGED: "!=", - type_defs.SCAN_TYPE.UNCHANGED: "=", - type_defs.SCAN_TYPE.UNKNOWN: "snapshot" + typedefs.SCAN_TYPE.INCREASED: "+", + typedefs.SCAN_TYPE.DECREASED: "-", + typedefs.SCAN_TYPE.CHANGED: "!=", + typedefs.SCAN_TYPE.UNCHANGED: "=", + typedefs.SCAN_TYPE.UNKNOWN: "snapshot", } if type_index in symbol_map: return symbol_map[type_index] # none of these should be possible to be true at the same time - current_type = self.comboBox_ValueType.currentData(Qt.UserRole) - if current_type == type_defs.VALUE_INDEX.INDEX_FLOAT: - # this is odd, since when searching for floats from command line it uses `.` and not `,` - search_for = search_for.replace(".", ",") - search_for2 = search_for2.replace(".", ",") - elif type_defs.VALUE_INDEX.is_string(current_type): - search_for = "\" " + search_for + scan_index = self.comboBox_ValueType.currentData(Qt.ItemDataRole.UserRole) + if scan_index >= typedefs.SCAN_INDEX.FLOAT_ANY and scan_index <= typedefs.SCAN_INDEX.FLOAT64: + # Adjust to locale whatever the input + if QLocale.system().decimalPoint() == ".": + search_for = search_for.replace(",", ".") + search_for2 = search_for2.replace(",", ".") + else: + search_for = search_for.replace(".", ",") + search_for2 = search_for2.replace(".", ",") + elif scan_index == typedefs.SCAN_INDEX.STRING: + search_for = '" ' + search_for elif self.checkBox_Hex.isChecked(): - if not search_for.startswith("0x"): - search_for = "0x" + search_for - if not search_for2.startswith("0x"): - search_for2 = "0x" + search_for2 - if type_index == type_defs.SCAN_TYPE.BETWEEN: + if not search_for.startswith(("0x", "-0x")): + negative_str = "-" if search_for.startswith("-") else "" + search_for = negative_str + "0x" + search_for.lstrip("-") + if not search_for2.startswith(("0x", "-0x")): + negative_str = "-" if search_for.startswith("-") else "" + search_for2 = negative_str + "0x" + search_for2.lstrip("-") + + if type_index == typedefs.SCAN_TYPE.BETWEEN: return search_for + ".." + search_for2 cmp_symbols = { - type_defs.SCAN_TYPE.LESS: "<", - type_defs.SCAN_TYPE.MORE: ">" + typedefs.SCAN_TYPE.INCREASED_BY: "+", + typedefs.SCAN_TYPE.DECREASED_BY: "-", + typedefs.SCAN_TYPE.LESS: "<", + typedefs.SCAN_TYPE.MORE: ">", } if type_index in cmp_symbols: return cmp_symbols[type_index] + " " + search_for + + if type_index == typedefs.SCAN_TYPE.NOT: + search_for = "!= " + search_for return search_for def pushButton_NextScan_clicked(self): - global ProgressRun - search_for = self.validate_search(self.lineEdit_Scan.text(), self.lineEdit_Scan2.text()) - - # ProgressBar - global threadpool - threadpool.start(Worker(self.update_progress_bar)) - # TODO add some validation for the search command - self.backend.send_command(search_for) - matches = self.backend.matches() - ProgressRun = 0 - self.label_MatchCount.setText("Match count: {}".format(self.backend.get_match_count())) + self.scan_values() + self.pushButton_UndoScan.setEnabled(True) + + def pushButton_ScanRegions_clicked(self): + scan_regions_dialog = ScanRegionsDialogForm(self) + scan_regions_dialog.exec() + + def scan_callback(self): + self.progress_bar_timer.stop() + self.progressBar.setValue(100) + matches = scanmem.matches() + self.update_match_count() self.tableWidget_valuesearchtable.setRowCount(0) - current_type = self.comboBox_ValueType.currentData(Qt.UserRole) + current_type = self.comboBox_ValueType.currentData(Qt.ItemDataRole.UserRole) length = self._scan_to_length(current_type) - - for n, address, offset, region_type, value, t in matches: - n = int(n) + mem_handle = debugcore.memory_handle() + row = 0 # go back to using n when unknown issue gets fixed + self.tableWidget_valuesearchtable.setSortingEnabled(False) + for n, address, offset, region_type, val, result_type in matches: address = "0x" + address - self.tableWidget_valuesearchtable.insertRow( - self.tableWidget_valuesearchtable.rowCount()) - self.tableWidget_valuesearchtable.setItem(n, 0, QTableWidgetItem(address)) - if current_type == type_defs.VALUE_INDEX.INDEX_STRING_UTF8: - value = GDB_Engine.read_memory(address, current_type, length) - self.tableWidget_valuesearchtable.setItem(n, 1, QTableWidgetItem(value)) - self.tableWidget_valuesearchtable.setItem(n, 2, QTableWidgetItem(value)) + result = result_type.split(" ")[0] + if result == "unknown": # Ignore unknown entries for now + continue + value_index = typedefs.scanmem_result_to_index_dict[result] + if self.checkBox_Hex.isChecked(): + value_repr = typedefs.VALUE_REPR.HEX + elif typedefs.VALUE_INDEX.is_integer(value_index) and result.endswith("s"): + value_repr = typedefs.VALUE_REPR.SIGNED + else: + value_repr = typedefs.VALUE_REPR.UNSIGNED + endian = self.comboBox_Endianness.currentData(Qt.ItemDataRole.UserRole) + current_item = QTableWidgetItem(address) + current_item.setData(Qt.ItemDataRole.UserRole, (value_index, value_repr, endian)) + value = str(debugcore.read_memory(address, value_index, length, True, value_repr, endian, mem_handle)) + self.tableWidget_valuesearchtable.insertRow(row) + self.tableWidget_valuesearchtable.setItem(row, SEARCH_TABLE_ADDRESS_COL, current_item) + self.tableWidget_valuesearchtable.setItem(row, SEARCH_TABLE_VALUE_COL, QTableWidgetItem(value)) + self.tableWidget_valuesearchtable.setItem(row, SEARCH_TABLE_PREVIOUS_COL, QTableWidgetItem(value)) + row += 1 + if row == 1000: + break + self.tableWidget_valuesearchtable.resizeColumnsToContents() + self.tableWidget_valuesearchtable.setSortingEnabled(True) + self.QWidget_Toolbox.setEnabled(True) def _scan_to_length(self, type_index): - if type_defs.VALUE_INDEX.has_length(type_index): - if type_index == type_defs.VALUE_INDEX.INDEX_AOB: - return self.lineEdit_Scan.text().count(" ") + 1 + if type_index == typedefs.SCAN_INDEX.AOB: + return self.lineEdit_Scan.text().count(" ") + 1 + if type_index == typedefs.SCAN_INDEX.STRING: return len(self.lineEdit_Scan.text()) return 0 - @GDB_Engine.execute_with_temporary_interruption + def update_match_count(self): + match_count = scanmem.get_match_count() + if match_count > 1000: + self.label_MatchCount.setText(tr.MATCH_COUNT_LIMITED.format(match_count, 1000)) + else: + self.label_MatchCount.setText(tr.MATCH_COUNT.format(match_count)) + def tableWidget_valuesearchtable_cell_double_clicked(self, row, col): - addr = self.tableWidget_valuesearchtable.item(row, 0).text() - current_type = self.comboBox_ValueType.currentData(Qt.UserRole) - length = self._scan_to_length(current_type) - self.add_entry_to_addresstable("", addr, current_type, length) + current_item = self.tableWidget_valuesearchtable.item(row, SEARCH_TABLE_ADDRESS_COL) + value_index, value_repr, endian = current_item.data(Qt.ItemDataRole.UserRole) + length = self._scan_to_length(self.comboBox_ValueType.currentData(Qt.ItemDataRole.UserRole)) + vt = typedefs.ValueType(value_index, length, True, value_repr, endian) + self.add_entry_to_addresstable(tr.NO_DESCRIPTION, current_item.text(), vt) + self.update_address_table() - def comboBox_ValueType_current_index_changed(self): - # used for making our types in the combo box into what scanmem uses - PINCE_TYPES_TO_SCANMEM = { - type_defs.VALUE_INDEX.INDEX_BYTE: "int8", - type_defs.VALUE_INDEX.INDEX_2BYTES: "int16", - type_defs.VALUE_INDEX.INDEX_4BYTES: "int32", - type_defs.VALUE_INDEX.INDEX_8BYTES: "int64", - type_defs.VALUE_INDEX.INDEX_FLOAT: "float32", - type_defs.VALUE_INDEX.INDEX_DOUBLE: "float64", - type_defs.VALUE_INDEX.INDEX_STRING_UTF8: "string", - type_defs.VALUE_INDEX.INDEX_AOB: "bytearray" - } - current_type = self.comboBox_ValueType.currentData(Qt.UserRole) - validator_map = { - "int": QRegExpValidator(QRegExp("-?[0-9]*"), parent=self.lineEdit_Scan), # integers - "float": QRegExpValidator(QRegExp("-?[0-9]+[.,]?[0-9]*")), - # floats, should work fine with the small amount of testing I did - "bytearray": QRegExpValidator(QRegExp("^(([A-Fa-f0-9?]{2} +)+)$"), parent=self.lineEdit_Scan), - # array of bytes - "string": None + def tableWidget_valuesearchtable_key_press_event(self, event: QKeyEvent) -> None: + current_item = self.tableWidget_valuesearchtable.currentItem() + if debugcore.currentpid == -1 or not current_item: + return + current_address = self.tableWidget_valuesearchtable.item(current_item.row(), SEARCH_TABLE_ADDRESS_COL).text() + actions = typedefs.KeyboardModifiersTupleDict( + [ + ( + QKeyCombination(Qt.KeyboardModifier.ControlModifier, Qt.Key.Key_C), + self.copy_valuesearchtable_selection, + ), + ( + QKeyCombination(Qt.KeyboardModifier.ControlModifier, Qt.Key.Key_B), + lambda: self.browse_region_for_address(current_address), + ), + ( + QKeyCombination(Qt.KeyboardModifier.ControlModifier, Qt.Key.Key_D), + lambda: self.disassemble_for_address(current_address), + ), + ( + QKeyCombination(Qt.KeyboardModifier.NoModifier, Qt.Key.Key_Delete), + self.delete_valuesearchtable_selection, + ), + ] + ) + try: + actions[QKeyCombination(event.modifiers(), Qt.Key(event.key()))]() + except KeyError: + self.tableWidget_valuesearchtable.keyPressEvent_original(event) + + def tableWidget_valuesearchtable_context_menu_event(self, event: QContextMenuEvent) -> None: + selected_indexes = self.tableWidget_valuesearchtable.selectionModel().selectedRows() + if debugcore.currentpid == -1 or not selected_indexes: + return + current_row = self.tableWidget_valuesearchtable.currentItem().row() + address = self.tableWidget_valuesearchtable.item(current_row, SEARCH_TABLE_ADDRESS_COL).text() + menu = QMenu() + if len(selected_indexes) > 1: + copy_selection = menu.addAction(f"{tr.COPY_ADDRESSES}[Ctrl+C]") + else: + copy_selection = menu.addAction(f"{tr.COPY_ADDRESS}[Ctrl+C]") + menu.addSeparator() + browse_region = menu.addAction(f"{tr.BROWSE_MEMORY_REGION}[Ctrl+B]") + disassemble = menu.addAction(f"{tr.DISASSEMBLE_ADDRESS}[Ctrl+D]") + menu.addSeparator() + delete_selection = menu.addAction(f"{tr.DELETE_SELECTION}[Del]") + font_size = self.tableWidget_valuesearchtable.font().pointSize() + menu.setStyleSheet(f"font-size: {font_size}pt;") + action = menu.exec(event.globalPos()) + actions = { + copy_selection: self.copy_valuesearchtable_selection, + browse_region: lambda: self.browse_region_for_address(address), + disassemble: lambda: self.disassemble_for_address(address), + delete_selection: self.delete_valuesearchtable_selection, } - scanmem_type = PINCE_TYPES_TO_SCANMEM[current_type] + try: + actions[action]() + except KeyError: + pass + + def copy_valuesearchtable_selection(self): + selected_indexes = self.tableWidget_valuesearchtable.selectionModel().selectedRows() + address_list = [] + for index in selected_indexes: + row = index.row() + address = self.tableWidget_valuesearchtable.item(row, SEARCH_TABLE_ADDRESS_COL).text() + address_list.append(address) + app.clipboard().setText(" ".join(address_list)) + + def delete_valuesearchtable_selection(self): + selected_rows = self.tableWidget_valuesearchtable.selectedItems() + if not selected_rows: + return + + # get the row indexes + rows = set() + for item in selected_rows: + rows.add(item.row()) + + scanmem.send_command("delete {}".format(",".join([str(row) for row in rows]))) + + # remove the rows from the table - removing in reverse sorted order to avoid index issues + for row in sorted(rows, reverse=True): + self.tableWidget_valuesearchtable.removeRow(row) + self.update_match_count() + + def comboBox_ValueType_current_index_changed(self): + current_type = self.comboBox_ValueType.currentData(Qt.ItemDataRole.UserRole) + scanmem_type = typedefs.scan_index_to_scanmem_dict[current_type] validator_str = scanmem_type # used to get the correct validator # TODO this can probably be made to look nicer, though it doesn't really matter if "int" in validator_str: validator_str = "int" self.checkBox_Hex.setEnabled(True) + # keep hex validator if hex is checked + if self.checkBox_Hex.isChecked(): + validator_str = "int_hex" else: self.checkBox_Hex.setChecked(False) self.checkBox_Hex.setEnabled(False) - if "float" in validator_str: + if "float" in validator_str or validator_str == "number": validator_str = "float" - self.lineEdit_Scan.setValidator(validator_map[validator_str]) - self.lineEdit_Scan2.setValidator(validator_map[validator_str]) - self.backend.send_command("option scan_data_type {}".format(scanmem_type)) + self.comboBox_ScanType_init() + self.lineEdit_Scan.setValidator(guiutils.validator_map[validator_str]) + self.lineEdit_Scan2.setValidator(guiutils.validator_map[validator_str]) + scanmem.send_command("option scan_data_type {}".format(scanmem_type)) # according to scanmem instructions you should always do `reset` after changing type - self.backend.send_command("reset") - - # shows the process select window + scanmem.reset() def pushButton_AttachProcess_clicked(self): self.processwindow = ProcessForm(self) self.processwindow.show() def pushButton_Open_clicked(self): - pct_file_path = SysUtils.get_user_path(type_defs.USER_PATHS.CHEAT_TABLES_PATH) - file_paths = QFileDialog.getOpenFileNames(self, "Open PCT file(s)", pct_file_path, - "PINCE Cheat Table (*.pct);;All files (*)")[0] + file_paths, _ = QFileDialog.getOpenFileNames(self, tr.OPEN_PCT_FILE, None, tr.FILE_TYPES_PCT) if not file_paths: return - if self.treeWidget_AddressTable.topLevelItemCount() > 0: - if InputDialogForm(item_list=[("Clear existing address table?",)]).exec_(): - self.treeWidget_AddressTable.clear() - + self.clear_address_table() for file_path in file_paths: - content = SysUtils.load_file(file_path) + content = utils.load_file(file_path) if content is None: - QMessageBox.information(self, "Error", "File " + file_path + " does not exist, " + - "is inaccessible or contains invalid content. Terminating...") + QMessageBox.information(self, tr.ERROR, tr.FILE_LOAD_ERROR.format(file_path)) break - self.insert_records(content, self.treeWidget_AddressTable.invisibleRootItem(), - self.treeWidget_AddressTable.topLevelItemCount()) + self.insert_records( + content, + self.treeWidget_AddressTable.invisibleRootItem(), + self.treeWidget_AddressTable.topLevelItemCount(), + ) def pushButton_Save_clicked(self): - pct_file_path = SysUtils.get_user_path(type_defs.USER_PATHS.CHEAT_TABLES_PATH) - file_path = QFileDialog.getSaveFileName(self, "Save PCT file", pct_file_path, - "PINCE Cheat Table (*.pct);;All files (*)")[0] + file_path, _ = QFileDialog.getSaveFileName(self, tr.SAVE_PCT_FILE, None, tr.FILE_TYPES_PCT) if not file_path: return - content = [self.read_address_table_recursively(self.treeWidget_AddressTable.topLevelItem(i)) - for i in range(self.treeWidget_AddressTable.topLevelItemCount())] - file_path = SysUtils.append_file_extension(file_path, "pct") - if not SysUtils.save_file(content, file_path): - QMessageBox.information(self, "Error", "Cannot save to file") + content = [ + self.read_address_table_recursively(self.treeWidget_AddressTable.topLevelItem(i)) + for i in range(self.treeWidget_AddressTable.topLevelItemCount()) + ] + file_path = utils.append_file_extension(file_path, "pct") + if not utils.save_file(content, file_path): + QMessageBox.information(self, tr.ERROR, tr.FILE_SAVE_ERROR) # Returns: a bool value indicates whether the operation succeeded. - def attach_to_pid(self, pid): - attach_result = GDB_Engine.attach(pid, gdb_path, ignore_sigsegv) - if attach_result[0] == type_defs.ATTACH_RESULT.ATTACH_SUCCESSFUL: - GDB_Engine.set_logging(gdb_logging) - self.backend.send_command("pid {}".format(pid)) + def attach_to_pid(self, pid: int): + attach_result = debugcore.attach(pid, settings.gdb_path) + if attach_result == typedefs.ATTACH_RESULT.SUCCESSFUL: + self.apply_after_init() + scanmem.pid(pid) + ptrscan.set_process(pid) + if debugcore.get_inferior_arch() == typedefs.INFERIOR_ARCH.ARCH_64: + ptr_size = 8 + else: + ptr_size = 4 + ptrscan.set_bitness(ptr_size) self.on_new_process() + process_signals.attach.emit() # TODO: This makes PINCE call on_process_stop twice when attaching # TODO: Signal design might have to change to something like mutexes eventually self.memory_view_window.on_process_stop() - GDB_Engine.continue_inferior() + debugcore.continue_inferior() return True else: - QMessageBox.information(app.focusWidget(), "Error", attach_result[1]) + messages = { + typedefs.ATTACH_RESULT.ATTACH_SELF: tr.SMARTASS, # easter egg + typedefs.ATTACH_RESULT.PROCESS_NOT_VALID: tr.PROCESS_NOT_VALID, + typedefs.ATTACH_RESULT.ALREADY_DEBUGGING: tr.ALREADY_DEBUGGING, + typedefs.ATTACH_RESULT.ALREADY_TRACED: tr.ALREADY_TRACED.format(utils.is_traced(pid)), + typedefs.ATTACH_RESULT.PERM_DENIED: tr.PERM_DENIED, + } + QMessageBox.information(app.focusWidget(), tr.ERROR, messages[attach_result]) return False # Returns: a bool value indicates whether the operation succeeded. def create_new_process(self, file_path, args, ld_preload_path): - if GDB_Engine.create_process(file_path, args, ld_preload_path, ignore_sigsegv): - GDB_Engine.set_logging(gdb_logging) + if debugcore.create_process(file_path, args, ld_preload_path): + self.apply_after_init() self.on_new_process() return True else: - QMessageBox.information(app.focusWidget(), "Error", "An error occurred while trying to create process") + QMessageBox.information(app.focusWidget(), tr.ERROR, tr.CREATE_PROCESS_ERROR) self.on_inferior_exit() return False - # This is called whenever a new process is created/attached to by PINCE - # in order to change the form appearance + # Changes appearance whenever a new process is created or attached def on_new_process(self): - # TODO add scanmem attachment here - p = SysUtils.get_process_information(GDB_Engine.currentpid) - self.label_SelectedProcess.setText(str(p.pid) + " - " + p.name()) + name = utils.get_process_name(debugcore.currentpid) + self.label_SelectedProcess.setText(str(debugcore.currentpid) + " - " + name) # enable scan GUI - self.lineEdit_Scan.setPlaceholderText("Scan for") - # self.QWidget_Toolbox.setEnabled(True) - # self.pushButton_NextScan.setEnabled(False) - # self.pushButton_UndoScan.setEnabled(False) - - def delete_address_table_contents(self): - confirm_dialog = InputDialogForm(item_list=[("This will clear the contents of address table\nProceed?",)]) - if confirm_dialog.exec_(): + self.lineEdit_Scan.setPlaceholderText(tr.SCAN_FOR) + self.QWidget_Toolbox.setEnabled(True) + self.pushButton_NextScan.setEnabled(False) + self.pushButton_UndoScan.setEnabled(False) + self.pushButton_AddAddressManually.setEnabled(True) + self.pushButton_MemoryView.setEnabled(True) + + # stop flashing attach button, timer will stop automatically on false value + self.flashAttachButton = False + + def clear_address_table(self): + if self.treeWidget_AddressTable.topLevelItemCount() == 0: + return + confirm_dialog = InputDialogForm(self, [(tr.CLEAR_TABLE,)]) + if confirm_dialog.exec(): self.treeWidget_AddressTable.clear() def copy_to_address_table(self): i = -1 - current_type = self.comboBox_ValueType.currentData(Qt.UserRole) - length = self._scan_to_length(current_type) + length = self._scan_to_length(self.comboBox_ValueType.currentData(Qt.ItemDataRole.UserRole)) for row in self.tableWidget_valuesearchtable.selectedItems(): i = i + 1 if i % 3 == 0: - self.add_entry_to_addresstable("", row.text(), current_type, length) + value_index, value_repr, endian = row.data(Qt.ItemDataRole.UserRole) + vt = typedefs.ValueType(value_index, length, True, value_repr, endian) + self.add_entry_to_addresstable(tr.NO_DESCRIPTION, row.text(), vt) + self.update_address_table() + + def reset_scan(self): + self.scan_mode = typedefs.SCAN_MODE.NEW + self.pushButton_NewFirstScan.setText(tr.FIRST_SCAN) + scanmem.reset() + self.tableWidget_valuesearchtable.setRowCount(0) + self.comboBox_ValueType.setEnabled(True) + self.comboBox_ScanScope.setEnabled(True) + self.comboBox_Endianness.setEnabled(True) + self.pushButton_NextScan.setEnabled(False) + self.pushButton_UndoScan.setEnabled(False) + self.progressBar.setValue(0) + self.label_MatchCount.setText(tr.MATCH_COUNT.format(0)) def on_inferior_exit(self): - if GDB_Engine.currentpid == -1: - self.on_status_running() - GDB_Engine.init_gdb(gdb_path, ignore_sigsegv) - GDB_Engine.set_logging(gdb_logging) - self.label_SelectedProcess.setText("No Process Selected") + self.pushButton_MemoryView.setEnabled(False) + self.pushButton_AddAddressManually.setEnabled(False) + self.QWidget_Toolbox.setEnabled(False) + self.lineEdit_Scan.setText("") + self.reset_scan() + self.on_status_running() + self.flashAttachButton = True + self.flashAttachButtonTimer.start(100) + self.label_SelectedProcess.setText(tr.NO_PROCESS_SELECTED) + self.memory_view_window.setWindowTitle(tr.NO_PROCESS_SELECTED) + gdb_path = settings.gdb_path + if os.environ.get("APPDIR"): + gdb_path = utils.get_default_gdb_path() + debugcore.init_gdb(gdb_path) + self.apply_after_init() + process_signals.exit.emit() def on_status_detached(self): self.label_SelectedProcess.setStyleSheet("color: blue") - self.label_InferiorStatus.setText("[detached]") + self.label_InferiorStatus.setText(tr.STATUS_DETACHED) self.label_InferiorStatus.setVisible(True) self.label_InferiorStatus.setStyleSheet("color: blue") def on_status_stopped(self): self.label_SelectedProcess.setStyleSheet("color: red") - self.label_InferiorStatus.setText("[stopped]") + self.label_InferiorStatus.setText(tr.STATUS_STOPPED) self.label_InferiorStatus.setVisible(True) self.label_InferiorStatus.setStyleSheet("color: red") - self.update_address_table() def on_status_running(self): self.label_SelectedProcess.setStyleSheet("") @@ -1173,23 +1793,18 @@ def on_status_running(self): # closes all windows on exit def closeEvent(self, event): - GDB_Engine.detach() + debugcore.detach() app.closeAllWindows() - # checks if item is a duplicate of something currently being frozen - def get_checked_state(self, address): - global FreezeVars - if address in FreezeVars: - return Qt.Checked - else: - return Qt.Unchecked - - def add_entry_to_addresstable(self, description, address_expr, address_type, length=0, zero_terminate=True): + # Call update_address_table manually after this + def add_entry_to_addresstable(self, description, address_expr, value_type=None): current_row = QTreeWidgetItem() - current_row.setCheckState(FROZEN_COL, self.get_checked_state(address_expr)) - address_type_text = GuiUtils.valuetype_to_text(address_type, length, zero_terminate) + current_row.setCheckState(FROZEN_COL, Qt.CheckState.Unchecked) + frozen = typedefs.Frozen("", typedefs.FREEZE_TYPE.DEFAULT) + current_row.setData(FROZEN_COL, Qt.ItemDataRole.UserRole, frozen) + value_type = typedefs.ValueType() if not value_type else value_type self.treeWidget_AddressTable.addTopLevelItem(current_row) - self.change_address_table_entries(current_row, description, address_expr, address_type_text) + self.change_address_table_entries(current_row, description, address_expr, value_type) self.show() # In case of getting called from elsewhere self.activateWindow() @@ -1198,432 +1813,641 @@ def treeWidget_AddressTable_item_double_clicked(self, row, column): VALUE_COL: self.treeWidget_AddressTable_edit_value, DESC_COL: self.treeWidget_AddressTable_edit_desc, ADDR_COL: self.treeWidget_AddressTable_edit_address, - TYPE_COL: self.treeWidget_AddressTable_edit_type + TYPE_COL: self.treeWidget_AddressTable_edit_type, } action_for_column = collections.defaultdict(lambda *args: lambda: None, action_for_column) action_for_column[column]() # ---------------------------------------------------- - # Async Functions + # QTimer loops def update_progress_bar(self): - global ProgressRun - global Exiting - self.progressBar.setValue(0) - ProgressRun = 1 - while (ProgressRun == 1 and Exiting == 0): - sleep(0.1) - value = int(round(self.backend.get_scan_progress() * 100)) - self.progressBar.setValue(value) - - def update_address_table_loop(self): - while (Exiting == 0): - sleep(table_update_interval / 1000) - if (update_table): - try: - self.update_address_table() - except: - print("Update Table failed :(") + value = int(round(scanmem.get_scan_progress() * 100)) + self.progressBar.setValue(value) - def freeze(self): - while (FreezeStop == 0 and Exiting == 0): - sleep(FreezeInterval / 1000) - for x in FreezeVars: - GDB_Engine.write_memory(x, FreezeVars[x][0], FreezeVars[x][1]) + # Loop restarts itself to wait for function execution, same for the functions below + def address_table_loop(self): + if self.update_table and not exiting: + try: + self.update_address_table() + except: + traceback.print_exc() + self.address_table_timer.start(self.table_update_interval) + + def search_table_loop(self): + if not exiting: + try: + self.update_search_table() + except: + traceback.print_exc() + self.search_table_timer.start(500) + + def freeze_loop(self): + if not exiting: + try: + self.freeze() + except: + traceback.print_exc() + self.freeze_timer.start(self.freeze_interval) # ---------------------------------------------------- - def freeze_consistancy(self, address, constant): - root = self.treeWidget_AddressTable.invisibleRootItem() - child_count = root.childCount() - for i in range(child_count): - item = root.child(i) - if item.text(ADDR_COL) == address: - if item.checkState(0) != constant: - item.setCheckState(0, constant) - - def treeWidget_AddressTable_item_clicked(self, row, column): - global FreezeVars - global FreezeStop - global FreezeInterval - global threadpool - if (column == 0): - if (row.checkState(0) == Qt.Checked): - value = row.text(VALUE_COL) - value_index = GuiUtils.text_to_valuetype(row.text(TYPE_COL))[0] - if (len(FreezeVars) == 0): - FreezeStop = 0 - FreezeThread = Worker(self.freeze) - threadpool.start(FreezeThread) - FreezeVars[row.text(ADDR_COL)] = [value_index, value] - self.freeze_consistancy(row.text(ADDR_COL), Qt.Checked) + def update_search_table(self): + if debugcore.currentpid == -1: + return + row_count = self.tableWidget_valuesearchtable.rowCount() + if row_count > 0: + length = self._scan_to_length(self.comboBox_ValueType.currentData(Qt.ItemDataRole.UserRole)) + mem_handle = debugcore.memory_handle() + for row_index in range(row_count): + address_item = self.tableWidget_valuesearchtable.item(row_index, SEARCH_TABLE_ADDRESS_COL) + value_item = self.tableWidget_valuesearchtable.item(row_index, SEARCH_TABLE_VALUE_COL) + previous_text = self.tableWidget_valuesearchtable.item(row_index, SEARCH_TABLE_PREVIOUS_COL).text() + value_index, value_repr, endian = address_item.data(Qt.ItemDataRole.UserRole) + address = address_item.text() + new_value = str( + debugcore.read_memory( + address, value_index, length, value_repr=value_repr, endian=endian, mem_handle=mem_handle + ) + ) + if new_value != previous_text: + value_item.setForeground(QBrush(QColor(255, 0, 0))) + value_item.setText(new_value) + + def freeze(self): + if debugcore.currentpid == -1: + return + it = QTreeWidgetItemIterator(self.treeWidget_AddressTable) + while True: + row = it.value() + if not row: + break + it += 1 + if row.checkState(FROZEN_COL) == Qt.CheckState.Checked: + vt: typedefs.ValueType = row.data(TYPE_COL, Qt.ItemDataRole.UserRole) + address = row.text(ADDR_COL).strip("P->") + frozen: typedefs.Frozen = row.data(FROZEN_COL, Qt.ItemDataRole.UserRole) + value = frozen.value + freeze_type = frozen.freeze_type + if typedefs.VALUE_INDEX.is_number(vt.value_index): + new_value = debugcore.read_memory(address, vt.value_index, endian=vt.endian) + if new_value == None: + continue + if ( + freeze_type == typedefs.FREEZE_TYPE.INCREMENT + and new_value > value + or freeze_type == typedefs.FREEZE_TYPE.DECREMENT + and new_value < value + ): + frozen.value = new_value + debugcore.write_memory(address, vt.value_index, new_value, endian=vt.endian) + continue + debugcore.write_memory(address, vt.value_index, value, vt.zero_terminate, vt.endian) + + def handle_freeze_change(self, row: QTreeWidgetItem, check_state: Qt.CheckState) -> None: + frozen: typedefs.Frozen = row.data(FROZEN_COL, Qt.ItemDataRole.UserRole) + is_checked = check_state == Qt.CheckState.Checked + frozen_state_toggled = (is_checked and not frozen.enabled) or (not is_checked and frozen.enabled) + row.setCheckState(FROZEN_COL, check_state) + # this helps determine whether the user clicked checkbox or the text + # if the user clicked the text, change the freeze type + + if frozen_state_toggled: + if is_checked: + frozen.enabled = True + # reapply the freeze type, to reflect the current freeze type in the UI + # otherwise the UI will show DEFAULT freeze type after enabling instead of the actual type + self.change_freeze_type(frozen.freeze_type, row) + vt: typedefs.ValueType = row.data(TYPE_COL, Qt.ItemDataRole.UserRole) + frozen.value = utils.parse_string(row.text(VALUE_COL), vt.value_index) else: - if row.text(ADDR_COL) in FreezeVars: - del FreezeVars[row.text(ADDR_COL)] - self.freeze_consistancy(row.text(ADDR_COL), Qt.Unchecked) - if (len(FreezeVars) == 0): - FreezeStop = 1 - - def treeWidget_AddressTable_show_hex(self): - row = GuiUtils.get_current_item(self.treeWidget_AddressTable) - value_text = row.text(TYPE_COL) - index, length, zero_terminate, byte_len, hex_repr = GuiUtils.text_to_valuetype(value_text) - hex_repr = not hex_repr + frozen.enabled = False # it has just been toggled off + self.change_freeze_type(typedefs.FREEZE_TYPE.DEFAULT, row) + + def treeWidget_AddressTable_change_repr(self, new_repr): + value_type = guiutils.get_current_item(self.treeWidget_AddressTable).data(TYPE_COL, Qt.ItemDataRole.UserRole) + value_type.value_repr = new_repr for row in self.treeWidget_AddressTable.selectedItems(): - row.setText(TYPE_COL, GuiUtils.valuetype_to_text(index, length, zero_terminate, hex_repr)) + row.setData(TYPE_COL, Qt.ItemDataRole.UserRole, value_type) + row.setText(TYPE_COL, value_type.text()) self.update_address_table() def treeWidget_AddressTable_edit_value(self): - global FreezeVars - row = GuiUtils.get_current_item(self.treeWidget_AddressTable) + row = guiutils.get_current_item(self.treeWidget_AddressTable) if not row: return value = row.text(VALUE_COL) - value_index = GuiUtils.text_to_valuetype(row.text(TYPE_COL))[0] - label_text = "Enter the new value" - if type_defs.VALUE_INDEX.is_string(value_index): - label_text += "\nPINCE doesn't automatically insert a null terminated string at the end" \ - "\nCopy-paste this character(\0) if you need to insert it at somewhere" - dialog = InputDialogForm(item_list=[(label_text, value)], parsed_index=0, value_index=value_index) - if dialog.exec_(): - table_contents = [] - value_text = dialog.get_values() + value_index = row.data(TYPE_COL, Qt.ItemDataRole.UserRole).value_index + dialog = InputDialogForm(self, [(tr.ENTER_VALUE, value)], 0, value_index) + if dialog.exec(): + new_value = dialog.get_values() + for row in self.treeWidget_AddressTable.selectedItems(): + address = row.text(ADDR_COL).strip("P->") + vt: typedefs.ValueType = row.data(TYPE_COL, Qt.ItemDataRole.UserRole) + parsed_value = utils.parse_string(new_value, vt.value_index) + if typedefs.VALUE_INDEX.has_length(vt.value_index) and parsed_value != None: + vt.length = len(parsed_value) + row.setText(TYPE_COL, vt.text()) + frozen: typedefs.Frozen = row.data(FROZEN_COL, Qt.ItemDataRole.UserRole) + frozen.value = parsed_value + debugcore.write_memory(address, vt.value_index, parsed_value, vt.zero_terminate, vt.endian) + self.update_address_table() + + def treeWidget_AddressTable_edit_offset(self): + row = guiutils.get_current_item(self.treeWidget_AddressTable) + if not row: + return + + dialog = InputDialogForm(self, item_list=[("Offset addresses by:",'')]) + if dialog.exec(): + offset_value = dialog.get_values() + offset_int = int(offset_value, 16) + print(offset_value) + print(offset_int) for row in self.treeWidget_AddressTable.selectedItems(): - address = row.text(ADDR_COL) - value_type = row.text(TYPE_COL) - value_index = GuiUtils.text_to_valuetype(value_type)[0] - if type_defs.VALUE_INDEX.is_string(value_index) or value_index == type_defs.VALUE_INDEX.INDEX_AOB: - unknown_type = SysUtils.parse_string(value_text, value_index) - if unknown_type is not None: - length = len(unknown_type) - row.setText(TYPE_COL, GuiUtils.change_text_length(value_type, length)) - if row.text(ADDR_COL) in FreezeVars: - FreezeVars[row.text(ADDR_COL)] = [value_index, value_text] - table_contents.append((address, value_index)) - GDB_Engine.write_memory_multiple(table_contents, value_text) + desc, address_expr, value_type = self.read_address_table_entries(row) + address = row.text(ADDR_COL).strip("P->") + address_int = int(address, 16) + address_new = hex(address_int + offset_int) + self.change_address_table_entries(row, desc, address_new, value_type) self.update_address_table() def treeWidget_AddressTable_edit_desc(self): - row = GuiUtils.get_current_item(self.treeWidget_AddressTable) + row = guiutils.get_current_item(self.treeWidget_AddressTable) if not row: return description = row.text(DESC_COL) - dialog = InputDialogForm(item_list=[("Enter the new description", description)]) - if dialog.exec_(): + dialog = InputDialogForm(self, [(tr.ENTER_DESCRIPTION, description)]) + if dialog.exec(): description_text = dialog.get_values() for row in self.treeWidget_AddressTable.selectedItems(): row.setText(DESC_COL, description_text) def treeWidget_AddressTable_edit_address(self): - row = GuiUtils.get_current_item(self.treeWidget_AddressTable) + row = guiutils.get_current_item(self.treeWidget_AddressTable) if not row: return - desc, address_expr, value_type = self.read_address_table_entries(row=row) - index, length, zero_terminate, byte_len, hex_repr = GuiUtils.text_to_valuetype(value_type) - manual_address_dialog = ManualAddressDialogForm(description=desc, address=address_expr, index=index, - length=length, zero_terminate=zero_terminate, hex_repr=hex_repr) - manual_address_dialog.setWindowTitle("Edit Address") - if manual_address_dialog.exec_(): - desc, address_expr, address_type, length, zero_terminate, hex_repr = manual_address_dialog.get_values() - address_type_text = GuiUtils.valuetype_to_text(address_type, length, zero_terminate, hex_repr) - self.change_address_table_entries(row, desc, address_expr, address_type_text) + desc, address_expr, vt = self.read_address_table_entries(row) + manual_address_dialog = ManualAddressDialogForm(self, desc, address_expr, vt) + manual_address_dialog.setWindowTitle(tr.EDIT_ADDRESS) + if manual_address_dialog.exec(): + desc, address_expr, vt = manual_address_dialog.get_values() + self.change_address_table_entries(row, desc, address_expr, vt) + self.update_address_table() def treeWidget_AddressTable_edit_type(self): - row = GuiUtils.get_current_item(self.treeWidget_AddressTable) + row = guiutils.get_current_item(self.treeWidget_AddressTable) if not row: return - value_type = row.text(TYPE_COL) - value_index, length, zero_terminate, byte_len, hex_repr = GuiUtils.text_to_valuetype(value_type) - dialog = EditTypeDialogForm(index=value_index, length=length, zero_terminate=zero_terminate, hex_repr=hex_repr) - if dialog.exec_(): - params = dialog.get_values() - type_text = GuiUtils.valuetype_to_text(*params) + vt = row.data(TYPE_COL, Qt.ItemDataRole.UserRole) + dialog = EditTypeDialogForm(self, vt) + if dialog.exec(): + vt = dialog.get_values() for row in self.treeWidget_AddressTable.selectedItems(): - row.setText(TYPE_COL, type_text) + row.setData(TYPE_COL, Qt.ItemDataRole.UserRole, vt) + row.setText(TYPE_COL, vt.text()) self.update_address_table() # Changes the column values of the given row - def change_address_table_entries(self, row, description="", address_expr="", address_type=""): - try: - address = GDB_Engine.examine_expression(address_expr).address - except type_defs.InferiorRunningException: - address = address_expr - value = '' - index, length, zero_terminate, byte_len, hex_repr = GuiUtils.text_to_valuetype(address_type) - if address: - value = GDB_Engine.read_memory(address, index, length, zero_terminate) - + def change_address_table_entries(self, row, description=tr.NO_DESCRIPTION, address_expr="", vt=None): assert isinstance(row, QTreeWidgetItem) row.setText(DESC_COL, description) - row.setData(ADDR_COL, Qt.UserRole, address_expr) - row.setText(ADDR_COL, address or address_expr) - row.setText(TYPE_COL, address_type) - row.setText(VALUE_COL, "" if value is None else str(value)) - self.update_address_table() + row.setData(ADDR_COL, Qt.ItemDataRole.UserRole, address_expr) + row.setData(TYPE_COL, Qt.ItemDataRole.UserRole, vt) + row.setText(TYPE_COL, vt.text()) # Returns the column values of the given row - def read_address_table_entries(self, row): + def read_address_table_entries(self, row, serialize=False): description = row.text(DESC_COL) - address_expr = row.data(ADDR_COL, Qt.UserRole) - value_type = row.text(TYPE_COL) + if serialize: + address_data = row.data(ADDR_COL, Qt.ItemDataRole.UserRole) + if isinstance(address_data, typedefs.PointerChainRequest): + address_expr = address_data.serialize() + else: + address_expr = address_data + value_type = row.data(TYPE_COL, Qt.ItemDataRole.UserRole).serialize() + else: + address_expr = row.data(ADDR_COL, Qt.ItemDataRole.UserRole) + value_type = row.data(TYPE_COL, Qt.ItemDataRole.UserRole) return description, address_expr, value_type # Returns the values inside the given row and all of its descendants. # All values except the last are the same as read_address_table_entries output. # Last value is an iterable of information about its direct children. def read_address_table_recursively(self, row): - return self.read_address_table_entries(row) + \ - ([self.read_address_table_recursively(row.child(i)) for i in range(row.childCount())],) + return self.read_address_table_entries(row, True) + ( + [self.read_address_table_recursively(row.child(i)) for i in range(row.childCount())], + ) + + # Flashing Attach Button when the process is not attached + def flash_attach_button(self): + if not self.flashAttachButton: + self.flashAttachButtonTimer.stop() + self.pushButton_AttachProcess.setStyleSheet("") + return + + case = self.flashAttachButton_gradiantState % 32 + + if case < 16: + borderstring = "QPushButton {border: 3px solid rgba(0,255,0," + str(case / 16) + ");}" + else: + borderstring = "QPushButton {border: 3px solid rgba(0,255,0," + str((32 - case) / 16) + ");}" + + self.pushButton_AttachProcess.setStyleSheet(borderstring) + self.flashAttachButton_gradiantState += 1 + if self.flashAttachButton_gradiantState > 768: # 32*24 + self.flashAttachButton_gradiantState = 0 # process select window class ProcessForm(QMainWindow, ProcessWindow): def __init__(self, parent=None): - super().__init__(parent=parent) + super().__init__(parent) self.setupUi(self) - GuiUtils.center_to_parent(self) - self.refresh_process_table(self.tableWidget_ProcessTable, SysUtils.iterate_processes()) + self.refresh_process_table(self.tableWidget_ProcessTable, utils.get_process_list()) self.pushButton_Close.clicked.connect(self.close) self.pushButton_Open.clicked.connect(self.pushButton_Open_clicked) self.pushButton_CreateProcess.clicked.connect(self.pushButton_CreateProcess_clicked) self.lineEdit_SearchProcess.textChanged.connect(self.generate_new_list) self.tableWidget_ProcessTable.itemDoubleClicked.connect(self.pushButton_Open_clicked) + guiutils.center_to_parent(self) # refreshes process list def generate_new_list(self): text = self.lineEdit_SearchProcess.text() - processlist = SysUtils.search_in_processes_by_name(text) + processlist = utils.search_processes(text) self.refresh_process_table(self.tableWidget_ProcessTable, processlist) - def keyPressEvent(self, e): - if e.key() == Qt.Key_Escape: - # closes the window whenever ESC key is pressed + def keyPressEvent(self, event): + if event.key() == Qt.Key.Key_Escape: self.close() - elif e.key() == Qt.Key_Return: + elif event.key() == Qt.Key.Key_Return or event.key() == Qt.Key.Key_Enter: self.pushButton_Open_clicked() - elif e.key() == Qt.Key_F1: + elif event.key() == Qt.Key.Key_F1: self.pushButton_CreateProcess_clicked() - elif e.key() == Qt.Key_Down or e.key() == Qt.Key_Up: - self.tableWidget_ProcessTable.keyPressEvent(QKeyEvent(QEvent.KeyPress, e.key(), Qt.NoModifier)) + else: + return super().keyPressEvent(event) # lists currently working processes to table def refresh_process_table(self, tablewidget, processlist): tablewidget.setRowCount(0) - for process in processlist: - pid = str(process.pid) - try: - username = process.username() - name = process.name() - except psutil.NoSuchProcess: - continue + for pid, user, name in processlist: current_row = tablewidget.rowCount() tablewidget.insertRow(current_row) tablewidget.setItem(current_row, 0, QTableWidgetItem(pid)) - tablewidget.setItem(current_row, 1, QTableWidgetItem(username)) + tablewidget.setItem(current_row, 1, QTableWidgetItem(user)) tablewidget.setItem(current_row, 2, QTableWidgetItem(name)) # gets the pid out of the selection to attach def pushButton_Open_clicked(self): + index = self.tableWidget_ProcessTable.currentIndex() + row_count = self.tableWidget_ProcessTable.rowCount() + if index.row() == -1 and row_count == 1: + # autoselect first row if there is only one row + self.tableWidget_ProcessTable.setCurrentCell(0, 0) + current_item = self.tableWidget_ProcessTable.item(self.tableWidget_ProcessTable.currentIndex().row(), 0) if current_item is None: - QMessageBox.information(self, "Error", "Please select a process first") + QMessageBox.information(self, tr.ERROR, tr.SELECT_PROCESS) else: pid = int(current_item.text()) - self.setCursor(QCursor(Qt.WaitCursor)) + self.setCursor(QCursor(Qt.CursorShape.WaitCursor)) if self.parent().attach_to_pid(pid): self.close() - self.setCursor(QCursor(Qt.ArrowCursor)) + self.setCursor(QCursor(Qt.CursorShape.ArrowCursor)) def pushButton_CreateProcess_clicked(self): - file_path = QFileDialog.getOpenFileName(self, "Select the target binary")[0] + file_path, _ = QFileDialog.getOpenFileName(self, tr.SELECT_BINARY) if file_path: - items = [("Enter the optional arguments", ""), ("LD_PRELOAD .so path (optional)", "")] - arg_dialog = InputDialogForm(item_list=items) - if arg_dialog.exec_(): + items = [(tr.ENTER_OPTIONAL_ARGS, ""), (tr.LD_PRELOAD_OPTIONAL, "")] + arg_dialog = InputDialogForm(self, items) + if arg_dialog.exec(): args, ld_preload_path = arg_dialog.get_values() else: return - self.setCursor(QCursor(Qt.WaitCursor)) + self.setCursor(QCursor(Qt.CursorShape.WaitCursor)) if self.parent().create_new_process(file_path, args, ld_preload_path): self.close() - self.setCursor(QCursor(Qt.ArrowCursor)) + self.setCursor(QCursor(Qt.CursorShape.ArrowCursor)) # Add Address Manually Dialog class ManualAddressDialogForm(QDialog, ManualAddressDialog): - def __init__(self, parent=None, description="No Description", address="0x", - index=type_defs.VALUE_INDEX.INDEX_4BYTES, length=10, zero_terminate=True, hex_repr=False): - super().__init__(parent=parent) + def __init__(self, parent, description=tr.NO_DESCRIPTION, address="0x", value_type=None): + super().__init__(parent) self.setupUi(self) - self.adjustSize() - self.setMinimumWidth(300) - self.setFixedHeight(self.height()) - self.lineEdit_length.setValidator(QHexValidator(999, self)) - self.hex_repr = hex_repr - GuiUtils.fill_value_combobox(self.comboBox_ValueType, index) - self.lineEdit_description.setText(description) - self.lineEdit_address.setText(address) - if type_defs.VALUE_INDEX.is_string(self.comboBox_ValueType.currentIndex()): - self.label_length.show() - self.lineEdit_length.show() + self.lineEdit_PtrStartAddress.setFixedWidth(180) + self.lineEdit_Address.setFixedWidth(180) + vt = typedefs.ValueType() if not value_type else value_type + self.lineEdit_Length.setValidator(QHexValidator(99, self)) + guiutils.fill_value_combobox(self.comboBox_ValueType, vt.value_index) + guiutils.fill_endianness_combobox(self.comboBox_Endianness, vt.endian) + self.lineEdit_Description.setText(description) + self.lineEdit_Description.setFixedWidth(180) + self.offsetsList: list[PointerChainOffset] = [] + if not isinstance(address, typedefs.PointerChainRequest): + self.lineEdit_Address.setText(address) + self.widget_Pointer.hide() + else: + self.checkBox_IsPointer.setChecked(True) + self.lineEdit_Address.setReadOnly(True) + self.lineEdit_PtrStartAddress.setText(address.get_base_address_as_str()) + self.create_offsets_list(address) + self.widget_Pointer.show() + if typedefs.VALUE_INDEX.is_string(self.comboBox_ValueType.currentIndex()): + self.widget_Length.show() try: - length = str(length) + length = str(vt.length) except: length = "10" - self.lineEdit_length.setText(length) - self.checkBox_zeroterminate.show() - self.checkBox_zeroterminate.setChecked(zero_terminate) - elif self.comboBox_ValueType.currentIndex() == type_defs.VALUE_INDEX.INDEX_AOB: - self.label_length.show() - self.lineEdit_length.show() + self.lineEdit_Length.setText(length) + self.checkBox_ZeroTerminate.show() + self.checkBox_ZeroTerminate.setChecked(vt.zero_terminate) + elif self.comboBox_ValueType.currentIndex() == typedefs.VALUE_INDEX.AOB: + self.widget_Length.show() try: - length = str(length) + length = str(vt.length) except: length = "10" - self.lineEdit_length.setText(length) - self.checkBox_zeroterminate.hide() + self.lineEdit_Length.setText(length) + self.checkBox_ZeroTerminate.hide() else: - self.label_length.hide() - self.lineEdit_length.hide() - self.checkBox_zeroterminate.hide() + self.widget_Length.hide() + if vt.value_repr == typedefs.VALUE_REPR.HEX: + self.checkBox_Hex.setChecked(True) + self.checkBox_Signed.setEnabled(False) + elif vt.value_repr == typedefs.VALUE_REPR.SIGNED: + self.checkBox_Signed.setChecked(True) + else: + self.checkBox_Signed.setChecked(False) self.comboBox_ValueType.currentIndexChanged.connect(self.comboBox_ValueType_current_index_changed) - self.lineEdit_length.textChanged.connect(self.update_value_of_address) - self.checkBox_zeroterminate.stateChanged.connect(self.update_value_of_address) - self.lineEdit_address.textChanged.connect(self.update_value_of_address) - self.label_valueofaddress.contextMenuEvent = self.label_valueofaddress_context_menu_event - self.update_value_of_address() - - def label_valueofaddress_context_menu_event(self, event): + self.comboBox_Endianness.currentIndexChanged.connect(self.update_value) + self.lineEdit_Length.textChanged.connect(self.update_value) + self.checkBox_Hex.stateChanged.connect(self.repr_changed) + self.checkBox_Signed.stateChanged.connect(self.repr_changed) + self.checkBox_ZeroTerminate.stateChanged.connect(self.update_value) + self.checkBox_IsPointer.stateChanged.connect(self.checkBox_IsPointer_state_changed) + self.lineEdit_PtrStartAddress.textChanged.connect(self.update_value) + self.lineEdit_Address.textChanged.connect(self.update_value) + self.pushButton_AddOffset.clicked.connect(lambda: self.addOffsetLayout(True)) + self.pushButton_RemoveOffset.clicked.connect(self.removeOffsetLayout) + self.label_Value.contextMenuEvent = self.label_Value_context_menu_event + self.update_value() + guiutils.center_to_parent(self) + + def label_Value_context_menu_event(self, event): menu = QMenu() - refresh = menu.addAction("Refresh") - font_size = self.label_valueofaddress.font().pointSize() + refresh = menu.addAction(tr.REFRESH) + font_size = self.label_Value.font().pointSize() menu.setStyleSheet("font-size: " + str(font_size) + "pt;") - action = menu.exec_(event.globalPos()) - actions = { - refresh: self.update_value_of_address - } + action = menu.exec(event.globalPos()) + actions = {refresh: self.update_value} try: actions[action]() except KeyError: pass - def update_value_of_address(self): - address = self.lineEdit_address.text() - try: - address = GDB_Engine.examine_expression(address).address - except type_defs.InferiorRunningException: - pass - if not address: - self.label_valueofaddress.setText("??") + def addOffsetLayout(self, should_update=True): + offsetFrame = PointerChainOffset(len(self.offsetsList), self.widget_Pointer) + self.offsetsList.append(offsetFrame) + self.verticalLayout_Pointers.insertWidget(0, self.offsetsList[-1]) + offsetFrame.offset_changed_signal.connect(self.update_value) + if should_update: + self.update_value() + + def removeOffsetLayout(self): + if len(self.offsetsList) == 1: return - address_type = self.comboBox_ValueType.currentIndex() - if address_type == type_defs.VALUE_INDEX.INDEX_AOB: - length = self.lineEdit_length.text() - value = GDB_Engine.read_memory(address, address_type, length) - elif type_defs.VALUE_INDEX.is_string(address_type): - length = self.lineEdit_length.text() - is_zeroterminate = self.checkBox_zeroterminate.isChecked() - value = GDB_Engine.read_memory(address, address_type, length, is_zeroterminate) + frame = self.offsetsList[-1] + frame.deleteLater() + self.verticalLayout_Pointers.removeWidget(frame) + del self.offsetsList[-1] + self.update_value() + + def update_deref_labels(self, pointer_chain_result: typedefs.PointerChainResult): + if pointer_chain_result != None: + base_deref = utils.upper_hex(hex(pointer_chain_result.pointer_chain[0])) + self.label_BaseAddressDeref.setText(f" → {base_deref}") + for index, offsetFrame in enumerate(self.offsetsList): + previousDerefText = self.caps_hex_or_error_indicator(pointer_chain_result.pointer_chain[index]) + currentDerefText = self.caps_hex_or_error_indicator(pointer_chain_result.pointer_chain[index + 1]) + offsetText = utils.upper_hex(offsetFrame.offsetText.text()) + operationalSign = "" if offsetText.startswith("-") else "+" + calculation = f"{previousDerefText}{operationalSign}{offsetText}" + if index != len(self.offsetsList) - 1: + offsetFrame.update_deref_label(f" [{calculation}] → {currentDerefText}") + else: + offsetFrame.update_deref_label(f" {calculation} = {currentDerefText}") + else: + self.label_BaseAddressDeref.setText(" → ??") + for offsetFrame in self.offsetsList: + offsetFrame.update_deref_label(" → ??") + + def caps_hex_or_error_indicator(self, address: int): + if address == 0: + return "??" + return utils.upper_hex(hex(address)) + + def update_value(self): + if self.checkBox_IsPointer.isChecked(): + hex_converted_expr = debugcore.convert_to_hex(self.lineEdit_PtrStartAddress.text()) + pointer_chain_req = typedefs.PointerChainRequest(hex_converted_expr, self.get_offsets_int_list()) + pointer_chain_result = debugcore.read_pointer_chain(pointer_chain_req) + address = None + if pointer_chain_result != None: + address_text = pointer_chain_result.get_final_address_as_hex() + address = pointer_chain_result.get_final_address() + else: + address_text = "??" + self.lineEdit_Address.setText(address_text) + self.update_deref_labels(pointer_chain_result) else: - value = GDB_Engine.read_memory(address, address_type) - self.label_valueofaddress.setText("??" if value is None else str(value)) + hex_converted_expr = debugcore.convert_to_hex(self.lineEdit_Address.text()) + address = debugcore.examine_expression(hex_converted_expr).address + if self.checkBox_Hex.isChecked(): + value_repr = typedefs.VALUE_REPR.HEX + elif self.checkBox_Signed.isChecked(): + value_repr = typedefs.VALUE_REPR.SIGNED + else: + value_repr = typedefs.VALUE_REPR.UNSIGNED + address_type = self.comboBox_ValueType.currentIndex() + length = self.lineEdit_Length.text() + zero_terminate = self.checkBox_ZeroTerminate.isChecked() + endian = self.comboBox_Endianness.currentData(Qt.ItemDataRole.UserRole) + value = debugcore.read_memory(address, address_type, length, zero_terminate, value_repr, endian) + self.label_Value.setText("??" if value is None else str(value)) + old_width = self.width() + app.processEvents() + self.adjustSize() + self.resize(old_width, self.minimumHeight()) def comboBox_ValueType_current_index_changed(self): - if type_defs.VALUE_INDEX.is_string(self.comboBox_ValueType.currentIndex()): - self.label_length.show() - self.lineEdit_length.show() - self.checkBox_zeroterminate.show() - elif self.comboBox_ValueType.currentIndex() == type_defs.VALUE_INDEX.INDEX_AOB: - self.label_length.show() - self.lineEdit_length.show() - self.checkBox_zeroterminate.hide() + if typedefs.VALUE_INDEX.is_string(self.comboBox_ValueType.currentIndex()): + self.widget_Length.show() + self.checkBox_ZeroTerminate.show() + elif self.comboBox_ValueType.currentIndex() == typedefs.VALUE_INDEX.AOB: + self.widget_Length.show() + self.checkBox_ZeroTerminate.hide() + else: + self.widget_Length.hide() + self.update_value() + + def repr_changed(self): + if self.checkBox_Hex.isChecked(): + self.checkBox_Signed.setEnabled(False) + else: + self.checkBox_Signed.setEnabled(True) + self.update_value() + + def checkBox_IsPointer_state_changed(self): + if self.checkBox_IsPointer.isChecked(): + self.lineEdit_Address.setReadOnly(True) + self.lineEdit_PtrStartAddress.setText(self.lineEdit_Address.text()) + if len(self.offsetsList) == 0: + self.addOffsetLayout(False) + self.widget_Pointer.show() else: - self.label_length.hide() - self.lineEdit_length.hide() - self.checkBox_zeroterminate.hide() - self.update_value_of_address() + self.lineEdit_Address.setText(self.lineEdit_PtrStartAddress.text()) + self.lineEdit_PtrStartAddress.setText("") + self.lineEdit_Address.setReadOnly(False) + self.widget_Pointer.hide() + self.update_value() def reject(self): - super(ManualAddressDialogForm, self).reject() + super().reject() def accept(self): - if self.label_length.isVisible(): - length = self.lineEdit_length.text() + if self.label_Length.isVisible(): + length = self.lineEdit_Length.text() try: length = int(length, 0) except: - QMessageBox.information(self, "Error", "Length is not valid") + QMessageBox.information(self, tr.ERROR, tr.LENGTH_NOT_VALID) return if not length > 0: - QMessageBox.information(self, "Error", "Length must be greater than 0") + QMessageBox.information(self, tr.ERROR, tr.LENGTH_GT) return - super(ManualAddressDialogForm, self).accept() + super().accept() def get_values(self): - description = self.lineEdit_description.text() - address = self.lineEdit_address.text() - length = self.lineEdit_length.text() + description = self.lineEdit_Description.text() + length = self.lineEdit_Length.text() try: length = int(length, 0) except: length = 0 - zero_terminate = False - if self.checkBox_zeroterminate.isChecked(): - zero_terminate = True - address_type = self.comboBox_ValueType.currentIndex() - return description, address, address_type, length, zero_terminate, self.hex_repr + zero_terminate = self.checkBox_ZeroTerminate.isChecked() + value_index = self.comboBox_ValueType.currentIndex() + if self.checkBox_Hex.isChecked(): + value_repr = typedefs.VALUE_REPR.HEX + elif self.checkBox_Signed.isChecked(): + value_repr = typedefs.VALUE_REPR.SIGNED + else: + value_repr = typedefs.VALUE_REPR.UNSIGNED + endian = self.comboBox_Endianness.currentData(Qt.ItemDataRole.UserRole) + vt = typedefs.ValueType(value_index, length, zero_terminate, value_repr, endian) + if self.checkBox_IsPointer.isChecked(): + base_expression = debugcore.convert_to_hex(self.lineEdit_PtrStartAddress.text()) + address = typedefs.PointerChainRequest(base_expression, self.get_offsets_int_list()) + else: + address = debugcore.convert_to_hex(self.lineEdit_Address.text()) + return description, address, vt + + def get_offsets_int_list(self): + offsetsIntList = [] + for frame in self.offsetsList: + offsetText = frame.layout().itemAt(1).widget().text() + try: + offsetValue = int(offsetText, 16) + except ValueError: + offsetValue = 0 + offsetsIntList.append(offsetValue) + return offsetsIntList + + def create_offsets_list(self, pointer_chain_req: typedefs.PointerChainRequest): + if not isinstance(pointer_chain_req, typedefs.PointerChainRequest): + raise TypeError("Passed non-PointerChainRequest type to create_offsets_list!") + + for offset in pointer_chain_req.offsets_list: + self.addOffsetLayout(False) + frame = self.offsetsList[-1] + frame.layout().itemAt(1).widget().setText(hex(offset)) + + def on_offset_arrow_clicked(self, offsetTextWidget, operator_func): + offsetText = offsetTextWidget.text() + try: + offsetValue = int(offsetText, 16) + except ValueError: + offsetValue = 0 + sizeVal = typedefs.index_to_valuetype_dict[self.comboBox_ValueType.currentIndex()][0] + offsetValue = operator_func(offsetValue, sizeVal) + offsetTextWidget.setText(hex(offsetValue)) + + def get_type_size(self): + return typedefs.index_to_valuetype_dict[self.comboBox_ValueType.currentIndex()][0] class EditTypeDialogForm(QDialog, EditTypeDialog): - def __init__(self, parent=None, index=type_defs.VALUE_INDEX.INDEX_4BYTES, length=10, zero_terminate=True, - hex_repr=False): - super().__init__(parent=parent) + def __init__(self, parent, value_type=None): + super().__init__(parent) self.setupUi(self) - self.setMaximumSize(100, 100) - self.lineEdit_Length.setValidator(QHexValidator(999, self)) - self.hex_repr = hex_repr - GuiUtils.fill_value_combobox(self.comboBox_ValueType, index) - if type_defs.VALUE_INDEX.is_string(self.comboBox_ValueType.currentIndex()): - self.label_Length.show() - self.lineEdit_Length.show() + vt = typedefs.ValueType() if not value_type else value_type + self.lineEdit_Length.setValidator(QHexValidator(99, self)) + self.lineEdit_Length.setFixedWidth(40) + guiutils.fill_value_combobox(self.comboBox_ValueType, vt.value_index) + guiutils.fill_endianness_combobox(self.comboBox_Endianness, vt.endian) + if typedefs.VALUE_INDEX.is_string(self.comboBox_ValueType.currentIndex()): + self.widget_Length.show() try: - length = str(length) + length = str(vt.length) except: length = "10" self.lineEdit_Length.setText(length) self.checkBox_ZeroTerminate.show() - self.checkBox_ZeroTerminate.setChecked(zero_terminate) - elif self.comboBox_ValueType.currentIndex() == type_defs.VALUE_INDEX.INDEX_AOB: - self.label_Length.show() - self.lineEdit_Length.show() + self.checkBox_ZeroTerminate.setChecked(vt.zero_terminate) + elif self.comboBox_ValueType.currentIndex() == typedefs.VALUE_INDEX.AOB: + self.widget_Length.show() try: - length = str(length) + length = str(vt.length) except: length = "10" self.lineEdit_Length.setText(length) self.checkBox_ZeroTerminate.hide() else: - self.label_Length.hide() - self.lineEdit_Length.hide() - self.checkBox_ZeroTerminate.hide() + self.widget_Length.hide() + if vt.value_repr == typedefs.VALUE_REPR.HEX: + self.checkBox_Hex.setChecked(True) + self.checkBox_Signed.setEnabled(False) + elif vt.value_repr == typedefs.VALUE_REPR.SIGNED: + self.checkBox_Signed.setChecked(True) + else: + self.checkBox_Signed.setChecked(False) self.comboBox_ValueType.currentIndexChanged.connect(self.comboBox_ValueType_current_index_changed) + self.checkBox_Hex.stateChanged.connect(self.repr_changed) + app.processEvents() + self.adjustSize() + guiutils.center_to_parent(self) def comboBox_ValueType_current_index_changed(self): - if type_defs.VALUE_INDEX.is_string(self.comboBox_ValueType.currentIndex()): - self.label_Length.show() - self.lineEdit_Length.show() + if typedefs.VALUE_INDEX.is_string(self.comboBox_ValueType.currentIndex()): + self.widget_Length.show() self.checkBox_ZeroTerminate.show() - elif self.comboBox_ValueType.currentIndex() == type_defs.VALUE_INDEX.INDEX_AOB: - self.label_Length.show() - self.lineEdit_Length.show() + elif self.comboBox_ValueType.currentIndex() == typedefs.VALUE_INDEX.AOB: + self.widget_Length.show() self.checkBox_ZeroTerminate.hide() else: - self.label_Length.hide() - self.lineEdit_Length.hide() - self.checkBox_ZeroTerminate.hide() + self.widget_Length.hide() + app.processEvents() + self.adjustSize() + + def repr_changed(self): + if self.checkBox_Hex.isChecked(): + self.checkBox_Signed.setEnabled(False) + else: + self.checkBox_Signed.setEnabled(True) def reject(self): - super(EditTypeDialogForm, self).reject() + super().reject() def accept(self): if self.label_Length.isVisible(): @@ -1631,34 +2455,50 @@ def accept(self): try: length = int(length, 0) except: - QMessageBox.information(self, "Error", "Length is not valid") + QMessageBox.information(self, tr.ERROR, tr.LENGTH_NOT_VALID) return if not length > 0: - QMessageBox.information(self, "Error", "Length must be greater than 0") + QMessageBox.information(self, tr.ERROR, tr.LENGTH_GT) return - super(EditTypeDialogForm, self).accept() + super().accept() def get_values(self): + value_index = self.comboBox_ValueType.currentIndex() length = self.lineEdit_Length.text() try: length = int(length, 0) except: length = 0 - zero_terminate = False - if self.checkBox_ZeroTerminate.isChecked(): - zero_terminate = True - address_type = self.comboBox_ValueType.currentIndex() - return address_type, length, zero_terminate, self.hex_repr + zero_terminate = self.checkBox_ZeroTerminate.isChecked() + if self.checkBox_Hex.isChecked(): + value_repr = typedefs.VALUE_REPR.HEX + elif self.checkBox_Signed.isChecked(): + value_repr = typedefs.VALUE_REPR.SIGNED + else: + value_repr = typedefs.VALUE_REPR.UNSIGNED + endian = self.comboBox_Endianness.currentData(Qt.ItemDataRole.UserRole) + return typedefs.ValueType(value_index, length, zero_terminate, value_repr, endian) + + +class TrackSelectorDialogForm(QDialog, TrackSelectorDialog): + def __init__(self, parent): + super().__init__(parent) + self.setupUi(self) + self.selection = None + self.pushButton_Pointer.clicked.connect(lambda: self.change_selection("pointer")) + self.pushButton_Pointed.clicked.connect(lambda: self.change_selection("pointed")) + guiutils.center_to_parent(self) + + def change_selection(self, selection): + self.selection = selection + self.close() class LoadingDialogForm(QDialog, LoadingDialog): - def __init__(self, parent=None): - super().__init__(parent=parent) + def __init__(self, parent): + super().__init__(parent) self.setupUi(self) - self.setWindowFlags(self.windowFlags() | Qt.FramelessWindowHint) - self.setAttribute(Qt.WA_TranslucentBackground) - if parent: - GuiUtils.center_to_parent(self) + self.setWindowFlags(self.windowFlags()) self.keyPressEvent = QEvent.ignore # Make use of this background_thread when you spawn a LoadingDialogForm @@ -1667,23 +2507,28 @@ def __init__(self, parent=None): # Check refresh_table method of FunctionsInfoWidgetForm for exemplary usage self.background_thread = self.BackgroundThread() self.background_thread.output_ready.connect(self.accept) - self.pushButton_Cancel.clicked.connect(self.cancel_thread) - media_directory = SysUtils.get_media_directory() + self.pushButton_Cancel.clicked.connect(self.close) + media_directory = utils.get_media_directory() self.movie = QMovie(media_directory + "/LoadingDialog/ajax-loader.gif", QByteArray()) self.label_Animated.setMovie(self.movie) self.movie.setScaledSize(QSize(25, 25)) - self.movie.setCacheMode(QMovie.CacheAll) + self.movie.setCacheMode(QMovie.CacheMode.CacheAll) self.movie.setSpeed(100) self.movie.start() + guiutils.center_to_parent(self) - # This function only cancels the last command sent - # Override this if you want to do dangerous stuff like, God forbid, background_thread.terminate() + # TODO: This function only cancels the last command sent, redesign this if it's needed to cancel non-gdb functions def cancel_thread(self): - GDB_Engine.cancel_last_command() + debugcore.cancel_last_command() + self.background_thread.wait() - def exec_(self): + def exec(self): self.background_thread.start() - super(LoadingDialogForm, self).exec_() + super().exec() + + def closeEvent(self, event: QCloseEvent): + self.cancel_thread() + super().closeEvent(event) class BackgroundThread(QThread): output_ready = pyqtSignal(object) @@ -1691,8 +2536,13 @@ class BackgroundThread(QThread): def __init__(self): super().__init__() + # Unhandled exceptions in this thread freezes PINCE def run(self): - output = self.overrided_func() + try: + output = self.overrided_func() + except: + traceback.print_exc() + output = None self.output_ready.emit(output) def overrided_func(self): @@ -1708,9 +2558,15 @@ class InputDialogForm(QDialog, InputDialog): # that points the current index of the QComboBox, for instance: ["0", "1", 1] will create a QCombobox with the items # "0" and "1" then will set current index to 1 (which is the item "1") # label_alignment is optional - def __init__(self, parent=None, item_list=None, parsed_index=-1, value_index=type_defs.VALUE_INDEX.INDEX_4BYTES, - buttons=(QDialogButtonBox.Ok, QDialogButtonBox.Cancel)): - super().__init__(parent=parent) + def __init__( + self, + parent, + item_list=None, + parsed_index=-1, + value_index=typedefs.VALUE_INDEX.INT32, + buttons=(QDialogButtonBox.StandardButton.Ok, QDialogButtonBox.StandardButton.Cancel), + ): + super().__init__(parent) self.setupUi(self) for button in buttons: self.buttonBox.addButton(button) @@ -1721,9 +2577,11 @@ def __init__(self, parent=None, item_list=None, parsed_index=-1, value_index=typ try: label.setAlignment(item[2]) except IndexError: - label.setAlignment(Qt.AlignCenter) + label.setAlignment(Qt.AlignmentFlag.AlignCenter) label.setText(item[0]) - label.setTextInteractionFlags(Qt.LinksAccessibleByMouse | Qt.TextSelectableByMouse) + label.setTextInteractionFlags( + Qt.TextInteractionFlag.LinksAccessibleByMouse | Qt.TextInteractionFlag.TextSelectableByMouse + ) self.verticalLayout.addWidget(label) try: item_data = item[1] @@ -1746,13 +2604,14 @@ def __init__(self, parent=None, item_list=None, parsed_index=-1, value_index=typ self.adjustSize() self.verticalLayout.removeWidget(self.buttonBox) # Pushing buttonBox to the end self.verticalLayout.addWidget(self.buttonBox) - for widget in GuiUtils.get_layout_widgets(self.verticalLayout): + for widget in guiutils.get_layout_widgets(self.verticalLayout): if isinstance(widget, QLabel): continue widget.setFocus() # Focus to the first input field break self.parsed_index = parsed_index self.value_index = value_index + guiutils.center_to_parent(self) def get_text(self, item): try: @@ -1762,202 +2621,201 @@ def get_text(self, item): return string def get_values(self): - return self.get_text(self.object_list[0]) if len(self.object_list) == 1 else [self.get_text(item) for item in - self.object_list] + return ( + self.get_text(self.object_list[0]) + if len(self.object_list) == 1 + else [self.get_text(item) for item in self.object_list] + ) def accept(self): if self.parsed_index != -1: item = self.object_list[self.parsed_index] - if SysUtils.parse_string(self.get_text(item), self.value_index) is None: - QMessageBox.information(self, "Error", "Can't parse the input") + if utils.parse_string(self.get_text(item), self.value_index) is None: + QMessageBox.information(self, tr.ERROR, tr.PARSE_ERROR) return - super(InputDialogForm, self).accept() + super().accept() class TextEditDialogForm(QDialog, TextEditDialog): - def __init__(self, parent=None, text=""): - super().__init__(parent=parent) + def __init__(self, parent, text=""): + super().__init__(parent) self.setupUi(self) self.textEdit.setPlainText(str(text)) self.accept_shortcut = QShortcut(QKeySequence("Ctrl+Return"), self) self.accept_shortcut.activated.connect(self.accept) + guiutils.center_to_parent(self) def get_values(self): return self.textEdit.toPlainText() def keyPressEvent(self, QKeyEvent): - if QKeyEvent.key() == Qt.Key_Enter: + if QKeyEvent.key() == Qt.Key.Key_Enter: pass else: - super(TextEditDialogForm, self).keyPressEvent(QKeyEvent) - - def accept(self): - super(TextEditDialogForm, self).accept() + super().keyPressEvent(QKeyEvent) class SettingsDialogForm(QDialog, SettingsDialog): - def __init__(self, set_default_settings_func, parent=None): - super().__init__(parent=parent) + def __init__(self, parent, set_default_settings_func): + super().__init__(parent) self.setupUi(self) + self.settings = QSettings() self.set_default_settings = set_default_settings_func self.hotkey_to_value = {} # Dict[str:str]-->Dict[Hotkey.name:settings_value] + self.handle_signals_data = "" + icons_directory = guiutils.get_icons_directory() + self.pushButton_GDBPath.setIcon(QIcon(QPixmap(icons_directory + "/folder.png"))) + locale_model = QStandardItemModel() + for loc, name in language_list.items(): + item = QStandardItem() + item.setData(name, Qt.ItemDataRole.DisplayRole) + item.setData(loc, Qt.ItemDataRole.UserRole) + locale_model.appendRow(item) + self.comboBox_Language.setModel(locale_model) + self.comboBox_InterruptSignal.addItem("SIGINT") + self.comboBox_InterruptSignal.addItems([f"SIG{x}" for x in range(signal.SIGRTMIN, signal.SIGRTMAX + 1)]) + self.comboBox_InterruptSignal.setStyleSheet("combobox-popup: 0;") # maxVisibleItems doesn't work otherwise + self.comboBox_Theme.addItems(theme_list) + logo_directory = utils.get_logo_directory() + logo_list = utils.search_files(logo_directory, r"\.(png|jpg|jpeg|svg)$") + for logo in logo_list: + self.comboBox_Logo.addItem(QIcon(os.path.join(logo_directory, logo)), logo) + for hotkey in hotkeys.get_hotkeys(): + self.listWidget_Functions.addItem(hotkey.desc) + self.config_gui() - # Yet another retarded hack, thanks to pyuic5 not supporting QKeySequenceEdit - self.keySequenceEdit = QKeySequenceEdit() - self.verticalLayout_Hotkey.addWidget(self.keySequenceEdit) self.listWidget_Options.currentRowChanged.connect(self.change_display) - icons_directory = GuiUtils.get_icons_directory() - self.pushButton_GDBPath.setIcon(QIcon(QPixmap(icons_directory + "/folder.png"))) self.listWidget_Functions.currentRowChanged.connect(self.listWidget_Functions_current_row_changed) - self.keySequenceEdit.keySequenceChanged.connect(self.keySequenceEdit_key_sequence_changed) self.pushButton_ClearHotkey.clicked.connect(self.pushButton_ClearHotkey_clicked) self.pushButton_ResetSettings.clicked.connect(self.pushButton_ResetSettings_clicked) self.pushButton_GDBPath.clicked.connect(self.pushButton_GDBPath_clicked) self.checkBox_AutoUpdateAddressTable.stateChanged.connect(self.checkBox_AutoUpdateAddressTable_state_changed) self.checkBox_AutoAttachRegex.stateChanged.connect(self.checkBox_AutoAttachRegex_state_changed) - self.checkBox_AutoAttachRegex_state_changed() - self.config_gui() + self.comboBox_Logo.currentIndexChanged.connect(self.comboBox_Logo_current_index_changed) + self.comboBox_Theme.currentIndexChanged.connect(self.comboBox_Theme_current_index_changed) + self.pushButton_HandleSignals.clicked.connect(self.pushButton_HandleSignals_clicked) + self.lineEdit_Hotkey.keyPressEvent = self.lineEdit_Hotkey_key_pressed_event + guiutils.center_to_parent(self) def accept(self): - try: - current_table_update_interval = int(self.lineEdit_UpdateInterval.text()) - except: - QMessageBox.information(self, "Error", "Update interval must be an int") - return - try: - freezeinterval = int(self.lineEdit_FreezeInterval.text()) - except: - QMessageBox.information(self, "Error", "Freeze interval must be an int") - return - try: - current_instructions_shown = int(self.lineEdit_InstructionsPerScroll.text()) - except: - QMessageBox.information(self, "Error", "Instruction count must be an integer") - return - if current_instructions_shown < 1: - QMessageBox.information(self, "Error", "Instruction count cannot be lower than 1" + - "\nIt would be silly anyway, wouldn't it?") - return - if not self.checkBox_AutoUpdateAddressTable.isChecked(): - pass - elif current_table_update_interval < 0 or freezeinterval < 0: - QMessageBox.information(self, "Error", "Interval cannot be a negative number") - return - elif current_table_update_interval == 0 or freezeinterval == 0: - - # Easter egg #2 - if not InputDialogForm(item_list=[("You are asking for it, aren't you?",)]).exec_(): - return - elif current_table_update_interval < 100: - if not InputDialogForm(item_list=[("Update interval should be bigger than 100 ms" + - "\nSetting update interval less than 100 ms may cause slowdown" - "\nProceed?",)]).exec_(): - return - self.settings.setValue("General/auto_update_address_table", self.checkBox_AutoUpdateAddressTable.isChecked()) if self.checkBox_AutoUpdateAddressTable.isChecked(): - self.settings.setValue("General/address_table_update_interval", current_table_update_interval) - self.settings.setValue("General/freeze_interval", freezeinterval) - self.settings.setValue("General/show_messagebox_on_exception", self.checkBox_MessageBoxOnException.isChecked()) - self.settings.setValue("General/show_messagebox_on_toggle_attach", - self.checkBox_MessageBoxOnToggleAttach.isChecked()) - current_gdb_output_mode = type_defs.gdb_output_mode(self.checkBox_OutputModeAsync.isChecked(), - self.checkBox_OutputModeCommand.isChecked(), - self.checkBox_OutputModeCommandInfo.isChecked()) - self.settings.setValue("General/gdb_output_mode", current_gdb_output_mode) + self.settings.setValue("General/address_table_update_interval", self.spinBox_UpdateInterval.value()) + self.settings.setValue("General/freeze_interval", self.spinBox_FreezeInterval.value()) + output_mode = [ + self.checkBox_OutputModeAsync.isChecked(), + self.checkBox_OutputModeCommand.isChecked(), + self.checkBox_OutputModeCommandInfo.isChecked(), + ] + self.settings.setValue("General/gdb_output_mode", json.dumps(output_mode)) if self.checkBox_AutoAttachRegex.isChecked(): try: - re.compile(self.lineEdit_AutoAttachList.text()) + re.compile(self.lineEdit_AutoAttach.text()) except: - QMessageBox.information(self, "Error", self.lineEdit_AutoAttachList.text() + " isn't a valid regex") + QMessageBox.information(self, tr.ERROR, tr.IS_INVALID_REGEX.format(self.lineEdit_AutoAttach.text())) return - self.settings.setValue("General/auto_attach_list", self.lineEdit_AutoAttachList.text()) - self.settings.setValue("General/logo_path", self.comboBox_Logo.currentText()) + self.settings.setValue("General/auto_attach", self.lineEdit_AutoAttach.text()) self.settings.setValue("General/auto_attach_regex", self.checkBox_AutoAttachRegex.isChecked()) - for hotkey in Hotkeys.get_hotkeys(): + new_locale = self.comboBox_Language.currentData(Qt.ItemDataRole.UserRole) + if new_locale != settings.locale: + QMessageBox.information(self, tr.INFO, tr.LANG_RESET) + self.settings.setValue("General/locale", new_locale) + self.settings.setValue("General/logo_path", self.comboBox_Logo.currentText()) + self.settings.setValue("General/theme", self.comboBox_Theme.currentText()) + for hotkey in hotkeys.get_hotkeys(): self.settings.setValue("Hotkeys/" + hotkey.name, self.hotkey_to_value[hotkey.name]) if self.radioButton_SimpleDLopenCall.isChecked(): - injection_method = type_defs.INJECTION_METHOD.SIMPLE_DLOPEN_CALL + injection_method = typedefs.INJECTION_METHOD.DLOPEN elif self.radioButton_AdvancedInjection.isChecked(): - injection_method = type_defs.INJECTION_METHOD.ADVANCED_INJECTION + injection_method = typedefs.INJECTION_METHOD.ADVANCED self.settings.setValue("CodeInjection/code_injection_method", injection_method) - self.settings.setValue("Disassemble/bring_disassemble_to_front", - self.checkBox_BringDisassembleToFront.isChecked()) - self.settings.setValue("Disassemble/instructions_per_scroll", current_instructions_shown) - selected_gdb_path = self.lineEdit_GDBPath.text() - current_gdb_path = self.settings.value("Debug/gdb_path", type=str) - if selected_gdb_path != current_gdb_path: - if InputDialogForm(item_list=[("You have changed the GDB path, reset GDB now?",)]).exec_(): - GDB_Engine.init_gdb(selected_gdb_path) - self.settings.setValue("Debug/gdb_path", selected_gdb_path) + self.settings.setValue("MemoryView/show_memory_view_on_stop", self.checkBox_ShowMemoryViewOnStop.isChecked()) + self.settings.setValue("MemoryView/instructions_per_scroll", self.spinBox_InstructionsPerScroll.value()) + self.settings.setValue("MemoryView/bytes_per_scroll", self.spinBox_BytesPerScroll.value()) + if not os.environ.get("APPDIR"): + selected_gdb_path = self.lineEdit_GDBPath.text() + current_gdb_path = self.settings.value("Debug/gdb_path", type=str) + if selected_gdb_path != current_gdb_path: + if InputDialogForm(self, [(tr.GDB_RESET,)]).exec(): + debugcore.init_gdb(selected_gdb_path) + self.settings.setValue("Debug/gdb_path", selected_gdb_path) self.settings.setValue("Debug/gdb_logging", self.checkBox_GDBLogging.isChecked()) - self.settings.setValue("Debug/ignore_sigsegv", self.checkBox_IgnoreSegfault.isChecked()) - super(SettingsDialogForm, self).accept() + self.settings.setValue("Debug/interrupt_signal", self.comboBox_InterruptSignal.currentText()) + self.settings.setValue("Java/ignore_segfault", self.checkBox_JavaSegfault.isChecked()) + if self.handle_signals_data: + self.settings.setValue("Debug/handle_signals", self.handle_signals_data) + super().accept() + + def reject(self): + logo_path = self.settings.value("General/logo_path", type=str) + app.setWindowIcon(QIcon(os.path.join(utils.get_logo_directory(), logo_path))) + theme = self.settings.value("General/theme", type=str) + app.setPalette(get_theme(theme)) + super().reject() def config_gui(self): - self.settings = QSettings() self.checkBox_AutoUpdateAddressTable.setChecked( - self.settings.value("General/auto_update_address_table", type=bool)) - self.lineEdit_UpdateInterval.setText( - str(self.settings.value("General/address_table_update_interval", type=int))) - self.lineEdit_FreezeInterval.setText( - str(self.settings.value("General/freeze_interval", type=int))) - self.checkBox_MessageBoxOnException.setChecked( - self.settings.value("General/show_messagebox_on_exception", type=bool)) - self.checkBox_MessageBoxOnToggleAttach.setChecked( - self.settings.value("General/show_messagebox_on_toggle_attach", type=bool)) - self.checkBox_OutputModeAsync.setChecked(self.settings.value("General/gdb_output_mode").async_output) - self.checkBox_OutputModeCommand.setChecked(self.settings.value("General/gdb_output_mode").command_output) - self.checkBox_OutputModeCommandInfo.setChecked(self.settings.value("General/gdb_output_mode").command_info) - self.lineEdit_AutoAttachList.setText(self.settings.value("General/auto_attach_list", type=str)) - logo_directory = SysUtils.get_logo_directory() - logo_list = SysUtils.search_files(logo_directory, "\.(png|jpg|jpeg|svg)$") - self.comboBox_Logo.clear() - for logo in logo_list: - self.comboBox_Logo.addItem(QIcon(os.path.join(logo_directory, logo)), logo) - self.comboBox_Logo.setCurrentIndex(logo_list.index(self.settings.value("General/logo_path", type=str))) + self.settings.value("General/auto_update_address_table", type=bool) + ) + self.spinBox_UpdateInterval.setValue(self.settings.value("General/address_table_update_interval", type=int)) + self.spinBox_FreezeInterval.setValue(self.settings.value("General/freeze_interval", type=int)) + output_mode = json.loads(self.settings.value("General/gdb_output_mode", type=str)) + output_mode = typedefs.gdb_output_mode(*output_mode) + self.checkBox_OutputModeAsync.setChecked(output_mode.async_output) + self.checkBox_OutputModeCommand.setChecked(output_mode.command_output) + self.checkBox_OutputModeCommandInfo.setChecked(output_mode.command_info) + self.lineEdit_AutoAttach.setText(self.settings.value("General/auto_attach", type=str)) self.checkBox_AutoAttachRegex.setChecked(self.settings.value("General/auto_attach_regex", type=bool)) - self.listWidget_Functions.clear() + current_locale = self.settings.value("General/locale", type=str) + self.comboBox_Language.setCurrentText(language_list.get(current_locale, "en_US")) + with QSignalBlocker(self.comboBox_Theme): + self.comboBox_Theme.setCurrentText(self.settings.value("General/theme", type=str)) + with QSignalBlocker(self.comboBox_Logo): + self.comboBox_Logo.setCurrentText(self.settings.value("General/logo_path", type=str)) self.hotkey_to_value.clear() - for hotkey in Hotkeys.get_hotkeys(): - self.listWidget_Functions.addItem(hotkey.desc) + for hotkey in hotkeys.get_hotkeys(): self.hotkey_to_value[hotkey.name] = self.settings.value("Hotkeys/" + hotkey.name) - injection_method = self.settings.value("CodeInjection/code_injection_method", type=int) - if injection_method == type_defs.INJECTION_METHOD.SIMPLE_DLOPEN_CALL: + self.listWidget_Functions_current_row_changed(self.listWidget_Functions.currentRow()) + code_injection_method = self.settings.value("CodeInjection/code_injection_method", type=int) + if code_injection_method == typedefs.INJECTION_METHOD.DLOPEN: self.radioButton_SimpleDLopenCall.setChecked(True) - elif injection_method == type_defs.INJECTION_METHOD.ADVANCED_INJECTION: + elif code_injection_method == typedefs.INJECTION_METHOD.ADVANCED: self.radioButton_AdvancedInjection.setChecked(True) - self.checkBox_BringDisassembleToFront.setChecked( - self.settings.value("Disassemble/bring_disassemble_to_front", type=bool)) - self.lineEdit_InstructionsPerScroll.setText( - str(self.settings.value("Disassemble/instructions_per_scroll", type=int))) + + self.checkBox_ShowMemoryViewOnStop.setChecked( + self.settings.value("MemoryView/show_memory_view_on_stop", type=bool) + ) + self.spinBox_InstructionsPerScroll.setValue(self.settings.value("MemoryView/instructions_per_scroll", type=int)) + self.spinBox_BytesPerScroll.setValue(self.settings.value("MemoryView/bytes_per_scroll", type=int)) self.lineEdit_GDBPath.setText(str(self.settings.value("Debug/gdb_path", type=str))) + if os.environ.get("APPDIR"): + self.label_GDBPath.setDisabled(True) + self.label_GDBPath.setToolTip(tr.UNUSED_APPIMAGE_SETTING) + self.lineEdit_GDBPath.setDisabled(True) + self.lineEdit_GDBPath.setToolTip(tr.UNUSED_APPIMAGE_SETTING) + self.pushButton_GDBPath.setDisabled(True) + self.pushButton_GDBPath.setToolTip(tr.UNUSED_APPIMAGE_SETTING) self.checkBox_GDBLogging.setChecked(self.settings.value("Debug/gdb_logging", type=bool)) - self.checkBox_IgnoreSegfault.setChecked(self.settings.value("Debug/ignore_sigsegv", type=bool)) + self.comboBox_InterruptSignal.setCurrentText(self.settings.value("Debug/interrupt_signal", type=str)) + self.checkBox_JavaSegfault.setChecked(self.settings.value("Java/ignore_segfault", type=bool)) def change_display(self, index): self.stackedWidget.setCurrentIndex(index) def listWidget_Functions_current_row_changed(self, index): if index == -1: - self.keySequenceEdit.clear() + self.lineEdit_Hotkey.clear() else: - self.keySequenceEdit.setKeySequence(self.hotkey_to_value[Hotkeys.get_hotkeys()[index].name]) - - def keySequenceEdit_key_sequence_changed(self): - index = self.listWidget_Functions.currentIndex().row() - if index == -1: - self.keySequenceEdit.clear() - else: - self.hotkey_to_value[Hotkeys.get_hotkeys()[index].name] = self.keySequenceEdit.keySequence().toString() + self.lineEdit_Hotkey.setText(self.hotkey_to_value[hotkeys.get_hotkeys()[index].name]) def pushButton_ClearHotkey_clicked(self): - self.keySequenceEdit.clear() + self.lineEdit_Hotkey.clear() def pushButton_ResetSettings_clicked(self): - confirm_dialog = InputDialogForm(item_list=[("This will reset to the default settings\nProceed?",)]) - if confirm_dialog.exec_(): + confirm_dialog = InputDialogForm(self, [(tr.RESET_DEFAULT_SETTINGS,)]) + if confirm_dialog.exec(): self.set_default_settings() + self.handle_signals_data = "" self.config_gui() def checkBox_AutoUpdateAddressTable_state_changed(self): @@ -1968,49 +2826,135 @@ def checkBox_AutoUpdateAddressTable_state_changed(self): def checkBox_AutoAttachRegex_state_changed(self): if self.checkBox_AutoAttachRegex.isChecked(): - self.lineEdit_AutoAttachList.setPlaceholderText("Mouse over on this text for examples") - self.lineEdit_AutoAttachList.setToolTip("'asdf|qwer' searches for asdf or qwer\n" + - "'[as]df' searches for both adf and sdf\n" + - "Use the char '\\' to escape special chars such as '['\n" + - "'\[asdf\]' searches for opcodes that contain '[asdf]'") + self.lineEdit_AutoAttach.setPlaceholderText(tr.MOUSE_OVER_EXAMPLES) + self.lineEdit_AutoAttach.setToolTip(tr.AUTO_ATTACH_TOOLTIP) else: - self.lineEdit_AutoAttachList.setPlaceholderText("Separate processes with ;") - self.lineEdit_AutoAttachList.setToolTip("") + self.lineEdit_AutoAttach.setPlaceholderText(tr.SEPARATE_PROCESSES_WITH.format(";")) + self.lineEdit_AutoAttach.setToolTip("") + + def comboBox_Logo_current_index_changed(self): + logo_path = self.comboBox_Logo.currentText() + app.setWindowIcon(QIcon(os.path.join(utils.get_logo_directory(), logo_path))) + + def comboBox_Theme_current_index_changed(self): + app.setPalette(get_theme(self.comboBox_Theme.currentText())) def pushButton_GDBPath_clicked(self): current_path = self.lineEdit_GDBPath.text() - file_path = QFileDialog.getOpenFileName(self, "Select the gdb binary", os.path.dirname(current_path))[0] + file_path, _ = QFileDialog.getOpenFileName(self, tr.SELECT_GDB_BINARY, os.path.dirname(current_path)) if file_path: self.lineEdit_GDBPath.setText(file_path) + def pushButton_HandleSignals_clicked(self): + if not self.handle_signals_data: + self.handle_signals_data = self.settings.value("Debug/handle_signals", type=str) + signal_dialog = HandleSignalsDialogForm(self, self.handle_signals_data) + if signal_dialog.exec(): + self.handle_signals_data = signal_dialog.get_values() + + def lineEdit_Hotkey_key_pressed_event(self, event: QKeyEvent): + """ + Instead of relying on the QT Event, we grab input from keyboard lib directly. + This reduces the amount of parsing from keys necessary and catches some more edge cases. + + One final caveat exists: system hotkeys or system wide defined hotkeys (xserver) + take precedence over the keyboard lib and are not caught completely. + """ + pressed_events: list[KeyboardEvent] = list(_pressed_events.values()) + if len(pressed_events) == 0: + # the keypress time was so short its not recognized by keyboard lib. + return + hotkey_string = "" + for ev in pressed_events: + # replacing keys with their respective base key, e.g "!" --> "1" + ev.name = to_name[(ev.scan_code, ())][-1] + # keyboard does recognize meta key (win key) as alt, setting manually + if ev.scan_code == 125 or ev.scan_code == 126: + ev.name = "windows" + hotkey_string += ev.name + "+" + + # remove the last plus + hotkey_string = hotkey_string[:-1] + + # moved from old keySequenceChanged event + self.lineEdit_Hotkey.setText(hotkey_string) + index = self.listWidget_Functions.currentIndex().row() + if index == -1: + self.lineEdit_Hotkey.clear() + else: + self.hotkey_to_value[hotkeys.get_hotkeys()[index].name] = self.lineEdit_Hotkey.text() + + +class HandleSignalsDialogForm(QDialog, HandleSignalsDialog): + def __init__(self, parent, signal_data): + super().__init__(parent) + self.setupUi(self) + self.signal_data = json.loads(signal_data) + self.tableWidget_Signals.setRowCount(len(self.signal_data)) + for index, (signal, stop, pass_to_program) in enumerate(self.signal_data): + self.tableWidget_Signals.setItem(index, 0, QTableWidgetItem(signal)) + widget, checkbox = self.create_checkbox_widget() + self.tableWidget_Signals.setCellWidget(index, 1, widget) + if stop: + checkbox.setCheckState(Qt.CheckState.Checked) + else: + checkbox.setCheckState(Qt.CheckState.Unchecked) + widget, checkbox = self.create_checkbox_widget() + self.tableWidget_Signals.setCellWidget(index, 2, widget) + if pass_to_program: + checkbox.setCheckState(Qt.CheckState.Checked) + else: + checkbox.setCheckState(Qt.CheckState.Unchecked) + self.tableWidget_Signals.resizeColumnsToContents() + guiutils.center_to_parent(self) + + def create_checkbox_widget(self): + widget = QWidget() + checkbox = QCheckBox() + layout = QHBoxLayout(widget) + layout.addWidget(checkbox) + layout.setAlignment(Qt.AlignmentFlag.AlignCenter) + layout.setContentsMargins(0, 0, 0, 0) + return widget, checkbox + + def get_values(self): + signal_data = [] + for index in range(len(self.signal_data)): + current_signal = [] + current_signal.append(self.signal_data[index][0]) + widget = self.tableWidget_Signals.cellWidget(index, 1) + checkbox = widget.findChild(QCheckBox) + current_signal.append(True if checkbox.checkState() == Qt.CheckState.Checked else False) + widget = self.tableWidget_Signals.cellWidget(index, 2) + checkbox = widget.findChild(QCheckBox) + current_signal.append(True if checkbox.checkState() == Qt.CheckState.Checked else False) + signal_data.append(current_signal) + return json.dumps(signal_data) + class ConsoleWidgetForm(QWidget, ConsoleWidget): - def __init__(self, parent=None): - super().__init__(parent=parent) + def __init__(self, parent): + super().__init__(parent) self.setupUi(self) - global instances - instances.append(self) - GuiUtils.center(self) + self.setWindowFlags(Qt.WindowType.Window) self.completion_model = QStringListModel() self.completer = QCompleter() self.completer.setModel(self.completion_model) - self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) + self.completer.setCompletionMode(QCompleter.CompletionMode.UnfilteredPopupCompletion) self.completer.setMaxVisibleItems(8) self.lineEdit.setCompleter(self.completer) self.quit_commands = ("q", "quit", "-gdb-exit") + self.continue_commands = ("c", "continue", "-exec-continue") self.input_history = [""] self.current_history_index = -1 self.await_async_output_thread = AwaitAsyncOutput() self.await_async_output_thread.async_output_ready.connect(self.on_async_output) self.await_async_output_thread.start() self.pushButton_Send.clicked.connect(self.communicate) - self.pushButton_SendCtrl.clicked.connect(lambda: self.communicate(control=True)) self.shortcut_send = QShortcut(QKeySequence("Return"), self) self.shortcut_send.activated.connect(self.communicate) self.shortcut_complete_command = QShortcut(QKeySequence("Tab"), self) self.shortcut_complete_command.activated.connect(self.complete_command) - self.shortcut_send_ctrl = QShortcut(QKeySequence("Ctrl+C"), self) - self.shortcut_send_ctrl.activated.connect(lambda: self.communicate(control=True)) self.shortcut_multiline_mode = QShortcut(QKeySequence("Ctrl+Return"), self) self.shortcut_multiline_mode.activated.connect(self.enter_multiline_mode) self.lineEdit.textEdited.connect(self.finish_completion) @@ -2019,37 +2963,30 @@ def __init__(self, parent=None): self.lineEdit.keyPressEvent_original = self.lineEdit.keyPressEvent self.lineEdit.keyPressEvent = self.lineEdit_key_press_event self.reset_console_text() + guiutils.center_to_parent(self) - def communicate(self, control=False): + def communicate(self): self.current_history_index = -1 self.input_history[-1] = "" - if control: - console_input = "/Ctrl+C" - else: - console_input = self.lineEdit.text() - last_input = self.input_history[-2] if len(self.input_history) > 1 else "" - if console_input != last_input and console_input != "": - self.input_history[-1] = console_input - self.input_history.append("") + console_input = self.lineEdit.text() + last_input = self.input_history[-2] if len(self.input_history) > 1 else "" + if console_input != last_input and console_input != "": + self.input_history[-1] = console_input + self.input_history.append("") if console_input.lower() == "/clear": self.lineEdit.clear() self.reset_console_text() return self.lineEdit.clear() if console_input.strip().lower() in self.quit_commands: - console_output = "Quitting current session will crash PINCE" + console_output = tr.QUIT_SESSION_CRASH + if console_input.strip().lower() in self.continue_commands: + console_output = tr.CONT_SESSION_CRASH else: - if not control: - if self.radioButton_CLI.isChecked(): - console_output = GDB_Engine.send_command(console_input, cli_output=True) - else: - console_output = GDB_Engine.send_command(console_input) - if not console_output: - if GDB_Engine.inferior_status == type_defs.INFERIOR_STATUS.INFERIOR_RUNNING: - console_output = "Inferior is running" + if self.radioButton_CLI.isChecked(): + console_output = debugcore.send_command(console_input, cli_output=True) else: - GDB_Engine.interrupt_inferior() - console_output = "" + console_output = debugcore.send_command(console_input) self.textBrowser.append("-->" + console_input) if console_output: self.textBrowser.append(console_output) @@ -2057,45 +2994,17 @@ def communicate(self, control=False): def reset_console_text(self): self.textBrowser.clear() - self.textBrowser.append("Hotkeys:") - self.textBrowser.append("----------------------------") - self.textBrowser.append("Send=Enter |") - self.textBrowser.append("Send ctrl+c=Ctrl+C |") - self.textBrowser.append("Multi-line mode=Ctrl+Enter |") - self.textBrowser.append("Complete command=Tab |") - self.textBrowser.append("----------------------------") - self.textBrowser.append("Commands:") - self.textBrowser.append("----------------------------------------------------------") - self.textBrowser.append("/clear: Clear the console |") - self.textBrowser.append("phase-out: Detach from the current process |") - self.textBrowser.append("phase-in: Attach back to the previously detached process |") - self.textBrowser.append( - "---------------------------------------------------------------------------------------------------") - self.textBrowser.append( - "pince-init-so-file so_file_path: Initializes 'lib' variable |") - self.textBrowser.append( - "pince-get-so-file-information: Get information about current lib |") - self.textBrowser.append( - "pince-execute-from-so-file lib.func(params): Execute a function from lib |") - self.textBrowser.append( - "# Check https://github.com/korcankaraokcu/PINCE/wiki#extending-pince-with-so-files for an example |") - self.textBrowser.append( - "# CLI output mode doesn't work very well with .so extensions, use MI output mode instead |") - self.textBrowser.append( - "---------------------------------------------------------------------------------------------------") - self.textBrowser.append("You can change the output mode from bottom right") - self.textBrowser.append("Note: Changing output mode only affects commands sent. Any other " + - "output coming from external sources(e.g async output) will be shown in MI format") + self.textBrowser.append(tr.GDB_CONSOLE_INIT) def scroll_to_bottom(self): cursor = self.textBrowser.textCursor() - cursor.movePosition(QTextCursor.End) + cursor.movePosition(QTextCursor.MoveOperation.End) self.textBrowser.setTextCursor(cursor) self.textBrowser.ensureCursorVisible() def enter_multiline_mode(self): - multiline_dialog = TextEditDialogForm(text=self.lineEdit.text()) - if multiline_dialog.exec_(): + multiline_dialog = TextEditDialogForm(self, self.lineEdit.text()) + if multiline_dialog.exec(): self.lineEdit.setText(multiline_dialog.get_values()) self.communicate() @@ -2120,12 +3029,14 @@ def scroll_forwards_history(self): self.lineEdit.setText(self.input_history[self.current_history_index]) def lineEdit_key_press_event(self, event): - actions = type_defs.KeyboardModifiersTupleDict([ - ((Qt.NoModifier, Qt.Key_Up), self.scroll_backwards_history), - ((Qt.NoModifier, Qt.Key_Down), self.scroll_forwards_history) - ]) + actions = typedefs.KeyboardModifiersTupleDict( + [ + (QKeyCombination(Qt.KeyboardModifier.NoModifier, Qt.Key.Key_Up), self.scroll_backwards_history), + (QKeyCombination(Qt.KeyboardModifier.NoModifier, Qt.Key.Key_Down), self.scroll_forwards_history), + ] + ) try: - actions[event.modifiers(), event.key()]() + actions[QKeyCombination(event.modifiers(), Qt.Key(event.key()))]() except KeyError: self.lineEdit.keyPressEvent_original(event) @@ -2133,31 +3044,32 @@ def finish_completion(self): self.completion_model.setStringList([]) def complete_command(self): - if GDB_Engine.gdb_initialized and GDB_Engine.currentpid != -1 and self.lineEdit.text() and \ - GDB_Engine.inferior_status == type_defs.INFERIOR_STATUS.INFERIOR_STOPPED: - self.completion_model.setStringList(GDB_Engine.complete_command(self.lineEdit.text())) + if debugcore.gdb_initialized and debugcore.currentpid != -1 and self.lineEdit.text(): + self.completion_model.setStringList(debugcore.complete_command(self.lineEdit.text())) self.completer.complete() else: self.finish_completion() - def closeEvent(self, QCloseEvent): + def closeEvent(self, event): self.await_async_output_thread.stop() - global instances - instances.remove(self) class AboutWidgetForm(QTabWidget, AboutWidget): - def __init__(self, parent=None): - super().__init__(parent=parent) + def __init__(self, parent): + super().__init__(parent) self.setupUi(self) - GuiUtils.center(self) - license_text = open("COPYING").read() - authors_text = open("AUTHORS").read() - thanks_text = open("THANKS").read() + self.setWindowFlags(Qt.WindowType.Window) + + # This section has untranslated text since it's just a placeholder + pince_dir = utils.get_script_directory() + license_text = open(f"{pince_dir}/COPYING").read() + authors_text = open(f"{pince_dir}/AUTHORS").read() + thanks_text = open(f"{pince_dir}/THANKS").read() self.textBrowser_License.setPlainText(license_text) self.textBrowser_Contributors.append( - "This is only a placeholder, this section may look different when the project finishes" + - "\nIn fact, something like a demo-scene for here would look absolutely fabulous <:") + "This is only a placeholder, this section may look different when the project finishes" + + "\nIn fact, something like a demo-scene for here would look absolutely fabulous <:" + ) self.textBrowser_Contributors.append("\n########") self.textBrowser_Contributors.append("#AUTHORS#") self.textBrowser_Contributors.append("########\n") @@ -2166,16 +3078,21 @@ def __init__(self, parent=None): self.textBrowser_Contributors.append("#THANKS#") self.textBrowser_Contributors.append("#######\n") self.textBrowser_Contributors.append(thanks_text) + guiutils.center_to_parent(self) class MemoryViewWindowForm(QMainWindow, MemoryViewWindow): process_stopped = pyqtSignal() process_running = pyqtSignal() + show_memory_view_on_stop: bool = False + instructions_per_scroll: int = 3 + bytes_per_scroll: int = 0x40 + stack_from_base_pointer: bool = False def set_dynamic_debug_hotkeys(self): - self.actionBreak.setText("Break[" + Hotkeys.break_hotkey.value + "]") - self.actionRun.setText("Run[" + Hotkeys.continue_hotkey.value + "]") - self.actionToggle_Attach.setText("Toggle Attach[" + Hotkeys.toggle_attach_hotkey.value + "]") + self.actionBreak.setText(tr.BREAK.format(hotkeys.break_hotkey.get_active_key())) + self.actionRun.setText(tr.RUN.format(hotkeys.continue_hotkey.get_active_key())) + self.actionToggle_Attach.setText(tr.TOGGLE_ATTACH.format(hotkeys.toggle_attach_hotkey.get_active_key())) def set_debug_menu_shortcuts(self): self.shortcut_step = QShortcut(QKeySequence("F7"), self) @@ -2193,9 +3110,9 @@ def initialize_file_context_menu(self): self.actionLoad_Trace.triggered.connect(self.show_trace_window) def initialize_debug_context_menu(self): - self.actionBreak.triggered.connect(GDB_Engine.interrupt_inferior) - self.actionRun.triggered.connect(GDB_Engine.continue_inferior) - self.actionToggle_Attach.triggered.connect(self.parent().toggle_attach_hotkey_pressed) + self.actionBreak.triggered.connect(debugcore.interrupt_inferior) + self.actionRun.triggered.connect(debugcore.continue_inferior) + self.actionToggle_Attach.triggered.connect(lambda: self.parent().toggle_attach_hotkey_pressed()) self.actionStep.triggered.connect(self.step_instruction) self.actionStep_Over.triggered.connect(self.step_over_instruction) self.actionExecute_Till_Return.triggered.connect(self.execute_till_return) @@ -2211,6 +3128,7 @@ def initialize_view_context_menu(self): self.actionFunctions.triggered.connect(self.actionFunctions_triggered) self.actionGDB_Log_File.triggered.connect(self.actionGDB_Log_File_triggered) self.actionMemory_Regions.triggered.connect(self.actionMemory_Regions_triggered) + self.actionRestore_Instructions.triggered.connect(self.actionRestore_Instructions_triggered) self.actionReferenced_Strings.triggered.connect(self.actionReferenced_Strings_triggered) self.actionReferenced_Calls.triggered.connect(self.actionReferenced_Calls_triggered) @@ -2223,12 +3141,12 @@ def initialize_tools_context_menu(self): def initialize_help_context_menu(self): self.actionlibpince.triggered.connect(self.actionlibpince_triggered) - def __init__(self, parent=None): - super().__init__() + def __init__(self, parent): + super().__init__(parent) self.setupUi(self) - self.parent = lambda: parent - GuiUtils.center(self) self.updating_memoryview = False + self.stacktrace_info_widget = StackTraceInfoWidgetForm(self) + self.float_registers_widget = FloatRegisterWidgetForm(self) self.process_stopped.connect(self.on_process_stop) self.process_running.connect(self.on_process_running) self.set_debug_menu_shortcuts() @@ -2247,11 +3165,14 @@ def __init__(self, parent=None): self.splitter_Disassemble_Registers.setStretchFactor(0, 1) self.splitter_MainMiddle.setStretchFactor(1, 1) - self.widget_StackView.resize(420, self.widget_StackView.height()) # blaze it + self.widget_StackView.resize(660, self.widget_StackView.height()) self.widget_Registers.resize(330, self.widget_Registers.height()) + guiutils.center(self) def initialize_register_view(self): self.pushButton_ShowFloatRegisters.clicked.connect(self.pushButton_ShowFloatRegisters_clicked) + if debugcore.currentpid == -1 or debugcore.inferior_status == typedefs.INFERIOR_STATUS.RUNNING: + self.pushButton_ShowFloatRegisters.setEnabled(False) def initialize_stack_view(self): self.stackedWidget_StackScreens.setCurrentWidget(self.StackTrace) @@ -2279,16 +3200,11 @@ def initialize_disassemble_view(self): self.disassemble_currently_displayed_address = "0" self.widget_Disassemble.wheelEvent = self.widget_Disassemble_wheel_event + self.bDisassemblyScrolling = False # rejects new scroll requests while scrolling self.tableWidget_Disassemble.wheelEvent = QEvent.ignore self.verticalScrollBar_Disassemble.wheelEvent = QEvent.ignore - - GuiUtils.center_scroll_bar(self.verticalScrollBar_Disassemble) - self.verticalScrollBar_Disassemble.mouseReleaseEvent = self.verticalScrollBar_Disassemble_mouse_release_event - - self.disassemble_scroll_bar_timer = QTimer() - self.disassemble_scroll_bar_timer.setInterval(100) - self.disassemble_scroll_bar_timer.timeout.connect(self.check_disassemble_scrollbar) - self.disassemble_scroll_bar_timer.start() + self.verticalScrollBar_Disassemble.sliderChange = self.disassemble_scrollbar_sliderchanged + guiutils.center_scroll_bar(self.verticalScrollBar_Disassemble) # Format: [address1, address2, ...] self.tableWidget_Disassemble.travel_history = [] @@ -2305,159 +3221,201 @@ def initialize_disassemble_view(self): self.tableWidget_Disassemble.itemSelectionChanged.connect(self.tableWidget_Disassemble_item_selection_changed) def initialize_hex_view(self): - self.cached_breakpoint_info = [] - self.hex_view_last_selected_address_int = 0 - self.hex_view_current_region = type_defs.tuple_region_info(0, 0, None) + # Determines where selection starts and ends + self.hex_selection_start = 0 + self.hex_selection_end = 0 + # Actual start and end addresses of the selection + self.hex_selection_address_begin = 0 + self.hex_selection_address_end = 0 + self.hex_view_current_region = typedefs.tuple_region_info(0, 0, None, None) + self.hex_model = QHexModel(HEX_VIEW_ROW_COUNT, HEX_VIEW_COL_COUNT) + self.ascii_model = QAsciiModel(HEX_VIEW_ROW_COUNT, HEX_VIEW_COL_COUNT) + self.tableView_HexView_Hex.setModel(self.hex_model) + self.tableView_HexView_Ascii.setModel(self.ascii_model) + # Adjust cell sizes after setting model to ensure correct size + self.tableView_HexView_Hex.adjust_cell_size(2) + self.tableView_HexView_Ascii.adjust_cell_size(1) + self.widget_HexView.wheelEvent = self.widget_HexView_wheel_event + # Saving the original function because super() doesn't work when we override functions like this + self.widget_HexView.keyPressEvent_original = self.widget_HexView.keyPressEvent + self.widget_HexView.keyPressEvent = self.widget_HexView_key_press_event + self.tableView_HexView_Hex.contextMenuEvent = self.widget_HexView_context_menu_event self.tableView_HexView_Ascii.contextMenuEvent = self.widget_HexView_context_menu_event - self.tableView_HexView_Hex.doubleClicked.connect(self.exec_hex_view_edit_dialog) - self.tableView_HexView_Ascii.doubleClicked.connect(self.exec_hex_view_edit_dialog) - - # Saving the original function because super() doesn't work when we override functions like this - self.tableView_HexView_Hex.keyPressEvent_original = self.tableView_HexView_Hex.keyPressEvent - self.tableView_HexView_Hex.keyPressEvent = self.widget_HexView_key_press_event - self.tableView_HexView_Ascii.keyPressEvent = self.widget_HexView_key_press_event + self.bHexViewScrolling = False # rejects new scroll requests while scrolling self.verticalScrollBar_HexView.wheelEvent = QEvent.ignore + self.verticalScrollBar_HexView.sliderChange = self.hex_view_scrollbar_sliderchanged + guiutils.center_scroll_bar(self.verticalScrollBar_HexView) + self.tableWidget_HexView_Address.wheelEvent = QEvent.ignore - self.scrollArea_Hex.keyPressEvent = QEvent.ignore self.tableWidget_HexView_Address.setAutoScroll(False) self.tableWidget_HexView_Address.setStyleSheet("QTableWidget {background-color: transparent;}") - self.tableWidget_HexView_Address.setSelectionMode(QAbstractItemView.NoSelection) - - self.hex_model = QHexModel(HEX_VIEW_ROW_COUNT, HEX_VIEW_COL_COUNT) - self.ascii_model = QAsciiModel(HEX_VIEW_ROW_COUNT, HEX_VIEW_COL_COUNT) - self.tableView_HexView_Hex.setModel(self.hex_model) - self.tableView_HexView_Ascii.setModel(self.ascii_model) - - self.tableView_HexView_Hex.selectionModel().currentChanged.connect(self.on_hex_view_current_changed) - self.tableView_HexView_Ascii.selectionModel().currentChanged.connect(self.on_ascii_view_current_changed) - - self.scrollArea_Hex.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) - self.scrollArea_Hex.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) - self.tableWidget_HexView_Address.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) - self.tableWidget_HexView_Address.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + self.tableWidget_HexView_Address.setSelectionMode(QAbstractItemView.SelectionMode.NoSelection) + + self.tableView_HexView_Hex.selectionModel().selectionChanged.connect(self.hex_view_selection_changed) + self.tableView_HexView_Ascii.selectionModel().selectionChanged.connect(self.hex_view_selection_changed) + + self.scrollArea_Hex.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) + self.scrollArea_Hex.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) + self.tableWidget_HexView_Address.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) + self.tableWidget_HexView_Address.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) + self.tableWidget_HexView_Address.verticalHeader().setMinimumSectionSize( + self.tableView_HexView_Hex.verticalHeader().minimumSectionSize() + ) self.tableWidget_HexView_Address.verticalHeader().setDefaultSectionSize( - self.tableView_HexView_Hex.verticalHeader().defaultSectionSize()) + self.tableView_HexView_Hex.verticalHeader().defaultSectionSize() + ) + self.tableWidget_HexView_Address.verticalHeader().setMaximumSectionSize( + self.tableView_HexView_Hex.verticalHeader().maximumSectionSize() + ) - GuiUtils.center_scroll_bar(self.verticalScrollBar_HexView) - self.hex_view_scroll_bar_timer = QTimer() - self.hex_view_scroll_bar_timer.setInterval(100) - self.hex_view_scroll_bar_timer.timeout.connect(self.check_hex_view_scrollbar) - self.hex_view_scroll_bar_timer.start() - self.verticalScrollBar_HexView.mouseReleaseEvent = self.verticalScrollBar_HexView_mouse_release_event + self.hex_update_timer = QTimer(timeout=self.hex_update_loop) + self.hex_update_timer.start(200) def show_trace_window(self): - trace_instructions_window = TraceInstructionsWindowForm(prompt_dialog=False) - trace_instructions_window.showMaximized() + TraceInstructionsWindowForm(self, prompt_dialog=False) def step_instruction(self): - if self.updating_memoryview: - return - GDB_Engine.step_instruction() + if not ( + debugcore.currentpid == -1 + or debugcore.active_trace + or debugcore.inferior_status == typedefs.INFERIOR_STATUS.RUNNING + or self.updating_memoryview + ): + debugcore.step_instruction() def step_over_instruction(self): - if self.updating_memoryview: - return - GDB_Engine.step_over_instruction() + if not ( + debugcore.currentpid == -1 + or debugcore.active_trace + or debugcore.inferior_status == typedefs.INFERIOR_STATUS.RUNNING + or self.updating_memoryview + ): + debugcore.step_over_instruction() def execute_till_return(self): - if self.updating_memoryview: - return - GDB_Engine.execute_till_return() + if not ( + debugcore.currentpid == -1 + or debugcore.active_trace + or debugcore.inferior_status == typedefs.INFERIOR_STATUS.RUNNING + or self.updating_memoryview + ): + debugcore.execute_till_return() def set_address(self): - selected_row = GuiUtils.get_current_row(self.tableWidget_Disassemble) + if debugcore.currentpid == -1 or debugcore.inferior_status == typedefs.INFERIOR_STATUS.RUNNING: + return + selected_row = guiutils.get_current_row(self.tableWidget_Disassemble) + current_address_text = self.tableWidget_Disassemble.item(selected_row, DISAS_ADDR_COL).text() + current_address = utils.extract_address(current_address_text) + debugcore.set_convenience_variable("pc", current_address) + self.refresh_disassemble_view() + + def edit_instruction(self): + selected_row = guiutils.get_current_row(self.tableWidget_Disassemble) + current_address_text = self.tableWidget_Disassemble.item(selected_row, DISAS_ADDR_COL).text() + current_address = utils.extract_address(current_address_text) + bytes_aob = self.tableWidget_Disassemble.item(selected_row, DISAS_BYTES_COL).text() + EditInstructionDialogForm(self, current_address, bytes_aob).exec() + + def nop_instruction(self): + if debugcore.currentpid == -1: + return + selected_row = guiutils.get_current_row(self.tableWidget_Disassemble) current_address_text = self.tableWidget_Disassemble.item(selected_row, DISAS_ADDR_COL).text() - current_address = SysUtils.extract_address(current_address_text) - GDB_Engine.set_convenience_variable("pc", current_address) + current_address = utils.extract_address(current_address_text) + current_address_int = int(current_address, 16) + array_of_bytes = self.tableWidget_Disassemble.item(selected_row, DISAS_BYTES_COL).text() + debugcore.nop_instruction(current_address_int, len(array_of_bytes.split())) self.refresh_disassemble_view() - @GDB_Engine.execute_with_temporary_interruption def toggle_breakpoint(self): - selected_row = GuiUtils.get_current_row(self.tableWidget_Disassemble) + if debugcore.currentpid == -1: + return + selected_row = guiutils.get_current_row(self.tableWidget_Disassemble) current_address_text = self.tableWidget_Disassemble.item(selected_row, DISAS_ADDR_COL).text() - current_address = SysUtils.extract_address(current_address_text) + current_address = utils.extract_address(current_address_text) current_address_int = int(current_address, 16) - if GDB_Engine.check_address_in_breakpoints(current_address_int): - GDB_Engine.delete_breakpoint(current_address) + if debugcore.get_breakpoints_in_range(current_address_int): + debugcore.delete_breakpoint(current_address) else: - if not GDB_Engine.add_breakpoint(current_address): - QMessageBox.information(self, "Error", "Failed to set breakpoint at address " + current_address) + if not debugcore.add_breakpoint(current_address): + QMessageBox.information(self, tr.ERROR, tr.BREAKPOINT_FAILED.format(current_address)) self.refresh_disassemble_view() - def toggle_watchpoint(self, address, watchpoint_type=type_defs.WATCHPOINT_TYPE.BOTH): - if GDB_Engine.check_address_in_breakpoints(address): - GDB_Engine.delete_breakpoint(hex(address)) + def toggle_watchpoint(self, address, length, watchpoint_type=typedefs.WATCHPOINT_TYPE.BOTH): + if debugcore.currentpid == -1: + return + breakpoints = debugcore.get_breakpoints_in_range(address, length) + if not breakpoints: + if len(debugcore.add_watchpoint(hex(address), length, watchpoint_type)) < 1: + QMessageBox.information(self, tr.ERROR, tr.WATCHPOINT_FAILED.format(hex(address))) else: - watchpoint_dialog = InputDialogForm(item_list=[("Enter the watchpoint length in size of bytes", "")]) - if watchpoint_dialog.exec_(): - user_input = watchpoint_dialog.get_values() - user_input_int = SysUtils.parse_string(user_input, type_defs.VALUE_INDEX.INDEX_4BYTES) - if user_input_int is None: - QMessageBox.information(self, "Error", user_input + " can't be parsed as an integer") - return - if user_input_int < 1: - QMessageBox.information(self, "Error", "Breakpoint length can't be lower than 1") - return - if len(GDB_Engine.add_watchpoint(hex(address), user_input_int, watchpoint_type)) < 1: - QMessageBox.information(self, "Error", "Failed to set watchpoint at address " + hex(address)) + for bp in breakpoints: + debugcore.delete_breakpoint(bp.address) self.refresh_hex_view() def label_HexView_Information_context_menu_event(self, event): + if debugcore.currentpid == -1: + return + def copy_to_clipboard(): app.clipboard().setText(self.label_HexView_Information.text()) menu = QMenu() - copy_label = menu.addAction("Copy to Clipboard") + copy_label = menu.addAction(tr.COPY_CLIPBOARD) font_size = self.label_HexView_Information.font().pointSize() menu.setStyleSheet("font-size: " + str(font_size) + "pt;") - action = menu.exec_(event.globalPos()) - actions = { - copy_label: copy_to_clipboard - } + action = menu.exec(event.globalPos()) + actions = {copy_label: copy_to_clipboard} try: actions[action]() except KeyError: pass def widget_HexView_context_menu_event(self, event): - selected_address = self.tableView_HexView_Hex.get_selected_address() + if debugcore.currentpid == -1: + return + addr = self.hex_selection_address_begin + length = self.get_hex_selection_length() menu = QMenu() - edit = menu.addAction("Edit") + edit = menu.addAction(tr.EDIT) menu.addSeparator() - go_to = menu.addAction("Go to expression[Ctrl+G]") - disassemble = menu.addAction("Disassemble this address[Ctrl+D]") + go_to = menu.addAction(f"{tr.GO_TO_EXPRESSION}[Ctrl+G]") + disassemble = menu.addAction(f"{tr.DISASSEMBLE_ADDRESS}[Ctrl+D]") menu.addSeparator() - add_address = menu.addAction("Add this address to address list[Ctrl+A]") + add_address = menu.addAction(f"{tr.ADD_TO_ADDRESS_LIST}[Ctrl+A]") menu.addSeparator() - refresh = menu.addAction("Refresh[R]") + copy_selection = menu.addAction(f"{tr.COPY}[Ctrl+C]") + refresh = menu.addAction(f"{tr.REFRESH}[R]") menu.addSeparator() - watchpoint_menu = menu.addMenu("Set Watchpoint") - watchpoint_write = watchpoint_menu.addAction("Write Only") - watchpoint_read = watchpoint_menu.addAction("Read Only") - watchpoint_both = watchpoint_menu.addAction("Both") - add_condition = menu.addAction("Add/Change condition for breakpoint") - delete_breakpoint = menu.addAction("Delete Breakpoint") - if not GDB_Engine.check_address_in_breakpoints(selected_address): - GuiUtils.delete_menu_entries(menu, [add_condition, delete_breakpoint]) + watchpoint_menu = menu.addMenu(tr.SET_WATCHPOINT) + watchpoint_write = watchpoint_menu.addAction(tr.WRITE_ONLY) + watchpoint_read = watchpoint_menu.addAction(tr.READ_ONLY) + watchpoint_both = watchpoint_menu.addAction(tr.BOTH) + add_condition = menu.addAction(tr.CHANGE_BREAKPOINT_CONDITION) + delete_breakpoint = menu.addAction(tr.DELETE_BREAKPOINT) + if not debugcore.get_breakpoints_in_range(addr, length): + guiutils.delete_menu_entries(menu, [add_condition, delete_breakpoint]) else: - GuiUtils.delete_menu_entries(menu, [watchpoint_menu.menuAction()]) + guiutils.delete_menu_entries(menu, [watchpoint_menu.menuAction()]) font_size = self.widget_HexView.font().pointSize() menu.setStyleSheet("font-size: " + str(font_size) + "pt;") - action = menu.exec_(event.globalPos()) + action = menu.exec(event.globalPos()) actions = { edit: self.exec_hex_view_edit_dialog, go_to: self.exec_hex_view_go_to_dialog, - disassemble: lambda: self.disassemble_expression(hex(selected_address), append_to_travel_history=True), + disassemble: lambda: self.disassemble_expression(hex(addr), append_history=True), add_address: self.exec_hex_view_add_address_dialog, + copy_selection: self.copy_hex_view_selection, refresh: self.refresh_hex_view, - watchpoint_write: lambda: self.toggle_watchpoint(selected_address, type_defs.WATCHPOINT_TYPE.WRITE_ONLY), - watchpoint_read: lambda: self.toggle_watchpoint(selected_address, type_defs.WATCHPOINT_TYPE.READ_ONLY), - watchpoint_both: lambda: self.toggle_watchpoint(selected_address, type_defs.WATCHPOINT_TYPE.BOTH), - add_condition: lambda: self.add_breakpoint_condition(selected_address), - delete_breakpoint: lambda: self.toggle_watchpoint(selected_address) + watchpoint_write: lambda: self.toggle_watchpoint(addr, length, typedefs.WATCHPOINT_TYPE.WRITE_ONLY), + watchpoint_read: lambda: self.toggle_watchpoint(addr, length, typedefs.WATCHPOINT_TYPE.READ_ONLY), + watchpoint_both: lambda: self.toggle_watchpoint(addr, length, typedefs.WATCHPOINT_TYPE.BOTH), + add_condition: lambda: self.add_breakpoint_condition(addr, length), + delete_breakpoint: lambda: self.toggle_watchpoint(addr, length), } try: actions[action]() @@ -2465,135 +3423,237 @@ def widget_HexView_context_menu_event(self, event): pass def exec_hex_view_edit_dialog(self): - selected_address = self.tableView_HexView_Hex.get_selected_address() - HexEditDialogForm(hex(selected_address)).exec_() + if debugcore.currentpid == -1: + return + HexEditDialogForm(self, self.hex_selection_address_begin, self.get_hex_selection_length()).exec() self.refresh_hex_view() def exec_hex_view_go_to_dialog(self): - current_address = hex(self.tableView_HexView_Hex.get_selected_address()) - go_to_dialog = InputDialogForm(item_list=[("Enter the expression", current_address)]) - if go_to_dialog.exec_(): + if debugcore.currentpid == -1: + return + go_to_dialog = InputDialogForm(self, [(tr.ENTER_EXPRESSION, hex(self.hex_selection_address_begin))]) + if go_to_dialog.exec(): expression = go_to_dialog.get_values() - dest_address = GDB_Engine.examine_expression(expression).address + dest_address = debugcore.examine_expression(expression).address if not dest_address: - QMessageBox.information(self, "Error", expression + " is invalid") + QMessageBox.information(self, tr.ERROR, tr.INVALID.format(expression)) return self.hex_dump_address(int(dest_address, 16)) def exec_hex_view_add_address_dialog(self): - selected_address = self.tableView_HexView_Hex.get_selected_address() - manual_address_dialog = ManualAddressDialogForm(address=hex(selected_address), - index=type_defs.VALUE_INDEX.INDEX_AOB) - if manual_address_dialog.exec_(): - desc, address_expr, address_type, length, zero_terminate, hex_repr = manual_address_dialog.get_values() - self.parent().add_entry_to_addresstable(desc, address_expr, address_type, length, zero_terminate) + if debugcore.currentpid == -1: + return + vt = typedefs.ValueType(typedefs.VALUE_INDEX.AOB, self.get_hex_selection_length()) + address_dialog = ManualAddressDialogForm(self, address=hex(self.hex_selection_address_begin), value_type=vt) + if address_dialog.exec(): + desc, address, vt = address_dialog.get_values() + self.parent().add_entry_to_addresstable(desc, address, vt) + self.parent().update_address_table() + + def copy_hex_view_selection(self): + data = debugcore.hex_dump(self.hex_selection_address_begin, self.get_hex_selection_length()) + if self.focusWidget() == self.tableView_HexView_Ascii: + display_text = utils.aob_to_str(data) + else: + display_text = " ".join(data) + app.clipboard().setText(display_text) - def verticalScrollBar_HexView_mouse_release_event(self, event): - GuiUtils.center_scroll_bar(self.verticalScrollBar_HexView) + def hex_view_scroll_up(self): + self.verticalScrollBar_HexView.setValue(1) - def verticalScrollBar_Disassemble_mouse_release_event(self, event): - GuiUtils.center_scroll_bar(self.verticalScrollBar_Disassemble) + def hex_view_scroll_down(self): + self.verticalScrollBar_HexView.setValue(-1) - def check_hex_view_scrollbar(self): - if GDB_Engine.inferior_status != type_defs.INFERIOR_STATUS.INFERIOR_STOPPED: + def hex_view_scrollbar_sliderchanged(self, event): + if self.bHexViewScrolling: return + self.bHexViewScrolling = True maximum = self.verticalScrollBar_HexView.maximum() minimum = self.verticalScrollBar_HexView.minimum() midst = (maximum + minimum) / 2 current_value = self.verticalScrollBar_HexView.value() - if midst - 10 < current_value < midst + 10: - return + # if midst - 10 < current_value < midst + 10: + # self.bHexViewScrolling = False + # return current_address = self.hex_model.current_address if current_value < midst: - next_address = current_address - 0x40 + next_address = current_address - self.bytes_per_scroll else: - next_address = current_address + 0x40 + next_address = current_address + self.bytes_per_scroll self.hex_dump_address(next_address) + guiutils.center_scroll_bar(self.verticalScrollBar_HexView) + self.bHexViewScrolling = False + + def disassemble_scroll_up(self): + self.verticalScrollBar_Disassemble.setValue(1) + + def disassemble_scroll_down(self): + self.verticalScrollBar_Disassemble.setValue(-1) - def check_disassemble_scrollbar(self): - if GDB_Engine.inferior_status != type_defs.INFERIOR_STATUS.INFERIOR_STOPPED: + def disassemble_scrollbar_sliderchanged(self, even): + if self.bDisassemblyScrolling: return + self.bDisassemblyScrolling = True maximum = self.verticalScrollBar_Disassemble.maximum() minimum = self.verticalScrollBar_Disassemble.minimum() midst = (maximum + minimum) / 2 current_value = self.verticalScrollBar_Disassemble.value() - if midst - 10 < current_value < midst + 10: - return + # if midst - 10 < current_value < midst + 10: + # self.bDisassemblyScrolling = False + # return if current_value < midst: - self.tableWidget_Disassemble_scroll("previous", instructions_per_scroll) + self.tableWidget_Disassemble_scroll("previous", self.instructions_per_scroll) + else: + self.tableWidget_Disassemble_scroll("next", self.instructions_per_scroll) + guiutils.center_scroll_bar(self.verticalScrollBar_Disassemble) + self.bDisassemblyScrolling = False + + def hex_view_selection_changed(self, selected, deselected): + sender_selection_model: QItemSelectionModel = self.sender() + sender_selection = sorted([(idx.row(), idx.column()) for idx in sender_selection_model.selectedIndexes()]) + first_selection = sender_selection[0] + last_selection = sender_selection[-1] + hex_start = self.address_to_hex_point(self.hex_selection_start) + hex_end = self.address_to_hex_point(self.hex_selection_end) + hex_start, hex_end = self.fix_selection_at_borders(hex_start, hex_end) + if len(sender_selection) == 1: + hex_start = first_selection + hex_end = first_selection + else: + # Selection ends in top left + if last_selection == hex_start: + hex_end = first_selection + # Selection ends in top right + elif last_selection[0] == hex_start[0]: + hex_end = (first_selection[0], last_selection[1]) + # Selection ends in bottom left + elif last_selection[1] == hex_start[1]: + hex_end = (last_selection[0], first_selection[1]) + # Selection ends in bottom right + else: + hex_end = last_selection + if hex_start < hex_end: + address_begin = hex_start + address_end = hex_end + else: + address_begin = hex_end + address_end = hex_start + self.hex_selection_start = self.hex_point_to_address(hex_start) + self.hex_selection_end = self.hex_point_to_address(hex_end) + self.hex_selection_address_begin = self.hex_point_to_address(address_begin) + self.hex_selection_address_end = self.hex_point_to_address(address_end) + self.handle_hex_selection() + + def handle_hex_selection(self): + hex_selection_model = self.tableView_HexView_Hex.selectionModel() + ascii_selection_model = self.tableView_HexView_Ascii.selectionModel() + start_point = self.address_to_hex_point(self.hex_selection_address_begin) + end_point = self.address_to_hex_point(self.hex_selection_address_end) + with QSignalBlocker(hex_selection_model), QSignalBlocker(ascii_selection_model): + hex_selection_model.clearSelection() + ascii_selection_model.clearSelection() + self.tableWidget_HexView_Address.clearSelection() + if start_point or end_point: + start_point, end_point = self.fix_selection_at_borders(start_point, end_point) + if start_point[0] == end_point[0]: + start = hex_selection_model.model().index(*start_point) + end = hex_selection_model.model().index(*end_point) + selection = QItemSelection(start, end) + hex_selection_model.select(selection, QItemSelectionModel.SelectionFlag.Select) + ascii_selection_model.select(selection, QItemSelectionModel.SelectionFlag.Select) + else: + # First line + start = hex_selection_model.model().index(*start_point) + end = hex_selection_model.model().index(start_point[0], HEX_VIEW_COL_COUNT - 1) + selection = QItemSelection(start, end) + hex_selection_model.select(selection, QItemSelectionModel.SelectionFlag.Select) + ascii_selection_model.select(selection, QItemSelectionModel.SelectionFlag.Select) + # Middle + if end_point[0] - start_point[0] > 1: + start = hex_selection_model.model().index(start_point[0] + 1, 0) + end = hex_selection_model.model().index(end_point[0] - 1, HEX_VIEW_COL_COUNT - 1) + selection = QItemSelection(start, end) + hex_selection_model.select(selection, QItemSelectionModel.SelectionFlag.Select) + ascii_selection_model.select(selection, QItemSelectionModel.SelectionFlag.Select) + # Last line + start = hex_selection_model.model().index(end_point[0], 0) + end = hex_selection_model.model().index(*end_point) + selection = QItemSelection(start, end) + hex_selection_model.select(selection, QItemSelectionModel.SelectionFlag.Select) + ascii_selection_model.select(selection, QItemSelectionModel.SelectionFlag.Select) + self.tableWidget_HexView_Address.setSelectionMode(QAbstractItemView.SelectionMode.MultiSelection) + for row in range(start_point[0], end_point[0] + 1): + self.tableWidget_HexView_Address.selectRow(row) + self.tableWidget_HexView_Address.setSelectionMode(QAbstractItemView.SelectionMode.NoSelection) + self.tableView_HexView_Hex.update() + self.tableView_HexView_Ascii.update() + self.tableWidget_HexView_Address.update() + + def hex_point_to_address(self, point): + address = self.hex_model.current_address + point[0] * HEX_VIEW_COL_COUNT + point[1] + return utils.modulo_address(address, debugcore.inferior_arch) + + def address_to_hex_point(self, address): + diff = address - self.hex_model.current_address + if 0 <= diff < HEX_VIEW_ROW_COUNT * HEX_VIEW_COL_COUNT: + return diff // HEX_VIEW_COL_COUNT, diff % HEX_VIEW_COL_COUNT + + def get_hex_selection_length(self): + return self.hex_selection_address_end - self.hex_selection_address_begin + 1 + + def fix_selection_at_borders(self, start_point, end_point): + if not start_point: + start_point = (0, 0) + if not end_point: + end_point = (HEX_VIEW_ROW_COUNT - 1, HEX_VIEW_COL_COUNT - 1) + return start_point, end_point + + def hex_update_loop(self): + offset = HEX_VIEW_ROW_COUNT * HEX_VIEW_COL_COUNT + if debugcore.currentpid == -1 or exiting: + updated_array = ["??"] * offset else: - self.tableWidget_Disassemble_scroll("next", instructions_per_scroll) - - def on_hex_view_current_changed(self, QModelIndex_current): - self.tableWidget_HexView_Address.setSelectionMode(QAbstractItemView.SingleSelection) - self.hex_view_last_selected_address_int = self.tableView_HexView_Hex.get_selected_address() - self.tableView_HexView_Ascii.selectionModel().setCurrentIndex(QModelIndex_current, - QItemSelectionModel.ClearAndSelect) - self.tableWidget_HexView_Address.selectRow(QModelIndex_current.row()) - self.tableWidget_HexView_Address.setSelectionMode(QAbstractItemView.NoSelection) - - def on_ascii_view_current_changed(self, QModelIndex_current): - self.tableWidget_HexView_Address.setSelectionMode(QAbstractItemView.SingleSelection) - self.tableView_HexView_Hex.selectionModel().setCurrentIndex(QModelIndex_current, - QItemSelectionModel.ClearAndSelect) - self.tableWidget_HexView_Address.selectRow(QModelIndex_current.row()) - self.tableWidget_HexView_Address.setSelectionMode(QAbstractItemView.NoSelection) + updated_array = debugcore.hex_dump(self.hex_model.current_address, offset) + self.hex_model.update_loop(updated_array) + self.ascii_model.update_loop(updated_array) # TODO: Consider merging HexView_Address, HexView_Hex and HexView_Ascii into one UI class # TODO: Move this function to that class if that happens # TODO: Also consider moving shared fields of HexView and HexModel to that class(such as HexModel.current_address) def hex_dump_address(self, int_address, offset=HEX_VIEW_ROW_COUNT * HEX_VIEW_COL_COUNT): - int_address = SysUtils.modulo_address(int_address, GDB_Engine.inferior_arch) + if debugcore.currentpid == -1: + return + int_address = utils.modulo_address(int_address, debugcore.inferior_arch) if not (self.hex_view_current_region.start <= int_address < self.hex_view_current_region.end): - information = SysUtils.get_region_info(GDB_Engine.currentpid, int_address) - if information: - self.hex_view_current_region = information - self.label_HexView_Information.setText("Protection:" + information.region.perms + " | Base:" + - hex(information.start) + "-" + hex(information.end)) + info = utils.get_region_info(debugcore.currentpid, int_address) + if info: + self.hex_view_current_region = info + self.label_HexView_Information.setText( + tr.REGION_INFO.format(info.perms, hex(info.start), hex(info.end), info.file_name) + ) else: - self.hex_view_current_region = type_defs.tuple_region_info(0, 0, None) - self.label_HexView_Information.setText("This region is invalid") + self.hex_view_current_region = typedefs.tuple_region_info(0, 0, None, None) + self.label_HexView_Information.setText(tr.INVALID_REGION) self.tableWidget_HexView_Address.setRowCount(0) self.tableWidget_HexView_Address.setRowCount(HEX_VIEW_ROW_COUNT * HEX_VIEW_COL_COUNT) for row, current_offset in enumerate(range(HEX_VIEW_ROW_COUNT)): - row_address = hex(SysUtils.modulo_address(int_address + current_offset * 16, GDB_Engine.inferior_arch)) - self.tableWidget_HexView_Address.setItem(row, 0, QTableWidgetItem(row_address)) + row_address = hex(utils.modulo_address(int_address + current_offset * 16, debugcore.inferior_arch)) + self.tableWidget_HexView_Address.setItem(row, 0, QTableWidgetItem(utils.upper_hex(row_address))) tableWidget_HexView_column_size = self.tableWidget_HexView_Address.sizeHintForColumn(0) + 5 self.tableWidget_HexView_Address.setMaximumWidth(tableWidget_HexView_column_size) self.tableWidget_HexView_Address.setMinimumWidth(tableWidget_HexView_column_size) self.tableWidget_HexView_Address.setColumnWidth(0, tableWidget_HexView_column_size) - data_array = GDB_Engine.hex_dump(int_address, offset) - - # TODO: Use GDB_Engine.breakpoint_on_hit_dict instead of caching breakpoints if possible - # Currently, breakpoint_on_hit_dict is not updated if the user manually adds a breakpoint via gdb - # A possible fix would be to hook the breakpoint commands but it needs to be tested thoroughly - if GDB_Engine.inferior_status == type_defs.INFERIOR_STATUS.INFERIOR_RUNNING: - breakpoint_info = self.cached_breakpoint_info - else: - breakpoint_info = GDB_Engine.get_breakpoint_info() - self.cached_breakpoint_info = breakpoint_info + data_array = debugcore.hex_dump(int_address, offset) + breakpoint_info = debugcore.get_breakpoint_info() self.hex_model.refresh(int_address, offset, data_array, breakpoint_info) self.ascii_model.refresh(int_address, offset, data_array, breakpoint_info) - for index in range(offset): - current_address = SysUtils.modulo_address(self.hex_model.current_address + index, GDB_Engine.inferior_arch) - if current_address == self.hex_view_last_selected_address_int: - row_index = int(index / HEX_VIEW_COL_COUNT) - model_index = QModelIndex(self.hex_model.index(row_index, index % HEX_VIEW_COL_COUNT)) - self.tableView_HexView_Hex.selectionModel().setCurrentIndex(model_index, - QItemSelectionModel.ClearAndSelect) - self.tableView_HexView_Ascii.selectionModel().setCurrentIndex(model_index, - QItemSelectionModel.ClearAndSelect) - self.tableWidget_HexView_Address.setSelectionMode(QAbstractItemView.SingleSelection) - self.tableWidget_HexView_Address.selectRow(row_index) - self.tableWidget_HexView_Address.setSelectionMode(QAbstractItemView.NoSelection) - break - else: - self.tableView_HexView_Hex.clearSelection() - self.tableView_HexView_Ascii.clearSelection() + self.handle_hex_selection() def refresh_hex_view(self): + if debugcore.currentpid == -1: + return if self.tableWidget_HexView_Address.rowCount() == 0: - entry_point = GDB_Engine.find_entry_point() + entry_point = debugcore.find_entry_point() if not entry_point: # **Shrugs** entry_point = "0x00400000" @@ -2605,30 +3665,31 @@ def refresh_hex_view(self): # offset can also be an address as hex str # returns True if the given expression is disassembled correctly, False if not - def disassemble_expression(self, expression, offset="+200", append_to_travel_history=False): - disas_data = GDB_Engine.disassemble(expression, offset) + def disassemble_expression(self, expression, offset="+200", append_history=False): + if debugcore.currentpid == -1: + return + disas_data = debugcore.disassemble(expression, offset) if not disas_data: - QMessageBox.information(app.focusWidget(), "Error", "Cannot access memory at expression " + expression) + QMessageBox.information(app.focusWidget(), tr.ERROR, tr.EXPRESSION_ACCESS_ERROR.format(expression)) return False - program_counter = GDB_Engine.examine_expression("$pc").address - program_counter_int = int(program_counter, 16) - row_colour = {} - breakpoint_info = GDB_Engine.get_breakpoint_info() + program_counter = debugcore.examine_expression("$pc").address + program_counter_int = int(program_counter, 16) if program_counter else None + row_color = {} + breakpoint_info = debugcore.get_breakpoint_info() # TODO: Change this nonsense when the huge refactorization happens - current_first_address = SysUtils.extract_address(disas_data[0][0]) # address of first list entry + current_first_address = utils.extract_address(disas_data[0][0]) # address of first list entry try: - previous_first_address = SysUtils.extract_address( - self.tableWidget_Disassemble.item(0, DISAS_ADDR_COL).text()) + previous_first_address = utils.extract_address(self.tableWidget_Disassemble.item(0, DISAS_ADDR_COL).text()) except AttributeError: previous_first_address = current_first_address self.tableWidget_Disassemble.setRowCount(0) self.tableWidget_Disassemble.setRowCount(len(disas_data)) - jmp_dict, call_dict = GDB_Engine.get_dissect_code_data(False, True, True) - for row, item in enumerate(disas_data): + jmp_dict, call_dict = debugcore.get_dissect_code_data(False, True, True) + for row, (address_info, bytes_aob, opcode) in enumerate(disas_data): comment = "" - current_address = int(SysUtils.extract_address(item[0]), 16) + current_address = int(utils.extract_address(address_info), 16) current_address_str = hex(current_address) jmp_ref_exists = False call_ref_exists = False @@ -2643,7 +3704,7 @@ def disassemble_expression(self, expression, offset="+200", append_to_travel_his except KeyError: pass if jmp_ref_exists or call_ref_exists: - tooltip_text = "Referenced by:\n" + tooltip_text = f"{tr.REFERENCED_BY}\n" ref_count = 0 if jmp_ref_exists: for referrer in jmp_referrers: @@ -2659,39 +3720,39 @@ def disassemble_expression(self, expression, offset="+200", append_to_travel_his ref_count += 1 if ref_count > 30: tooltip_text += "\n..." - tooltip_text += "\n\nPress 'Ctrl+E' to see a detailed list of referrers" + tooltip_text += f"\n\n{tr.SEE_REFERRERS}" try: - row_colour[row].append(REF_COLOUR) + row_color[row].append(REF_COLOR) except KeyError: - row_colour[row] = [REF_COLOUR] + row_color[row] = [REF_COLOR] real_ref_count = 0 if jmp_ref_exists: real_ref_count += len(jmp_referrers) if call_ref_exists: real_ref_count += len(call_referrers) - item[0] = "{" + str(real_ref_count) + "}" + item[0] + address_info = "{" + str(real_ref_count) + "}" + address_info if current_address == program_counter_int: - item[0] = ">>>" + item[0] + address_info = ">>>" + address_info try: - row_colour[row].append(PC_COLOUR) + row_color[row].append(PC_COLOR) except KeyError: - row_colour[row] = [PC_COLOUR] + row_color[row] = [PC_COLOR] for bookmark_item in self.tableWidget_Disassemble.bookmarks.keys(): if current_address == bookmark_item: try: - row_colour[row].append(BOOKMARK_COLOUR) + row_color[row].append(BOOKMARK_COLOR) except KeyError: - row_colour[row] = [BOOKMARK_COLOUR] - item[0] = "(M)" + item[0] + row_color[row] = [BOOKMARK_COLOR] + address_info = "(M)" + address_info comment = self.tableWidget_Disassemble.bookmarks[bookmark_item] break for breakpoint in breakpoint_info: int_breakpoint_address = int(breakpoint.address, 16) if current_address == int_breakpoint_address: try: - row_colour[row].append(BREAKPOINT_COLOUR) + row_color[row].append(BREAKPOINT_COLOR) except KeyError: - row_colour[row] = [BREAKPOINT_COLOUR] + row_color[row] = [BREAKPOINT_COLOR] breakpoint_mark = "(B" if breakpoint.enabled == "n": breakpoint_mark += "-disabled" @@ -2701,13 +3762,13 @@ def disassemble_expression(self, expression, offset="+200", append_to_travel_his if breakpoint.enable_count: breakpoint_mark += "-" + breakpoint.enable_count breakpoint_mark += ")" - item[0] = breakpoint_mark + item[0] + address_info = breakpoint_mark + address_info break if current_address == self.disassemble_last_selected_address_int: self.tableWidget_Disassemble.selectRow(row) - addr_item = QTableWidgetItem(item[0]) - bytes_item = QTableWidgetItem(item[1]) - opcodes_item = QTableWidgetItem(item[2]) + addr_item = QTableWidgetItem(address_info) + bytes_item = QTableWidgetItem(bytes_aob) + opcodes_item = QTableWidgetItem(opcode) comment_item = QTableWidgetItem(comment) if jmp_ref_exists or call_ref_exists: addr_item.setToolTip(tooltip_text) @@ -2720,111 +3781,115 @@ def disassemble_expression(self, expression, offset="+200", append_to_travel_his self.tableWidget_Disassemble.setItem(row, DISAS_COMMENT_COL, comment_item) jmp_dict.close() call_dict.close() - self.handle_colours(row_colour) - self.tableWidget_Disassemble.horizontalHeader().setStretchLastSection(True) + self.handle_colors(row_color) # We append the old record to travel history as last action because we wouldn't like to see unnecessary # addresses in travel history if any error occurs while displaying the next location - if append_to_travel_history: + if append_history: self.tableWidget_Disassemble.travel_history.append(previous_first_address) self.disassemble_currently_displayed_address = current_first_address return True def refresh_disassemble_view(self): + if debugcore.currentpid == -1: + return self.disassemble_expression(self.disassemble_currently_displayed_address) - # Set colour of a row if a specific address is encountered(e.g $pc, a bookmarked address etc.) - def handle_colours(self, row_colour): - for row in row_colour: - current_row = row_colour[row] - if PC_COLOUR in current_row: - if BREAKPOINT_COLOUR in current_row: - colour = Qt.green - elif BOOKMARK_COLOUR in current_row: - colour = Qt.yellow + # Set color of a row if a specific address is encountered(e.g $pc, a bookmarked address etc.) + def handle_colors(self, row_color): + if debugcore.currentpid == -1: + return + for row in row_color: + current_row = row_color[row] + if PC_COLOR in current_row: + if BREAKPOINT_COLOR in current_row: + color = QColorConstants.Green + elif BOOKMARK_COLOR in current_row: + color = QColorConstants.Yellow else: - colour = PC_COLOUR - self.set_row_colour(row, colour) + color = PC_COLOR + self.set_row_color(row, color) continue - if BREAKPOINT_COLOUR in current_row: - if BOOKMARK_COLOUR in current_row: - colour = Qt.magenta + if BREAKPOINT_COLOR in current_row: + if BOOKMARK_COLOR in current_row: + color = QColorConstants.Magenta else: - colour = BREAKPOINT_COLOUR - self.set_row_colour(row, colour) + color = BREAKPOINT_COLOR + self.set_row_color(row, color) continue - if BOOKMARK_COLOUR in current_row: - self.set_row_colour(row, BOOKMARK_COLOUR) + if BOOKMARK_COLOR in current_row: + self.set_row_color(row, BOOKMARK_COLOR) continue - if REF_COLOUR in current_row: - self.set_row_colour(row, REF_COLOUR) + if REF_COLOR in current_row: + self.set_row_color(row, REF_COLOR) - # color parameter should be Qt.colour - def set_row_colour(self, row, colour): + def set_row_color(self, row, color): + if debugcore.currentpid == -1: + return for col in range(self.tableWidget_Disassemble.columnCount()): - self.tableWidget_Disassemble.item(row, col).setData(Qt.BackgroundColorRole, QColor(colour)) + color = QColor(color) + color.setAlpha(96) + self.tableWidget_Disassemble.item(row, col).setData(Qt.ItemDataRole.BackgroundRole, color) def on_process_stop(self): - if GDB_Engine.stop_reason == type_defs.STOP_REASON.PAUSE: - self.setWindowTitle("Memory Viewer - Paused") + if debugcore.stop_reason == typedefs.STOP_REASON.PAUSE: + self.setWindowTitle(tr.MV_PAUSED) return self.updating_memoryview = True time0 = time() - thread_info = GDB_Engine.get_current_thread_information() - if thread_info: - self.setWindowTitle("Memory Viewer - Currently debugging " + thread_info) - else: - self.setWindowTitle("Error while getting thread information: " + - "Please invoke 'info threads' command in GDB Console and open an issue with the output") + self.setWindowTitle(tr.MV_DEBUGGING.format(debugcore.get_thread_info())) self.disassemble_expression("$pc") self.update_registers() if self.stackedWidget_StackScreens.currentWidget() == self.StackTrace: self.update_stacktrace() elif self.stackedWidget_StackScreens.currentWidget() == self.Stack: self.update_stack() + + # These tableWidgets are never emptied but initially both are empty, so this runs only once + if self.tableWidget_StackTrace.rowCount() == 0: + self.update_stacktrace() + if self.tableWidget_Stack.rowCount() == 0: + self.update_stack() self.refresh_hex_view() - if bring_disassemble_to_front: + if self.show_memory_view_on_stop: self.showMaximized() self.activateWindow() - try: - if self.stacktrace_info_widget.isVisible(): - self.stacktrace_info_widget.update_stacktrace() - except AttributeError: - pass - try: - if self.float_registers_widget.isVisible(): - self.float_registers_widget.update_registers() - except AttributeError: - pass + if self.stacktrace_info_widget.isVisible(): + self.stacktrace_info_widget.update_stacktrace() + self.pushButton_ShowFloatRegisters.setEnabled(True) + if self.float_registers_widget.isVisible(): + self.float_registers_widget.update_registers() app.processEvents() time1 = time() print("UPDATED MEMORYVIEW IN:" + str(time1 - time0)) self.updating_memoryview = False def on_process_running(self): - self.setWindowTitle("Memory Viewer - Running") - - def add_breakpoint_condition(self, int_address): - condition_text = "Enter the expression for condition, for instance:\n\n" + \ - "$eax==0x523\n" + \ - "$rax>0 && ($rbp<0 || $rsp==0)\n" + \ - "printf($r10)==3" - breakpoint = GDB_Engine.check_address_in_breakpoints(int_address) - if breakpoint: - condition_line_edit_text = breakpoint.condition + self.setWindowTitle(tr.MV_RUNNING) + self.pushButton_ShowFloatRegisters.setEnabled(False) + + def add_breakpoint_condition(self, int_address, length=1): + if debugcore.currentpid == -1: + return + breakpoints = debugcore.get_breakpoints_in_range(int_address, length) + if breakpoints: + condition_line_edit_text = breakpoints[0].condition else: condition_line_edit_text = "" - condition_dialog = InputDialogForm(item_list=[(condition_text, condition_line_edit_text, Qt.AlignLeft)]) - if condition_dialog.exec_(): + items = [(tr.ENTER_BP_CONDITION, condition_line_edit_text, Qt.AlignmentFlag.AlignLeft)] + condition_dialog = InputDialogForm(self, items) + if condition_dialog.exec(): condition = condition_dialog.get_values() - if not GDB_Engine.modify_breakpoint(hex(int_address), type_defs.BREAKPOINT_MODIFY.CONDITION, - condition=condition): - QMessageBox.information(app.focusWidget(), "Error", "Failed to set condition for address " + - hex(int_address) + "\nCheck terminal for details") + for bp in breakpoints: + addr = bp.address + if not debugcore.modify_breakpoint(addr, typedefs.BREAKPOINT_MODIFY.CONDITION, condition): + QMessageBox.information(app.focusWidget(), tr.ERROR, tr.BP_CONDITION_FAILED.format(addr)) def update_registers(self): - registers = GDB_Engine.read_registers() - if GDB_Engine.inferior_arch == type_defs.INFERIOR_ARCH.ARCH_64: + if debugcore.currentpid == -1: + return + registers = debugcore.read_registers() + if debugcore.inferior_arch == typedefs.INFERIOR_ARCH.ARCH_64: self.stackedWidget.setCurrentWidget(self.registers_64) self.RAX.set_value(registers["rax"]) self.RBX.set_value(registers["rbx"]) @@ -2843,7 +3908,7 @@ def update_registers(self): self.R13.set_value(registers["r13"]) self.R14.set_value(registers["r14"]) self.R15.set_value(registers["r15"]) - elif GDB_Engine.inferior_arch == type_defs.INFERIOR_ARCH.ARCH_32: + elif debugcore.inferior_arch == typedefs.INFERIOR_ARCH.ARCH_32: self.stackedWidget.setCurrentWidget(self.registers_32) self.EAX.set_value(registers["eax"]) self.EBX.set_value(registers["ebx"]) @@ -2871,7 +3936,9 @@ def update_registers(self): self.FS.set_value(registers["fs"]) def update_stacktrace(self): - stack_trace_info = GDB_Engine.get_stacktrace_info() + if debugcore.currentpid == -1 or debugcore.inferior_status == typedefs.INFERIOR_STATUS.RUNNING: + return + stack_trace_info = debugcore.get_stacktrace_info() self.tableWidget_StackTrace.setRowCount(0) self.tableWidget_StackTrace.setRowCount(len(stack_trace_info)) for row, item in enumerate(stack_trace_info): @@ -2879,6 +3946,8 @@ def update_stacktrace(self): self.tableWidget_StackTrace.setItem(row, STACKTRACE_FRAME_ADDRESS_COL, QTableWidgetItem(item[1])) def set_stack_widget(self, stack_widget): + if debugcore.currentpid == -1: + return self.stackedWidget_StackScreens.setCurrentWidget(stack_widget) if stack_widget == self.Stack: self.update_stack() @@ -2889,25 +3958,27 @@ def tableWidget_StackTrace_context_menu_event(self, event): def copy_to_clipboard(row, column): app.clipboard().setText(self.tableWidget_StackTrace.item(row, column).text()) - selected_row = GuiUtils.get_current_row(self.tableWidget_StackTrace) - + selected_row = guiutils.get_current_row(self.tableWidget_StackTrace) menu = QMenu() - switch_to_stack = menu.addAction("Full Stack") + switch_to_stack = menu.addAction(tr.FULL_STACK) menu.addSeparator() - clipboard_menu = menu.addMenu("Copy to Clipboard") - copy_return = clipboard_menu.addAction("Copy Return Address") - copy_frame = clipboard_menu.addAction("Copy Frame Address") + clipboard_menu = menu.addMenu(tr.COPY_CLIPBOARD) + copy_return = clipboard_menu.addAction(tr.COPY_RETURN_ADDRESS) + copy_frame = clipboard_menu.addAction(tr.COPY_FRAME_ADDRESS) if selected_row == -1: - GuiUtils.delete_menu_entries(menu, [clipboard_menu.menuAction()]) - refresh = menu.addAction("Refresh[R]") + guiutils.delete_menu_entries(menu, [clipboard_menu.menuAction()]) + refresh = menu.addAction(f"{tr.REFRESH}[R]") + if debugcore.currentpid == -1: + menu.clear() + menu.addMenu(clipboard_menu) font_size = self.tableWidget_StackTrace.font().pointSize() menu.setStyleSheet("font-size: " + str(font_size) + "pt;") - action = menu.exec_(event.globalPos()) + action = menu.exec(event.globalPos()) actions = { switch_to_stack: lambda: self.set_stack_widget(self.Stack), copy_return: lambda: copy_to_clipboard(selected_row, STACKTRACE_RETURN_ADDRESS_COL), copy_frame: lambda: copy_to_clipboard(selected_row, STACKTRACE_FRAME_ADDRESS_COL), - refresh: self.update_stacktrace + refresh: self.update_stacktrace, } try: actions[action]() @@ -2915,7 +3986,9 @@ def copy_to_clipboard(row, column): pass def update_stack(self): - stack_info = GDB_Engine.get_stack_info() + if debugcore.currentpid == -1 or debugcore.inferior_status == typedefs.INFERIOR_STATUS.RUNNING: + return + stack_info: list[str] = debugcore.get_stack_info(from_base_pointer=self.stack_from_base_pointer) self.tableWidget_Stack.setRowCount(0) self.tableWidget_Stack.setRowCount(len(stack_info)) for row, item in enumerate(stack_info): @@ -2925,19 +3998,37 @@ def update_stack(self): self.tableWidget_Stack.resizeColumnToContents(STACK_POINTER_ADDRESS_COL) self.tableWidget_Stack.resizeColumnToContents(STACK_VALUE_COL) + def toggle_stack_from_sp_bp(self): + self.stack_from_base_pointer = not self.stack_from_base_pointer + self.update_stack() + def tableWidget_Stack_key_press_event(self, event): - selected_row = GuiUtils.get_current_row(self.tableWidget_Stack) - current_address_text = self.tableWidget_Stack.item(selected_row, STACK_VALUE_COL).text() - current_address = SysUtils.extract_address(current_address_text) - - actions = type_defs.KeyboardModifiersTupleDict([ - ((Qt.NoModifier, Qt.Key_R), self.update_stack), - ((Qt.ControlModifier, Qt.Key_D), - lambda: self.disassemble_expression(current_address, append_to_travel_history=True)), - ((Qt.ControlModifier, Qt.Key_H), lambda: self.hex_dump_address(int(current_address, 16))) - ]) + if debugcore.currentpid == -1: + return + selected_row = guiutils.get_current_row(self.tableWidget_Stack) + if selected_row == -1: + actions = typedefs.KeyboardModifiersTupleDict( + [(QKeyCombination(Qt.KeyboardModifier.NoModifier, Qt.Key.Key_R), self.update_stack)] + ) + else: + current_address_text = self.tableWidget_Stack.item(selected_row, STACK_VALUE_COL).text() + current_address = utils.extract_address(current_address_text) + + actions = typedefs.KeyboardModifiersTupleDict( + [ + (QKeyCombination(Qt.KeyboardModifier.NoModifier, Qt.Key.Key_R), self.update_stack), + ( + QKeyCombination(Qt.KeyboardModifier.ControlModifier, Qt.Key.Key_D), + lambda: self.disassemble_expression(current_address, append_history=True), + ), + ( + QKeyCombination(Qt.KeyboardModifier.ControlModifier, Qt.Key.Key_H), + lambda: self.hex_dump_address(int(current_address, 16)), + ), + ] + ) try: - actions[event.modifiers(), event.key()]() + actions[QKeyCombination(event.modifiers(), Qt.Key(event.key()))]() except KeyError: pass self.tableWidget_Stack.keyPressEvent_original(event) @@ -2946,34 +4037,43 @@ def tableWidget_Stack_context_menu_event(self, event): def copy_to_clipboard(row, column): app.clipboard().setText(self.tableWidget_Stack.item(row, column).text()) - selected_row = GuiUtils.get_current_row(self.tableWidget_Stack) - current_address_text = self.tableWidget_Stack.item(selected_row, STACK_VALUE_COL).text() - current_address = SysUtils.extract_address(current_address_text) - + selected_row = guiutils.get_current_row(self.tableWidget_Stack) + if selected_row == -1: + current_address = None + else: + current_address_text = self.tableWidget_Stack.item(selected_row, STACK_VALUE_COL).text() + current_address = utils.extract_address(current_address_text) menu = QMenu() - switch_to_stacktrace = menu.addAction("Stacktrace") + switch_to_stacktrace = menu.addAction(tr.STACKTRACE) + toggle_stack_pointer = menu.addAction(tr.TOGGLE_STACK_FROM_SP_BP) + if debugcore.inferior_status == typedefs.INFERIOR_STATUS.RUNNING: + toggle_stack_pointer.setEnabled(False) menu.addSeparator() - clipboard_menu = menu.addMenu("Copy to Clipboard") - copy_address = clipboard_menu.addAction("Copy Address") - copy_value = clipboard_menu.addAction("Copy Value") - copy_points_to = clipboard_menu.addAction("Copy Points to") - refresh = menu.addAction("Refresh[R]") + clipboard_menu = menu.addMenu(tr.COPY_CLIPBOARD) + copy_address = clipboard_menu.addAction(tr.COPY_ADDRESS) + copy_value = clipboard_menu.addAction(tr.COPY_VALUE) + copy_points_to = clipboard_menu.addAction(tr.COPY_POINTS_TO) + refresh = menu.addAction(f"{tr.REFRESH}[R]") menu.addSeparator() - show_in_disas = menu.addAction("Disassemble 'value' pointer address[Ctrl+D]") - show_in_hex = menu.addAction("Show 'value' pointer in HexView[Ctrl+H]") + show_in_disas = menu.addAction(f"{tr.DISASSEMBLE_VALUE_POINTER}[Ctrl+D]") + show_in_hex = menu.addAction(f"{tr.HEXVIEW_VALUE_POINTER}[Ctrl+H]") if selected_row == -1: - GuiUtils.delete_menu_entries(menu, [clipboard_menu.menuAction(), show_in_disas, show_in_hex]) + guiutils.delete_menu_entries(menu, [clipboard_menu.menuAction(), show_in_disas, show_in_hex]) + if debugcore.currentpid == -1: + menu.clear() + menu.addMenu(clipboard_menu) font_size = self.tableWidget_Stack.font().pointSize() menu.setStyleSheet("font-size: " + str(font_size) + "pt;") - action = menu.exec_(event.globalPos()) + action = menu.exec(event.globalPos()) actions = { switch_to_stacktrace: lambda: self.set_stack_widget(self.StackTrace), + toggle_stack_pointer: self.toggle_stack_from_sp_bp, copy_address: lambda: copy_to_clipboard(selected_row, STACK_POINTER_ADDRESS_COL), copy_value: lambda: copy_to_clipboard(selected_row, STACK_VALUE_COL), copy_points_to: lambda: copy_to_clipboard(selected_row, STACK_POINTS_TO_COL), refresh: self.update_stack, - show_in_disas: lambda: self.disassemble_expression(current_address, append_to_travel_history=True), - show_in_hex: lambda: self.hex_dump_address(int(current_address, 16)) + show_in_disas: lambda: self.disassemble_expression(current_address, append_history=True), + show_in_hex: lambda: self.hex_dump_address(int(current_address, 16)), } try: actions[action]() @@ -2981,50 +4081,60 @@ def copy_to_clipboard(row, column): pass def tableWidget_Stack_double_click(self, index): - selected_row = GuiUtils.get_current_row(self.tableWidget_Stack) + if debugcore.currentpid == -1: + return + selected_row = guiutils.get_current_row(self.tableWidget_Stack) if index.column() == STACK_POINTER_ADDRESS_COL: current_address_text = self.tableWidget_Stack.item(selected_row, STACK_POINTER_ADDRESS_COL).text() - current_address = SysUtils.extract_address(current_address_text) + current_address = utils.extract_address(current_address_text) self.hex_dump_address(int(current_address, 16)) else: points_to_text = self.tableWidget_Stack.item(selected_row, STACK_POINTS_TO_COL).text() current_address_text = self.tableWidget_Stack.item(selected_row, STACK_VALUE_COL).text() - current_address = SysUtils.extract_address(current_address_text) + current_address = utils.extract_address(current_address_text) if points_to_text.startswith("(str)"): self.hex_dump_address(int(current_address, 16)) else: - self.disassemble_expression(current_address, append_to_travel_history=True) + self.disassemble_expression(current_address, append_history=True) def tableWidget_StackTrace_double_click(self, index): - selected_row = GuiUtils.get_current_row(self.tableWidget_StackTrace) + if debugcore.currentpid == -1: + return + selected_row = guiutils.get_current_row(self.tableWidget_StackTrace) if index.column() == STACKTRACE_RETURN_ADDRESS_COL: current_address_text = self.tableWidget_StackTrace.item(selected_row, STACKTRACE_RETURN_ADDRESS_COL).text() - current_address = SysUtils.extract_address(current_address_text) - self.disassemble_expression(current_address, append_to_travel_history=True) + current_address = utils.extract_address(current_address_text) + self.disassemble_expression(current_address, append_history=True) if index.column() == STACKTRACE_FRAME_ADDRESS_COL: current_address_text = self.tableWidget_StackTrace.item(selected_row, STACKTRACE_FRAME_ADDRESS_COL).text() - current_address = SysUtils.extract_address(current_address_text) + current_address = utils.extract_address(current_address_text) self.hex_dump_address(int(current_address, 16)) def tableWidget_StackTrace_key_press_event(self, event): - actions = type_defs.KeyboardModifiersTupleDict([ - ((Qt.NoModifier, Qt.Key_R), self.update_stacktrace) - ]) + if debugcore.currentpid == -1: + return + actions = typedefs.KeyboardModifiersTupleDict( + [(QKeyCombination(Qt.KeyboardModifier.NoModifier, Qt.Key.Key_R), self.update_stacktrace)] + ) try: - actions[event.modifiers(), event.key()]() + actions[QKeyCombination(event.modifiers(), Qt.Key(event.key()))]() except KeyError: pass self.tableWidget_StackTrace.keyPressEvent_original(event) def widget_Disassemble_wheel_event(self, event): + if debugcore.currentpid == -1: + return steps = event.angleDelta() if steps.y() > 0: - self.tableWidget_Disassemble_scroll("previous", instructions_per_scroll) + self.tableWidget_Disassemble_scroll("previous", self.instructions_per_scroll) else: - self.tableWidget_Disassemble_scroll("next", instructions_per_scroll) + self.tableWidget_Disassemble_scroll("next", self.instructions_per_scroll) def disassemble_check_viewport(self, where, instruction_count): - current_row = GuiUtils.get_current_row(self.tableWidget_Disassemble) + if debugcore.currentpid == -1: + return + current_row = guiutils.get_current_row(self.tableWidget_Disassemble) current_row_height = self.tableWidget_Disassemble.rowViewportPosition(current_row) row_height = self.tableWidget_Disassemble.verticalHeader().defaultSectionSize() max_height = self.tableWidget_Disassemble.maximumViewportSize().height() @@ -3036,94 +4146,150 @@ def disassemble_check_viewport(self, where, instruction_count): if self.tableWidget_Disassemble.rowViewportPosition(row) > height: break last_visible_row += 1 - current_address = SysUtils.extract_address( - self.tableWidget_Disassemble.item(current_row, DISAS_ADDR_COL).text()) - new_address = GDB_Engine.find_address_of_closest_instruction(current_address, "previous", last_visible_row) + current_address = utils.extract_address( + self.tableWidget_Disassemble.item(current_row, DISAS_ADDR_COL).text() + ) + new_address = debugcore.find_closest_instruction_address(current_address, "previous", last_visible_row) self.disassemble_expression(new_address) elif (where == "previous" and current_row == 0) or (where == "next" and current_row_height > height): self.tableWidget_Disassemble_scroll(where, instruction_count) def tableWidget_Disassemble_scroll(self, where, instruction_count): + if debugcore.currentpid == -1: + return current_address = self.disassemble_currently_displayed_address - new_address = GDB_Engine.find_address_of_closest_instruction(current_address, where, instruction_count) + new_address = debugcore.find_closest_instruction_address(current_address, where, instruction_count) self.disassemble_expression(new_address) def widget_HexView_wheel_event(self, event): + if debugcore.currentpid == -1: + return steps = event.angleDelta() current_address = self.hex_model.current_address if steps.y() > 0: - next_address = current_address - 0x40 + next_address = current_address - self.bytes_per_scroll else: - next_address = current_address + 0x40 + next_address = current_address + self.bytes_per_scroll self.hex_dump_address(next_address) def widget_HexView_key_press_event(self, event): - selected_address = self.tableView_HexView_Hex.get_selected_address() - - actions = type_defs.KeyboardModifiersTupleDict([ - ((Qt.ControlModifier, Qt.Key_G), self.exec_hex_view_go_to_dialog), - ((Qt.ControlModifier, Qt.Key_D), - lambda: self.disassemble_expression(hex(selected_address), append_to_travel_history=True)), - ((Qt.ControlModifier, Qt.Key_A), self.exec_hex_view_add_address_dialog), - ((Qt.NoModifier, Qt.Key_R), self.refresh_hex_view) - ]) + if debugcore.currentpid == -1: + return + actions = typedefs.KeyboardModifiersTupleDict( + [ + (QKeyCombination(Qt.KeyboardModifier.ControlModifier, Qt.Key.Key_G), self.exec_hex_view_go_to_dialog), + ( + QKeyCombination(Qt.KeyboardModifier.ControlModifier, Qt.Key.Key_D), + lambda: self.disassemble_expression(hex(self.hex_selection_address_begin), append_history=True), + ), + ( + QKeyCombination(Qt.KeyboardModifier.ControlModifier, Qt.Key.Key_A), + self.exec_hex_view_add_address_dialog, + ), + (QKeyCombination(Qt.KeyboardModifier.ControlModifier, Qt.Key.Key_C), self.copy_hex_view_selection), + (QKeyCombination(Qt.KeyboardModifier.NoModifier, Qt.Key.Key_R), self.refresh_hex_view), + (QKeyCombination(Qt.KeyboardModifier.NoModifier, Qt.Key.Key_PageUp), self.hex_view_scroll_up), + (QKeyCombination(Qt.KeyboardModifier.NoModifier, Qt.Key.Key_PageDown), self.hex_view_scroll_down), + ] + ) try: - actions[event.modifiers(), event.key()]() + actions[QKeyCombination(event.modifiers(), Qt.Key(event.key()))]() except KeyError: pass - self.tableView_HexView_Hex.keyPressEvent_original(event) + self.widget_HexView.keyPressEvent_original(event) def tableWidget_Disassemble_key_press_event(self, event): - selected_row = GuiUtils.get_current_row(self.tableWidget_Disassemble) + if debugcore.currentpid == -1: + return + selected_row = guiutils.get_current_row(self.tableWidget_Disassemble) + if selected_row == -1: + selected_row = 0 current_address_text = self.tableWidget_Disassemble.item(selected_row, DISAS_ADDR_COL).text() - current_address = SysUtils.extract_address(current_address_text) + current_address = utils.extract_address(current_address_text) current_address_int = int(current_address, 16) - actions = type_defs.KeyboardModifiersTupleDict([ - ((Qt.NoModifier, Qt.Key_Space), lambda: self.follow_instruction(selected_row)), - ((Qt.ControlModifier, Qt.Key_E), lambda: self.exec_examine_referrers_widget(current_address_text)), - ((Qt.ControlModifier, Qt.Key_G), self.exec_disassemble_go_to_dialog), - ((Qt.ControlModifier, Qt.Key_H), lambda: self.hex_dump_address(current_address_int)), - ((Qt.ControlModifier, Qt.Key_B), lambda: self.bookmark_address(current_address_int)), - ((Qt.ControlModifier, Qt.Key_D), self.dissect_current_region), - ((Qt.ControlModifier, Qt.Key_T), self.exec_trace_instructions_dialog), - ((Qt.NoModifier, Qt.Key_R), self.refresh_disassemble_view), - ((Qt.NoModifier, Qt.Key_Down), lambda: self.disassemble_check_viewport("next", 1)), - ((Qt.NoModifier, Qt.Key_Up), lambda: self.disassemble_check_viewport("previous", 1)) - ]) + actions = typedefs.KeyboardModifiersTupleDict( + [ + ( + QKeyCombination(Qt.KeyboardModifier.NoModifier, Qt.Key.Key_Space), + lambda: self.follow_instruction(selected_row), + ), + ( + QKeyCombination(Qt.KeyboardModifier.ControlModifier, Qt.Key.Key_E), + lambda: self.exec_examine_referrers_widget(current_address_text), + ), + ( + QKeyCombination(Qt.KeyboardModifier.ControlModifier, Qt.Key.Key_G), + self.exec_disassemble_go_to_dialog, + ), + ( + QKeyCombination(Qt.KeyboardModifier.ControlModifier, Qt.Key.Key_H), + lambda: self.hex_dump_address(current_address_int), + ), + ( + QKeyCombination(Qt.KeyboardModifier.ControlModifier, Qt.Key.Key_B), + lambda: self.bookmark_address(current_address_int), + ), + (QKeyCombination(Qt.KeyboardModifier.ControlModifier, Qt.Key.Key_D), self.dissect_current_region), + ( + QKeyCombination(Qt.KeyboardModifier.ControlModifier, Qt.Key.Key_T), + self.exec_trace_instructions_dialog, + ), + (QKeyCombination(Qt.KeyboardModifier.NoModifier, Qt.Key.Key_R), self.refresh_disassemble_view), + ( + QKeyCombination(Qt.KeyboardModifier.NoModifier, Qt.Key.Key_Down), + lambda: self.disassemble_check_viewport("next", 1), + ), + ( + QKeyCombination(Qt.KeyboardModifier.NoModifier, Qt.Key.Key_Up), + lambda: self.disassemble_check_viewport("previous", 1), + ), + (QKeyCombination(Qt.KeyboardModifier.NoModifier, Qt.Key.Key_PageUp), self.disassemble_scroll_up), + (QKeyCombination(Qt.KeyboardModifier.NoModifier, Qt.Key.Key_PageDown), self.disassemble_scroll_down), + ] + ) try: - actions[event.modifiers(), event.key()]() + actions[QKeyCombination(event.modifiers(), Qt.Key(event.key()))]() except KeyError: pass self.tableWidget_Disassemble.keyPressEvent_original(event) def tableWidget_Disassemble_item_double_clicked(self, index): + if debugcore.currentpid == -1: + return if index.column() == DISAS_COMMENT_COL: - selected_row = GuiUtils.get_current_row(self.tableWidget_Disassemble) + selected_row = guiutils.get_current_row(self.tableWidget_Disassemble) current_address_text = self.tableWidget_Disassemble.item(selected_row, DISAS_ADDR_COL).text() - current_address = int(SysUtils.extract_address(current_address_text), 16) + current_address = int(utils.extract_address(current_address_text), 16) if current_address in self.tableWidget_Disassemble.bookmarks: self.change_bookmark_comment(current_address) else: self.bookmark_address(current_address) def tableWidget_Disassemble_item_selection_changed(self): + if debugcore.currentpid == -1: + return try: - selected_row = GuiUtils.get_current_row(self.tableWidget_Disassemble) + selected_row = guiutils.get_current_row(self.tableWidget_Disassemble) selected_address_text = self.tableWidget_Disassemble.item(selected_row, DISAS_ADDR_COL).text() - self.disassemble_last_selected_address_int = int(SysUtils.extract_address(selected_address_text), 16) + self.disassemble_last_selected_address_int = int(utils.extract_address(selected_address_text), 16) except (TypeError, ValueError, AttributeError): pass # Search the item in given row for location changing instructions # Go to the address pointed by that instruction if it contains any def follow_instruction(self, selected_row): - address = SysUtils.instruction_follow_address( - self.tableWidget_Disassemble.item(selected_row, DISAS_OPCODES_COL).text()) + if debugcore.currentpid == -1: + return + address = utils.instruction_follow_address( + self.tableWidget_Disassemble.item(selected_row, DISAS_OPCODES_COL).text() + ) if address: - self.disassemble_expression(address, append_to_travel_history=True) + self.disassemble_expression(address, append_history=True) def disassemble_go_back(self): + if debugcore.currentpid == -1: + return if self.tableWidget_Disassemble.travel_history: last_location = self.tableWidget_Disassemble.travel_history[-1] self.disassemble_expression(last_location) @@ -3139,56 +4305,64 @@ def copy_all_columns(row): copied_string += self.tableWidget_Disassemble.item(row, column).text() + "\t" app.clipboard().setText(copied_string) - selected_row = GuiUtils.get_current_row(self.tableWidget_Disassemble) + selected_row = guiutils.get_current_row(self.tableWidget_Disassemble) current_address_text = self.tableWidget_Disassemble.item(selected_row, DISAS_ADDR_COL).text() - current_address = SysUtils.extract_address(current_address_text) + current_address = utils.extract_address(current_address_text) current_address_int = int(current_address, 16) menu = QMenu() - go_to = menu.addAction("Go to expression[Ctrl+G]") - back = menu.addAction("Back") - show_in_hex_view = menu.addAction("Show this address in HexView[Ctrl+H]") + go_to = menu.addAction(f"{tr.GO_TO_EXPRESSION}[Ctrl+G]") + back = menu.addAction(tr.BACK) + show_in_hex_view = menu.addAction(f"{tr.HEXVIEW_ADDRESS}[Ctrl+H]") menu.addSeparator() - followable = SysUtils.instruction_follow_address( - self.tableWidget_Disassemble.item(selected_row, DISAS_OPCODES_COL).text()) - follow = menu.addAction("Follow[Space]") + followable = utils.instruction_follow_address( + self.tableWidget_Disassemble.item(selected_row, DISAS_OPCODES_COL).text() + ) + follow = menu.addAction(f"{tr.FOLLOW}[Space]") if not followable: - GuiUtils.delete_menu_entries(menu, [follow]) - examine_referrers = menu.addAction("Examine Referrers[Ctrl+E]") - if not GuiUtils.contains_reference_mark(current_address_text): - GuiUtils.delete_menu_entries(menu, [examine_referrers]) - bookmark = menu.addAction("Bookmark this address[Ctrl+B]") - delete_bookmark = menu.addAction("Delete this bookmark") - change_comment = menu.addAction("Change comment") + guiutils.delete_menu_entries(menu, [follow]) + examine_referrers = menu.addAction(f"{tr.EXAMINE_REFERRERS}[Ctrl+E]") + if not guiutils.contains_reference_mark(current_address_text): + guiutils.delete_menu_entries(menu, [examine_referrers]) + bookmark = menu.addAction(f"{tr.BOOKMARK_ADDRESS}[Ctrl+B]") + delete_bookmark = menu.addAction(tr.DELETE_BOOKMARK) + change_comment = menu.addAction(tr.CHANGE_COMMENT) is_bookmarked = current_address_int in self.tableWidget_Disassemble.bookmarks if not is_bookmarked: - GuiUtils.delete_menu_entries(menu, [delete_bookmark, change_comment]) + guiutils.delete_menu_entries(menu, [delete_bookmark, change_comment]) else: - GuiUtils.delete_menu_entries(menu, [bookmark]) - go_to_bookmark = menu.addMenu("Go to bookmarked address") + guiutils.delete_menu_entries(menu, [bookmark]) + go_to_bookmark = menu.addMenu(tr.GO_TO_BOOKMARK_ADDRESS) address_list = [hex(address) for address in self.tableWidget_Disassemble.bookmarks.keys()] - bookmark_actions = [go_to_bookmark.addAction(item.all) for item in GDB_Engine.examine_expressions(address_list)] + bookmark_actions = [go_to_bookmark.addAction(item.all) for item in debugcore.examine_expressions(address_list)] menu.addSeparator() - toggle_breakpoint = menu.addAction("Toggle Breakpoint[F5]") - add_condition = menu.addAction("Add/Change condition for breakpoint") - if not GDB_Engine.check_address_in_breakpoints(current_address_int): - GuiUtils.delete_menu_entries(menu, [add_condition]) + toggle_breakpoint = menu.addAction(f"{tr.TOGGLE_BREAKPOINT}[F5]") + add_condition = menu.addAction(tr.CHANGE_BREAKPOINT_CONDITION) + if not debugcore.get_breakpoints_in_range(current_address_int): + guiutils.delete_menu_entries(menu, [add_condition]) menu.addSeparator() - track_breakpoint = menu.addAction("Find out which addresses this instruction accesses") - trace_instructions = menu.addAction("Break and trace instructions[Ctrl+T]") - dissect_region = menu.addAction("Dissect this region[Ctrl+D]") + edit_instruction = menu.addAction(tr.EDIT_INSTRUCTION) + nop_instruction = menu.addAction(tr.REPLACE_WITH_NOPS) + if self.tableWidget_Disassemble.item(selected_row, DISAS_BYTES_COL).text() == "90": + guiutils.delete_menu_entries(menu, [nop_instruction]) menu.addSeparator() - refresh = menu.addAction("Refresh[R]") + track_breakpoint = menu.addAction(tr.WHAT_ACCESSES_INSTRUCTION) + trace_instructions = menu.addAction(f"{tr.TRACE_INSTRUCTION}[Ctrl+T]") + dissect_region = menu.addAction(f"{tr.DISSECT_REGION}[Ctrl+D]") menu.addSeparator() - clipboard_menu = menu.addMenu("Copy to Clipboard") - copy_address = clipboard_menu.addAction("Copy Address") - copy_bytes = clipboard_menu.addAction("Copy Bytes") - copy_opcode = clipboard_menu.addAction("Copy Opcode") - copy_comment = clipboard_menu.addAction("Copy Comment") - copy_all = clipboard_menu.addAction("Copy All") + refresh = menu.addAction(f"{tr.REFRESH}[R]") + menu.addSeparator() + if debugcore.currentpid == -1: + menu.clear() + clipboard_menu = menu.addMenu(tr.COPY_CLIPBOARD) + copy_address = clipboard_menu.addAction(tr.COPY_ADDRESS) + copy_bytes = clipboard_menu.addAction(tr.COPY_BYTES) + copy_opcode = clipboard_menu.addAction(tr.COPY_OPCODE) + copy_comment = clipboard_menu.addAction(tr.COPY_COMMENT) + copy_all = clipboard_menu.addAction(tr.COPY_ALL) font_size = self.tableWidget_Disassemble.font().pointSize() menu.setStyleSheet("font-size: " + str(font_size) + "pt;") - action = menu.exec_(event.globalPos()) + action = menu.exec(event.globalPos()) actions = { go_to: self.exec_disassemble_go_to_dialog, back: self.disassemble_go_back, @@ -3200,6 +4374,8 @@ def copy_all_columns(row): change_comment: lambda: self.change_bookmark_comment(current_address_int), toggle_breakpoint: self.toggle_breakpoint, add_condition: lambda: self.add_breakpoint_condition(current_address_int), + edit_instruction: self.edit_instruction, + nop_instruction: self.nop_instruction, track_breakpoint: self.exec_track_breakpoint_dialog, trace_instructions: self.exec_trace_instructions_dialog, dissect_region: self.dissect_current_region, @@ -3208,63 +4384,82 @@ def copy_all_columns(row): copy_bytes: lambda: copy_to_clipboard(selected_row, DISAS_BYTES_COL), copy_opcode: lambda: copy_to_clipboard(selected_row, DISAS_OPCODES_COL), copy_comment: lambda: copy_to_clipboard(selected_row, DISAS_COMMENT_COL), - copy_all: lambda: copy_all_columns(selected_row) + copy_all: lambda: copy_all_columns(selected_row), } try: actions[action]() except KeyError: pass if action in bookmark_actions: - self.disassemble_expression(SysUtils.extract_address(action.text()), append_to_travel_history=True) + self.disassemble_expression(utils.extract_address(action.text()), append_history=True) def dissect_current_region(self): - selected_row = GuiUtils.get_current_row(self.tableWidget_Disassemble) + if debugcore.currentpid == -1: + return + selected_row = guiutils.get_current_row(self.tableWidget_Disassemble) + if selected_row == -1: + selected_row = 0 current_address_text = self.tableWidget_Disassemble.item(selected_row, DISAS_ADDR_COL).text() - current_address = SysUtils.extract_address(current_address_text) - dissect_code_dialog = DissectCodeDialogForm(int_address=int(current_address, 16)) + current_address = utils.extract_address(current_address_text) + dissect_code_dialog = DissectCodeDialogForm(self, int(current_address, 16)) dissect_code_dialog.scan_finished_signal.connect(dissect_code_dialog.accept) - dissect_code_dialog.exec_() + dissect_code_dialog.exec() self.refresh_disassemble_view() def exec_examine_referrers_widget(self, current_address_text): - if not GuiUtils.contains_reference_mark(current_address_text): + if debugcore.currentpid == -1: + return + if not guiutils.contains_reference_mark(current_address_text): return - current_address = SysUtils.extract_address(current_address_text) + current_address = utils.extract_address(current_address_text) current_address_int = int(current_address, 16) - examine_referrers_widget = ExamineReferrersWidgetForm(current_address_int, self) + examine_referrers_widget = ExamineReferrersWidgetForm(self, current_address_int) examine_referrers_widget.show() def exec_trace_instructions_dialog(self): - selected_row = GuiUtils.get_current_row(self.tableWidget_Disassemble) + if debugcore.currentpid == -1: + return + selected_row = guiutils.get_current_row(self.tableWidget_Disassemble) + if selected_row == -1: + selected_row = 0 current_address_text = self.tableWidget_Disassemble.item(selected_row, DISAS_ADDR_COL).text() - current_address = SysUtils.extract_address(current_address_text) - trace_instructions_window = TraceInstructionsWindowForm(current_address, parent=self) - trace_instructions_window.showMaximized() + current_address = utils.extract_address(current_address_text) + TraceInstructionsWindowForm(self, current_address) def exec_track_breakpoint_dialog(self): - selected_row = GuiUtils.get_current_row(self.tableWidget_Disassemble) + if debugcore.currentpid == -1: + return + selected_row = guiutils.get_current_row(self.tableWidget_Disassemble) current_address_text = self.tableWidget_Disassemble.item(selected_row, DISAS_ADDR_COL).text() - current_address = SysUtils.extract_address(current_address_text) + current_address = utils.extract_address(current_address_text) current_instruction = self.tableWidget_Disassemble.item(selected_row, DISAS_OPCODES_COL).text() - track_breakpoint_widget = TrackBreakpointWidgetForm(current_address, current_instruction, self) - track_breakpoint_widget.show() + register_expression_dialog = InputDialogForm(self, [(tr.ENTER_TRACK_BP_EXPRESSION, "")]) + if register_expression_dialog.exec(): + exp = register_expression_dialog.get_values() + TrackBreakpointWidgetForm(self, current_address, current_instruction, exp) def exec_disassemble_go_to_dialog(self): - selected_row = GuiUtils.get_current_row(self.tableWidget_Disassemble) + if debugcore.currentpid == -1: + return + selected_row = guiutils.get_current_row(self.tableWidget_Disassemble) + if selected_row == -1: + selected_row = 0 current_address_text = self.tableWidget_Disassemble.item(selected_row, DISAS_ADDR_COL).text() - current_address = SysUtils.extract_address(current_address_text) + current_address = utils.extract_address(current_address_text) - go_to_dialog = InputDialogForm(item_list=[("Enter the expression", current_address)]) - if go_to_dialog.exec_(): + go_to_dialog = InputDialogForm(self, [(tr.ENTER_EXPRESSION, current_address)]) + if go_to_dialog.exec(): traveled_exp = go_to_dialog.get_values() - self.disassemble_expression(traveled_exp, append_to_travel_history=True) + self.disassemble_expression(traveled_exp, append_history=True) def bookmark_address(self, int_address): + if debugcore.currentpid == -1: + return if int_address in self.tableWidget_Disassemble.bookmarks: - QMessageBox.information(app.focusWidget(), "Error", "This address has already been bookmarked") + QMessageBox.information(app.focusWidget(), tr.ERROR, tr.ALREADY_BOOKMARKED) return - comment_dialog = InputDialogForm(item_list=[("Enter the comment for bookmarked address", "")]) - if comment_dialog.exec_(): + comment_dialog = InputDialogForm(self, [(tr.ENTER_BOOKMARK_COMMENT, "")]) + if comment_dialog.exec(): comment = comment_dialog.get_values() else: return @@ -3272,9 +4467,11 @@ def bookmark_address(self, int_address): self.refresh_disassemble_view() def change_bookmark_comment(self, int_address): + if debugcore.currentpid == -1: + return current_comment = self.tableWidget_Disassemble.bookmarks[int_address] - comment_dialog = InputDialogForm(item_list=[("Enter the comment for bookmarked address", current_comment)]) - if comment_dialog.exec_(): + comment_dialog = InputDialogForm(self, [(tr.ENTER_BOOKMARK_COMMENT, current_comment)]) + if comment_dialog.exec(): new_comment = comment_dialog.get_values() else: return @@ -3282,6 +4479,8 @@ def change_bookmark_comment(self, int_address): self.refresh_disassemble_view() def delete_bookmark(self, int_address): + if debugcore.currentpid == -1: + return if int_address in self.tableWidget_Disassemble.bookmarks: del self.tableWidget_Disassemble.bookmarks[int_address] self.refresh_disassemble_view() @@ -3292,90 +4491,108 @@ def actionBookmarks_triggered(self): bookmark_widget.activateWindow() def actionStackTrace_Info_triggered(self): - self.stacktrace_info_widget = StackTraceInfoWidgetForm() + if debugcore.currentpid == -1: + return + self.stacktrace_info_widget.update_stacktrace() + guiutils.center_to_parent(self.stacktrace_info_widget) self.stacktrace_info_widget.show() + self.stacktrace_info_widget.activateWindow() def actionBreakpoints_triggered(self): + if debugcore.currentpid == -1: + return breakpoint_widget = BreakpointInfoWidgetForm(self) breakpoint_widget.show() breakpoint_widget.activateWindow() def actionFunctions_triggered(self): + if debugcore.currentpid == -1: + return functions_info_widget = FunctionsInfoWidgetForm(self) functions_info_widget.show() def actionGDB_Log_File_triggered(self): - log_file_widget = LogFileWidgetForm() + log_file_widget = LogFileWidgetForm(self) log_file_widget.showMaximized() def actionMemory_Regions_triggered(self): + if debugcore.currentpid == -1: + return memory_regions_widget = MemoryRegionsWidgetForm(self) memory_regions_widget.show() + def actionRestore_Instructions_triggered(self): + if debugcore.currentpid == -1: + return + restore_instructions_widget = RestoreInstructionsWidgetForm(self) + restore_instructions_widget.show() + restore_instructions_widget.activateWindow() + def actionReferenced_Strings_triggered(self): + if debugcore.currentpid == -1: + return ref_str_widget = ReferencedStringsWidgetForm(self) ref_str_widget.show() def actionReferenced_Calls_triggered(self): + if debugcore.currentpid == -1: + return ref_call_widget = ReferencedCallsWidgetForm(self) ref_call_widget.show() def actionInject_so_file_triggered(self): - file_path = QFileDialog.getOpenFileName(self, "Select the .so file", "", "Shared object library (*.so)")[0] + if debugcore.currentpid == -1: + return + file_path, _ = QFileDialog.getOpenFileName(self, tr.SELECT_SO_FILE, "", tr.SHARED_OBJECT_TYPE) if file_path: - if GDB_Engine.inject_with_dlopen_call(file_path): - QMessageBox.information(self, "Success!", "The file has been injected") + if debugcore.inject_with_dlopen_call(file_path): + QMessageBox.information(self, tr.SUCCESS, tr.FILE_INJECTED) else: - QMessageBox.information(self, "Error", "Failed to inject the .so file") + QMessageBox.information(self, tr.ERROR, tr.FILE_INJECT_FAILED) def actionCall_Function_triggered(self): - label_text = "Enter the expression for the function that'll be called from the inferior" \ - "\nYou can view functions list from View->Functions" \ - "\n\nFor instance:" \ - '\nCalling printf("1234") will yield something like this' \ - '\n↓' \ - '\n$28 = 4' \ - '\n\n$28 is the assigned convenience variable' \ - '\n4 is the result' \ - '\nYou can use the assigned variable from the GDB Console' - call_dialog = InputDialogForm(item_list=[(label_text, "")]) - if call_dialog.exec_(): - result = GDB_Engine.call_function_from_inferior(call_dialog.get_values()) + if debugcore.currentpid == -1: + return + call_dialog = InputDialogForm(self, [(tr.ENTER_CALL_EXPRESSION, "")]) + if call_dialog.exec(): + result = debugcore.call_function_from_inferior(call_dialog.get_values()) if result[0]: - QMessageBox.information(self, "Success!", result[0] + " = " + result[1]) + QMessageBox.information(self, tr.SUCCESS, result[0] + " = " + result[1]) else: - QMessageBox.information(self, "Failed", "Failed to call the expression " + call_dialog.get_values()) + QMessageBox.information(self, tr.ERROR, tr.CALL_EXPRESSION_FAILED.format(call_dialog.get_values())) def actionSearch_Opcode_triggered(self): + if debugcore.currentpid == -1: + return start_address = int(self.disassemble_currently_displayed_address, 16) end_address = start_address + 0x30000 - search_opcode_widget = SearchOpcodeWidgetForm(hex(start_address), hex(end_address), self) + search_opcode_widget = SearchOpcodeWidgetForm(self, hex(start_address), hex(end_address)) search_opcode_widget.show() def actionDissect_Code_triggered(self): - self.dissect_code_dialog = DissectCodeDialogForm() - self.dissect_code_dialog.exec_() + if debugcore.currentpid == -1: + return + dissect_code_dialog = DissectCodeDialogForm(self) + dissect_code_dialog.exec() self.refresh_disassemble_view() def actionlibpince_triggered(self): - libpince_widget = LibpinceReferenceWidgetForm(is_window=True) - libpince_widget.showMaximized() + utils.execute_command_as_user('python3 -m webbrowser "https://korcankaraokcu.github.io/PINCE/"') def pushButton_ShowFloatRegisters_clicked(self): - self.float_registers_widget = FloatRegisterWidgetForm() + if debugcore.currentpid == -1 or debugcore.inferior_status == typedefs.INFERIOR_STATUS.RUNNING: + return + self.float_registers_widget.update_registers() + guiutils.center_to_parent(self.float_registers_widget) self.float_registers_widget.show() - GuiUtils.center_to_window(self.float_registers_widget, self.widget_Registers) + self.float_registers_widget.activateWindow() class BookmarkWidgetForm(QWidget, BookmarkWidget): - def __init__(self, parent=None): - super().__init__() + def __init__(self, parent): + super().__init__(parent) self.setupUi(self) - self.parent = lambda: parent - global instances - instances.append(self) - GuiUtils.center(self) - self.setWindowFlags(Qt.Window) + self.setWindowFlags(Qt.WindowType.Window) self.listWidget.contextMenuEvent = self.listWidget_context_menu_event self.listWidget.currentRowChanged.connect(self.change_display) self.listWidget.itemDoubleClicked.connect(self.listWidget_item_double_clicked) @@ -3384,27 +4601,34 @@ def __init__(self, parent=None): self.shortcut_refresh = QShortcut(QKeySequence("R"), self) self.shortcut_refresh.activated.connect(self.refresh_table) self.refresh_table() + guiutils.center_to_parent(self) def refresh_table(self): self.listWidget.clear() address_list = [hex(address) for address in self.parent().tableWidget_Disassemble.bookmarks.keys()] - self.listWidget.addItems([item.all for item in GDB_Engine.examine_expressions(address_list)]) + if debugcore.currentpid == -1: + self.listWidget.addItems(address_list) + else: + self.listWidget.addItems([item.all for item in debugcore.examine_expressions(address_list)]) def change_display(self, row): - current_address = SysUtils.extract_address(self.listWidget.item(row).text()) - self.lineEdit_Info.setText(GDB_Engine.get_address_info(current_address)) + current_address = utils.extract_address(self.listWidget.item(row).text()) + if debugcore.currentpid == -1: + self.lineEdit_Info.clear() + else: + self.lineEdit_Info.setText(debugcore.get_address_info(current_address)) self.lineEdit_Comment.setText(self.parent().tableWidget_Disassemble.bookmarks[int(current_address, 16)]) def listWidget_item_double_clicked(self, item): - self.parent().disassemble_expression(SysUtils.extract_address(item.text()), append_to_travel_history=True) + self.parent().disassemble_expression(utils.extract_address(item.text()), append_history=True) def exec_add_entry_dialog(self): - entry_dialog = InputDialogForm(item_list=[("Enter the expression", "")]) - if entry_dialog.exec_(): + entry_dialog = InputDialogForm(self, [(tr.ENTER_EXPRESSION, "")]) + if entry_dialog.exec(): text = entry_dialog.get_values() - address = GDB_Engine.examine_expression(text).address + address = debugcore.examine_expression(text).address if not address: - QMessageBox.information(self, "Error", "Invalid expression or address") + QMessageBox.information(self, tr.ERROR, tr.INVALID_EXPRESSION) return self.parent().bookmark_address(int(address, 16)) self.refresh_table() @@ -3414,31 +4638,31 @@ def exec_change_comment_dialog(self, current_address): self.refresh_table() def listWidget_context_menu_event(self, event): - current_item = GuiUtils.get_current_item(self.listWidget) + current_item = guiutils.get_current_item(self.listWidget) if current_item: - current_address = int(SysUtils.extract_address(current_item.text()), 16) + current_address = int(utils.extract_address(current_item.text()), 16) if current_address not in self.parent().tableWidget_Disassemble.bookmarks: - QMessageBox.information(self, "Error", "Invalid entries detected, refreshing the page") + QMessageBox.information(self, tr.ERROR, tr.INVALID_ENTRY) self.refresh_table() return else: current_address = None menu = QMenu() - add_entry = menu.addAction("Add new entry") - change_comment = menu.addAction("Change comment of this record") - delete_record = menu.addAction("Delete this record[Del]") + add_entry = menu.addAction(tr.ADD_ENTRY) + change_comment = menu.addAction(tr.CHANGE_COMMENT) + delete_record = menu.addAction(f"{tr.DELETE}[Del]") if current_item is None: - GuiUtils.delete_menu_entries(menu, [change_comment, delete_record]) + guiutils.delete_menu_entries(menu, [change_comment, delete_record]) menu.addSeparator() - refresh = menu.addAction("Refresh[R]") + refresh = menu.addAction(f"{tr.REFRESH}[R]") font_size = self.listWidget.font().pointSize() menu.setStyleSheet("font-size: " + str(font_size) + "pt;") - action = menu.exec_(event.globalPos()) + action = menu.exec(event.globalPos()) actions = { add_entry: self.exec_add_entry_dialog, change_comment: lambda: self.exec_change_comment_dialog(current_address), delete_record: self.delete_record, - refresh: self.refresh_table + refresh: self.refresh_table, } try: actions[action]() @@ -3446,24 +4670,19 @@ def listWidget_context_menu_event(self, event): pass def delete_record(self): - current_item = GuiUtils.get_current_item(self.listWidget) + current_item = guiutils.get_current_item(self.listWidget) if not current_item: return - current_address = int(SysUtils.extract_address(current_item.text()), 16) + current_address = int(utils.extract_address(current_item.text()), 16) self.parent().delete_bookmark(current_address) self.refresh_table() - def closeEvent(self, QCloseEvent): - global instances - instances.remove(self) - class FloatRegisterWidgetForm(QTabWidget, FloatRegisterWidget): - def __init__(self, parent=None): - super().__init__(parent=parent) + def __init__(self, parent): + super().__init__(parent) self.setupUi(self) - self.setWindowFlags(Qt.Window) - self.update_registers() + self.setWindowFlags(Qt.WindowType.Window) self.tableWidget_FPU.itemDoubleClicked.connect(self.set_register) self.tableWidget_XMM.itemDoubleClicked.connect(self.set_register) @@ -3471,15 +4690,20 @@ def update_registers(self): self.tableWidget_FPU.setRowCount(0) self.tableWidget_FPU.setRowCount(8) self.tableWidget_XMM.setRowCount(0) - self.tableWidget_XMM.setRowCount(8) - float_registers = GDB_Engine.read_float_registers() - for row, (st, xmm) in enumerate(zip(type_defs.REGISTERS.FLOAT.ST, type_defs.REGISTERS.FLOAT.XMM)): - self.tableWidget_FPU.setItem(row, FLOAT_REGISTERS_NAME_COL, QTableWidgetItem(st)) - self.tableWidget_FPU.setItem(row, FLOAT_REGISTERS_VALUE_COL, QTableWidgetItem(float_registers[st])) - self.tableWidget_XMM.setItem(row, FLOAT_REGISTERS_NAME_COL, QTableWidgetItem(xmm)) - self.tableWidget_XMM.setItem(row, FLOAT_REGISTERS_VALUE_COL, QTableWidgetItem(float_registers[xmm])) + self.tableWidget_XMM.setRowCount(16) + float_registers = list(debugcore.read_float_registers().items()) + st_registers = float_registers[:8] + xmm_registers = float_registers[8:] + for row, (name, value) in enumerate(st_registers): + self.tableWidget_FPU.setItem(row, FLOAT_REGISTERS_NAME_COL, QTableWidgetItem(name)) + self.tableWidget_FPU.setItem(row, FLOAT_REGISTERS_VALUE_COL, QTableWidgetItem(value)) + for row, (name, value) in enumerate(xmm_registers): + self.tableWidget_XMM.setItem(row, FLOAT_REGISTERS_NAME_COL, QTableWidgetItem(name)) + self.tableWidget_XMM.setItem(row, FLOAT_REGISTERS_VALUE_COL, QTableWidgetItem(value)) def set_register(self, index): + if debugcore.currentpid == -1 or debugcore.inferior_status == typedefs.INFERIOR_STATUS.RUNNING: + return current_row = index.row() if self.currentWidget() == self.FPU: current_table_widget = self.tableWidget_FPU @@ -3489,43 +4713,117 @@ def set_register(self, index): raise Exception("Current widget is invalid: " + str(self.currentWidget().objectName())) current_register = current_table_widget.item(current_row, FLOAT_REGISTERS_NAME_COL).text() current_value = current_table_widget.item(current_row, FLOAT_REGISTERS_VALUE_COL).text() - label_text = "Enter the new value of register " + current_register.upper() - register_dialog = InputDialogForm(item_list=[(label_text, current_value)]) - if register_dialog.exec_(): + label_text = tr.ENTER_REGISTER_VALUE.format(current_register.upper()) + register_dialog = InputDialogForm(self, [(label_text, current_value)]) + if register_dialog.exec(): + if debugcore.currentpid == -1 or debugcore.inferior_status == typedefs.INFERIOR_STATUS.RUNNING: + return if self.currentWidget() == self.XMM: current_register += ".v4_float" - GDB_Engine.set_convenience_variable(current_register, register_dialog.get_values()) + debugcore.set_convenience_variable(current_register, register_dialog.get_values()) self.update_registers() class StackTraceInfoWidgetForm(QWidget, StackTraceInfoWidget): - def __init__(self, parent=None): - super().__init__(parent=parent) + def __init__(self, parent): + super().__init__(parent) self.setupUi(self) - GuiUtils.center(self) - self.setWindowFlags(Qt.Window) + self.setWindowFlags(Qt.WindowType.Window) self.listWidget_ReturnAddresses.currentRowChanged.connect(self.update_frame_info) - self.update_stacktrace() def update_stacktrace(self): self.listWidget_ReturnAddresses.clear() - return_addresses = GDB_Engine.get_stack_frame_return_addresses() + if debugcore.inferior_status == typedefs.INFERIOR_STATUS.RUNNING: + return + return_addresses = debugcore.get_stack_frame_return_addresses() self.listWidget_ReturnAddresses.addItems(return_addresses) def update_frame_info(self, index): - frame_info = GDB_Engine.get_stack_frame_info(index) + if debugcore.inferior_status == typedefs.INFERIOR_STATUS.RUNNING: + self.textBrowser_Info.setText(tr.PROCESS_RUNNING) + return + frame_info = debugcore.get_stack_frame_info(index) self.textBrowser_Info.setText(frame_info) +class RestoreInstructionsWidgetForm(QWidget, RestoreInstructionsWidget): + def __init__(self, parent): + super().__init__(parent) + self.setupUi(self) + self.setWindowFlags(Qt.WindowType.Window) + + # Saving the original function because super() doesn't work when we override functions like this + self.tableWidget_Instructions.keyPressEvent_original = self.tableWidget_Instructions.keyPressEvent + self.tableWidget_Instructions.keyPressEvent = self.tableWidget_Instructions_key_press_event + self.tableWidget_Instructions.contextMenuEvent = self.tableWidget_Instructions_context_menu_event + self.tableWidget_Instructions.itemDoubleClicked.connect(self.tableWidget_Instructions_double_clicked) + self.refresh() + guiutils.center_to_parent(self) + + def tableWidget_Instructions_context_menu_event(self, event): + selected_row = guiutils.get_current_row(self.tableWidget_Instructions) + menu = QMenu() + restore_instruction = menu.addAction(tr.RESTORE_INSTRUCTION) + if selected_row != -1: + selected_address_text = self.tableWidget_Instructions.item(selected_row, INSTR_ADDR_COL).text() + selected_address = utils.extract_address(selected_address_text) + selected_address_int = int(selected_address, 16) + else: + guiutils.delete_menu_entries(menu, [restore_instruction]) + selected_address_int = None + menu.addSeparator() + refresh = menu.addAction(f"{tr.REFRESH}[R]") + font_size = self.tableWidget_Instructions.font().pointSize() + menu.setStyleSheet("font-size: " + str(font_size) + "pt;") + action = menu.exec(event.globalPos()) + actions = {restore_instruction: lambda: self.restore_instruction(selected_address_int), refresh: self.refresh} + try: + actions[action]() + except KeyError: + pass + + def restore_instruction(self, selected_address_int): + debugcore.restore_instruction(selected_address_int) + self.refresh_all() + + def refresh(self): + modified_instructions = debugcore.get_modified_instructions() + self.tableWidget_Instructions.setRowCount(len(modified_instructions)) + for row, (address, aob) in enumerate(modified_instructions.items()): + self.tableWidget_Instructions.setItem(row, INSTR_ADDR_COL, QTableWidgetItem(hex(address))) + self.tableWidget_Instructions.setItem(row, INSTR_AOB_COL, QTableWidgetItem(aob)) + instr_name = utils.get_opcodes(address, aob, debugcore.get_inferior_arch()) + if not instr_name: + instr_name = "??" + self.tableWidget_Instructions.setItem(row, INSTR_NAME_COL, QTableWidgetItem(instr_name)) + guiutils.resize_to_contents(self.tableWidget_Instructions) + + def refresh_all(self): + self.parent().refresh_hex_view() + self.parent().refresh_disassemble_view() + self.refresh() + + def tableWidget_Instructions_key_press_event(self, event): + actions = typedefs.KeyboardModifiersTupleDict( + [(QKeyCombination(Qt.KeyboardModifier.NoModifier, Qt.Key.Key_R), self.refresh)] + ) + try: + actions[QKeyCombination(event.modifiers(), Qt.Key(event.key()))]() + except KeyError: + pass + self.tableWidget_Instructions.keyPressEvent_original(event) + + def tableWidget_Instructions_double_clicked(self, index): + current_address_text = self.tableWidget_Instructions.item(index.row(), INSTR_ADDR_COL).text() + current_address = utils.extract_address(current_address_text) + self.parent().disassemble_expression(current_address, append_history=True) + + class BreakpointInfoWidgetForm(QTabWidget, BreakpointInfoWidget): - def __init__(self, parent=None): - super().__init__() + def __init__(self, parent): + super().__init__(parent) self.setupUi(self) - self.parent = lambda: parent - global instances - instances.append(self) - GuiUtils.center(self) - self.setWindowFlags(Qt.Window) + self.setWindowFlags(Qt.WindowType.Window) self.tableWidget_BreakpointInfo.contextMenuEvent = self.tableWidget_BreakpointInfo_context_menu_event # Saving the original function because super() doesn't work when we override functions like this @@ -3533,9 +4831,10 @@ def __init__(self, parent=None): self.tableWidget_BreakpointInfo.keyPressEvent = self.tableWidget_BreakpointInfo_key_press_event self.tableWidget_BreakpointInfo.itemDoubleClicked.connect(self.tableWidget_BreakpointInfo_double_clicked) self.refresh() + guiutils.center_to_parent(self) def refresh(self): - break_info = GDB_Engine.get_breakpoint_info() + break_info = debugcore.get_breakpoint_info() self.tableWidget_BreakpointInfo.setRowCount(0) self.tableWidget_BreakpointInfo.setRowCount(len(break_info)) for row, item in enumerate(break_info): @@ -3548,87 +4847,98 @@ def refresh(self): self.tableWidget_BreakpointInfo.setItem(row, BREAK_ON_HIT_COL, QTableWidgetItem(item.on_hit)) self.tableWidget_BreakpointInfo.setItem(row, BREAK_HIT_COUNT_COL, QTableWidgetItem(item.hit_count)) self.tableWidget_BreakpointInfo.setItem(row, BREAK_COND_COL, QTableWidgetItem(item.condition)) - self.tableWidget_BreakpointInfo.resizeColumnsToContents() - self.tableWidget_BreakpointInfo.horizontalHeader().setStretchLastSection(True) + guiutils.resize_to_contents(self.tableWidget_BreakpointInfo) self.textBrowser_BreakpointInfo.clear() - self.textBrowser_BreakpointInfo.setText(GDB_Engine.send_command("info break", cli_output=True)) + self.textBrowser_BreakpointInfo.setText(debugcore.send_command("info break", cli_output=True)) def delete_breakpoint(self, address): if address is not None: - GDB_Engine.delete_breakpoint(address) + debugcore.delete_breakpoint(address) self.refresh_all() def tableWidget_BreakpointInfo_key_press_event(self, event): - selected_row = GuiUtils.get_current_row(self.tableWidget_BreakpointInfo) + selected_row = guiutils.get_current_row(self.tableWidget_BreakpointInfo) if selected_row != -1: current_address_text = self.tableWidget_BreakpointInfo.item(selected_row, BREAK_ADDR_COL).text() - current_address = SysUtils.extract_address(current_address_text) + current_address = utils.extract_address(current_address_text) else: current_address = None - actions = type_defs.KeyboardModifiersTupleDict([ - ((Qt.NoModifier, Qt.Key_Delete), lambda: self.delete_breakpoint(current_address)), - ((Qt.NoModifier, Qt.Key_R), self.refresh) - ]) + actions = typedefs.KeyboardModifiersTupleDict( + [ + ( + QKeyCombination(Qt.KeyboardModifier.NoModifier, Qt.Key.Key_Delete), + lambda: self.delete_breakpoint(current_address), + ), + (QKeyCombination(Qt.KeyboardModifier.NoModifier, Qt.Key.Key_R), self.refresh), + ] + ) try: - actions[event.modifiers(), event.key()]() + actions[QKeyCombination(event.modifiers(), Qt.Key(event.key()))]() except KeyError: pass self.tableWidget_BreakpointInfo.keyPressEvent_original(event) def exec_enable_count_dialog(self, current_address): - hit_count_dialog = InputDialogForm(item_list=[("Enter the hit count(1 or higher)", "")]) - if hit_count_dialog.exec_(): + hit_count_dialog = InputDialogForm(self, [(tr.ENTER_HIT_COUNT.format(1), "")]) + if hit_count_dialog.exec(): count = hit_count_dialog.get_values() try: count = int(count) except ValueError: - QMessageBox.information(self, "Error", "Hit count must be an integer") + QMessageBox.information(self, tr.ERROR, tr.HIT_COUNT_ASSERT_INT) else: if count < 1: - QMessageBox.information(self, "Error", "Hit count can't be lower than 1") + QMessageBox.information(self, tr.ERROR, tr.HIT_COUNT_ASSERT_LT.format(1)) else: - GDB_Engine.modify_breakpoint(current_address, type_defs.BREAKPOINT_MODIFY.ENABLE_COUNT, - count=count) + debugcore.modify_breakpoint(current_address, typedefs.BREAKPOINT_MODIFY.ENABLE_COUNT, count=count) def tableWidget_BreakpointInfo_context_menu_event(self, event): - selected_row = GuiUtils.get_current_row(self.tableWidget_BreakpointInfo) + selected_row = guiutils.get_current_row(self.tableWidget_BreakpointInfo) if selected_row != -1: current_address_text = self.tableWidget_BreakpointInfo.item(selected_row, BREAK_ADDR_COL).text() - current_address = SysUtils.extract_address(current_address_text) + current_address = utils.extract_address(current_address_text) current_address_int = int(current_address, 16) else: current_address = None current_address_int = None menu = QMenu() - change_condition = menu.addAction("Change condition of this breakpoint") - enable = menu.addAction("Enable this breakpoint") - disable = menu.addAction("Disable this breakpoint") - enable_once = menu.addAction("Disable this breakpoint after hit") - enable_count = menu.addAction("Disable this breakpoint after X hits") - enable_delete = menu.addAction("Delete this breakpoint after hit") + change_condition = menu.addAction(tr.CHANGE_CONDITION) + enable = menu.addAction(tr.ENABLE) + disable = menu.addAction(tr.DISABLE) + enable_once = menu.addAction(tr.DISABLE_AFTER_HIT) + enable_count = menu.addAction(tr.DISABLE_AFTER_COUNT) + enable_delete = menu.addAction(tr.DELETE_AFTER_HIT) menu.addSeparator() - delete_breakpoint = menu.addAction("Delete this breakpoint[Del]") + delete_breakpoint = menu.addAction(f"{tr.DELETE}[Del]") menu.addSeparator() if current_address is None: - deletion_list = [change_condition, enable, disable, enable_once, enable_count, enable_delete, - delete_breakpoint] - GuiUtils.delete_menu_entries(menu, deletion_list) - refresh = menu.addAction("Refresh[R]") + deletion_list = [ + change_condition, + enable, + disable, + enable_once, + enable_count, + enable_delete, + delete_breakpoint, + ] + guiutils.delete_menu_entries(menu, deletion_list) + refresh = menu.addAction(f"{tr.REFRESH}[R]") font_size = self.tableWidget_BreakpointInfo.font().pointSize() menu.setStyleSheet("font-size: " + str(font_size) + "pt;") - action = menu.exec_(event.globalPos()) + action = menu.exec(event.globalPos()) actions = { change_condition: lambda: self.parent().add_breakpoint_condition(current_address_int), - enable: lambda: GDB_Engine.modify_breakpoint(current_address, type_defs.BREAKPOINT_MODIFY.ENABLE), - disable: lambda: GDB_Engine.modify_breakpoint(current_address, type_defs.BREAKPOINT_MODIFY.DISABLE), - enable_once: lambda: GDB_Engine.modify_breakpoint(current_address, type_defs.BREAKPOINT_MODIFY.ENABLE_ONCE), + enable: lambda: debugcore.modify_breakpoint(current_address, typedefs.BREAKPOINT_MODIFY.ENABLE), + disable: lambda: debugcore.modify_breakpoint(current_address, typedefs.BREAKPOINT_MODIFY.DISABLE), + enable_once: lambda: debugcore.modify_breakpoint(current_address, typedefs.BREAKPOINT_MODIFY.ENABLE_ONCE), enable_count: lambda: self.exec_enable_count_dialog(current_address), - enable_delete: lambda: GDB_Engine.modify_breakpoint(current_address, - type_defs.BREAKPOINT_MODIFY.ENABLE_DELETE), - delete_breakpoint: lambda: GDB_Engine.delete_breakpoint(current_address), - refresh: self.refresh + enable_delete: lambda: debugcore.modify_breakpoint( + current_address, typedefs.BREAKPOINT_MODIFY.ENABLE_DELETE + ), + delete_breakpoint: lambda: debugcore.delete_breakpoint(current_address), + refresh: self.refresh, } try: actions[action]() @@ -3644,7 +4954,7 @@ def refresh_all(self): def tableWidget_BreakpointInfo_double_clicked(self, index): current_address_text = self.tableWidget_BreakpointInfo.item(index.row(), BREAK_ADDR_COL).text() - current_address = SysUtils.extract_address(current_address_text) + current_address = utils.extract_address(current_address_text) current_address_int = int(current_address, 16) if index.column() == BREAK_COND_COL: @@ -3653,56 +4963,46 @@ def tableWidget_BreakpointInfo_double_clicked(self, index): else: current_breakpoint_type = self.tableWidget_BreakpointInfo.item(index.row(), BREAK_TYPE_COL).text() if "breakpoint" in current_breakpoint_type: - self.parent().disassemble_expression(current_address, append_to_travel_history=True) + self.parent().disassemble_expression(current_address, append_history=True) else: self.parent().hex_dump_address(current_address_int) - def closeEvent(self, QCloseEvent): - global instances - instances.remove(self) - class TrackWatchpointWidgetForm(QWidget, TrackWatchpointWidget): - def __init__(self, address, length, watchpoint_type, parent=None): - super().__init__() + def __init__(self, parent, address, length, watchpoint_type): + super().__init__(parent) self.setupUi(self) - self.parent = lambda: parent - global instances - instances.append(self) - GuiUtils.center(self) - self.setWindowFlags(Qt.Window) - if watchpoint_type == type_defs.WATCHPOINT_TYPE.WRITE_ONLY: - string = "writing to" - elif watchpoint_type == type_defs.WATCHPOINT_TYPE.READ_ONLY: - string = "reading from" - elif watchpoint_type == type_defs.WATCHPOINT_TYPE.BOTH: - string = "accessing to" - else: - raise Exception("Watchpoint type is invalid: " + str(watchpoint_type)) - self.setWindowTitle("Opcodes " + string + " the address " + address) - breakpoints = GDB_Engine.track_watchpoint(address, length, watchpoint_type) - if not breakpoints: - QMessageBox.information(self, "Error", "Unable to track watchpoint at expression " + address) - return + self.setWindowFlags(Qt.WindowType.Window) + self.update_timer = QTimer(timeout=self.update_list) + self.stopped = False self.address = address - self.breakpoints = breakpoints self.info = {} self.last_selected_row = 0 - self.stopped = False + if watchpoint_type == typedefs.WATCHPOINT_TYPE.WRITE_ONLY: + string = tr.OPCODE_WRITING_TO.format(address) + elif watchpoint_type == typedefs.WATCHPOINT_TYPE.READ_ONLY: + string = tr.OPCODE_READING_FROM.format(address) + elif watchpoint_type == typedefs.WATCHPOINT_TYPE.BOTH: + string = tr.OPCODE_ACCESSING_TO.format(address) + else: + raise Exception("Watchpoint type is invalid: " + str(watchpoint_type)) + self.setWindowTitle(string) + guiutils.center_to_parent(self) # Called before the QMessageBox to center its position properly + self.breakpoints = debugcore.track_watchpoint(address, length, watchpoint_type) + if not self.breakpoints: + QMessageBox.information(self, tr.ERROR, tr.TRACK_WATCHPOINT_FAILED.format(address)) + self.close() + return self.pushButton_Stop.clicked.connect(self.pushButton_Stop_clicked) self.pushButton_Refresh.clicked.connect(self.update_list) self.tableWidget_Opcodes.itemDoubleClicked.connect(self.tableWidget_Opcodes_item_double_clicked) self.tableWidget_Opcodes.selectionModel().currentChanged.connect(self.tableWidget_Opcodes_current_changed) - self.update_timer = QTimer() - self.update_timer.setInterval(100) - self.update_timer.timeout.connect(self.update_list) - self.update_timer.start() + self.update_timer.start(100) + self.show() def update_list(self): - info = GDB_Engine.get_track_watchpoint_info(self.breakpoints) - if not info: - return - if self.info == info: + info = debugcore.get_track_watchpoint_info(self.breakpoints) + if not info or self.info == info: return self.info = info self.tableWidget_Opcodes.setRowCount(0) @@ -3710,8 +5010,7 @@ def update_list(self): for row, key in enumerate(info): self.tableWidget_Opcodes.setItem(row, TRACK_WATCHPOINT_COUNT_COL, QTableWidgetItem(str(info[key][0]))) self.tableWidget_Opcodes.setItem(row, TRACK_WATCHPOINT_ADDR_COL, QTableWidgetItem(info[key][1])) - self.tableWidget_Opcodes.resizeColumnsToContents() - self.tableWidget_Opcodes.horizontalHeader().setStretchLastSection(True) + guiutils.resize_to_contents(self.tableWidget_Opcodes) self.tableWidget_Opcodes.selectRow(self.last_selected_row) def tableWidget_Opcodes_current_changed(self, QModelIndex_current): @@ -3733,80 +5032,62 @@ def tableWidget_Opcodes_current_changed(self, QModelIndex_current): def tableWidget_Opcodes_item_double_clicked(self, index): self.parent().memory_view_window.disassemble_expression( - self.tableWidget_Opcodes.item(index.row(), TRACK_WATCHPOINT_ADDR_COL).text(), - append_to_travel_history=True) + self.tableWidget_Opcodes.item(index.row(), TRACK_WATCHPOINT_ADDR_COL).text(), append_history=True + ) self.parent().memory_view_window.show() self.parent().memory_view_window.activateWindow() def pushButton_Stop_clicked(self): if self.stopped: self.close() - if not GDB_Engine.execute_func_temporary_interruption(GDB_Engine.delete_breakpoint, self.address): - QMessageBox.information(self, "Error", "Unable to delete watchpoint at expression " + self.address) + return + if not debugcore.delete_breakpoint(self.address): + QMessageBox.information(self, tr.ERROR, tr.DELETE_WATCHPOINT_FAILED.format(self.address)) return self.stopped = True - self.pushButton_Stop.setText("Close") + self.pushButton_Stop.setText(tr.CLOSE) - @GDB_Engine.execute_with_temporary_interruption - def closeEvent(self, QCloseEvent): - global instances + def closeEvent(self, event: QCloseEvent): self.update_timer.stop() - GDB_Engine.delete_breakpoint(self.address) - self.deleteLater() - instances.remove(self) + if self.breakpoints: + if not self.stopped: + debugcore.delete_breakpoint(self.address) + watchpoint_file = utils.get_track_watchpoint_file(debugcore.currentpid, self.breakpoints) + if os.path.exists(watchpoint_file): + os.remove(watchpoint_file) + super().closeEvent(event) class TrackBreakpointWidgetForm(QWidget, TrackBreakpointWidget): - def __init__(self, address, instruction, parent=None): - super().__init__() + def __init__(self, parent, address, instruction, register_expressions): + super().__init__(parent) self.setupUi(self) - self.parent = lambda: parent - global instances - instances.append(self) - self.setWindowFlags(Qt.Window) - GuiUtils.center_to_parent(self) - self.setWindowTitle("Addresses accessed by instruction '" + instruction + "'") - label_text = "Enter the register expression(s) you want to track" \ - "\nRegister names should start with a '$' sign" \ - "\nEach expression should be separated with a comma" \ - "\n\nFor instance:" \ - "\nLet's say the instruction is 'mov [rax+rbx],30'" \ - "\nThen you should enter '$rax+$rbx'(without quotes)" \ - "\nSo PINCE can track address [rax+rbx]" \ - "\n\nAnother example:" \ - "\nIf you enter '$rax,$rbx*$rcx+4,$rbp'(without quotes)" \ - "\nPINCE will track down addresses [rax],[rbx*rcx+4] and [rbp]" - register_expression_dialog = InputDialogForm(item_list=[(label_text, "")]) - if register_expression_dialog.exec_(): - register_expressions = register_expression_dialog.get_values() - else: - return - breakpoint = GDB_Engine.track_breakpoint(address, register_expressions) - if not breakpoint: - QMessageBox.information(self, "Error", "Unable to track breakpoint at expression " + address) - return - self.label_Info.setText("Pause the process to refresh 'Value' part of the table(" + - Hotkeys.pause_hotkey.value + " or " + Hotkeys.break_hotkey.value + ")") + self.update_list_timer = QTimer(timeout=self.update_list) + self.update_values_timer = QTimer(timeout=self.update_values) + self.stopped = False self.address = address - self.breakpoint = breakpoint self.info = {} self.last_selected_row = 0 - self.stopped = False - GuiUtils.fill_value_combobox(self.comboBox_ValueType) + self.setWindowFlags(Qt.WindowType.Window) + self.setWindowTitle(tr.ACCESSED_BY_INSTRUCTION.format(instruction)) + guiutils.center_to_parent(self) # Called before the QMessageBox to center its position properly + self.breakpoint = debugcore.track_breakpoint(address, register_expressions) + if not self.breakpoint: + QMessageBox.information(self, tr.ERROR, tr.TRACK_BREAKPOINT_FAILED.format(address)) + self.close() + return + guiutils.fill_value_combobox(self.comboBox_ValueType) self.pushButton_Stop.clicked.connect(self.pushButton_Stop_clicked) self.tableWidget_TrackInfo.itemDoubleClicked.connect(self.tableWidget_TrackInfo_item_double_clicked) self.tableWidget_TrackInfo.selectionModel().currentChanged.connect(self.tableWidget_TrackInfo_current_changed) self.comboBox_ValueType.currentIndexChanged.connect(self.update_values) - self.comboBox_ValueType.setToolTip("Allan please add details") # planned easter egg - self.update_timer = QTimer() - self.update_timer.setInterval(100) - self.update_timer.timeout.connect(self.update_list) - self.update_timer.start() - self.parent().process_stopped.connect(self.update_values) + self.update_list_timer.start(100) + self.update_values_timer.start(500) self.parent().refresh_disassemble_view() + self.show() def update_list(self): - info = GDB_Engine.get_track_breakpoint_info(self.breakpoint) + info = debugcore.get_track_breakpoint_info(self.breakpoint) if not info: return if info == self.info: @@ -3816,27 +5097,24 @@ def update_list(self): for register_expression in info: for row, address in enumerate(info[register_expression]): self.tableWidget_TrackInfo.setRowCount(self.tableWidget_TrackInfo.rowCount() + 1) - self.tableWidget_TrackInfo.setItem(row, TRACK_BREAKPOINT_COUNT_COL, - QTableWidgetItem(str(info[register_expression][address]))) + self.tableWidget_TrackInfo.setItem( + row, TRACK_BREAKPOINT_COUNT_COL, QTableWidgetItem(str(info[register_expression][address])) + ) self.tableWidget_TrackInfo.setItem(row, TRACK_BREAKPOINT_ADDR_COL, QTableWidgetItem(address)) - self.tableWidget_TrackInfo.setItem(row, TRACK_BREAKPOINT_SOURCE_COL, - QTableWidgetItem("[" + register_expression + "]")) - self.tableWidget_TrackInfo.resizeColumnsToContents() - self.tableWidget_TrackInfo.horizontalHeader().setStretchLastSection(True) - self.tableWidget_TrackInfo.selectRow(self.last_selected_row) + self.tableWidget_TrackInfo.setItem( + row, TRACK_BREAKPOINT_SOURCE_COL, QTableWidgetItem("[" + register_expression + "]") + ) + self.update_values() def update_values(self): - param_list = [] + mem_handle = debugcore.memory_handle() value_type = self.comboBox_ValueType.currentIndex() for row in range(self.tableWidget_TrackInfo.rowCount()): address = self.tableWidget_TrackInfo.item(row, TRACK_BREAKPOINT_ADDR_COL).text() - param_list.append((address, value_type, 10)) - value_list = GDB_Engine.read_memory_multiple(param_list) - for row, value in enumerate(value_list): + value = debugcore.read_memory(address, value_type, 10, mem_handle=mem_handle) value = "" if value is None else str(value) self.tableWidget_TrackInfo.setItem(row, TRACK_BREAKPOINT_VALUE_COL, QTableWidgetItem(value)) - self.tableWidget_TrackInfo.resizeColumnsToContents() - self.tableWidget_TrackInfo.horizontalHeader().setStretchLastSection(True) + guiutils.resize_to_contents(self.tableWidget_TrackInfo) self.tableWidget_TrackInfo.selectRow(self.last_selected_row) def tableWidget_TrackInfo_current_changed(self, QModelIndex_current): @@ -3846,144 +5124,141 @@ def tableWidget_TrackInfo_current_changed(self, QModelIndex_current): def tableWidget_TrackInfo_item_double_clicked(self, index): address = self.tableWidget_TrackInfo.item(index.row(), TRACK_BREAKPOINT_ADDR_COL).text() - self.parent().parent().add_entry_to_addresstable("Accessed by " + self.address, address, - self.comboBox_ValueType.currentData(Qt.UserRole), 10, True) + vt = typedefs.ValueType(self.comboBox_ValueType.currentIndex()) + self.parent().parent().add_entry_to_addresstable(tr.ACCESSED_BY.format(self.address), address, vt) + self.parent().parent().update_address_table() def pushButton_Stop_clicked(self): if self.stopped: self.close() - if not GDB_Engine.delete_breakpoint(self.address): - QMessageBox.information(self, "Error", "Unable to delete breakpoint at expression " + self.address) + return + if not debugcore.delete_breakpoint(self.address): + QMessageBox.information(self, tr.ERROR, tr.DELETE_BREAKPOINT_FAILED.format(self.address)) return self.stopped = True - self.pushButton_Stop.setText("Close") + self.pushButton_Stop.setText(tr.CLOSE) self.parent().refresh_disassemble_view() - def closeEvent(self, QCloseEvent): - if GDB_Engine.inferior_status == type_defs.INFERIOR_STATUS.INFERIOR_RUNNING: - QCloseEvent.ignore() - raise type_defs.InferiorRunningException - try: - self.update_timer.stop() - except AttributeError: - pass - global instances - instances.remove(self) - GDB_Engine.execute_func_temporary_interruption(GDB_Engine.delete_breakpoint, self.address) + def closeEvent(self, event: QCloseEvent): + self.update_list_timer.stop() + self.update_values_timer.stop() + if self.breakpoint: + if not self.stopped: + debugcore.delete_breakpoint(self.address) + breakpoint_file = utils.get_track_breakpoint_file(debugcore.currentpid, self.breakpoint) + if os.path.exists(breakpoint_file): + os.remove(breakpoint_file) self.parent().refresh_disassemble_view() + super().closeEvent(event) class TraceInstructionsPromptDialogForm(QDialog, TraceInstructionsPromptDialog): - def __init__(self, parent=None): - super().__init__(parent=parent) + def __init__(self, parent): + super().__init__(parent) self.setupUi(self) + guiutils.center_to_parent(self) def get_values(self): max_trace_count = int(self.lineEdit_MaxTraceCount.text()) trigger_condition = self.lineEdit_TriggerCondition.text() stop_condition = self.lineEdit_StopCondition.text() if self.checkBox_StepOver.isChecked(): - step_mode = type_defs.STEP_MODE.STEP_OVER + step_mode = typedefs.STEP_MODE.STEP_OVER else: - step_mode = type_defs.STEP_MODE.SINGLE_STEP + step_mode = typedefs.STEP_MODE.SINGLE_STEP stop_after_trace = self.checkBox_StopAfterTrace.isChecked() - collect_general_registers = self.checkBox_GeneralRegisters.isChecked() - collect_flag_registers = self.checkBox_FlagRegisters.isChecked() - collect_segment_registers = self.checkBox_SegmentRegisters.isChecked() - collect_float_registers = self.checkBox_FloatRegisters.isChecked() - return (max_trace_count, trigger_condition, stop_condition, step_mode, stop_after_trace, - collect_general_registers, collect_flag_registers, collect_segment_registers, collect_float_registers) + collect_registers = self.checkBox_CollectRegisters.isChecked() + return max_trace_count, trigger_condition, stop_condition, step_mode, stop_after_trace, collect_registers def accept(self): if int(self.lineEdit_MaxTraceCount.text()) >= 1: - super(TraceInstructionsPromptDialogForm, self).accept() + super().accept() else: - QMessageBox.information(self, "Error", "Max trace count must be greater than or equal to 1") + QMessageBox.information(self, tr.ERROR, tr.MAX_TRACE_COUNT_ASSERT_GT.format(1)) class TraceInstructionsWaitWidgetForm(QWidget, TraceInstructionsWaitWidget): widget_closed = pyqtSignal() - def __init__(self, address, breakpoint, parent=None): - super().__init__(parent=parent) + def __init__(self, parent, address: str, tracer: debugcore.Tracer): + super().__init__(parent) self.setupUi(self) - self.setWindowFlags(self.windowFlags() | Qt.Window | Qt.FramelessWindowHint) - GuiUtils.center(self) + self.status_to_text = { + typedefs.TRACE_STATUS.IDLE: tr.WAITING_FOR_BREAKPOINT, + typedefs.TRACE_STATUS.FINISHED: tr.TRACING_COMPLETED, + } + self.setWindowFlags(Qt.WindowType.Window) self.address = address - self.breakpoint = breakpoint - media_directory = SysUtils.get_media_directory() + self.tracer = tracer + media_directory = utils.get_media_directory() self.movie = QMovie(media_directory + "/TraceInstructionsWaitWidget/ajax-loader.gif", QByteArray()) self.label_Animated.setMovie(self.movie) self.movie.setScaledSize(QSize(215, 100)) - self.setAttribute(Qt.WA_TranslucentBackground) - self.movie.setCacheMode(QMovie.CacheAll) + self.movie.setCacheMode(QMovie.CacheMode.CacheAll) self.movie.setSpeed(100) self.movie.start() self.pushButton_Cancel.clicked.connect(self.close) + tracer_thread = Worker(tracer.tracer_loop) + tracer_thread.signals.finished.connect(self.close) + threadpool.start(tracer_thread) self.status_timer = QTimer() - self.status_timer.setInterval(30) + self.status_timer.setInterval(50) self.status_timer.timeout.connect(self.change_status) self.status_timer.start() + guiutils.center_to_parent(self) def change_status(self): - status_info = GDB_Engine.get_trace_instructions_status(self.breakpoint) - if status_info[0] == type_defs.TRACE_STATUS.STATUS_FINISHED or \ - status_info[0] == type_defs.TRACE_STATUS.STATUS_PROCESSING: - self.close() - return - self.label_StatusText.setText(status_info[1]) + if self.tracer.trace_status == typedefs.TRACE_STATUS.TRACING: + self.label_StatusText.setText(f"{self.tracer.current_trace_count} / {self.tracer.max_trace_count}") + else: + self.label_StatusText.setText(self.status_to_text[self.tracer.trace_status]) app.processEvents() - def closeEvent(self, QCloseEvent): + def closeEvent(self, event: QCloseEvent): self.status_timer.stop() - self.label_StatusText.setText("Processing the collected data") + self.label_StatusText.setText(tr.TRACING_COMPLETED) self.pushButton_Cancel.setVisible(False) self.adjustSize() app.processEvents() - status_info = GDB_Engine.get_trace_instructions_status(self.breakpoint) - if status_info[0] == type_defs.TRACE_STATUS.STATUS_TRACING or \ - status_info[0] == type_defs.TRACE_STATUS.STATUS_PROCESSING: - GDB_Engine.cancel_trace_instructions(self.breakpoint) - while GDB_Engine.get_trace_instructions_status(self.breakpoint)[0] \ - != type_defs.TRACE_STATUS.STATUS_FINISHED: + if self.tracer.trace_status == typedefs.TRACE_STATUS.TRACING: + self.tracer.cancel_trace() + while self.tracer.trace_status != typedefs.TRACE_STATUS.FINISHED: sleep(0.1) app.processEvents() - try: - GDB_Engine.delete_breakpoint(self.address) - except type_defs.InferiorRunningException: - pass self.widget_closed.emit() + super().closeEvent(event) class TraceInstructionsWindowForm(QMainWindow, TraceInstructionsWindow): - def __init__(self, address="", prompt_dialog=True, parent=None): - super().__init__() + def __init__(self, parent, address="", prompt_dialog=True): + super().__init__(parent) self.setupUi(self) - self.parent = lambda: parent - global instances - instances.append(self) - GuiUtils.center(self) self.address = address - self.trace_data = None + self.tracer = debugcore.Tracer() self.treeWidget_InstructionInfo.currentItemChanged.connect(self.display_collected_data) self.treeWidget_InstructionInfo.itemDoubleClicked.connect(self.treeWidget_InstructionInfo_item_double_clicked) self.treeWidget_InstructionInfo.contextMenuEvent = self.treeWidget_InstructionInfo_context_menu_event self.actionOpen.triggered.connect(self.load_file) self.actionSave.triggered.connect(self.save_file) self.splitter.setStretchFactor(0, 1) + guiutils.center_to_parent(self) if not prompt_dialog: + self.showMaximized() return - prompt_dialog = TraceInstructionsPromptDialogForm() - if prompt_dialog.exec_(): + prompt_dialog = TraceInstructionsPromptDialogForm(self) + if prompt_dialog.exec(): params = (address,) + prompt_dialog.get_values() - breakpoint = GDB_Engine.trace_instructions(*params) + breakpoint = self.tracer.set_breakpoint(*params) if not breakpoint: - QMessageBox.information(self, "Error", "Failed to set breakpoint at address " + address) + QMessageBox.information(self, tr.ERROR, tr.BREAKPOINT_FAILED.format(address)) + self.close() return - self.breakpoint = breakpoint - self.wait_dialog = TraceInstructionsWaitWidgetForm(address, breakpoint, self) + self.showMaximized() + self.wait_dialog = TraceInstructionsWaitWidgetForm(self, address, self.tracer) self.wait_dialog.widget_closed.connect(self.show_trace_info) self.wait_dialog.show() + else: + self.close() def display_collected_data(self, QTreeWidgetItem_current): self.textBrowser_RegisterInfo.clear() @@ -3992,21 +5267,14 @@ def display_collected_data(self, QTreeWidgetItem_current): for key in current_dict: self.textBrowser_RegisterInfo.append(str(key) + " = " + str(current_dict[key])) self.textBrowser_RegisterInfo.verticalScrollBar().setValue( - self.textBrowser_RegisterInfo.verticalScrollBar().minimum()) + self.textBrowser_RegisterInfo.verticalScrollBar().minimum() + ) - def show_trace_info(self, trace_data=None): + def show_trace_info(self): self.treeWidget_InstructionInfo.setStyleSheet("QTreeWidget::item{ height: 16px; }") parent = QTreeWidgetItem(self.treeWidget_InstructionInfo) self.treeWidget_InstructionInfo.setRootIndex(self.treeWidget_InstructionInfo.indexFromItem(parent)) - if trace_data: - trace_tree, current_root_index = trace_data - else: - trace_data = GDB_Engine.get_trace_instructions_info(self.breakpoint) - if trace_data: - trace_tree, current_root_index = trace_data - else: - return - self.trace_data = copy.deepcopy(trace_data) + trace_tree, current_root_index = copy.deepcopy(self.tracer.trace_data) while current_root_index is not None: try: current_index = trace_tree[current_root_index][2][0] # Get the first child @@ -4025,37 +5293,33 @@ def show_trace_info(self, trace_data=None): self.treeWidget_InstructionInfo.expandAll() def save_file(self): - trace_file_path = SysUtils.get_user_path(type_defs.USER_PATHS.TRACE_INSTRUCTIONS_PATH) - file_path = QFileDialog.getSaveFileName(self, "Save trace file", trace_file_path, - "Trace File (*.trace);;All Files (*)")[0] + file_path, _ = QFileDialog.getSaveFileName(self, tr.SAVE_TRACE_FILE, None, tr.FILE_TYPES_TRACE) if file_path: - file_path = SysUtils.append_file_extension(file_path, "trace") - if not SysUtils.save_file(self.trace_data, file_path): - QMessageBox.information(self, "Error", "Cannot save to file") + file_path = utils.append_file_extension(file_path, "trace") + if not utils.save_file(self.tracer.trace_data, file_path): + QMessageBox.information(self, tr.ERROR, tr.FILE_SAVE_ERROR) def load_file(self): - trace_file_path = SysUtils.get_user_path(type_defs.USER_PATHS.TRACE_INSTRUCTIONS_PATH) - file_path = QFileDialog.getOpenFileName(self, "Open trace file", trace_file_path, - "Trace File (*.trace);;All Files (*)")[0] + file_path, _ = QFileDialog.getOpenFileName(self, tr.OPEN_TRACE_FILE, None, tr.FILE_TYPES_TRACE) if file_path: - content = SysUtils.load_file(file_path) + content = utils.load_file(file_path) if content is None: - QMessageBox.information(self, "Error", "File " + file_path + " does not exist, " + - "is inaccessible or contains invalid content. Terminating...") + QMessageBox.information(self, tr.ERROR, tr.FILE_LOAD_ERROR.format(file_path)) return self.treeWidget_InstructionInfo.clear() - self.show_trace_info(content) + self.tracer.trace_data = content + self.show_trace_info() def treeWidget_InstructionInfo_context_menu_event(self, event): menu = QMenu() - expand_all = menu.addAction("Expand All") - collapse_all = menu.addAction("Collapse All") + expand_all = menu.addAction(tr.EXPAND_ALL) + collapse_all = menu.addAction(tr.COLLAPSE_ALL) font_size = self.treeWidget_InstructionInfo.font().pointSize() menu.setStyleSheet("font-size: " + str(font_size) + "pt;") - action = menu.exec_(event.globalPos()) + action = menu.exec(event.globalPos()) actions = { expand_all: self.treeWidget_InstructionInfo.expandAll, - collapse_all: self.treeWidget_InstructionInfo.collapseAll + collapse_all: self.treeWidget_InstructionInfo.collapseAll, } try: actions[action]() @@ -4063,27 +5327,19 @@ def treeWidget_InstructionInfo_context_menu_event(self, event): pass def treeWidget_InstructionInfo_item_double_clicked(self, index): - current_item = GuiUtils.get_current_item(self.treeWidget_InstructionInfo) + current_item = guiutils.get_current_item(self.treeWidget_InstructionInfo) if not current_item: return - address = SysUtils.extract_address(current_item.trace_data[0]) + address = utils.extract_address(current_item.trace_data[0]) if address: - self.parent().disassemble_expression(address, append_to_travel_history=True) - - def closeEvent(self, QCloseEvent): - global instances - instances.remove(self) + self.parent().disassemble_expression(address, append_history=True) class FunctionsInfoWidgetForm(QWidget, FunctionsInfoWidget): - def __init__(self, parent=None): - super().__init__() + def __init__(self, parent): + super().__init__(parent) self.setupUi(self) - self.parent = lambda: parent - global instances - instances.append(self) - GuiUtils.center(self) - self.setWindowFlags(Qt.Window) + self.setWindowFlags(Qt.WindowType.Window) self.textBrowser_AddressInfo.setFixedHeight(100) self.pushButton_Search.clicked.connect(self.refresh_table) self.shortcut_search = QShortcut(QKeySequence("Return"), self) @@ -4091,9 +5347,10 @@ def __init__(self, parent=None): self.tableWidget_SymbolInfo.selectionModel().currentChanged.connect(self.tableWidget_SymbolInfo_current_changed) self.tableWidget_SymbolInfo.itemDoubleClicked.connect(self.tableWidget_SymbolInfo_item_double_clicked) self.tableWidget_SymbolInfo.contextMenuEvent = self.tableWidget_SymbolInfo_context_menu_event - icons_directory = GuiUtils.get_icons_directory() + icons_directory = guiutils.get_icons_directory() self.pushButton_Help.setIcon(QIcon(QPixmap(icons_directory + "/help.png"))) self.pushButton_Help.clicked.connect(self.pushButton_Help_clicked) + guiutils.center_to_parent(self) def refresh_table(self): input_text = self.lineEdit_SearchInput.text() @@ -4102,58 +5359,60 @@ def refresh_table(self): self.background_thread = self.loading_dialog.background_thread self.background_thread.overrided_func = lambda: self.process_data(input_text, case_sensitive) self.background_thread.output_ready.connect(self.apply_data) - self.loading_dialog.exec_() + self.loading_dialog.exec() def process_data(self, gdb_input, case_sensitive): - return GDB_Engine.search_functions(gdb_input, case_sensitive) + return debugcore.search_functions(gdb_input, case_sensitive) def apply_data(self, output): self.tableWidget_SymbolInfo.setSortingEnabled(False) self.tableWidget_SymbolInfo.setRowCount(0) self.tableWidget_SymbolInfo.setRowCount(len(output)) + defined_color = QColor(QColorConstants.Green) + defined_color.setAlpha(96) for row, item in enumerate(output): address = item[0] if address: address_item = QTableWidgetItem(address) else: - address_item = QTableWidgetItem("DEFINED") - address_item.setBackground(Qt.green) + address_item = QTableWidgetItem(tr.DEFINED) + address_item.setBackground(defined_color) self.tableWidget_SymbolInfo.setItem(row, FUNCTIONS_INFO_ADDR_COL, address_item) self.tableWidget_SymbolInfo.setItem(row, FUNCTIONS_INFO_SYMBOL_COL, QTableWidgetItem(item[1])) self.tableWidget_SymbolInfo.setSortingEnabled(True) - self.tableWidget_SymbolInfo.resizeColumnsToContents() - self.tableWidget_SymbolInfo.horizontalHeader().setStretchLastSection(True) + guiutils.resize_to_contents(self.tableWidget_SymbolInfo) def tableWidget_SymbolInfo_current_changed(self, QModelIndex_current): self.textBrowser_AddressInfo.clear() - address = self.tableWidget_SymbolInfo.item(QModelIndex_current.row(), FUNCTIONS_INFO_ADDR_COL).text() - if SysUtils.extract_address(address): - symbol = self.tableWidget_SymbolInfo.item(QModelIndex_current.row(), FUNCTIONS_INFO_SYMBOL_COL).text() - for item in SysUtils.split_symbol(symbol): - info = GDB_Engine.get_symbol_info(item) + current_row = QModelIndex_current.row() + if current_row < 0: + return + address = self.tableWidget_SymbolInfo.item(current_row, FUNCTIONS_INFO_ADDR_COL).text() + if utils.extract_address(address): + symbol = self.tableWidget_SymbolInfo.item(current_row, FUNCTIONS_INFO_SYMBOL_COL).text() + for item in utils.split_symbol(symbol): + info = debugcore.get_symbol_info(item) self.textBrowser_AddressInfo.append(info) else: - text = "This symbol is defined. You can use its body as a gdb expression. For instance:\n\n" \ - "void func(param) can be used as 'func' as a gdb expression" - self.textBrowser_AddressInfo.append(text) + self.textBrowser_AddressInfo.append(tr.DEFINED_SYMBOL) def tableWidget_SymbolInfo_context_menu_event(self, event): def copy_to_clipboard(row, column): app.clipboard().setText(self.tableWidget_SymbolInfo.item(row, column).text()) - selected_row = GuiUtils.get_current_row(self.tableWidget_SymbolInfo) + selected_row = guiutils.get_current_row(self.tableWidget_SymbolInfo) menu = QMenu() - copy_address = menu.addAction("Copy Address") - copy_symbol = menu.addAction("Copy Symbol") + copy_address = menu.addAction(tr.COPY_ADDRESS) + copy_symbol = menu.addAction(tr.COPY_SYMBOL) if selected_row == -1: - GuiUtils.delete_menu_entries(menu, [copy_address, copy_symbol]) + guiutils.delete_menu_entries(menu, [copy_address, copy_symbol]) font_size = self.tableWidget_SymbolInfo.font().pointSize() menu.setStyleSheet("font-size: " + str(font_size) + "pt;") - action = menu.exec_(event.globalPos()) + action = menu.exec(event.globalPos()) actions = { copy_address: lambda: copy_to_clipboard(selected_row, FUNCTIONS_INFO_ADDR_COL), - copy_symbol: lambda: copy_to_clipboard(selected_row, FUNCTIONS_INFO_SYMBOL_COL) + copy_symbol: lambda: copy_to_clipboard(selected_row, FUNCTIONS_INFO_SYMBOL_COL), } try: actions[action]() @@ -4162,35 +5421,94 @@ def copy_to_clipboard(row, column): def tableWidget_SymbolInfo_item_double_clicked(self, index): address = self.tableWidget_SymbolInfo.item(index.row(), FUNCTIONS_INFO_ADDR_COL).text() - self.parent().disassemble_expression(address, append_to_travel_history=True) + if address == tr.DEFINED: + return + self.parent().disassemble_expression(address, append_history=True) def pushButton_Help_clicked(self): - text = "\tHere's some useful regex tips:" \ - "\n'^string' searches for everything that starts with 'string'" \ - "\n'[ab]cd' searches for both 'acd' and 'bcd'" \ - "\n\n\tHow to interpret symbols:" \ - "\nA symbol that looks like 'func(param)@plt' consists of 3 pieces" \ - "\nfunc, func(param), func(param)@plt" \ - "\nThese 3 functions will have different addresses" \ - "\n@plt means this function is a subroutine for the original one" \ - "\nThere can be more than one of the same function" \ - "\nIt means that the function is overloaded" - InputDialogForm(item_list=[(text, None, Qt.AlignLeft)], buttons=[QDialogButtonBox.Ok]).exec_() - - def closeEvent(self, QCloseEvent): - global instances - instances.remove(self) + InputDialogForm( + self, + [(tr.FUNCTIONS_INFO_HELPER, None, Qt.AlignmentFlag.AlignLeft)], + buttons=[QDialogButtonBox.StandardButton.Ok], + ).exec() + + +class EditInstructionDialogForm(QDialog, EditInstructionDialog): + def __init__(self, parent, address, bytes_aob): + super().__init__(parent) + self.setupUi(self) + self.orig_bytes = bytes_aob + self.lineEdit_Address.setText(address) + self.lineEdit_Bytes.setText(bytes_aob) + self.lineEdit_Bytes_text_edited() + self.lineEdit_Bytes.textEdited.connect(self.lineEdit_Bytes_text_edited) + self.lineEdit_Instruction.textEdited.connect(self.lineEdit_Instruction_text_edited) + guiutils.center_to_parent(self) + + def set_valid(self, valid): + if valid: + self.is_valid = True + self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setEnabled(True) + else: + self.is_valid = False + self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setEnabled(False) + + def lineEdit_Bytes_text_edited(self): + bytes_aob = self.lineEdit_Bytes.text() + if utils.parse_string(bytes_aob, typedefs.VALUE_INDEX.AOB): + address = int(self.lineEdit_Address.text(), 0) + instruction = utils.get_opcodes(address, bytes_aob, debugcore.inferior_arch) + if instruction: + self.set_valid(True) + self.lineEdit_Instruction.setText(instruction) + return + self.set_valid(False) + self.lineEdit_Instruction.setText("??") + + def lineEdit_Instruction_text_edited(self): + instruction = self.lineEdit_Instruction.text() + address = int(self.lineEdit_Address.text(), 0) + result = utils.assemble(instruction, address, debugcore.inferior_arch) + if result: + byte_list = result[0] + self.set_valid(True) + bytes_str = " ".join([format(num, "02x") for num in byte_list]) + self.lineEdit_Bytes.setText(bytes_str) + else: + self.set_valid(False) + self.lineEdit_Bytes.setText("??") + + def accept(self): + if not self.is_valid: + return + + # No need to check for validity since address is not editable and opcode is checked in text_edited + address = int(self.lineEdit_Address.text(), 0) + bytes_aob = self.lineEdit_Bytes.text() + if bytes_aob != self.orig_bytes: + new_length = len(bytes_aob.split()) + old_length = len(self.orig_bytes.split()) + if new_length < old_length: + bytes_aob += " 90" * (old_length - new_length) # Append NOPs if we are short on bytes + elif new_length > old_length: + if not InputDialogForm(self, [(tr.NEW_OPCODE.format(new_length, old_length),)]).exec(): + return + debugcore.modify_instruction(address, bytes_aob) + self.parent().refresh_hex_view() + self.parent().refresh_disassemble_view() + super().accept() class HexEditDialogForm(QDialog, HexEditDialog): - def __init__(self, address, parent=None): - super().__init__(parent=parent) + def __init__(self, parent, address, length=20): + super().__init__(parent) self.setupUi(self) self.lineEdit_Length.setValidator(QHexValidator(999, self)) - self.lineEdit_Address.setText(address) - self.lineEdit_Length.setText("20") + self.lineEdit_Address.setText(hex(address)) + self.lineEdit_Length.setText(str(length)) self.refresh_view() self.lineEdit_AsciiView.selectionChanged.connect(self.lineEdit_AsciiView_selection_changed) + guiutils.center_to_parent(self) # TODO: Implement this # self.lineEdit_HexView.selectionChanged.connect(self.lineEdit_HexView_selection_changed) @@ -4201,9 +5519,9 @@ def __init__(self, address, parent=None): self.lineEdit_Length.textChanged.connect(self.refresh_view) def lineEdit_AsciiView_selection_changed(self): - length = len(SysUtils.str_to_aob(self.lineEdit_AsciiView.selectedText(), "utf-8")) + length = len(utils.str_to_aob(self.lineEdit_AsciiView.selectedText(), "utf-8")) start_index = self.lineEdit_AsciiView.selectionStart() - start_index = len(SysUtils.str_to_aob(self.lineEdit_AsciiView.text()[0:start_index], "utf-8")) + start_index = len(utils.str_to_aob(self.lineEdit_AsciiView.text()[0:start_index], "utf-8")) if start_index > 0: start_index += 1 self.lineEdit_HexView.deselect() @@ -4216,28 +5534,28 @@ def lineEdit_HexView_selection_changed(self): def lineEdit_HexView_text_edited(self): aob_string = self.lineEdit_HexView.text() - if not SysUtils.parse_string(aob_string, type_defs.VALUE_INDEX.INDEX_AOB): - self.lineEdit_HexView.setStyleSheet("QLineEdit {background-color: red;}") + if not utils.parse_string(aob_string, typedefs.VALUE_INDEX.AOB): + self.lineEdit_HexView.setStyleSheet("QLineEdit {background-color: rgba(255, 0, 0, 96);}") return aob_array = aob_string.split() try: - self.lineEdit_AsciiView.setText(SysUtils.aob_to_str(aob_array, "utf-8")) - self.lineEdit_HexView.setStyleSheet("QLineEdit {background-color: white;}") + self.lineEdit_AsciiView.setText(utils.aob_to_str(aob_array, "utf-8", replace_unprintable=False)) + self.lineEdit_HexView.setStyleSheet("") # This should set background color back to QT default except ValueError: - self.lineEdit_HexView.setStyleSheet("QLineEdit {background-color: red;}") + self.lineEdit_HexView.setStyleSheet("QLineEdit {background-color: rgba(255, 0, 0, 96);}") def lineEdit_AsciiView_text_edited(self): ascii_str = self.lineEdit_AsciiView.text() try: - self.lineEdit_HexView.setText(SysUtils.str_to_aob(ascii_str, "utf-8")) - self.lineEdit_AsciiView.setStyleSheet("QLineEdit {background-color: white;}") + self.lineEdit_HexView.setText(utils.str_to_aob(ascii_str, "utf-8")) + self.lineEdit_AsciiView.setStyleSheet("") except ValueError: - self.lineEdit_AsciiView.setStyleSheet("QLineEdit {background-color: red;}") + self.lineEdit_AsciiView.setStyleSheet("QLineEdit {background-color: rgba(255, 0, 0, 96);}") def refresh_view(self): self.lineEdit_AsciiView.clear() self.lineEdit_HexView.clear() - address = GDB_Engine.examine_expression(self.lineEdit_Address.text()).address + address = debugcore.examine_expression(self.lineEdit_Address.text()).address if not address: return length = self.lineEdit_Length.text() @@ -4246,302 +5564,48 @@ def refresh_view(self): address = int(address, 0) except ValueError: return - aob_array = GDB_Engine.hex_dump(address, length) - ascii_str = SysUtils.aob_to_str(aob_array, "utf-8") + aob_array = debugcore.hex_dump(address, length) + ascii_str = utils.aob_to_str(aob_array, "utf-8", replace_unprintable=False) self.lineEdit_AsciiView.setText(ascii_str) self.lineEdit_HexView.setText(" ".join(aob_array)) def accept(self): expression = self.lineEdit_Address.text() - address = GDB_Engine.examine_expression(expression).address + address = debugcore.examine_expression(expression).address if not address: - QMessageBox.information(self, "Error", expression + " isn't a valid expression") + QMessageBox.information(self, tr.ERROR, tr.IS_INVALID_EXPRESSION.format(expression)) return value = self.lineEdit_HexView.text() - GDB_Engine.write_memory(address, type_defs.VALUE_INDEX.INDEX_AOB, value) - super(HexEditDialogForm, self).accept() - - -class LibpinceReferenceWidgetForm(QWidget, LibpinceReferenceWidget): - def convert_to_modules(self, module_strings): - return [eval(item) for item in module_strings] - - def __init__(self, is_window=False, parent=None): - super().__init__(parent=parent) - self.setupUi(self) - self.found_count = 0 - self.current_found = 0 - global instances - instances.append(self) - if is_window: - GuiUtils.center(self) - self.setWindowFlags(Qt.Window) - self.show_type_defs() - self.splitter.setStretchFactor(0, 1) - self.widget_Resources.resize(700, self.widget_Resources.height()) - libpince_directory = SysUtils.get_libpince_directory() - self.textBrowser_TypeDefs.setText(open(libpince_directory + "/type_defs.py").read()) - source_menu_items = ["(Tagged only)", "(All)"] - self.libpince_source_files = ["GDB_Engine", "SysUtils", "GuiUtils"] - source_menu_items.extend(self.libpince_source_files) - self.comboBox_SourceFile.addItems(source_menu_items) - self.comboBox_SourceFile.setCurrentIndex(0) - self.fill_resource_tree() - icons_directory = GuiUtils.get_icons_directory() - self.pushButton_TextUp.setIcon(QIcon(QPixmap(icons_directory + "/bullet_arrow_up.png"))) - self.pushButton_TextDown.setIcon(QIcon(QPixmap(icons_directory + "/bullet_arrow_down.png"))) - self.comboBox_SourceFile.currentIndexChanged.connect(self.comboBox_SourceFile_current_index_changed) - self.pushButton_ShowTypeDefs.clicked.connect(self.toggle_type_defs) - self.lineEdit_SearchText.textChanged.connect(self.highlight_text) - self.pushButton_TextDown.clicked.connect(self.pushButton_TextDown_clicked) - self.pushButton_TextUp.clicked.connect(self.pushButton_TextUp_clicked) - self.lineEdit_Search.textChanged.connect(self.comboBox_SourceFile_current_index_changed) - self.tableWidget_ResourceTable.contextMenuEvent = self.tableWidget_ResourceTable_context_menu_event - self.treeWidget_ResourceTree.contextMenuEvent = self.treeWidget_ResourceTree_context_menu_event - self.treeWidget_ResourceTree.expanded.connect(self.resize_resource_tree) - self.treeWidget_ResourceTree.collapsed.connect(self.resize_resource_tree) - - def tableWidget_ResourceTable_context_menu_event(self, event): - def copy_to_clipboard(row, column): - app.clipboard().setText(self.tableWidget_ResourceTable.item(row, column).text()) - - selected_row = GuiUtils.get_current_row(self.tableWidget_ResourceTable) - - menu = QMenu() - refresh = menu.addAction("Refresh") - menu.addSeparator() - copy_item = menu.addAction("Copy Item") - copy_value = menu.addAction("Copy Value") - if selected_row == -1: - GuiUtils.delete_menu_entries(menu, [copy_item, copy_value]) - font_size = self.tableWidget_ResourceTable.font().pointSize() - menu.setStyleSheet("font-size: " + str(font_size) + "pt;") - action = menu.exec_(event.globalPos()) - actions = { - refresh: self.fill_resource_table, - copy_item: lambda: copy_to_clipboard(selected_row, LIBPINCE_REFERENCE_ITEM_COL), - copy_value: lambda: copy_to_clipboard(selected_row, LIBPINCE_REFERENCE_VALUE_COL) - } - try: - actions[action]() - except KeyError: - pass - - def treeWidget_ResourceTree_context_menu_event(self, event): - def copy_to_clipboard(column): - current_item = GuiUtils.get_current_item(self.treeWidget_ResourceTree) - if current_item: - app.clipboard().setText(current_item.text(column)) - - def expand_all(): - self.treeWidget_ResourceTree.expandAll() - self.resize_resource_tree() - - def collapse_all(): - self.treeWidget_ResourceTree.collapseAll() - self.resize_resource_tree() - - selected_row = GuiUtils.get_current_row(self.treeWidget_ResourceTree) - - menu = QMenu() - refresh = menu.addAction("Refresh") - menu.addSeparator() - copy_item = menu.addAction("Copy Item") - copy_value = menu.addAction("Copy Value") - if selected_row == -1: - GuiUtils.delete_menu_entries(menu, [copy_item, copy_value]) - menu.addSeparator() - expand_all_items = menu.addAction("Expand All") - collapse_all_items = menu.addAction("Collapse All") - font_size = self.treeWidget_ResourceTree.font().pointSize() - menu.setStyleSheet("font-size: " + str(font_size) + "pt;") - action = menu.exec_(event.globalPos()) - actions = { - refresh: self.fill_resource_tree, - copy_item: lambda: copy_to_clipboard(LIBPINCE_REFERENCE_ITEM_COL), - copy_value: lambda: copy_to_clipboard(LIBPINCE_REFERENCE_VALUE_COL), - expand_all_items: expand_all, - collapse_all_items: collapse_all - } - - # Thanks QT, for this unexplainable, mind blowing bug of yours - self.treeWidget_ResourceTree.blockSignals(True) - try: - actions[action]() - except KeyError: - pass - self.treeWidget_ResourceTree.blockSignals(False) - - def comboBox_SourceFile_current_index_changed(self): - if self.comboBox_SourceFile.currentIndex() == 0: # (Tagged only) - self.fill_resource_tree() - else: - self.fill_resource_table() - - def resize_resource_tree(self): - self.treeWidget_ResourceTree.resizeColumnToContents(LIBPINCE_REFERENCE_ITEM_COL) - - def fill_resource_tree(self): - self.treeWidget_ResourceTree.setStyleSheet("QTreeWidget::item{ height: 16px; }") - self.stackedWidget_Resources.setCurrentIndex(0) - self.treeWidget_ResourceTree.clear() - parent = self.treeWidget_ResourceTree - checked_source_files = self.convert_to_modules(self.libpince_source_files) - tag_dict = SysUtils.get_tags(checked_source_files, type_defs.tag_to_string, self.lineEdit_Search.text()) - docstring_dict = SysUtils.get_docstrings(checked_source_files, self.lineEdit_Search.text()) - for tag in tag_dict: - child = QTreeWidgetItem(parent) - child.setText(0, tag) - for item in tag_dict[tag]: - docstring = docstring_dict.get(item) - docstr_child = QTreeWidgetItem(child) - docstr_child.setText(LIBPINCE_REFERENCE_ITEM_COL, item) - docstr_child.setText(LIBPINCE_REFERENCE_VALUE_COL, str(eval(item))) - docstr_child.setToolTip(LIBPINCE_REFERENCE_ITEM_COL, docstring) - docstr_child.setToolTip(LIBPINCE_REFERENCE_VALUE_COL, docstring) - - # Magic and mystery - self.treeWidget_ResourceTree.blockSignals(True) - if self.lineEdit_Search.text(): - self.treeWidget_ResourceTree.expandAll() - self.resize_resource_tree() - self.treeWidget_ResourceTree.blockSignals(False) - - def fill_resource_table(self): - self.stackedWidget_Resources.setCurrentIndex(1) - self.tableWidget_ResourceTable.setSortingEnabled(False) - self.tableWidget_ResourceTable.setRowCount(0) - if self.comboBox_SourceFile.currentIndex() == 1: # (All) - checked_source_files = self.libpince_source_files - else: - checked_source_files = [self.comboBox_SourceFile.currentText()] - checked_source_files = self.convert_to_modules(checked_source_files) - element_dict = SysUtils.get_docstrings(checked_source_files, self.lineEdit_Search.text()) - self.tableWidget_ResourceTable.setRowCount(len(element_dict)) - for row, item in enumerate(element_dict): - docstring = element_dict.get(item) - table_widget_item = QTableWidgetItem(item) - table_widget_item_value = QTableWidgetItem(str(eval(item))) - table_widget_item.setToolTip(docstring) - table_widget_item_value.setToolTip(docstring) - self.tableWidget_ResourceTable.setItem(row, LIBPINCE_REFERENCE_ITEM_COL, table_widget_item) - self.tableWidget_ResourceTable.setItem(row, LIBPINCE_REFERENCE_VALUE_COL, table_widget_item_value) - self.tableWidget_ResourceTable.setSortingEnabled(True) - self.tableWidget_ResourceTable.sortByColumn(LIBPINCE_REFERENCE_ITEM_COL, Qt.AscendingOrder) - self.tableWidget_ResourceTable.resizeColumnsToContents() - self.tableWidget_ResourceTable.horizontalHeader().setStretchLastSection(True) - - def pushButton_TextDown_clicked(self): - if self.found_count == 0: - return - cursor = self.textBrowser_TypeDefs.textCursor() - cursor.clearSelection() - cursor.movePosition(QTextCursor.Start) - self.textBrowser_TypeDefs.setTextCursor(cursor) - if self.current_found == self.found_count: - self.current_found = 1 - else: - self.current_found += 1 - pattern = self.lineEdit_SearchText.text() - for x in range(self.current_found): - self.textBrowser_TypeDefs.find(pattern) - self.label_FoundCount.setText(str(self.current_found) + "/" + str(self.found_count)) - - def pushButton_TextUp_clicked(self): - if self.found_count == 0: - return - cursor = self.textBrowser_TypeDefs.textCursor() - cursor.clearSelection() - cursor.movePosition(QTextCursor.Start) - self.textBrowser_TypeDefs.setTextCursor(cursor) - if self.current_found == 1: - self.current_found = self.found_count - else: - self.current_found -= 1 - pattern = self.lineEdit_SearchText.text() - for x in range(self.current_found): - self.textBrowser_TypeDefs.find(pattern) - self.label_FoundCount.setText(str(self.current_found) + "/" + str(self.found_count)) - - def highlight_text(self): - self.textBrowser_TypeDefs.selectAll() - self.textBrowser_TypeDefs.setTextBackgroundColor(QColor("white")) - cursor = self.textBrowser_TypeDefs.textCursor() - cursor.clearSelection() - cursor.movePosition(QTextCursor.Start) - self.textBrowser_TypeDefs.setTextCursor(cursor) - - highlight_format = QTextCharFormat() - highlight_format.setBackground(QBrush(QColor("red"))) - pattern = self.lineEdit_SearchText.text() - found_count = 0 - while True: - if not self.textBrowser_TypeDefs.find(pattern): - break - cursor = self.textBrowser_TypeDefs.textCursor() - cursor.mergeCharFormat(highlight_format) - found_count += 1 - self.found_count = found_count - if found_count == 0: - self.label_FoundCount.setText("0/0") - return - cursor = self.textBrowser_TypeDefs.textCursor() - cursor.clearSelection() - cursor.movePosition(QTextCursor.Start) - self.textBrowser_TypeDefs.setTextCursor(cursor) - self.textBrowser_TypeDefs.find(pattern) - self.current_found = 1 - self.label_FoundCount.setText("1/" + str(found_count)) - - def toggle_type_defs(self): - if self.type_defs_shown: - self.hide_type_defs() - else: - self.show_type_defs() - - def hide_type_defs(self): - self.type_defs_shown = False - self.widget_TypeDefs.hide() - self.pushButton_ShowTypeDefs.setText("Show type_defs") - - def show_type_defs(self): - self.type_defs_shown = True - self.widget_TypeDefs.show() - self.pushButton_ShowTypeDefs.setText("Hide type_defs") - - def closeEvent(self, QCloseEvent): - global instances - instances.remove(self) + debugcore.write_memory(address, typedefs.VALUE_INDEX.AOB, value) + super().accept() class LogFileWidgetForm(QWidget, LogFileWidget): - def __init__(self, parent=None): - super().__init__(parent=parent) + def __init__(self, parent): + super().__init__(parent) self.setupUi(self) - GuiUtils.center(self) - global instances - instances.append(self) - self.setWindowFlags(Qt.Window) + self.setWindowFlags(Qt.WindowType.Window) self.contents = "" self.refresh_contents() self.refresh_timer = QTimer() self.refresh_timer.setInterval(500) self.refresh_timer.timeout.connect(self.refresh_contents) self.refresh_timer.start() + guiutils.center_to_parent(self) def refresh_contents(self): - log_path = SysUtils.get_logging_file(GDB_Engine.currentpid) - self.setWindowTitle("Log File of PID " + str(GDB_Engine.currentpid)) - self.label_FilePath.setText("Contents of " + log_path + " (only last 20000 bytes are shown)") - logging_status = "ON" if gdb_logging else "OFF" - self.label_LoggingStatus.setText("LOGGING: " + logging_status + "") + log_path = utils.get_logging_file(debugcore.currentpid) + self.setWindowTitle(tr.LOG_FILE.format(debugcore.currentpid)) + self.label_FilePath.setText(tr.LOG_CONTENTS.format(log_path, 20000)) + log_status = f"{tr.ON}" if settings.gdb_logging else f"{tr.OFF}" + self.label_LoggingStatus.setText(f"{tr.LOG_STATUS.format(log_status)}") try: log_file = open(log_path) except OSError: self.textBrowser_LogContent.clear() - error_message = "Unable to read log file at " + log_path + "\n" - if not gdb_logging: - error_message += "Go to Settings->Debug to enable logging" + error_message = tr.LOG_READ_ERROR.format(log_path) + "\n" + if not settings.gdb_logging: + error_message += tr.SETTINGS_ENABLE_LOG self.textBrowser_LogContent.setText(error_message) return log_file.seek(0, io.SEEK_END) @@ -4558,30 +5622,25 @@ def refresh_contents(self): # Scrolling to bottom cursor = self.textBrowser_LogContent.textCursor() - cursor.movePosition(QTextCursor.End) + cursor.movePosition(QTextCursor.MoveOperation.End) self.textBrowser_LogContent.setTextCursor(cursor) self.textBrowser_LogContent.ensureCursorVisible() log_file.close() - def closeEvent(self, QCloseEvent): - global instances - instances.remove(self) + def closeEvent(self, event: QCloseEvent): self.refresh_timer.stop() + super().closeEvent(event) class SearchOpcodeWidgetForm(QWidget, SearchOpcodeWidget): - def __init__(self, start="", end="", parent=None): - super().__init__() + def __init__(self, parent, start="", end=""): + super().__init__(parent) self.setupUi(self) - self.parent = lambda: parent - global instances - instances.append(self) - GuiUtils.center(self) - self.setWindowFlags(Qt.Window) + self.setWindowFlags(Qt.WindowType.Window) self.lineEdit_Start.setText(start) self.lineEdit_End.setText(end) self.tableWidget_Opcodes.setColumnWidth(SEARCH_OPCODE_ADDR_COL, 250) - icons_directory = GuiUtils.get_icons_directory() + icons_directory = guiutils.get_icons_directory() self.pushButton_Help.setIcon(QIcon(QPixmap(icons_directory + "/help.png"))) self.pushButton_Help.clicked.connect(self.pushButton_Help_clicked) self.pushButton_Search.clicked.connect(self.refresh_table) @@ -4589,6 +5648,7 @@ def __init__(self, start="", end="", parent=None): self.shortcut_search.activated.connect(self.refresh_table) self.tableWidget_Opcodes.itemDoubleClicked.connect(self.tableWidget_Opcodes_item_double_clicked) self.tableWidget_Opcodes.contextMenuEvent = self.tableWidget_Opcodes_context_menu_event + guiutils.center_to_parent(self) def refresh_table(self): start_address = self.lineEdit_Start.text() @@ -4598,17 +5658,18 @@ def refresh_table(self): enable_regex = self.checkBox_Regex.isChecked() self.loading_dialog = LoadingDialogForm(self) self.background_thread = self.loading_dialog.background_thread - self.background_thread.overrided_func = lambda: self.process_data(regex, start_address, end_address, - case_sensitive, enable_regex) + self.background_thread.overrided_func = lambda: self.process_data( + regex, start_address, end_address, case_sensitive, enable_regex + ) self.background_thread.output_ready.connect(self.apply_data) - self.loading_dialog.exec_() + self.loading_dialog.exec() def process_data(self, regex, start_address, end_address, case_sensitive, enable_regex): - return GDB_Engine.search_opcode(regex, start_address, end_address, case_sensitive, enable_regex) + return debugcore.search_opcode(regex, start_address, end_address, case_sensitive, enable_regex) def apply_data(self, disas_data): if disas_data is None: - QMessageBox.information(self, "Error", "Given regex isn't valid, check terminal to see the error") + QMessageBox.information(self, tr.ERROR, tr.INVALID_REGEX) return self.tableWidget_Opcodes.setSortingEnabled(False) self.tableWidget_Opcodes.setRowCount(0) @@ -4619,110 +5680,87 @@ def apply_data(self, disas_data): self.tableWidget_Opcodes.setSortingEnabled(True) def pushButton_Help_clicked(self): - text = "\tHere's some useful regex examples:" \ - "\n'call|rax' searches for opcodes that contain 'call' or 'rax'" \ - "\n'[re]cx' searches for both 'rcx' and 'ecx'" \ - "\nUse the char '\\' to escape special chars such as '['" \ - "\n'\[rsp\]' searches for opcodes that contain '[rsp]'" - InputDialogForm(item_list=[(text, None, Qt.AlignLeft)], buttons=[QDialogButtonBox.Ok]).exec_() + InputDialogForm( + self, + [(tr.SEARCH_OPCODE_HELPER, None, Qt.AlignmentFlag.AlignLeft)], + buttons=[QDialogButtonBox.StandardButton.Ok], + ).exec() def tableWidget_Opcodes_item_double_clicked(self, index): row = index.row() address = self.tableWidget_Opcodes.item(row, SEARCH_OPCODE_ADDR_COL).text() - self.parent().disassemble_expression(SysUtils.extract_address(address), append_to_travel_history=True) + self.parent().disassemble_expression(utils.extract_address(address), append_history=True) def tableWidget_Opcodes_context_menu_event(self, event): def copy_to_clipboard(row, column): app.clipboard().setText(self.tableWidget_Opcodes.item(row, column).text()) - selected_row = GuiUtils.get_current_row(self.tableWidget_Opcodes) + selected_row = guiutils.get_current_row(self.tableWidget_Opcodes) menu = QMenu() - copy_address = menu.addAction("Copy Address") - copy_opcode = menu.addAction("Copy Opcode") + copy_address = menu.addAction(tr.COPY_ADDRESS) + copy_opcode = menu.addAction(tr.COPY_OPCODE) if selected_row == -1: - GuiUtils.delete_menu_entries(menu, [copy_address, copy_opcode]) + guiutils.delete_menu_entries(menu, [copy_address, copy_opcode]) font_size = self.tableWidget_Opcodes.font().pointSize() menu.setStyleSheet("font-size: " + str(font_size) + "pt;") - action = menu.exec_(event.globalPos()) + action = menu.exec(event.globalPos()) actions = { copy_address: lambda: copy_to_clipboard(selected_row, SEARCH_OPCODE_ADDR_COL), - copy_opcode: lambda: copy_to_clipboard(selected_row, SEARCH_OPCODE_OPCODES_COL) + copy_opcode: lambda: copy_to_clipboard(selected_row, SEARCH_OPCODE_OPCODES_COL), } try: actions[action]() except KeyError: pass - def closeEvent(self, QCloseEvent): - global instances - instances.remove(self) - class MemoryRegionsWidgetForm(QWidget, MemoryRegionsWidget): - def __init__(self, parent=None): - super().__init__() + def __init__(self, parent): + super().__init__(parent) self.setupUi(self) - self.parent = lambda: parent - global instances - instances.append(self) - GuiUtils.center(self) - self.setWindowFlags(Qt.Window) + self.setWindowFlags(Qt.WindowType.Window) self.refresh_table() self.tableWidget_MemoryRegions.contextMenuEvent = self.tableWidget_MemoryRegions_context_menu_event self.tableWidget_MemoryRegions.itemDoubleClicked.connect(self.tableWidget_MemoryRegions_item_double_clicked) self.shortcut_refresh = QShortcut(QKeySequence("R"), self) self.shortcut_refresh.activated.connect(self.refresh_table) + guiutils.center_to_parent(self) def refresh_table(self): - memory_regions = SysUtils.get_memory_regions(GDB_Engine.currentpid) + memory_regions = utils.get_regions(debugcore.currentpid) self.tableWidget_MemoryRegions.setRowCount(0) self.tableWidget_MemoryRegions.setRowCount(len(memory_regions)) - for row, region in enumerate(memory_regions): - self.tableWidget_MemoryRegions.setItem(row, MEMORY_REGIONS_ADDR_COL, QTableWidgetItem(region.addr)) - self.tableWidget_MemoryRegions.setItem(row, MEMORY_REGIONS_PERM_COL, QTableWidgetItem(region.perms)) - self.tableWidget_MemoryRegions.setItem(row, MEMORY_REGIONS_SIZE_COL, QTableWidgetItem(hex(region.size))) - self.tableWidget_MemoryRegions.setItem(row, MEMORY_REGIONS_PATH_COL, QTableWidgetItem(region.path)) - self.tableWidget_MemoryRegions.setItem(row, MEMORY_REGIONS_RSS_COL, QTableWidgetItem(hex(region.rss))) - self.tableWidget_MemoryRegions.setItem(row, MEMORY_REGIONS_PSS_COL, QTableWidgetItem(hex(region.pss))) - self.tableWidget_MemoryRegions.setItem(row, MEMORY_REGIONS_SHRCLN_COL, - QTableWidgetItem(hex(region.shared_clean))) - self.tableWidget_MemoryRegions.setItem(row, MEMORY_REGIONS_SHRDRTY_COL, - QTableWidgetItem(hex(region.shared_dirty))) - self.tableWidget_MemoryRegions.setItem(row, MEMORY_REGIONS_PRIVCLN_COL, - QTableWidgetItem(hex(region.private_clean))) - self.tableWidget_MemoryRegions.setItem(row, MEMORY_REGIONS_PRIVDRTY_COL, - QTableWidgetItem(hex(region.private_dirty))) - self.tableWidget_MemoryRegions.setItem(row, MEMORY_REGIONS_REF_COL, - QTableWidgetItem(hex(region.referenced))) - self.tableWidget_MemoryRegions.setItem(row, MEMORY_REGIONS_ANON_COL, - QTableWidgetItem(hex(region.anonymous))) - self.tableWidget_MemoryRegions.setItem(row, MEMORY_REGIONS_SWAP_COL, QTableWidgetItem(hex(region.swap))) - self.tableWidget_MemoryRegions.resizeColumnsToContents() - self.tableWidget_MemoryRegions.horizontalHeader().setStretchLastSection(True) + for row, (start, end, perms, offset, _, _, path) in enumerate(memory_regions): + address = start + "-" + end + self.tableWidget_MemoryRegions.setItem(row, MEMORY_REGIONS_ADDR_COL, QTableWidgetItem(address)) + self.tableWidget_MemoryRegions.setItem(row, MEMORY_REGIONS_PERM_COL, QTableWidgetItem(perms)) + self.tableWidget_MemoryRegions.setItem(row, MEMORY_REGIONS_OFFSET_COL, QTableWidgetItem(offset)) + self.tableWidget_MemoryRegions.setItem(row, MEMORY_REGIONS_PATH_COL, QTableWidgetItem(path)) + guiutils.resize_to_contents(self.tableWidget_MemoryRegions) def tableWidget_MemoryRegions_context_menu_event(self, event): def copy_to_clipboard(row, column): app.clipboard().setText(self.tableWidget_MemoryRegions.item(row, column).text()) - selected_row = GuiUtils.get_current_row(self.tableWidget_MemoryRegions) + selected_row = guiutils.get_current_row(self.tableWidget_MemoryRegions) menu = QMenu() - refresh = menu.addAction("Refresh[R]") + refresh = menu.addAction(f"{tr.REFRESH}[R]") menu.addSeparator() - copy_addresses = menu.addAction("Copy Addresses") - copy_size = menu.addAction("Copy Size") - copy_path = menu.addAction("Copy Path") + copy_addresses = menu.addAction(tr.COPY_ADDRESSES) + copy_offset = menu.addAction(tr.COPY_OFFSET) + copy_path = menu.addAction(tr.COPY_PATH) if selected_row == -1: - GuiUtils.delete_menu_entries(menu, [copy_addresses, copy_size, copy_path]) + guiutils.delete_menu_entries(menu, [copy_addresses, copy_offset, copy_path]) font_size = self.tableWidget_MemoryRegions.font().pointSize() menu.setStyleSheet("font-size: " + str(font_size) + "pt;") - action = menu.exec_(event.globalPos()) + action = menu.exec(event.globalPos()) actions = { refresh: self.refresh_table, copy_addresses: lambda: copy_to_clipboard(selected_row, MEMORY_REGIONS_ADDR_COL), - copy_size: lambda: copy_to_clipboard(selected_row, MEMORY_REGIONS_SIZE_COL), - copy_path: lambda: copy_to_clipboard(selected_row, MEMORY_REGIONS_PATH_COL) + copy_offset: lambda: copy_to_clipboard(selected_row, MEMORY_REGIONS_OFFSET_COL), + copy_path: lambda: copy_to_clipboard(selected_row, MEMORY_REGIONS_PATH_COL), } try: actions[action]() @@ -4735,16 +5773,12 @@ def tableWidget_MemoryRegions_item_double_clicked(self, index): address_int = int(address.split("-")[0], 16) self.parent().hex_dump_address(address_int) - def closeEvent(self, QCloseEvent): - global instances - instances.remove(self) - class DissectCodeDialogForm(QDialog, DissectCodeDialog): scan_finished_signal = pyqtSignal() - def __init__(self, parent=None, int_address=-1): - super().__init__(parent=parent) + def __init__(self, parent, int_address=-1): + super().__init__(parent) self.setupUi(self) self.init_pre_scan_gui() self.update_dissect_results() @@ -4764,10 +5798,11 @@ def __init__(self, parent=None, int_address=-1): self.pushButton_StartCancel_clicked() break else: - QMessageBox.information(self, "Error", hex(int_address) + " isn't in a valid region range") + QMessageBox.information(self, tr.ERROR, tr.INVALID_REGION) else: if self.tableWidget_ExecutableMemoryRegions.rowCount() > 0: self.tableWidget_ExecutableMemoryRegions.selectRow(0) + guiutils.center_to_parent(self) class BackgroundThread(QThread): output_ready = pyqtSignal() @@ -4779,22 +5814,22 @@ def __init__(self, region_list, discard_invalid_strings): self.discard_invalid_strings = discard_invalid_strings def run(self): - GDB_Engine.dissect_code(self.region_list, self.discard_invalid_strings) + debugcore.dissect_code(self.region_list, self.discard_invalid_strings) if not self.is_canceled: self.output_ready.emit() def init_pre_scan_gui(self): self.is_scanning = False self.is_canceled = False - self.pushButton_StartCancel.setText("Start") + self.pushButton_StartCancel.setText(tr.START) def init_after_scan_gui(self): self.is_scanning = True - self.label_ScanInfo.setText("Currently scanning region:") - self.pushButton_StartCancel.setText("Cancel") + self.label_ScanInfo.setText(tr.CURRENT_SCAN_REGION) + self.pushButton_StartCancel.setText(tr.CANCEL) def refresh_dissect_status(self): - region, region_count, range, string_count, jump_count, call_count = GDB_Engine.get_dissect_code_status() + region, region_count, range, string_count, jump_count, call_count = debugcore.get_dissect_code_status() if not region: return self.label_RegionInfo.setText(region) @@ -4806,7 +5841,7 @@ def refresh_dissect_status(self): def update_dissect_results(self): try: - referenced_strings, referenced_jumps, referenced_calls = GDB_Engine.get_dissect_code_data() + referenced_strings, referenced_jumps, referenced_calls = debugcore.get_dissect_code_data() except: return self.label_StringReferenceCount.setText(str(len(referenced_strings))) @@ -4814,20 +5849,21 @@ def update_dissect_results(self): self.label_CallReferenceCount.setText(str(len(referenced_calls))) def show_memory_regions(self): - executable_regions = SysUtils.filter_memory_regions(GDB_Engine.currentpid, "perms", "..x.") - self.region_list = executable_regions + executable_regions = utils.filter_regions(debugcore.currentpid, "permissions", "..x.") + self.region_list = [] self.tableWidget_ExecutableMemoryRegions.setRowCount(0) self.tableWidget_ExecutableMemoryRegions.setRowCount(len(executable_regions)) - for row, region in enumerate(executable_regions): - self.tableWidget_ExecutableMemoryRegions.setItem(row, DISSECT_CODE_ADDR_COL, QTableWidgetItem(region.addr)) - self.tableWidget_ExecutableMemoryRegions.setItem(row, DISSECT_CODE_PATH_COL, QTableWidgetItem(region.path)) - self.tableWidget_ExecutableMemoryRegions.resizeColumnsToContents() - self.tableWidget_ExecutableMemoryRegions.horizontalHeader().setStretchLastSection(True) + for row, (start, end, _, _, _, _, path) in enumerate(executable_regions): + address = start + "-" + end + self.region_list.append((start, end)) + self.tableWidget_ExecutableMemoryRegions.setItem(row, DISSECT_CODE_ADDR_COL, QTableWidgetItem(address)) + self.tableWidget_ExecutableMemoryRegions.setItem(row, DISSECT_CODE_PATH_COL, QTableWidgetItem(path)) + guiutils.resize_to_contents(self.tableWidget_ExecutableMemoryRegions) def scan_finished(self): self.init_pre_scan_gui() if not self.is_canceled: - self.label_ScanInfo.setText("Scan finished") + self.label_ScanInfo.setText(tr.SCAN_FINISHED) self.is_canceled = False self.refresh_timer.stop() self.refresh_dissect_status() @@ -4838,60 +5874,57 @@ def pushButton_StartCancel_clicked(self): if self.is_scanning: self.is_canceled = True self.background_thread.is_canceled = True - GDB_Engine.cancel_dissect_code() + debugcore.cancel_dissect_code() self.refresh_timer.stop() self.update_dissect_results() - self.label_ScanInfo.setText("Scan was canceled") + self.label_ScanInfo.setText(tr.SCAN_CANCELED) self.init_pre_scan_gui() else: - if not GDB_Engine.inferior_status == type_defs.INFERIOR_STATUS.INFERIOR_STOPPED: - QMessageBox.information(self, "Error", "Please stop the process first") - return selected_rows = self.tableWidget_ExecutableMemoryRegions.selectionModel().selectedRows() if not selected_rows: - QMessageBox.information(self, "Error", "Select at least one region") + QMessageBox.information(self, tr.ERROR, tr.SELECT_ONE_REGION) return selected_indexes = [selected_row.row() for selected_row in selected_rows] selected_regions = [self.region_list[selected_index] for selected_index in selected_indexes] - self.background_thread = self.BackgroundThread(selected_regions, - self.checkBox_DiscardInvalidStrings.isChecked()) + self.background_thread = self.BackgroundThread( + selected_regions, self.checkBox_DiscardInvalidStrings.isChecked() + ) self.background_thread.output_ready.connect(self.scan_finished) self.init_after_scan_gui() self.refresh_timer.start() self.background_thread.start() - def closeEvent(self, QCloseEvent): - GDB_Engine.cancel_dissect_code() + def closeEvent(self, event: QCloseEvent): + debugcore.cancel_dissect_code() self.refresh_timer.stop() + super().closeEvent(event) class ReferencedStringsWidgetForm(QWidget, ReferencedStringsWidget): - def __init__(self, parent=None): - super().__init__() + def __init__(self, parent): + super().__init__(parent) self.setupUi(self) - GuiUtils.fill_value_combobox(self.comboBox_ValueType, type_defs.VALUE_INDEX.INDEX_STRING_UTF8) - self.parent = lambda: parent - global instances - instances.append(self) - GuiUtils.center_to_parent(self) - self.setWindowFlags(Qt.Window) + guiutils.fill_value_combobox(self.comboBox_ValueType, typedefs.VALUE_INDEX.STRING_UTF8) + self.setWindowFlags(Qt.WindowType.Window) self.tableWidget_References.setColumnWidth(REF_STR_ADDR_COL, 150) self.tableWidget_References.setColumnWidth(REF_STR_COUNT_COL, 80) self.splitter.setStretchFactor(0, 1) self.listWidget_Referrers.resize(400, self.listWidget_Referrers.height()) - self.hex_len = 16 if GDB_Engine.inferior_arch == type_defs.INFERIOR_ARCH.ARCH_64 else 8 - str_dict, jmp_dict, call_dict = GDB_Engine.get_dissect_code_data() - if len(str_dict) == 0 and len(jmp_dict) == 0 and len(call_dict) == 0: - confirm_dialog = InputDialogForm(item_list=[("You need to dissect code first\nProceed?",)]) - if confirm_dialog.exec_(): - dissect_code_dialog = DissectCodeDialogForm() - dissect_code_dialog.scan_finished_signal.connect(dissect_code_dialog.accept) - dissect_code_dialog.exec_() + guiutils.center_to_parent(self) + self.hex_len = 16 if debugcore.inferior_arch == typedefs.INFERIOR_ARCH.ARCH_64 else 8 + str_dict, jmp_dict, call_dict = debugcore.get_dissect_code_data() + str_dict_len, jmp_dict_len, call_dict_len = len(str_dict), len(jmp_dict), len(call_dict) str_dict.close() jmp_dict.close() call_dict.close() + if str_dict_len == 0 and jmp_dict_len == 0 and call_dict_len == 0: + confirm_dialog = InputDialogForm(self, [(tr.DISSECT_CODE,)]) + if confirm_dialog.exec(): + dissect_code_dialog = DissectCodeDialogForm(self) + dissect_code_dialog.scan_finished_signal.connect(dissect_code_dialog.accept) + dissect_code_dialog.exec() self.refresh_table() - self.tableWidget_References.sortByColumn(REF_STR_ADDR_COL, Qt.AscendingOrder) + self.tableWidget_References.sortByColumn(REF_STR_ADDR_COL, Qt.SortOrder.AscendingOrder) self.tableWidget_References.selectionModel().currentChanged.connect(self.tableWidget_References_current_changed) self.listWidget_Referrers.itemDoubleClicked.connect(self.listWidget_Referrers_item_double_clicked) self.tableWidget_References.itemDoubleClicked.connect(self.tableWidget_References_item_double_clicked) @@ -4908,16 +5941,17 @@ def pad_hex(self, hex_str): self_len = 0 else: self_len = len(hex_str) - index - return '0x' + hex_str[2:].zfill(self.hex_len + self_len) + return "0x" + hex_str[2:].zfill(self.hex_len + self_len) def refresh_table(self): - item_list = GDB_Engine.search_referenced_strings(self.lineEdit_Regex.text(), - self.comboBox_ValueType.currentIndex(), - self.checkBox_CaseSensitive.isChecked(), - self.checkBox_Regex.isChecked()) + item_list = debugcore.search_referenced_strings( + self.lineEdit_Regex.text(), + self.comboBox_ValueType.currentIndex(), + self.checkBox_CaseSensitive.isChecked(), + self.checkBox_Regex.isChecked(), + ) if item_list is None: - QMessageBox.information(self, "Error", - "An exception occurred while trying to compile the given regex\n") + QMessageBox.information(self, tr.ERROR, tr.INVALID_REGEX) return self.tableWidget_References.setSortingEnabled(False) self.tableWidget_References.setRowCount(0) @@ -4925,10 +5959,10 @@ def refresh_table(self): for row, item in enumerate(item_list): self.tableWidget_References.setItem(row, REF_STR_ADDR_COL, QTableWidgetItem(self.pad_hex(item[0]))) table_widget_item = QTableWidgetItem() - table_widget_item.setData(Qt.EditRole, item[1]) + table_widget_item.setData(Qt.ItemDataRole.EditRole, item[1]) self.tableWidget_References.setItem(row, REF_STR_COUNT_COL, table_widget_item) table_widget_item = QTableWidgetItem() - table_widget_item.setData(Qt.EditRole, item[2]) + table_widget_item.setData(Qt.ItemDataRole.EditRole, item[2]) self.tableWidget_References.setItem(row, REF_STR_VAL_COL, table_widget_item) self.tableWidget_References.setSortingEnabled(True) @@ -4936,12 +5970,12 @@ def tableWidget_References_current_changed(self, QModelIndex_current): if QModelIndex_current.row() < 0: return self.listWidget_Referrers.clear() - str_dict = GDB_Engine.get_dissect_code_data(True, False, False)[0] + str_dict = debugcore.get_dissect_code_data(True, False, False)[0] addr = self.tableWidget_References.item(QModelIndex_current.row(), REF_STR_ADDR_COL).text() referrers = str_dict[hex(int(addr, 16))] addrs = [hex(address) for address in referrers] - self.listWidget_Referrers.addItems([self.pad_hex(item.all) for item in GDB_Engine.examine_expressions(addrs)]) - self.listWidget_Referrers.sortItems(Qt.AscendingOrder) + self.listWidget_Referrers.addItems([self.pad_hex(item.all) for item in debugcore.examine_expressions(addrs)]) + self.listWidget_Referrers.sortItems(Qt.SortOrder.AscendingOrder) str_dict.close() def tableWidget_References_item_double_clicked(self, index): @@ -4950,25 +5984,25 @@ def tableWidget_References_item_double_clicked(self, index): self.parent().hex_dump_address(int(address, 16)) def listWidget_Referrers_item_double_clicked(self, item): - self.parent().disassemble_expression(SysUtils.extract_address(item.text()), append_to_travel_history=True) + self.parent().disassemble_expression(utils.extract_address(item.text()), append_history=True) def tableWidget_References_context_menu_event(self, event): def copy_to_clipboard(row, column): app.clipboard().setText(self.tableWidget_References.item(row, column).text()) - selected_row = GuiUtils.get_current_row(self.tableWidget_References) + selected_row = guiutils.get_current_row(self.tableWidget_References) menu = QMenu() - copy_address = menu.addAction("Copy Address") - copy_value = menu.addAction("Copy Value") + copy_address = menu.addAction(tr.COPY_ADDRESS) + copy_value = menu.addAction(tr.COPY_VALUE) if selected_row == -1: - GuiUtils.delete_menu_entries(menu, [copy_address, copy_value]) + guiutils.delete_menu_entries(menu, [copy_address, copy_value]) font_size = self.tableWidget_References.font().pointSize() menu.setStyleSheet("font-size: " + str(font_size) + "pt;") - action = menu.exec_(event.globalPos()) + action = menu.exec(event.globalPos()) actions = { copy_address: lambda: copy_to_clipboard(selected_row, REF_STR_ADDR_COL), - copy_value: lambda: copy_to_clipboard(selected_row, REF_STR_VAL_COL) + copy_value: lambda: copy_to_clipboard(selected_row, REF_STR_VAL_COL), } try: actions[action]() @@ -4979,53 +6013,45 @@ def listWidget_Referrers_context_menu_event(self, event): def copy_to_clipboard(row): app.clipboard().setText(self.listWidget_Referrers.item(row).text()) - selected_row = GuiUtils.get_current_row(self.listWidget_Referrers) + selected_row = guiutils.get_current_row(self.listWidget_Referrers) menu = QMenu() - copy_address = menu.addAction("Copy Address") + copy_address = menu.addAction(tr.COPY_ADDRESS) if selected_row == -1: - GuiUtils.delete_menu_entries(menu, [copy_address]) + guiutils.delete_menu_entries(menu, [copy_address]) font_size = self.listWidget_Referrers.font().pointSize() menu.setStyleSheet("font-size: " + str(font_size) + "pt;") - action = menu.exec_(event.globalPos()) - actions = { - copy_address: lambda: copy_to_clipboard(selected_row) - } + action = menu.exec(event.globalPos()) + actions = {copy_address: lambda: copy_to_clipboard(selected_row)} try: actions[action]() except KeyError: pass - def closeEvent(self, QCloseEvent): - global instances - instances.remove(self) - class ReferencedCallsWidgetForm(QWidget, ReferencedCallsWidget): - def __init__(self, parent=None): - super().__init__() + def __init__(self, parent): + super().__init__(parent) self.setupUi(self) - self.parent = lambda: parent - global instances - instances.append(self) - GuiUtils.center_to_parent(self) - self.setWindowFlags(Qt.Window) + self.setWindowFlags(Qt.WindowType.Window) self.tableWidget_References.setColumnWidth(REF_CALL_ADDR_COL, 480) self.splitter.setStretchFactor(0, 1) self.listWidget_Referrers.resize(400, self.listWidget_Referrers.height()) - self.hex_len = 16 if GDB_Engine.inferior_arch == type_defs.INFERIOR_ARCH.ARCH_64 else 8 - str_dict, jmp_dict, call_dict = GDB_Engine.get_dissect_code_data() - if len(str_dict) == 0 and len(jmp_dict) == 0 and len(call_dict) == 0: - confirm_dialog = InputDialogForm(item_list=[("You need to dissect code first\nProceed?",)]) - if confirm_dialog.exec_(): - dissect_code_dialog = DissectCodeDialogForm() - dissect_code_dialog.scan_finished_signal.connect(dissect_code_dialog.accept) - dissect_code_dialog.exec_() + guiutils.center_to_parent(self) + self.hex_len = 16 if debugcore.inferior_arch == typedefs.INFERIOR_ARCH.ARCH_64 else 8 + str_dict, jmp_dict, call_dict = debugcore.get_dissect_code_data() + str_dict_len, jmp_dict_len, call_dict_len = len(str_dict), len(jmp_dict), len(call_dict) str_dict.close() jmp_dict.close() call_dict.close() + if str_dict_len == 0 and jmp_dict_len == 0 and call_dict_len == 0: + confirm_dialog = InputDialogForm(self, [(tr.DISSECT_CODE,)]) + if confirm_dialog.exec(): + dissect_code_dialog = DissectCodeDialogForm(self) + dissect_code_dialog.scan_finished_signal.connect(dissect_code_dialog.accept) + dissect_code_dialog.exec() self.refresh_table() - self.tableWidget_References.sortByColumn(REF_CALL_ADDR_COL, Qt.AscendingOrder) + self.tableWidget_References.sortByColumn(REF_CALL_ADDR_COL, Qt.SortOrder.AscendingOrder) self.tableWidget_References.selectionModel().currentChanged.connect(self.tableWidget_References_current_changed) self.listWidget_Referrers.itemDoubleClicked.connect(self.listWidget_Referrers_item_double_clicked) self.tableWidget_References.itemDoubleClicked.connect(self.tableWidget_References_item_double_clicked) @@ -5041,15 +6067,14 @@ def pad_hex(self, hex_str): self_len = 0 else: self_len = len(hex_str) - index - return '0x' + hex_str[2:].zfill(self.hex_len + self_len) + return "0x" + hex_str[2:].zfill(self.hex_len + self_len) def refresh_table(self): - item_list = GDB_Engine.search_referenced_calls(self.lineEdit_Regex.text(), - self.checkBox_CaseSensitive.isChecked(), - self.checkBox_Regex.isChecked()) + item_list = debugcore.search_referenced_calls( + self.lineEdit_Regex.text(), self.checkBox_CaseSensitive.isChecked(), self.checkBox_Regex.isChecked() + ) if item_list is None: - QMessageBox.information(self, "Error", - "An exception occurred while trying to compile the given regex\n") + QMessageBox.information(self, tr.ERROR, tr.INVALID_REGEX) return self.tableWidget_References.setSortingEnabled(False) self.tableWidget_References.setRowCount(0) @@ -5057,7 +6082,7 @@ def refresh_table(self): for row, item in enumerate(item_list): self.tableWidget_References.setItem(row, REF_CALL_ADDR_COL, QTableWidgetItem(self.pad_hex(item[0]))) table_widget_item = QTableWidgetItem() - table_widget_item.setData(Qt.EditRole, item[1]) + table_widget_item.setData(Qt.ItemDataRole.EditRole, item[1]) self.tableWidget_References.setItem(row, REF_CALL_COUNT_COL, table_widget_item) self.tableWidget_References.setSortingEnabled(True) @@ -5065,38 +6090,36 @@ def tableWidget_References_current_changed(self, QModelIndex_current): if QModelIndex_current.row() < 0: return self.listWidget_Referrers.clear() - call_dict = GDB_Engine.get_dissect_code_data(False, False, True)[0] + call_dict = debugcore.get_dissect_code_data(False, False, True)[0] addr = self.tableWidget_References.item(QModelIndex_current.row(), REF_CALL_ADDR_COL).text() - referrers = call_dict[hex(int(SysUtils.extract_address(addr), 16))] + referrers = call_dict[hex(int(utils.extract_address(addr), 16))] addrs = [hex(address) for address in referrers] - self.listWidget_Referrers.addItems([self.pad_hex(item.all) for item in GDB_Engine.examine_expressions(addrs)]) - self.listWidget_Referrers.sortItems(Qt.AscendingOrder) + self.listWidget_Referrers.addItems([self.pad_hex(item.all) for item in debugcore.examine_expressions(addrs)]) + self.listWidget_Referrers.sortItems(Qt.SortOrder.AscendingOrder) call_dict.close() def tableWidget_References_item_double_clicked(self, index): row = index.row() address = self.tableWidget_References.item(row, REF_CALL_ADDR_COL).text() - self.parent().disassemble_expression(SysUtils.extract_address(address), append_to_travel_history=True) + self.parent().disassemble_expression(utils.extract_address(address), append_history=True) def listWidget_Referrers_item_double_clicked(self, item): - self.parent().disassemble_expression(SysUtils.extract_address(item.text()), append_to_travel_history=True) + self.parent().disassemble_expression(utils.extract_address(item.text()), append_history=True) def tableWidget_References_context_menu_event(self, event): def copy_to_clipboard(row, column): app.clipboard().setText(self.tableWidget_References.item(row, column).text()) - selected_row = GuiUtils.get_current_row(self.tableWidget_References) + selected_row = guiutils.get_current_row(self.tableWidget_References) menu = QMenu() - copy_address = menu.addAction("Copy Address") + copy_address = menu.addAction(tr.COPY_ADDRESS) if selected_row == -1: - GuiUtils.delete_menu_entries(menu, [copy_address]) + guiutils.delete_menu_entries(menu, [copy_address]) font_size = self.tableWidget_References.font().pointSize() menu.setStyleSheet("font-size: " + str(font_size) + "pt;") - action = menu.exec_(event.globalPos()) - actions = { - copy_address: lambda: copy_to_clipboard(selected_row, REF_CALL_ADDR_COL) - } + action = menu.exec(event.globalPos()) + actions = {copy_address: lambda: copy_to_clipboard(selected_row, REF_CALL_ADDR_COL)} try: actions[action]() except KeyError: @@ -5106,50 +6129,41 @@ def listWidget_Referrers_context_menu_event(self, event): def copy_to_clipboard(row): app.clipboard().setText(self.listWidget_Referrers.item(row).text()) - selected_row = GuiUtils.get_current_row(self.listWidget_Referrers) + selected_row = guiutils.get_current_row(self.listWidget_Referrers) menu = QMenu() - copy_address = menu.addAction("Copy Address") + copy_address = menu.addAction(tr.COPY_ADDRESS) if selected_row == -1: - GuiUtils.delete_menu_entries(menu, [copy_address]) + guiutils.delete_menu_entries(menu, [copy_address]) font_size = self.listWidget_Referrers.font().pointSize() menu.setStyleSheet("font-size: " + str(font_size) + "pt;") - action = menu.exec_(event.globalPos()) - actions = { - copy_address: lambda: copy_to_clipboard(selected_row) - } + action = menu.exec(event.globalPos()) + actions = {copy_address: lambda: copy_to_clipboard(selected_row)} try: actions[action]() except KeyError: pass - def closeEvent(self, QCloseEvent): - global instances - instances.remove(self) - class ExamineReferrersWidgetForm(QWidget, ExamineReferrersWidget): - def __init__(self, int_address, parent=None): - super().__init__() + def __init__(self, parent, int_address): + super().__init__(parent) self.setupUi(self) - self.parent = lambda: parent - global instances - instances.append(self) - GuiUtils.center_to_parent(self) - self.setWindowFlags(Qt.Window) + self.setWindowFlags(Qt.WindowType.Window) self.splitter.setStretchFactor(0, 1) self.textBrowser_DisasInfo.resize(600, self.textBrowser_DisasInfo.height()) self.referenced_hex = hex(int_address) - self.hex_len = 16 if GDB_Engine.inferior_arch == type_defs.INFERIOR_ARCH.ARCH_64 else 8 + self.hex_len = 16 if debugcore.inferior_arch == typedefs.INFERIOR_ARCH.ARCH_64 else 8 self.collect_referrer_data() self.refresh_table() - self.listWidget_Referrers.sortItems(Qt.AscendingOrder) + self.listWidget_Referrers.sortItems(Qt.SortOrder.AscendingOrder) self.listWidget_Referrers.selectionModel().currentChanged.connect(self.listWidget_Referrers_current_changed) self.listWidget_Referrers.itemDoubleClicked.connect(self.listWidget_Referrers_item_double_clicked) self.listWidget_Referrers.contextMenuEvent = self.listWidget_Referrers_context_menu_event self.pushButton_Search.clicked.connect(self.refresh_table) self.shortcut_search = QShortcut(QKeySequence("Return"), self) self.shortcut_search.activated.connect(self.refresh_table) + guiutils.center_to_parent(self) def pad_hex(self, hex_str): index = hex_str.find(" ") @@ -5157,10 +6171,10 @@ def pad_hex(self, hex_str): self_len = 0 else: self_len = len(hex_str) - index - return '0x' + hex_str[2:].zfill(self.hex_len + self_len) + return "0x" + hex_str[2:].zfill(self.hex_len + self_len) def collect_referrer_data(self): - jmp_dict, call_dict = GDB_Engine.get_dissect_code_data(False, True, True) + jmp_dict, call_dict = debugcore.get_dissect_code_data(False, True, True) self.referrer_data = [] try: jmp_referrers = jmp_dict[self.referenced_hex] @@ -5168,14 +6182,14 @@ def collect_referrer_data(self): pass else: jmp_referrers = [hex(item) for item in jmp_referrers] - self.referrer_data.extend([item.all for item in GDB_Engine.examine_expressions(jmp_referrers)]) + self.referrer_data.extend([item.all for item in debugcore.examine_expressions(jmp_referrers)]) try: call_referrers = call_dict[self.referenced_hex] except KeyError: pass else: call_referrers = [hex(item) for item in call_referrers] - self.referrer_data.extend([item.all for item in GDB_Engine.examine_expressions(call_referrers)]) + self.referrer_data.extend([item.all for item in debugcore.examine_expressions(call_referrers)]) jmp_dict.close() call_dict.close() @@ -5190,8 +6204,7 @@ def refresh_table(self): else: regex = re.compile(searched_str, re.IGNORECASE) except: - QMessageBox.information(self, "Error", - "An exception occurred while trying to compile the given regex\n") + QMessageBox.information(self, tr.ERROR, tr.INVALID_REGEX) return self.listWidget_Referrers.setSortingEnabled(False) self.listWidget_Referrers.clear() @@ -5208,58 +6221,303 @@ def refresh_table(self): continue self.listWidget_Referrers.addItem(item) self.listWidget_Referrers.setSortingEnabled(True) - self.listWidget_Referrers.sortItems(Qt.AscendingOrder) + self.listWidget_Referrers.sortItems(Qt.SortOrder.AscendingOrder) def listWidget_Referrers_current_changed(self, QModelIndex_current): if QModelIndex_current.row() < 0: return self.textBrowser_DisasInfo.clear() - disas_data = GDB_Engine.disassemble( - SysUtils.extract_address(self.listWidget_Referrers.item(QModelIndex_current.row()).text()), "+200") - for item in disas_data: - self.textBrowser_DisasInfo.append(item[0] + item[2]) + disas_data = debugcore.disassemble( + utils.extract_address(self.listWidget_Referrers.item(QModelIndex_current.row()).text()), "+200" + ) + for address_info, _, opcode in disas_data: + self.textBrowser_DisasInfo.append(address_info + opcode) cursor = self.textBrowser_DisasInfo.textCursor() - cursor.movePosition(QTextCursor.Start) + cursor.movePosition(QTextCursor.MoveOperation.Start) self.textBrowser_DisasInfo.setTextCursor(cursor) self.textBrowser_DisasInfo.ensureCursorVisible() def listWidget_Referrers_item_double_clicked(self, item): - self.parent().disassemble_expression(SysUtils.extract_address(item.text()), append_to_travel_history=True) + self.parent().disassemble_expression(utils.extract_address(item.text()), append_history=True) def listWidget_Referrers_context_menu_event(self, event): def copy_to_clipboard(row): app.clipboard().setText(self.listWidget_Referrers.item(row).text()) - selected_row = GuiUtils.get_current_row(self.listWidget_Referrers) + selected_row = guiutils.get_current_row(self.listWidget_Referrers) menu = QMenu() - copy_address = menu.addAction("Copy Address") + copy_address = menu.addAction(tr.COPY_ADDRESS) if selected_row == -1: - GuiUtils.delete_menu_entries(menu, [copy_address]) + guiutils.delete_menu_entries(menu, [copy_address]) font_size = self.listWidget_Referrers.font().pointSize() menu.setStyleSheet("font-size: " + str(font_size) + "pt;") - action = menu.exec_(event.globalPos()) - actions = { - copy_address: lambda: copy_to_clipboard(selected_row) - } + action = menu.exec(event.globalPos()) + actions = {copy_address: lambda: copy_to_clipboard(selected_row)} try: actions[action]() except KeyError: pass - def closeEvent(self, QCloseEvent): - global instances - instances.remove(self) + +class PointerScanSearchDialogForm(QDialog, PointerScanSearchDialog): + def __init__(self, parent, address) -> None: + super().__init__(parent) + self.setupUi(self) + guiutils.center_to_parent(self) + self.lineEdit_Address.setText(address) + self.lineEdit_Path.setText(os.getcwd() + f"/{utils.get_process_name(debugcore.currentpid)}.scandata") + self.pushButton_PathBrowse.clicked.connect(self.pushButton_PathBrowse_clicked) + self.scan_button: QPushButton | None = self.buttonBox.addButton(tr.SCAN, QDialogButtonBox.ButtonRole.ActionRole) + if self.scan_button: + self.scan_button.clicked.connect(self.scan_button_clicked) + self.ptrscan_thread: InterruptableWorker | None = None + + def pushButton_PathBrowse_clicked(self) -> None: + file_path, _ = QFileDialog.getSaveFileName(self, tr.SELECT_POINTER_MAP, None, tr.FILE_TYPES_SCANDATA) + if file_path != "": + file_path = utils.append_file_extension(file_path, "scandata") + self.lineEdit_Path.setText(file_path) + + def reject(self) -> None: + if self.ptrscan_thread: + self.ptrscan_thread.stop() + return super().reject() + + def scan_button_clicked(self) -> None: + if debugcore.currentpid == -1 or self.scan_button == None: + return + self.scan_button.setText(tr.SCANNING) + self.scan_button.setEnabled(False) + self.pushButton_PathBrowse.setEnabled(False) + params: FFIParam = FFIParam() + try: + addr_val = int(self.lineEdit_Address.text(), 16) + except ValueError: + addr_val = 0 + params.addr(addr_val) + params.depth(self.spinBox_Depth.value()) + params.srange(FFIRange(self.spinBox_ScanRangeStart.value(), self.spinBox_ScanRangeEnd.value())) + lrange_start: int = self.spinBox_ScanLRangeStart.value() + lrange_end: int = self.spinBox_ScanLRangeEnd.value() + if lrange_start == 0 and lrange_end == 0: + lrange_val = None + else: + lrange_val = FFIRange(lrange_start, lrange_end) + params.lrange(lrange_val) + params.node(utils.return_optional_int(self.spinBox_Node.value())) + try: + last_val = int(self.lineEdit_Last.text(), 16) + except ValueError: + last_val = None + params.last(last_val) + params.max(utils.return_optional_int(self.spinBox_Max.value())) + params.cycle(self.checkBox_Cycle.isChecked()) + ptrscan.set_modules(ptrscan.list_modules_pince()) # TODO: maybe cache this and let user refresh with a button + ptrscan.create_pointer_map() # TODO: maybe cache this and let user refresh with a button + ptrmap_file_path = self.lineEdit_Path.text() + if os.path.isfile(ptrmap_file_path): + os.remove(ptrmap_file_path) + self.ptrscan_thread = InterruptableWorker(ptrscan.scan_pointer_chain, params, ptrmap_file_path) + self.ptrscan_thread.signals.finished.connect(self.ptrscan_callback) + self.ptrscan_thread.start() + + def ptrscan_callback(self) -> None: + self.accept() + + +class PointerScanFilterDialogForm(QDialog, PointerScanFilterDialog): + def __init__(self, parent) -> None: + super().__init__(parent) + self.setupUi(self) + guiutils.center_to_parent(self) + self.pushButton_File1Browse.clicked.connect(self.pushButton_File1Browse_clicked) + self.pushButton_File2Browse.clicked.connect(self.pushButton_File2Browse_clicked) + self.filter_button: QPushButton | None = self.buttonBox.addButton( + tr.FILTER, QDialogButtonBox.ButtonRole.ActionRole + ) + if self.filter_button: + self.filter_button.clicked.connect(self.filter_button_clicked) + self.filter_button.setEnabled(False) + self.filter_result: list[str] | None = None + + def browse_scandata_file(self, file_path_field: QLineEdit) -> None: + file_path, _ = QFileDialog.getOpenFileName(self, tr.SELECT_POINTER_MAP, None, tr.FILE_TYPES_SCANDATA) + if file_path != "": + file_path_field.setText(file_path) + self.check_filterable_state() + + def check_filterable_state(self) -> None: + if self.lineEdit_File1Path.text() != "" and self.lineEdit_File2Path.text() != "" and self.filter_button: + self.filter_button.setEnabled(True) + + def pushButton_File1Browse_clicked(self) -> None: + self.browse_scandata_file(self.lineEdit_File1Path) + + def pushButton_File2Browse_clicked(self) -> None: + self.browse_scandata_file(self.lineEdit_File2Path) + + def filter_button_clicked(self) -> None: + if self.lineEdit_File1Path.text() == "" or self.lineEdit_File2Path.text() == "" or self.filter_button == None: + return + self.filter_button.setEnabled(False) + self.filter_button.setText(tr.FILTERING) + lines: list[str] + with open(self.lineEdit_File1Path.text()) as file: + lines = file.read().split(os.linesep) + with open(self.lineEdit_File2Path.text()) as file: + lines.extend(file.read().split(os.linesep)) + counts = collections.Counter(lines) + self.filter_result = list(set([line for line in lines if counts[line] > 1 and line != ""])) + self.accept() + + def get_filter_result(self) -> list[str] | None: + return self.filter_result + + +class PointerScanWindowForm(QMainWindow, PointerScanWindow): + def __init__(self, parent) -> None: + super().__init__(parent) + self.setupUi(self) + self.tableWidget_ScanResult.hide() + process_signals.attach.connect(self.on_process_changed) + process_signals.exit.connect(self.on_process_changed) + self.pushButton_Clear.pressed.connect(self.pushButton_Clear_pressed) + self.pushButton_Sort.pressed.connect(self.pushButton_Sort_pressed) + self.actionOpen.triggered.connect(self.actionOpen_triggered) + self.actionSaveAs.triggered.connect(self.actionSaveAs_triggered) + self.actionScan.triggered.connect(self.scan_triggered) + self.actionFilter.triggered.connect(self.filter_triggered) + if debugcore.currentpid == -1: + self.actionScan.setEnabled(False) + guiutils.center_to_parent(self) + + def on_process_changed(self) -> None: + val: bool = False if debugcore.currentpid == -1 else True + self.actionScan.setEnabled(val) + + def pushButton_Clear_pressed(self) -> None: + self.textEdit.clear() + + def pushButton_Sort_pressed(self) -> None: + text: str = self.textEdit.toPlainText() + if text == "": + return + text_list: list[str] = text.split(os.linesep) + # Sometimes files will have ending newlines. + # We want to get rid of them otherwise they'll be at top. + if text_list[-1] == "": + del text_list[-1] + text_list.sort() + self.textEdit.setText(os.linesep.join(text_list)) + + def actionOpen_triggered(self) -> None: + file_path, _ = QFileDialog.getOpenFileName(self, tr.SELECT_POINTER_MAP, None, tr.FILE_TYPES_SCANDATA) + if file_path != "": + self.textEdit.clear() + with open(file_path) as file: + self.textEdit.setText(file.read()) + + def actionSaveAs_triggered(self) -> None: + file_path, _ = QFileDialog.getSaveFileName(self, tr.SELECT_POINTER_MAP, None, tr.FILE_TYPES_SCANDATA) + if file_path != "": + file_path = utils.append_file_extension(file_path, "scandata") + with open(file_path, "w") as file: + file.write(self.textEdit.toPlainText()) + + def scan_triggered(self) -> None: + dialog = PointerScanSearchDialogForm(self, "0x0") + dialog.exec() + + def filter_triggered(self) -> None: + dialog = PointerScanFilterDialogForm(self) + if dialog.exec(): + filter_result: list[str] | None = dialog.get_filter_result() + if filter_result == None: + return + self.textEdit.clear() + self.textEdit.setText(os.linesep.join(filter_result)) + + +class ScanRegionsDialogForm(QDialog, ScanRegionsDialog): + def __init__(self, parent) -> None: + super().__init__(parent) + self.setupUi(self) + regions_text = scanmem.send_command("lregions", True).decode("utf-8") + regex = re.compile(r"\[\s*(\d+)\] (\w+),\s+(\d+) bytes,\s+(\w+),\s+(\w+),\s+([rwx-]+),\s+(.+)") + data = regex.findall(regions_text) + self.tableWidget_Regions.setRowCount(len(data)) + for row, (region_id, start_address, size, region_type, load_address, perms, file) in enumerate(data): + id_item = QTableWidgetItem(region_id) + id_item.setCheckState(Qt.CheckState.Unchecked) + self.tableWidget_Regions.setItem(row, 0, id_item) + self.tableWidget_Regions.setItem(row, 1, QTableWidgetItem(start_address)) + self.tableWidget_Regions.setItem(row, 2, QTableWidgetItem(size)) + self.tableWidget_Regions.setItem(row, 3, QTableWidgetItem(region_type)) + self.tableWidget_Regions.setItem(row, 4, QTableWidgetItem(load_address)) + self.tableWidget_Regions.setItem(row, 5, QTableWidgetItem(perms)) + self.tableWidget_Regions.setItem(row, 6, QTableWidgetItem(file)) + self.tableWidget_Regions.resizeColumnsToContents() + guiutils.center_to_parent(self) + self.tableWidget_Regions.mousePressEvent_original = self.tableWidget_Regions.mousePressEvent + self.tableWidget_Regions.mousePressEvent = self.tableWidget_Regions_mouse_press_event + self.tableWidget_Regions.mouseReleaseEvent_original = self.tableWidget_Regions.mouseReleaseEvent + self.tableWidget_Regions.mouseReleaseEvent = self.tableWidget_Regions_mouse_release_event + self.tableWidget_Regions.keyPressEvent_original = self.tableWidget_Regions.keyPressEvent + self.tableWidget_Regions.keyPressEvent = self.tableWidget_Regions_key_press_event + self.pushButton_Invert.clicked.connect(self.invert_selection) + + def tableWidget_Regions_mouse_press_event(self, event: QMouseEvent) -> None: + self.tableWidget_Regions.mousePressEvent_original(event) + item = self.tableWidget_Regions.itemAt(event.pos()) + if item and item.column() == 0: + row = item.row() + self.tableWidget_Regions.selectRow(row) + + def tableWidget_Regions_mouse_release_event(self, event: QMouseEvent) -> None: + self.tableWidget_Regions.mouseReleaseEvent_original(event) + item = self.tableWidget_Regions.itemAt(event.pos()) + if item and item.column() == 0: + new_state = item.checkState() + for selected_item in self.tableWidget_Regions.selectedItems(): + if selected_item.column() == 0: + selected_item.setCheckState(new_state) + + def tableWidget_Regions_key_press_event(self, event: QKeyEvent) -> None: + if event.key() == Qt.Key.Key_Space: + current_row = self.tableWidget_Regions.currentRow() + if current_row != -1: + cur_state = self.tableWidget_Regions.item(current_row, 0).checkState() + new_state = Qt.CheckState.Unchecked if cur_state == Qt.CheckState.Checked else Qt.CheckState.Checked + for selected_item in self.tableWidget_Regions.selectedItems(): + if selected_item.column() == 0: + selected_item.setCheckState(new_state) + else: + self.tableWidget_Regions.keyPressEvent_original(event) + + def invert_selection(self) -> None: + for row in range(self.tableWidget_Regions.rowCount()): + item = self.tableWidget_Regions.item(row, 0) + cur_state = item.checkState() + new_state = Qt.CheckState.Unchecked if cur_state == Qt.CheckState.Checked else Qt.CheckState.Checked + item.setCheckState(new_state) + + def accept(self) -> None: + for row in range(self.tableWidget_Regions.rowCount()): + item = self.tableWidget_Regions.item(row, 0) + if item.checkState() == Qt.CheckState.Checked: + region_id = int(item.text()) + scanmem.send_command(f"dregion {region_id}") + return super().accept() -def exitHandler(): - global Exiting - Exiting = 1 +def handle_exit(): + global exiting + exiting = 1 if __name__ == "__main__": - app = QApplication(sys.argv) - app.aboutToQuit.connect(exitHandler) + app.aboutToQuit.connect(handle_exit) window = MainForm() window.show() - sys.exit(app.exec_()) + sys.exit(app.exec()) diff --git a/PINCE.sh b/PINCE.sh index e89fe068..c6f4cdde 100755 --- a/PINCE.sh +++ b/PINCE.sh @@ -16,11 +16,19 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ' -# Change this bullcrap when polkit is implemented -OS=$(lsb_release -si) -# Get rid of gksudo when Debian 8 support drops or polkit gets implemented -if [ $OS = "Debian" ] && [ -x "$(command -v gksudo)" ]; then - gksudo python3 PINCE.py -else - sudo -E python3 PINCE.py +if [ "$(id -u)" = "0" ]; then + echo "Please do not run this script as root!" + exit 1 fi + +SCRIPTDIR=$(cd -- "$(dirname -- "$0")" && pwd -P) +if [ ! -d "${SCRIPTDIR}/.venv/PINCE" ]; then + echo "Please run \"sh install.sh\" first!" + exit 1 +fi +. ${SCRIPTDIR}/.venv/PINCE/bin/activate + +# Preserve env vars to keep settings like theme preferences. +# Debian/Ubuntu does not preserve PATH through sudo even with -E for security reasons +# so we need to force PATH preservation with venv activated user's PATH. +sudo -E --preserve-env=PATH PYTHONDONTWRITEBYTECODE=1 ${SCRIPTDIR}/.venv/PINCE/bin/python3 ${SCRIPTDIR}/PINCE.py diff --git a/README.md b/README.md index 6706fb20..37cf99fc 100644 --- a/README.md +++ b/README.md @@ -3,168 +3,87 @@ TODO: Include build status with the title when test coverage increases and Travis is maintained [![Build Status](https://travis-ci.org/korcankaraokcu/PINCE.svg?branch=master)](https://travis-ci.org/korcankaraokcu/PINCE) --> -PINCE is a front-end/reverse engineering tool for the GNU Project Debugger (GDB), focused on games. However, it can be used for any reverse-engineering related stuff. PINCE is an abbreviation for "PINCE is not Cheat Engine". PINCE is in development right now, read [Features](#features) part of the project to see what is done and [Roadmap](#current-roadmap) part to see what is currently planned. Also, please read [Wiki Page](https://github.com/korcankaraokcu/PINCE/wiki) of the project to understand how PINCE works. +PINCE is a front-end/reverse engineering tool for the GNU Project Debugger (GDB), focused on games. However, it can be used for any reverse-engineering related stuff. PINCE is an abbreviation for "PINCE is not Cheat Engine". PINCE is in development right now, read [Features](#features) part of the project to see what is done and [Roadmap](CONTRIBUTING.md#roadmap) part to see what is currently planned. Also, please read [Wiki Page](https://github.com/korcankaraokcu/PINCE/wiki) of the project to understand how PINCE works. -### [Feel free to join our discord server!](https://discord.gg/KCNDp9m) +### [Feel free to join our discord server!](https://discord.gg/jVt3BzTSpz) *Disclaimer: Do not trust to any source other than [Trusted Sources](#trusted-sources) that claims to have the source code or package for PINCE and remember to report them **immediately*** *Disclaimer: **YOU** are responsible for your actions. PINCE does **NOT** take any responsibility for the damage caused by the users* -Pre-release screenshots: -![](media/screenshots/pince.png) -![](media/screenshots/pince1.png) -![](media/screenshots/pince2.png) -![](media/screenshots/pince3.png) -![](media/screenshots/pince4.png) -![](media/screenshots/pince5.png) -![](media/screenshots/pince6.png) -![](media/screenshots/pince7.png) -![](media/screenshots/pince8.png) -![](media/screenshots/pince9.png) +![pince1](https://github.com/user-attachments/assets/7344c33d-3ea7-408a-8a5b-793f0b4c78ec) +![pince2](https://github.com/user-attachments/assets/271cbbe7-b588-48e0-b939-f59e82f36812) +![pince3](https://github.com/user-attachments/assets/479b4f56-7b62-4100-a3d9-3f9cd11ff5b8) +![pince4](https://github.com/user-attachments/assets/08d8a6fe-6960-481b-9b55-aa550f860dc7) + # Features -- **Memory searching:** PINCE uses [libscanmem](https://github.com/scanmem/scanmem) to search the memory efficiently **[Done]** -- **Variable Inspection&Modification** **[Done/Basic]** - * **CheatEngine-like value type support:** Byte to 8 Bytes, Float, Double, Strings(including utf-8, utf-16, utf-32 and zero-terminate strings), Array of Bytes **[Done]** - * **Symbol Recognition:** See [here](https://github.com/korcankaraokcu/PINCE/wiki/About-GDB-Expressions) **[Done]** - * **Automatic Variable Allocation:** See [here](https://github.com/korcankaraokcu/PINCE/wiki/About-GDB-Expressions) **[Done]** - * **Dynamic Address Table:** Supports drag&drop, recursive copy&pasting&inserting and many more **[Done]** - * **Smart casting:** PINCE lets you modify multiple different-type values together as long as the input is parsable. All parsing/memory errors are directed to the terminal **[Done]** - * **Continuous Address Table Update:** You can adjust update timer or cancel updating by modifying settings **[Done]** - * **Variable Locking:** PINCE lets you freeze(constantly write a value to memory cell) variables **[Done]** -- **Memory View** **[Done/Basic]** - * **Infinite Scrolling:** PINCE automatically disassembles the next available instruction(s) on mouse wheel/scrollbar move. Instruction count can be changed from settings. Hex View also supports this feature **[Done]** - * **Dissect Code:** You can dissect desired memory regions to find referenced calls, jumps and strings. Disassemble screen will automatically handle the referenced data and show you if there's a referenced address in the current dissasemble view. It can be used from Tools->Dissect Code in the MemoryView window. Using its hotkey instead in the MemoryView window automatically dissects the currently viewed region. You can separately view referenced calls and strings after the search from View->Referenced Calls/Strings. *Note: If you decide to uncheck 'Discard invalid strings' before the search, PINCE will try to search for regular pointers as well* **[Done]** - * **Bookmarking:** Bookmark menu is dynamically created when right clicked in the disassemble screen. So unlike Cheat Engine, PINCE lets you set unlimited number of bookmarks. List of bookmarks can also be viewed from View->Bookmarks in the MemoryView window. Commenting on an address automatically bookmarks it. **[Done]** - * **Modify on the fly:** PINCE lets you modify registers on the fly. Unlike CE, you can also change XMM and FPU registers. Check [GDB expressions in the Wiki page](https://github.com/korcankaraokcu/PINCE/wiki/About-GDB-Expressions) for additional information **[Done]** - * **Opcode Search:** You can search opcodes with python regular expressions. To use this feature, click Tools->Search Opcode in the MemoryView window. **[Done]** -- **Debugging** **[Done/Basic]** +- **Memory scanning:** PINCE uses a specialized fork of [libscanmem](https://github.com/brkzlr/scanmem-PINCE) to scan the memory efficiently +- **Pointer scanning:** PINCE uses [PointerScanner-X](https://github.com/kekeimiku/PointerSearcher-X/) to scan pointers efficiently +- **Background Execution:** PINCE uses background execution by default, allowing users to run GDB commands while process is running +- **Variable Inspection&Modification** + * **CheatEngine-like value type support:** Currently supports all types of CE and scanmem along with extended strings(utf-8, utf-16, utf-32) + * **Symbol Recognition:** See [here](https://github.com/korcankaraokcu/PINCE/wiki/GDB-Expressions) + * **Automatic Variable Allocation:** See [here](https://github.com/korcankaraokcu/PINCE/wiki/GDB-Expressions) + * **Dynamic Address Table:** Supports drag&drop, recursive copy&pasting&inserting and many more + * **Smart casting:** PINCE lets you modify multiple different-type values together as long as the input is parsable. All parsing/memory errors are directed to the terminal + * **Variable Locking:** PINCE lets you freeze(constantly write a value to memory cell) variables +- **Memory View** + * **Assembler:** PINCE uses keystone engine to assemble code on the fly + * **Dissect Code:** You can dissect desired memory regions to find referenced calls, jumps and strings. Disassemble screen will automatically handle the referenced data and show you if there's a referenced address in the current dissasemble view. It can be used from Tools->Dissect Code in the MemoryView window. Using its hotkey instead in the MemoryView window automatically dissects the currently viewed region. You can separately view referenced calls and strings after the search from View->Referenced Calls/Strings. *Note: If you decide to uncheck 'Discard invalid strings' before the search, PINCE will try to search for regular pointers as well* + * **Bookmarking:** Bookmark menu is dynamically created when right clicked in the disassemble screen. So unlike Cheat Engine, PINCE lets you set unlimited number of bookmarks. List of bookmarks can also be viewed from View->Bookmarks in the MemoryView window. Commenting on an address automatically bookmarks it + * **Modify on the fly:** PINCE lets you modify registers on the fly. Check [GDB expressions in the Wiki page](https://github.com/korcankaraokcu/PINCE/wiki/GDB-Expressions) for additional information + * **Opcode Search:** You can search opcodes with python regular expressions. To use this feature, click Tools->Search Opcode in the MemoryView window +- **Debugging** * Has basic debugging features such as stepping, stepping over, execute till return, break, continue. Also has breakpoints, watchpoints and breakpoint conditions. Has advanced debugging utilities such as Watchpoint/Breakpoint Tracking and Tracing - * **Chained Breakpoints:** Just like CE, PINCE allows you to set multiple, connected breakpoints at once. If an event(such as condition modification or deletion) happens in one of the breakpoints, other connected breakpoints will get affected as well **[Done]** - * **Watchpoint Tracking:** Allows you to see which instructions have been accessing to the specified address, just like "What accesses/writes to this address" feature in CE **[Done]** - * **Breakpoint Tracking:** Allows you to track down addresses calculated by the given register expressions at the specified instruction, just like "Find out what addresses this instruction accesses" feature in CE with a little addon, you can enter multiple register expressions, this allows you to check the value of "esi" even if the instruction is something irrelevant like "mov [eax],edx" **[Done]** - * **Tracing:** Almost the same with CE. But unlike CE, you can stop tracing whenever you want. Created from scratch with shittons of custom features instead of using gdb's trace&collect commands because some people have too much time on their hands **[Done]** - * **Collision Detection:** GDB normally permits setting unlimited watchpoints next to each other. But this behaviour leads to unexpected outcomes such as causing GDB or the inferior become completely inoperable. GDB also doesn't care about the number(max 4) or the size(x86->max 4, x64->max 8) of hardware breakpoints. Fortunately, PINCE checks for these problems whenever you set a new breakpoint and detects them before they happen and then inhibits them in a smart way. Lets say you want to set a breakpoint in the size of 32 bytes. But the maximum size for a breakpoint is 8! So, PINCE creates 4 different breakpoints with the size of 8 bytes and then chains them for future actions **[Done]** -- **Code Injection** **[Working on it]** - * **Run-time injection:** Only .so injection is supported for now. In Memory View window, click Tools->Inject .so file to select the .so file. An example for creating .so file can be found in "libpince/Injection/". PINCE will be able to inject single line instructions or code caves in near future **[Partially Done?]** -- **GDB Console** **[Done]** - * Is the power of PINCE not enough for you? Then you can use the gdb console provided by PINCE, it's on the top right in main window -- **Simplified/Optimized gdb command alternatives** **[Working on it]** - * Custom scripts instead of using gdb's x command for reading memory **[Done]** - * Custom scripts instead of using gdb's set command for modifying memory **[Done]** -- **libpince - A reusable python library** - * PINCE provides a reusable python library. You can either read the code or check Reference Widget by clicking Help->libpince in Memory Viewer window to see docstrings. Contents of this widget is automatically generated by looking at the docstrings of the source files. PINCE has a unique parsing technique that allows parsing variables. Check the function get_comments_of_variables in SysUtils for the details. This feature might be replaced with Sphinx in the future -- **Extendable with .so files at runtime** - * See [here](https://github.com/korcankaraokcu/PINCE/wiki/Extending-PINCE-with-.so-files) -- **Automatic Trainer Generation:** **[Planned]** - * PINCE provides a trainer auto-generated from current address table on demand by using libpince and PyQT5 together + * **Chained Breakpoints:** Just like CE, PINCE allows you to set multiple, connected breakpoints at once. If an event(such as condition modification or deletion) happens in one of the breakpoints, other connected breakpoints will get affected as well + * **Watchpoint Tracking:** Allows you to see which instructions have been accessing to the specified address, just like "What accesses/writes to this address" feature in CE + * **Breakpoint Tracking:** Allows you to track down addresses calculated by the given register expressions at the specified instruction, just like "Find out what addresses this instruction accesses" feature in CE with a little addon, you can enter multiple register expressions, this allows you to check the value of "esi" even if the instruction is something irrelevant like "mov [eax],edx" + * **Tracing:** Almost the same with CE. But unlike CE, you can stop tracing whenever you want. Created from scratch with custom features instead of using gdb's built-in trace commands, this allows tracing to be done without the need of a gdbserver + * **Collision Detection:** GDB normally permits setting unlimited watchpoints next to each other. But this behaviour leads to unexpected outcomes such as causing GDB or the inferior become completely inoperable. GDB also doesn't care about the number(max 4) or the size(x86->max 4, x64->max 8) of hardware breakpoints. Fortunately, PINCE checks for these problems whenever you set a new breakpoint and detects them before they happen and then inhibits them in a smart way. Lets say you want to set a breakpoint in the size of 32 bytes. But the maximum size for a breakpoint is 8! So, PINCE creates 4 different breakpoints with the size of 8 bytes and then chains them for future actions +- **Code Injection** + * **Run-time injection:** Only .so injection is supported for now. In Memory View window, click Tools->Inject .so file to select the .so file. An example for creating .so file can be found in "libpince/Injection/". PINCE will be able to inject single line instructions or code caves in near future +- **GDB Console:** You can use the GDB Console to interact with GDB, it's on the top right in main window +- **libpince:** PINCE provides a reusable python library. You can either read the code or check the [Github Pages](https://korcankaraokcu.github.io/PINCE/) for documentation. Currently, libpince can be used via console by following [these instructions](https://github.com/korcankaraokcu/PINCE/issues/232#issuecomment-1872906700). In the future, it'll be directly integrated into PINCE when we develop the scripting engine (IDE for PINCE) +- **Extendable with .so files at runtime:** See [here](https://github.com/korcankaraokcu/PINCE/wiki/Extending-PINCE-with-.so-files) -# Installing -Clone this repo by running `git clone --recursive https://github.com/korcankaraokcu/PINCE` then run ```sudo sh install_pince.sh``` in the PINCE directory. For Archlinux, you can also use the [AUR package](https://aur.archlinux.org/packages/pince-git/) as an alternative. +# Installing and running PINCE +### Users: +- No need to install. Just grab the latest AppImage over at [Releases](https://github.com/korcankaraokcu/PINCE/releases) and run the following commands in the same folder: +```bash +chmod +x PINCE-x86_64.AppImage +sudo -E ./PINCE-x86_64.AppImage +``` +- Our AppImage should run on any distro that is as new or newer than Ubuntu 22.04. Anything older than this might not work and is not officially supported. +- For Arch users, there's also an [AUR package](https://aur.archlinux.org/packages/pince-git/) but please bear in mind that **we're not the maintainers of the AUR package and it's not officially supported by us**. + - Please do not open an Issue unless you can reproduce the issue you're experiencing on our AppImages or local install. -If you like to uninstall PINCE, just delete this folder, almost everything is installed locally. Config and user files of PINCE can be found in "~/.config/PINCE", you can manually delete them if you want. +### Developers and Contributors: +- If you want to have a local install of PINCE so you can modify code or contribute with PRs, you'll have to use our installer script in the repo to setup a venv dev environment. +- To install local dev environment, run the following commands in a terminal anywhere you'd like to have the PINCE folder: +```bash +git clone --recursive https://github.com/korcankaraokcu/PINCE +sh PINCE/install.sh +``` +- Make sure to check our [Officially supported platforms](#officially-supported-platforms) section below. Our installer might not work on distros that are not listed there, but it will still try to install using packages from supported distros, just follow the on-screen instructions. +- If installer fails trying to install on an unsupported distro, you're on your own on trying to get the local dev environment up and running. Check `install.sh` to get an idea about what you might need. +- If you'd like to uninstall PINCE, just delete this folder, almost everything is installed locally. Config and user files of PINCE can be found in "~/.config/PINCE", you can manually delete them as well if you want. ***Notes:*** -- GDB enhancements (peda, pwndbg, etc) that use a global gdbinit file might cause PINCE to misfunction at times. Please disable them or use them locally before starting PINCE -- If you are having problems with your default gdb version, you can use the `install_gdb.sh` script to install another version locally. Read the comments in it for more information - -# Running PINCE -Just run ```sh PINCE.sh``` in the PINCE directory +- If you are having problems with your default gdb version, you can use the `compile_gdb.sh` script to compile another version locally. Read the comments in it for more information +- Check https://github.com/korcankaraokcu/PINCE/issues/116 for a possible fix if you encounter `'GtkSettings' has no property named 'gtk-fallback-icon-theme'` -### For developers: -``` -sudo apt-get install qttools5-dev-tools (qt5 form designer) -sudo apt-get install pyqt5-dev-tools (pyuic5) -sudo pip3 install line_profiler (for performance testing) -``` -How to use line_profiler: Add ```@profile``` tag to the desired function and run PINCE with ```sudo kernprof -l -v PINCE.py``` -# History -- A few weeks till 17/01/2016 : Learned GDB, process of analysis -- 17/01/2016-22/01/2016 : Basic design, grasping of Python3 and Pyqt5, proof-testing -- 22/01/2016 : First commit -- 19/02/2016 : Moved to Github from Bitbucket -- 25/02/2016 : First successful implementation of thread injection[Update-08/05/2016 : PINCE now uses ```linux-inject``` as a secondary injection method] -- 18/06/2016 : PINCE now supports all-stop mode instead of non-stop mode -- 21/06/2016 : Variable Inspection&Modification is finished(At basic level) -- 21/08/2016 : Memory View is finished(At basic level) -- 24/08/2016 : PINCE no more uses linux-inject because of stability issues(a fix for the [race conditions in the inferior](https://github.com/gaffe23/linux-inject/issues/7) would be nice) -- 26/12/2016 : Debugging is finished(At basic level) -- 19/05/2020 : libscanmem integration is complete, enjoy memory searching. Huge thanks to fisu, xk and 12345ieee (libscanmem team) +# Officially supported platforms +Local dev installs of PINCE should technically run on any distro that comes with **Python 3.10+** and **PyQt 6.6+** installed or available in the package manager, but below is the list of distros that we officially support, as in we actively test on these and help with issues: +- Ubuntu 22.04+ +- Debian 12+ (or Testing) +- Arch Linux +- Fedora 35+ -# Current Roadmap -- Refactor file naming conventions(decide on snake_case or camelCase for modules etc) -- Create ```CONTRIBUTING.md``` and combine all non-tutorial notes within it -- Consider replacing read/write_memory_multiple functions with mem_handle&read/write_memory functions, this fixes the "read_memory_multiple follows a bad design pattern" step -- Refactorize memory write/read functions -- - ReferencedStringsWidgetForm refreshes the cache everytime the comboBox_ValueType changes, this creates serious performance issues if total results are more than 800k. Only update the visible rows to prevent this(check ```disassemble_check_viewport``` for an example) -- - Implement same system for the TrackBreakpointWidgetForm if necessary. Do performance tests -- - Consider using a class instead of primitive return types to store the raw bytes. This also gets rid of the unnecessary parameter only_bytes. This class should also include a method to display None type as red '??' text for Qt -- - text_to_valuetype is a bad design pattern. Store the information inside the items of tableWidget_AddressTable instead -- - read_memory_multiple follows a bad design pattern, use named tuples or something like that -- - Provide an option to cut BOM bytes when writing to memory with the types UTF-16 and UTF-32 -- - Put a warning for users about replacement bytes for non UTF-8 types -- - Extend string types with LE and BE variants of UTF-16 and UTF-32 -- - Change comboBox_ValueType string order to be ... String_UTF-8 String_Others Array of Bytes -- - Implement a custom combobox class for comboBox_ValueType and create a context menu for String_Others item -- Indent docstrings properly like GDB_Engine.get_breakpoint_info does(independent from other steps) -- Implement "Investigate Registers" button to gather information about the addresses registers point to(independent from other steps) -- Implement selectionChanged signal of lineEdit_HexView -- Implement multi selection for HexView -- Add the ability to track down registers and addresses in tracer(unsure)(independent from other steps) -- Implement CE's Ultimap-like feature for tracing data, dissect code data and raw instruction list. Search for calls and store their hit counts to filter out the functions that haven't or have executed specific number of times. Implement a flexible input field for the execution count. For instance, 2^x only searches for hit counts 2, 4, 8 and so on, 3x only searches for 3, 6, 9 etc.(independent from other steps)([CE#358](https://github.com/cheat-engine/cheat-engine/issues/358)) -- Extend search_referenced_strings with relative search -- Consider adding type guessing for the StackView(independent from other steps) -- Move GUI classes of PINCE.py to their own files -- Handle signals and internal errors that causes gdb to halt, such as SIGSEGV and overlapping breakpoints(independent from other steps) -- Use gdb python API breakpoints instead of breakpoint commands for optimization, also find a way to eliminate output coming from stepping commands such as ```stepi``` or ```nexti```(independent from other steps) -- Implement a psuedo-terminal for the inferior like edb does(independent from other steps) -- Implement libpince engine -- Implement auto-ESP&aimbot (depends on libpince engine) -- Try to optimize TrackBreakpoint and TrackWatchpoint return data structures further, adding an id field might simplify traversing of the tree, performance tests are required(independent from other steps) -- Extend tagging system to PINCE GUI functions -- Implement inject_with_advanced_injection(independent from other steps) -- Implement single-line code injection -- Implement multi-line code injection -- Break on/Catch signals and syscalls -- Move non-communication functions in GDB_Engine to ScriptUtils and create corresponding fields in GDB_Engine and GDBCommandExtensions automatically. This lets entire functionality of libpince to be used with both python scripts and gdb python scripts, thus allowing it to be used as a plugin for projects such as radare2 -- Flowcharts based on disassembled output -- Automatic function bypassing(make it return the desired value, hook specific parts etc.) -- Implement speedhack(independent from other steps) -- Implement unrandomizer(independent from other steps) -- Implement pointer-scan -- Write at least one test for each function in libpince -- Migrate to Sphinx documentation from the custom libpince documentation(independent from other steps) -- Embedded tutorial videos -- Super-Uber-Rad credits roll with chiptune tunes -- Implement extra MemoryViewerWindow tabs(independent from other steps) -- ~~Consider removing the command file layer of IPC system for GDB_Engine.send_command to speed up things~~(independent from other steps)[Update-29/04/2018 : Delaying this until GDB/MI implements a native multiline command feature or improves ```interpreter-exec``` command to cover every single multiline command type(including ```define``` commands)] -- Implement thread info widget -- Add ability to change logo and other assets if people contribute more than one asset per usage. Also consider using [PINCE-media](https://github.com/korcankaraokcu/PINCE-media) for development if needed(independent from other steps) -- Implement developer mode in settings. Developer mode will include features like dissection of GUI elements on events such as mouse-over(independent from other steps) -- Add ability to include non-absolute calls for dissect code feature(i.e call rax). Should be considered after the first version release. Might be useful for multi-breakpoint related features -- Implement toggling of arrows for easier navigation for dissected regions(independent from other steps) -- Provide information about absolute addresses in disassemble screen(independent from other steps) -- Use type hints(py 3.5) and variable annotations(py 3.6) when support drops for older systems(independent from other steps) -- All tables that hold large amount of data should only update the visible rows(check ```disassemble_check_viewport``` for an example)(independent from other steps) -- Add different kinds of themes and the ability to change between them on runtime. Implement dark theme first. Also add the ability to create a custom theme and modify the existing ones(independent from other steps) +# Contributing +Want to help? Check out [CONTRIBUTING.md](CONTRIBUTING.md) # License -GPLv3+. See COPYING file for details - -# Contact Information -Korcan Karaokçu([korcankaraokcu](https://github.com/cagriulas)) -Çağrı Ulaş([cagriulas](https://github.com/cagriulas)) -Jakob Kreuze([TsarFox](https://github.com/TsarFox)) -Gibus - -# Supported platforms -- Ubuntu and its flavors, actively tested on Kubuntu -- Debian -- Kali Linux -- Parrot OS -- Linux Mint (install the package "python3-psutil" if you encounter ImportError or NameError, thanks Gibus) -- Archlinux(tag [cagriulas](https://github.com/cagriulas) or [TsarFox](https://github.com/TsarFox) when creating an issue) +GPLv3+. See [COPYING](COPYING) file for details # Trusted Sources * [Official github page](https://github.com/korcankaraokcu/PINCE) - * [AUR package for Archlinux](https://aur.archlinux.org/packages/pince-git/) diff --git a/ci/package.sh b/ci/package.sh new file mode 100755 index 00000000..f590ce43 --- /dev/null +++ b/ci/package.sh @@ -0,0 +1,197 @@ +#!/bin/bash +: ' +Copyright (C) 2024 brkzlr + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +' + +# Check if the file is in the correct directory and cd there +# This script should be only in PINCE/ci folder +PACKAGEDIR="$(dirname "$(readlink -f "$0")")" +case $PACKAGEDIR in + *"PINCE/ci") ;; + *) echo "package.sh is not in PINCE/ci folder!"; exit 1;; +esac +cd $PACKAGEDIR + +# Check what distro we use for lrelease path +LSB_RELEASE="$(command -v lsb_release)" +if [ -n "$LSB_RELEASE" ]; then + OS_NAME="$(${LSB_RELEASE} -d -s)" +else + # shellcheck disable=SC1091 + . /etc/os-release + OS_NAME="$NAME" +fi +case $OS_NAME in +*SUSE*) + LRELEASE_CMD="lrelease6" + ;; +*Arch*) + LRELEASE_CMD="/usr/lib/qt6/bin/lrelease" + ;; +*Fedora*) + LRELEASE_CMD="lrelease-qt6" + ;; +*Debian*|*Ubuntu*) + LRELEASE_CMD="/usr/lib/qt6/bin/lrelease" + ;; +*) + LRELEASE_CMD="$(which lrelease6)" # Placeholder + ;; +esac + +# Download necessary tools +curl -L -O https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage +DEPLOYTOOL=./linuxdeploy-x86_64.AppImage +chmod +x $DEPLOYTOOL + +curl -L -O https://raw.githubusercontent.com/TheAssassin/linuxdeploy-plugin-conda/master/linuxdeploy-plugin-conda.sh +CONDAPLUGIN=./linuxdeploy-plugin-conda.sh +chmod +x $CONDAPLUGIN + +# Create cleanup function to remove remaining deps/files +cleanup () { + cd $PACKAGEDIR + # Remove everything outside of package.sh and AppImage output + ls --hide=package.sh --hide=PINCE*.AppImage | xargs rm -rf +} +trap cleanup EXIT + +# Error checking function +exit_on_failure() { + if [ "$?" -ne 0 ]; then + echo + echo "Error occured while creating AppImage! Check the log above!" + exit 1 + fi +} + +# Create AppImage's AppDir with a Conda environment pre-baked +# containing our required pip packages +export PIP_REQUIREMENTS="-r ../requirements.txt" +# Need this to get libstdc++ higher than default 6.0.29 and libxcb-cursor for Debian family +export CONDA_PACKAGES="libstdcxx-ng;xcb-util-cursor" +$DEPLOYTOOL --appdir AppDir -pconda || exit_on_failure + +# Create PINCE directory +mkdir -p AppDir/opt/PINCE + +# Install libscanmem +NUM_MAKE_JOBS="$(nproc --ignore=1)" +cd .. +git submodule update --init --recursive +if [ ! -d "libpince/libscanmem" ]; then + mkdir libpince/libscanmem +fi +cd libscanmem-PINCE +cmake -DCMAKE_BUILD_TYPE=Release . || exit_on_failure +make -j"$NUM_MAKE_JOBS" || exit_on_failure +cp --preserve libscanmem.so ../libpince/libscanmem +cp --preserve wrappers/scanmem.py ../libpince/libscanmem +cd .. + +# Install libptrscan +if [ ! -d "libpince/libptrscan" ]; then + mkdir libpince/libptrscan +fi +cd libpince/libptrscan +curl -L -o libptrscan.tar.gz https://github.com/kekeimiku/PointerSearcher-X/releases/download/v0.7.3-dylib/libptrscan_pince-x86_64-unknown-linux-gnu.tar.gz || exit_on_failure +tar xf libptrscan.tar.gz --strip-components 1 || exit_on_failure +rm -f libptrscan.tar.gz +mv libptrscan_pince.so libptrscan.so +cd ../.. + +# Compile translations +${LRELEASE_CMD} i18n/ts/* || exit_on_failure +mkdir -p i18n/qm +mv i18n/ts/*.qm i18n/qm/ + +# Copy necessary PINCE folders/files to inside AppDir +cp -r GUI i18n libpince media tr AUTHORS COPYING COPYING.CC-BY PINCE.py THANKS ci/AppDir/opt/PINCE/ +cd ci + +# Create a wrapper so GDB can correctly link against the +# included conda's python environment to ensure compatibility +# Taken from: https://github.com/pwndbg/pwndbg/pull/892 +cat > wrapper.sh <<\EOF +#!/bin/bash +if [[ -z "$CONDA_PREFIX" ]]; then + echo "Error: CONDA_PREFIX not set" + exit 2 +fi +echo "$(date +%F) -- $@" >> /tmp/args.txt +if [[ $1 != *"python-config.py"* ]]; then + exec "$CONDA_PREFIX"/bin/python3 +fi +# get rid of the first parameter, which is the path to the python-config.py script +shift +# python3-config --ldflags lacks the python library +# also gdb won't link on GitHub actions without libtinfow, which is not provided by the conda environment +if [[ "$1" == "--ldflags" ]]; then + echo -n "-lpython3.12 -ltinfow " +fi +exec "$CONDA_PREFIX"/bin/python3-config "$@" +EOF +chmod +x wrapper.sh + +# Prepare some env vars for GDB compilation +INSTALLDIR=$(pwd)/AppDir +export CONDA_PREFIX="$(readlink -f $INSTALLDIR/usr/conda)" + +# Grab latest GDB at time of writing and compile it with our conda Python +curl -L -O "https://ftp.gnu.org/gnu/gdb/gdb-14.2.tar.gz" +tar xf gdb-14.2.tar.gz +rm gdb-14.2.tar.gz +cd gdb-14.2 +./configure --with-python="$(readlink -f ../wrapper.sh)" --prefix=/usr || exit_on_failure +make -j"$NUM_MAKE_JOBS" || exit_on_failure +make install DESTDIR=$INSTALLDIR +cd .. +rm -rf gdb-14.2 +rm wrapper.sh + +# Create a fake but needed desktop file for AppImage +cat > AppDir/usr/share/applications/PINCE.desktop <<\EOF +[Desktop Entry] +Name=PINCE +Exec=PINCE +Icon=PINCE +Type=Application +Terminal=true +Categories=Development; +EOF + +# Placeholder icon for above desktop file +touch AppDir/usr/share/icons/hicolor/scalable/apps/PINCE.svg + +# Create main running script +cat > AppRun.sh <<\EOF +#!/bin/bash +if [ "$(id -u)" != "0" ]; then + echo "Please run this AppImage using 'sudo -E'!" + exit 1 +fi +export APPDIR="$(dirname "$0")" +export PYTHONHOME=$APPDIR/usr/conda +$APPDIR/usr/bin/python3 $APPDIR/opt/PINCE/PINCE.py +EOF +chmod +x AppRun.sh + +# Patch libqxcb's runpath (not rpath) to point to our packaged libxcb-cursor to fix X11 issues +patchelf --add-rpath "\$ORIGIN/../../../../../../" AppDir/usr/conda/lib/python3.12/site-packages/PyQt6/Qt6/plugins/platforms/libqxcb.so + +# Package AppDir into AppImage +export LD_LIBRARY_PATH="$(readlink -f ./AppDir/usr/conda/lib)" +$DEPLOYTOOL --appdir AppDir/ --output appimage --custom-apprun AppRun.sh || exit_on_failure diff --git a/install_gdb.sh b/compile_gdb.sh old mode 100644 new mode 100755 similarity index 64% rename from install_gdb.sh rename to compile_gdb.sh index 50ca2db3..a61b623a --- a/install_gdb.sh +++ b/compile_gdb.sh @@ -19,41 +19,44 @@ along with this program. If not, see . # This script installs a specific gdb version locally, the default installation script doesn't need this anymore, you can use it as a fallback if system gdb is being problematic # After installing a local gdb, you must specify its binary location via the Settings->Debug -GDB_VERSION="gdb-10.2" +echo "This script will install GDB locally in the gdb_pince folder" +echo "To see the available GDB versions, visit -> https://ftp.gnu.org/gnu/gdb/" +read -r -p "Enter the GDB version number (e.g., 14.1): " version_number + +GDB_VERSION="gdb-$version_number" + +if [ -z "$NUM_MAKE_JOBS" ]; then + NUM_MAKE_JOBS=$(lscpu -p=core | uniq | awk '!/#/' | wc -l) + MAX_NUM_MAKE_JOBS=8 + if [ "$NUM_MAKE_JOBS" -gt "$MAX_NUM_MAKE_JOBS" ]; then # set an upper limit to prevent Out-Of-Memory + NUM_MAKE_JOBS=$MAX_NUM_MAKE_JOBS + fi + if ! echo "$NUM_MAKE_JOBS" | grep -Eq '^[0-9]+$'; then # fallback + NUM_MAKE_JOBS=$MAX_NUM_MAKE_JOBS + fi +fi mkdir -p gdb_pince -cd gdb_pince +cd gdb_pince || exit # clean the directory if another installation happened -rm -rf $GDB_VERSION +rm -rf "$GDB_VERSION" -if [ ! -e ${GDB_VERSION}.tar.gz ] ; then - wget "http://ftp.gnu.org/gnu/gdb/${GDB_VERSION}.tar.gz" +if [ ! -e "${GDB_VERSION}".tar.gz ] ; then + wget "http://ftp.gnu.org/gnu/gdb/${GDB_VERSION}.tar.gz" || exit fi -tar -zxvf ${GDB_VERSION}.tar.gz -cd $GDB_VERSION +tar -zxvf "${GDB_VERSION}".tar.gz +cd "$GDB_VERSION" || exit echo "-------------------------------------------------------------------------" echo "DISCLAIMER" echo "-------------------------------------------------------------------------" echo "If you're not on debian or a similar distro with the 'apt' package manager the follow will not work if you don't have gcc and g++ installed" echo "Please install them manually for this to work, this issue will be addressed at a later date" -command -v gcc g++ # extremely lazy fix for other distros, if gcc&g++ is available it will work, if not it won't -if [ $? -gt 0 ]; then - # Dependencies required for compiling GDB - sudo apt-get install python3-dev - sudo apt-get install gcc g++ - if [ $? -gt 0 ]; then - sudo apt-get install software-properties-common - sudo add-apt-repository ppa:ubuntu-toolchain-r/test - sudo apt-get update - sudo apt-get install gcc g++ - if [ $? -gt 0 ]; then - echo "Failed to install gcc or g++, aborting..." - exit 1 - fi - fi -fi -CC=gcc CXX=g++ ./configure --prefix="$(pwd)" --with-python=python3 && make -j $(grep -m 1 "cpu cores" /proc/cpuinfo | cut -d: -f 2 | xargs) MAKEINFO=true && sudo make -C gdb install + +sudo apt-get install python3-dev libgmp3-dev libmpc-dev + +CC=gcc CXX=g++ ./configure --prefix="$(pwd)" --with-python=python3 && make -j"$NUM_MAKE_JOBS" MAKEINFO=true && sudo make -C gdb install + if [ ! -e bin/gdb ] ; then echo "Failed to install GDB, restart the installation process" exit 1 diff --git a/compile_ts.sh b/compile_ts.sh new file mode 100755 index 00000000..13e8b0f0 --- /dev/null +++ b/compile_ts.sh @@ -0,0 +1,28 @@ +#!/bin/bash +supported_locale_list=$(python3 -c " +import locale +print('\n'.join(value.split('.')[0] for value in locale.locale_alias.values())) +") +list_ts=$(find i18n/ts -maxdepth 1 -type f -name '*.ts') + +# If there's a user parameter, create a new locale based on it +if [ -n "$1" ]; then + list_ts="$list_ts i18n/ts/$1.ts" +fi + +for ts in $list_ts; do + # Check if the locale is valid + if echo "$supported_locale_list" | grep -q "$(basename "$ts" .ts)"; then + pylupdate6 GUI/*.ui tr/tr.py --no-obsolete --ts "$ts" + python3 fix_ts.py "$ts" + else + list_invalidts="$list_invalidts $ts" + fi +done + +if [ -n "$list_invalidts" ]; then + echo + echo "ERROR: The following locales are invalid, please check:" + echo "$list_invalidts" + exit 1 +fi diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..d0c3cbf1 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/build_html.sh b/docs/build_html.sh new file mode 100755 index 00000000..e5b8c739 --- /dev/null +++ b/docs/build_html.sh @@ -0,0 +1,3 @@ +. ../.venv/PINCE/bin/activate +make clean +make html diff --git a/docs/install_sphinx.sh b/docs/install_sphinx.sh new file mode 100755 index 00000000..59456245 --- /dev/null +++ b/docs/install_sphinx.sh @@ -0,0 +1,3 @@ +. ../.venv/PINCE/bin/activate +pip install sphinx +pip install sphinx-autodoc-typehints diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 00000000..14164f62 --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,38 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +import os +import sys + +sys.path.insert(0, os.path.abspath(os.path.join("..", ".."))) + +project = "PINCE" +copyright = "2024, PINCE Contributors" +author = "PINCE Contributors" + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.napoleon", + "sphinx.ext.todo", + "sphinx.ext.viewcode", + "sphinx.ext.intersphinx", + "sphinx_autodoc_typehints", +] + +templates_path = ["_templates"] +exclude_patterns = [] + + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = "alabaster" +html_static_path = ["_static"] diff --git a/docs/source/guiutils.rst b/docs/source/guiutils.rst new file mode 100644 index 00000000..b60d0540 --- /dev/null +++ b/docs/source/guiutils.rst @@ -0,0 +1,7 @@ +GUI.Utils.guiutils module +========================= + +.. automodule:: GUI.Utils.guiutils + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 00000000..37ff02b7 --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,19 @@ +.. PINCE documentation master file, created by sphinx-quickstart + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to PINCE's documentation! +================================= + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + modules + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/source/libpince.rst b/docs/source/libpince.rst new file mode 100644 index 00000000..74b57ad6 --- /dev/null +++ b/docs/source/libpince.rst @@ -0,0 +1,45 @@ +libpince package +================ + +Submodules +---------- + +libpince.debugcore module +------------------------- + +.. automodule:: libpince.debugcore + :members: + :undoc-members: + :show-inheritance: + +libpince.regexes module +----------------------- + +.. automodule:: libpince.regexes + :members: + :undoc-members: + :show-inheritance: + +libpince.typedefs module +------------------------ + +.. automodule:: libpince.typedefs + :members: + :undoc-members: + :show-inheritance: + +libpince.utils module +--------------------- + +.. automodule:: libpince.utils + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: libpince + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/modules.rst b/docs/source/modules.rst new file mode 100644 index 00000000..194e8bba --- /dev/null +++ b/docs/source/modules.rst @@ -0,0 +1,15 @@ +libpince +======== + +.. toctree:: + :maxdepth: 4 + + libpince + +guiutils +======== + +.. toctree:: + :maxdepth: 4 + + guiutils diff --git a/fix_ts.py b/fix_ts.py new file mode 100644 index 00000000..8c619bf9 --- /dev/null +++ b/fix_ts.py @@ -0,0 +1,28 @@ +import xml.etree.ElementTree as ET +import sys +import os + +file = sys.argv[1] +tree = ET.parse(file, parser=ET.XMLParser(encoding="utf-8")) +root = tree.getroot() + +# pylupdate6 doesn't set locale on creation, make sure it's there +locale = os.path.splitext(os.path.basename(file))[0] +root.set("language", locale) + +# Removing line info so updating tr.py affects git history much less +for location in root.findall(".//location"): + location.set("line", "0") + +modified_xml = ET.tostring(root, encoding="utf-8", xml_declaration=False).decode() +with open(file, "r", encoding="utf-8") as f: + original_xml = f.read() + +# These declarations are hardcoded in pylupdate6, make sure everything is correct +declarations = original_xml.split("\n", 2)[:2] +assert declarations[0] == '', "xml format has changed" +assert declarations[1] == "", "doctype format has changed" +final_xml = "\n".join(declarations) + "\n" + modified_xml + "\n" + +with open(file, "w", encoding="utf-8") as f: + f.write(final_xml) diff --git a/i18n/ts/it_IT.ts b/i18n/ts/it_IT.ts new file mode 100644 index 00000000..f42ea358 --- /dev/null +++ b/i18n/ts/it_IT.ts @@ -0,0 +1,2766 @@ + + + + + Dialog + + + Add Address Manually + + + + + Type: + + + + + Endianness: + + + + + + Hex + + + + + + Signed + + + + + + Length: + + + + + + Zero-Terminated + + + + + Base Address: + + + + + Add Offset + + + + + Remove Offset + + + + + Description: + + + + + + + Address: + Indirizzo: + + + + + Pointer + + + + + Dissect Code + + + + + Regions + + + + + Path + + + + + Selected regions will be scanned + + + + + Currently scanning range: + + + + + String references found: + + + + + Jumps found: + + + + + Calls found: + + + + + Entries that can't be decoded as utf-8 won't be included in referenced strings +Unchecking it makes ReferencedStringsWidget load slower but allows you to examine non-string pointers on it + + + + + Discard invalid strings + + + + + Edit Instruction + + + + + Instruction: + + + + + Multiple entries are separated with ; + + + + + + + Type + + + + + Endianness + + + + + Length + Lunghezza + + + + + Handle Signals + + + + + Signal + + + + + Stop & Print + + + + + Pass to Program + + + + + Hex Edit + + + + + Refresh + + + + + Processing + + + + + Cancel + Annulla + + + + Filter Pointers + + + + + File 1: + + + + + + + Browse + + + + + File 2: + + + + + Scan for Pointers + + + + + Address + Indirizzo + + + + Depth + + + + + Scan Range + + + + + + + + 0x + + + + + + <-> + + + + + File Path: + + + + + Optional Parameters (0 or empty will use defaults) + + + + + Last Offset Scan Range + + + + + Minimum Chain Length + + + + + Last Offset Value + + + + + Max Results + + + + + Solve Circular References + + + + + Manage Scan Regions + + + + + Selected regions will be deleted from the current scan + + + + + Invert Selection + + + + + ID + + + + + Start Address + + + + + Size(bytes) + + + + + Load Address + + + + + Perms + + + + + File + + + + + Settings + Impostazioni + + + + General + + + + + Hotkeys + + + + + Code Injection + + + + + Memory View + + + + + Debug + + + + + Java + + + + + Auto-update address table + + + + + Update Interval + + + + + Freeze Interval + + + + + GDB output: + + + + + Async + + + + + Command + + + + + Command info + + + + + On start, automatically attach to processes with name matching one of the entries +Patterns at former positions have higher priority if regex is off + + + + + Auto-attach to processes named + + + + + Regex + + + + + Language + + + + + Logo + + + + + Theme + + + + + Functions + + + + + Hotkey + + + + + Press shortcut + + + + + Clear + + + + + Code injection method: + + + + + Simp&le dlopen call + + + + + Advanced In&jection + + + + + Bring Memory View to front when the inferior is stopped + + + + + Instructions shown per scroll in Disassembly View + + + + + Bytes shown per scroll in Hex View + + + + + GDB Path + + + + + GDB Logging + + + + + Interruption signal + + + + + Ignore SIGSEGV for Java processes (overrides signal settings if enabled) + + + + + Reset Settings + + + + + Dialog + + + + + Hit Esc to cancel and Ctrl+Enter to accept + + + + + Parameters for tracing + + + + + Number of the instructions that'll be traced + + + + + Max trace count(1 or greater): + + + + + Tracing will start if this condition is met + + + + + Trigger condition(Optional, gdb expression): + + + + + Tracing will stop whenever this condition is met + + + + + Stop condition(Optional, gdb expression): + + + + + Step over instead of single step + + + + + Stop when tracing ends + + + + + Collect registers + + + + + Select an address to track + + + + + Pointed Address + + + + + Form + + + Bookmarks + + + + + Bookmarked Addresses + + + + + + Info + + + + + Comment + + + + + GDB Console + + + + + Send + + + + + CLI + + + + + MI + + + + + Examine Referrers + + + + + + + + + Enter a string or a python regex + + + + + + + + + Ignore case if checked + + + + + + + + + Case sensitive + + + + + + + + Your string will be treated as a regex if checked + + + + + + + + + Regex + + + + + + + + + Search(Enter) + + + + + Functions + + + + + + + + + + + Address + Indirizzo + + + + Symbol + + + + + Enter the regex. Leave blank to see all functions + + + + + + + Form + + + + + + TextLabel + + + + + Memory Regions + + + + + Regions + + + + + Perms + + + + + Offset + + + + + Path + + + + + Referenced Calls + + + + + + Refcount + + + + + Referenced Strings and Values + + + + + + Value + Valore + + + + Restore Instructions + + + + + Original OpCode + + + + + Original Instruction + + + + + Search for Opcodes + + + + + Start + Inizio + + + + End + Fine + + + + Opcodes + + + + + StackTrace Information + + + + + Return Address + + + + + Tracer Status + + + + + Cancel + Annulla + + + + + Count + + + + + Source + + + + + + Stop + + + + + Refresh + + + + + MainWindow + + + Freeze + + + + + Freeze the value + + + + + Description + + + + + + Address + Indirizzo + + + + Type + + + + + + Value + Valore + + + + Memory View + + + + + Copy selected items to the address table + + + + + Erase all the table contents + + + + + Refresh the address table[R] + + + + + Add Address Manually + + + + + Create or attach to a process + + + + + Open a cheat table + + + + + Save current table to a file + + + + + Wiki + + + + + About + + + + + No Process Selected + Nessun Processo Selezionato + + + + Open a gdb console + + + + + Configure options + + + + + Match count: 0 + + + + + Previous + Precedente + + + + First Scan + Prima Ricerca + + + + Next Scan + + + + + Undo Scan + + + + + Hex + + + + + Scan Type: + + + + + Value Type: + + + + + Scan Scope: + + + + + Endianness: + + + + + Manage Scan Regions + + + + + Pointer Scanner + + + + + Sort + + + + + Clear + + + + + Fi&le + + + + + Actio&ns + + + + + &Open + + + + + &Save As... + + + + + &Scan + + + + + &Filter + + + + + Please select a Process + + + + + Name of the Process: + Nome del Processo: + + + + PID + + + + + Username + Nome Utente + + + + Process Name + Nome Processo + + + + Attach to the selected process + + + + + + Open + + + + + Cancel + Annulla + + + + Open an executable + + + + + Create Process[F1] + + + + + Tracer + + + + + File + + + + + Save + + + + + Save as a text file + + + + + MainWindow_MemoryView + + + Memory Viewer + + + + + + + Address + Indirizzo + + + + Bytes + + + + + Opcodes + + + + + Comment + + + + + Registers + + + + + Flags + + + + + Segment Registers + + + + + Show Float Registers + + + + + Return Address + + + + + Frame Address + + + + + Value + Valore + + + + Points to + + + + + V&iew + + + + + &Debug + + + + + &Tools + + + + + Fi&le + + + + + Help + + + + + &Bookmarks + + + + + &StackTrace Info + + + + + &Inject .so file + + + + + &Break + + + + + &Run + + + + + &Step[F7] + + + + + Step &Over[F8] + + + + + &Execute Till Return[Shift+F8] + + + + + &Toggle Breakpoint[F5] + + + + + B&reakpoints + + + + + &Functions + + + + + Set Address[Shift+F4] + + + + + &Call Function + + + + + &Load Trace + + + + + &libpince + + + + + &GDB Log File + + + + + &Search Opcode + + + + + &Memory Regions + + + + + &Dissect Code + + + + + R&eferenced Strings + + + + + Referenced &Calls + + + + + To&ggle Attach + + + + + Restore Instructions + + + + + QPlatformTheme + + + OK + + + + + &Open + + + + + &Save + + + + + Cancel + Annulla + + + + Close + + + + + Discard + + + + + Apply + + + + + Reset + + + + + Restore Defaults + + + + + Help + + + + + Save All + + + + + &Yes + + + + + &No + + + + + Abort + + + + + Retry + + + + + Ignore + + + + + N&o to All + + + + + Yes to &All + + + + + TabWidget + + + About PINCE + + + + + Contributors + + + + + License + + + + + Breakpoints + + + + + Interactive + + + + + No + + + + + Type + + + + + Disp + + + + + Enabled + + + + + Address + Indirizzo + + + + Size + + + + + On Hit + + + + + Hit Count + + + + + Condition + + + + + Raw + + + + + Floating Point Registers + + + + + + Register + + + + + + Value + Valore + + + + TranslationConstants + + + Pause the process + + + + + Break the process + + + + + Continue the process + + + + + Toggle attach/detach + + + + + Next Scan - Exact + + + + + Next Scan - Increased + + + + + Next Scan - Decreased + + + + + Next Scan - Changed + + + + + Next Scan - Unchanged + + + + + Error + + + + + Success + + + + + Information + + + + + GDB isn't initialized yet + + + + + Unable to initialize GDB +You might want to reinstall GDB or use the system GDB +To change the current GDB path, check Settings->Debug + + + + + Edit + + + + + Show as hexadecimal + + + + + Show as decimal + + + + + Show as unsigned + + + + + Show as signed + + + + + Toggle + + + + + Toggle including children + + + + + Freeze + + + + + Default + + + + + Incremental + + + + + Decremental + + + + + Browse this memory region + + + + + Disassemble this address + + + + + Delete + + + + + Delete selection + + + + + Cut + + + + + Copy + + + + + Paste + + + + + Paste inside + + + + + Pointer scan for this address + + + + + Open pointer scanner + + + + + Find out what writes to this address + + + + + Find out what reads this address + + + + + Find out what accesses this address + + + + + Add to a new group + + + + + Create a new group + + + + + Group + + + + + Invalid clipboard content + + + + + New Scan + + + + + Match count: {} ({} shown) + + + + + Match count: {} + + + + + No Description + + + + + Open PCT file(s) + + + + + PINCE Cheat Table (*.pct) + + + + + Shared object library (*.so) + + + + + Trace File (*.trace) + + + + + Pointer Scan Data (*.scandata) + + + + + Clear address table? + + + + + File {} is inaccessible or contains invalid content + + + + + Save PCT file + + + + + Cannot save to file + + + + + Nice try, smartass + + + + + Selected process is not valid + + + + + You're debugging this process already + + + + + That process is already being traced by {}, could not attach to the process + + + + + Permission denied, could not attach to the process + + + + + An error occurred while trying to create process + + + + + Scan for + + + + + First Scan + Prima Ricerca + + + + No Process Selected + Nessun Processo Selezionato + + + + [detached] + + + + + [stopped] + + + + + Process is running + + + + + Enter the new value + + + + + Enter the new description + + + + + Edit Address + + + + + Please select a process first + + + + + Select the target binary + + + + + Enter the optional arguments + + + + + LD_PRELOAD .so path (optional) + + + + + Refresh + + + + + Length is not valid + + + + + Length must be greater than 0 + + + + + Can't parse the input + + + + + {} isn't a valid regex + + + + + Language settings will take effect upon the next restart + + + + + You have changed the GDB path, reset GDB now? + + + + + This will reset to the default settings +Proceed? + + + + + Mouse over on this text for examples + + + + + asdf|qwer --> search for asdf or qwer +[as]df --> search for both adf and sdf +Use the char \ to escape special chars such as [ +\[asdf\] --> search for opcodes that contain [asdf] + + + + + Separate processes with {} + + + + + This setting is unused in AppImage builds + + + + + Select the gdb binary + + + + + Quitting current session will crash PINCE + + + + + Use global hotkeys or the commands 'interrupt' and 'c&' to stop/run the inferior + + + + + Hotkeys: +----------------------------- +Send: Enter | +Multi-line mode: Ctrl+Enter | +Complete command: Tab | +----------------------------- +Commands: +---------------------------------------------------------- +/clear: Clear the console | +phase-out: Detach from the current process | +phase-in: Attach back to the previously detached process | +--------------------------------------------------------------------------------------------------- +pince-init-so-file so_file_path: Initializes 'lib' variable | +pince-get-so-file-information: Get information about current lib | +pince-execute-from-so-file lib.func(params): Execute a function from lib | +# Check https://github.com/korcankaraokcu/PINCE/wiki#extending-pince-with-so-files for an example | +# CLI output mode doesn't work very well with .so extensions, use MI output mode instead | +--------------------------------------------------------------------------------------------------- +You can change the output mode from bottom right +Changing output mode only affects commands sent. Any other output coming from external sources(e.g async output) will be shown in MI format + + + + + Break[{}] + + + + + Run[{}] + + + + + Toggle Attach[{}] + + + + + Failed to set breakpoint at address {} + + + + + Failed to set watchpoint at address {} + + + + + Copy to Clipboard + + + + + Go to expression + + + + + Add this address to address list + + + + + Set Watchpoint + + + + + Write Only + + + + + Read Only + + + + + Both + + + + + Add/Change condition for breakpoint + + + + + Delete Breakpoint + + + + + Enter the expression + + + + + {} is invalid + + + + + Protection:{} | Base:{}-{} | Module:{} + + + + + Invalid Region + + + + + Cannot access memory at expression {} + + + + + Referenced by: + + + + + Press 'Ctrl+E' to see a detailed list of referrers + + + + + Memory Viewer - Paused + + + + + Memory Viewer - Currently debugging {} + + + + + Memory Viewer - Running + + + + + Enter the expression for condition, for instance: + +$eax==0x523 +$rax>0 && ($rbp<0 || $rsp==0) +printf($r10)==3 + + + + + Failed to set condition for address {} +Check terminal for details + + + + + Full Stack + + + + + Copy Return Address + + + + + Copy Frame Address + + + + + Stacktrace + + + + + Toggle stack from BP/SP register + + + + + Copy Address + + + + + Copy Value + + + + + Copy Points to + + + + + Disassemble 'value' pointer address + + + + + Show 'value' pointer in HexView + + + + + Back + + + + + Show this address in HexView + + + + + Follow + + + + + Examine Referrers + + + + + Bookmark this address + + + + + Delete this bookmark + + + + + Change comment + + + + + Go to bookmarked address + + + + + Toggle Breakpoint + + + + + Edit instruction + + + + + Replace instruction with NOPs + + + + + Find out which addresses this instruction accesses + + + + + Break and trace instructions + + + + + Dissect this region + + + + + Copy Bytes + + + + + Copy Opcode + + + + + Copy Comment + + + + + Copy All + + + + + Enter the register expression(s) you want to track +Register names must start with $ +Each expression must be separated with a comma + +For instance: +Let's say the instruction is mov [rax+rbx],30 +Then you should enter $rax+$rbx +So PINCE can track address [rax+rbx] + +Another example: +If you enter $rax,$rbx*$rcx+4,$rbp +PINCE will track down addresses [rax],[rbx*rcx+4] and [rbp] + + + + + This address has already been bookmarked + + + + + Enter the comment for bookmarked address + + + + + Select the .so file + + + + + The file has been injected + + + + + Failed to inject the .so file + + + + + Enter the expression for the function that'll be called from the inferior +You can view functions list from View->Functions + +For instance: +Calling printf("1234") will yield something like this +↓ +$28 = 4 + +$28 is the assigned convenience variable +4 is the result +You can use the assigned variable from the GDB Console + + + + + Failed to call the expression {} + + + + + Invalid expression or address + + + + + Invalid entries detected, refreshing the page + + + + + Add new entry + + + + + Enter the new value of register {} + + + + + Enter the new value of flag {} + + + + + Restore this instruction + + + + + Enter the hit count({} or higher) + + + + + Hit count must be an integer + + + + + Hit count can't be lower than {} + + + + + Change condition + + + + + Enable + + + + + Disable + + + + + Disable after hit + + + + + Disable after X hits + + + + + Delete after hit + + + + + Opcodes writing to the address {} + + + + + Opcodes reading from the address {} + + + + + Opcodes accessing to the address {} + + + + + Unable to track watchpoint at expression {} + + + + + Unable to delete watchpoint at expression {} + + + + + Close + + + + + Addresses accessed by instruction {} + + + + + Unable to track breakpoint at expression {} + + + + + Accessed by {} + + + + + Unable to delete breakpoint at expression {} + + + + + Max trace count must be greater than or equal to {} + + + + + Save trace file + + + + + Open trace file + + + + + Expand All + + + + + Collapse All + + + + + Select a pointer map file + + + + + Scan + + + + + Scanning + + + + + Filter + + + + + Filtering + + + + + DEFINED + + + + + This symbol is defined. You can use its body as a gdb expression. For instance: + +void func(param) can be used as 'func' as a gdb expression + + + + + Copy Symbol + + + + + Here's some useful regex tips: +^quaso --> search for everything that starts with quaso +[ab]cd --> search for both acd and bcd + + How to interpret symbols: +A symbol that looks like 'func(param)@plt' consists of 3 pieces +func, func(param), func(param)@plt +These 3 functions will have different addresses +@plt means this function is a subroutine for the original one +There can be more than one of the same function +It means that the function is overloaded + + + + + New opcode is {} bytes long but old opcode is only {} bytes long +This will cause an overflow, proceed? + + + + + {} isn't a valid expression + + + + + Log File of PID {} + + + + + Contents of {} (only last {} bytes are shown) + + + + + ON + + + + + OFF + + + + + LOGGING: {} + + + + + Unable to read log file at {} + + + + + Go to Settings->Debug to enable logging + + + + + Invalid Regex + + + + + Here's some useful regex examples: +call|rax --> search for opcodes that contain call or rax +[re]cx --> search for both rcx and ecx +Use the char \ to escape special chars such as [ +\[rsp\] --> search for opcodes that contain [rsp] + + + + + Copy Addresses + + + + + Copy Offset + + + + + Copy Path + + + + + Start + Inizio + + + + Currently scanning region: + + + + + Cancel + Annulla + + + + Scan finished + + + + + Scan was canceled + + + + + Select at least one region + + + + + You need to dissect code first +Proceed? + + + + + Waiting for breakpoint to trigger + + + + + Tracing has been completed + + + + + Not + + + + + Exact + + + + + Increased + + + + + Increased by + + + + + Decreased + + + + + Decreased by + + + + + Less Than + + + + + More Than + + + + + Between + + + + + Changed + + + + + Unchanged + + + + + Unknown Value + + + + + Basic + + + + + Normal + + + + + Read+Write + + + + + Full + + + + + Host + + + + + Little + + + + + Big + + + + + Show in HexView + + + + + Show in Disassembler + + + + diff --git a/i18n/ts/zh_CN.ts b/i18n/ts/zh_CN.ts new file mode 100644 index 00000000..56eb13eb --- /dev/null +++ b/i18n/ts/zh_CN.ts @@ -0,0 +1,2837 @@ + + + + + Dialog + + + Add Address Manually + 手动添加地址 + + + + Type: + 类型: + + + + Endianness: + 字节序: + + + + + Hex + 十六进制 + + + + + Signed + 有符号 + + + + + Length: + 长度: + + + + + Zero-Terminated + 以零终止 + + + + Base Address: + 基址: + + + + Add Offset + 添加偏移量 + + + + Remove Offset + 移除偏移量 + + + + Description: + 备注: + + + + + + Address: + 地址: + + + + + Pointer + 指针 + + + + Dissect Code + 分析代码 + + + + Regions + 区域 + + + + Path + 路径 + + + + Selected regions will be scanned + 选中区域将会被扫描 + + + + Currently scanning range: + 当前扫描范围: + + + + String references found: + 找到的 字符串引用: + + + + Jumps found: + 找到的 跳转: + + + + Calls found: + 找到的 调用: + + + + Entries that can't be decoded as utf-8 won't be included in referenced strings +Unchecking it makes ReferencedStringsWidget load slower but allows you to examine non-string pointers on it + 无法解码为 UTF-8 的条目将不会包含在引用的字符串中 +取消勾选此选项会导致 ReferencedStringsWidget 加载变慢,但允许你检查其中的非字符串指针 + + + + Discard invalid strings + 丢弃无效的字符串 + + + + Edit Instruction + 编辑指令 + + + + Instruction: + 指令: + + + + Multiple entries are separated with ; + 多个条目用分号(;)分隔 + + + + + + Type + 类型 + + + + Endianness + 字节序 + + + + Length + 长度 + + + + + Handle Signals + 处理信号 + + + + Signal + 信号 + + + + Stop & Print + 停止和打印 + + + + Pass to Program + 传递到程序 + + + + Hex Edit + 十六进制编辑 + + + + Refresh + 刷新 + + + + Processing + 处理中 + + + + Cancel + 取消 + + + + Filter Pointers + + + + + File 1: + + + + + + + Browse + + + + + File 2: + + + + + Scan for Pointers + + + + + Address + 地址 + + + + Depth + + + + + Scan Range + + + + + + + + 0x + + + + + + <-> + + + + + File Path: + + + + + Optional Parameters (0 or empty will use defaults) + + + + + Last Offset Scan Range + + + + + Minimum Chain Length + + + + + Last Offset Value + + + + + Max Results + + + + + Solve Circular References + + + + + Manage Scan Regions + + + + + Selected regions will be deleted from the current scan + + + + + Invert Selection + + + + + ID + + + + + Start Address + + + + + Size(bytes) + + + + + Load Address + + + + + Perms + 权限 + + + + File + 文件 + + + + Settings + 设置 + + + + General + 通用 + + + + Hotkeys + 热键 + + + + Code Injection + 代码注入 + + + + Memory View + 内存查看器 + + + + Debug + 调试 + + + + Java + Java + + + + Auto-update address table + 自动更新地址表 + + + + Update Interval + 更新间隔 + + + + Freeze Interval + 冻结间隔 + + + + GDB output: + GDB 输出: + + + + Async + 异步 + + + + Command + 命令 + + + + Command info + 命令信息 + + + + On start, automatically attach to processes with name matching one of the entries +Patterns at former positions have higher priority if regex is off + 启动时,自动附加到名称与其中一个条目匹配的进程 +如果正则表达式关闭,靠前的条目有更高的优先级 + + + + Auto-attach to processes named + 自动附加到的进程名为 + + + + Regex + 正则表达式 + + + + Language + 语言 + + + + Logo + 图标 + + + + Theme + 主题 + + + + Functions + 功能 + + + + Hotkey + 热键 + + + + Press shortcut + 按下快捷键 + + + + Clear + 清除 + + + + Code injection method: + 代码注入模式: + + + + Simp&le dlopen call + 简单的 dlopen 调用[&l] + + + + Advanced In&jection + 高级注入[&j] + + + + Bring Memory View to front when the inferior is stopped + + + + + Instructions shown per scroll in Disassembly View + + + + + Bytes shown per scroll in Hex View + + + + + GDB Path + GDB 路径 + + + + GDB Logging + GDB 日志记录 + + + + Interruption signal + 中断信号 + + + + Ignore SIGSEGV for Java processes (overrides signal settings if enabled) + 忽略 Java 进程的分段错误(SIGSEGV)(如果启用,将会覆盖信号设置) + + + + Reset Settings + 重置设置 + + + + Dialog + 对话框 + + + + Hit Esc to cancel and Ctrl+Enter to accept + 按 Esc 取消,按 Ctrl+Enter 接受 + + + + Parameters for tracing + 用于追踪的参数 + + + + Number of the instructions that'll be traced + 将被追踪的指令数量 + + + + Max trace count(1 or greater): + 最大追踪次数(1 或更多): + + + + Tracing will start if this condition is met + 如果满足此条件,将开始追踪 + + + + Trigger condition(Optional, gdb expression): + 触发条件(可选,gdb表达式): + + + + Tracing will stop whenever this condition is met + 每当满足此条件时,追踪将停止 + + + + Stop condition(Optional, gdb expression): + 停止条件(可选,gdb表达式): + + + + Step over instead of single step + 步过(Step over)而不是单步 + + + + Stop when tracing ends + 当追踪结束时停止 + + + + Collect registers + + + + + Select an address to track + 选择要跟踪的地址 + + + + Pointed Address + 指向的地址 + + + + Form + + + Bookmarks + 书签 + + + + Bookmarked Addresses + 书签地址 + + + + + Info + 信息 + + + + Comment + 注解 + + + + GDB Console + GDB 控制台 + + + + Send + 发送 + + + + CLI + CLI + + + + MI + MI + + + + Examine Referrers + 检查引用来源 + + + + + + + + Enter a string or a python regex + 输入字符串或 Python 正则表达式 + + + + + + + + Ignore case if checked + 如果选中则忽略大小写 + + + + + + + + Case sensitive + 大小写敏感 + + + + + + + Your string will be treated as a regex if checked + 如果选中,你的字符串将被视为正则表达式 + + + + + + + + Regex + 正则 + + + + + + + + Search(Enter) + 搜索(Enter) + + + + Functions + 函数 + + + + + + + + + + Address + 地址 + + + + Symbol + 符号 + + + + Enter the regex. Leave blank to see all functions + 请输入正则表达式。如果不输入任何内容,则会显示所有函数 + + + + + + Form + 表格 + + + + + TextLabel + 文本标签 + + + + Memory Regions + 内存区域 + + + + Regions + 区域 + + + + Perms + 权限 + + + + Offset + 偏移量 + + + + Path + 路径 + + + + Referenced Calls + 引用的调用 + + + + + Refcount + 引用计数 + + + + Referenced Strings and Values + 引用的字符串和值 + + + + + Value + + + + + Restore Instructions + 还原指令 + + + + Original OpCode + 原始操作码 + + + + Original Instruction + 原始指令 + + + + Search for Opcodes + 搜索操作码 + + + + Start + 开始 + + + + End + 结束 + + + + Opcodes + 操作码 + + + + StackTrace Information + 堆栈跟踪信息 + + + + Return Address + 返回地址 + + + + Tracer Status + + + + + Cancel + 取消 + + + + + Count + 计数 + + + + Source + + + + + + Stop + 停止 + + + + Refresh + 刷新 + + + + MainWindow + + + Freeze + 冻结 + + + + Freeze the value + 冻结这个值 + + + + Description + 备注 + + + + + Address + 地址 + + + + Type + 类型 + + + + + Value + + + + + Memory View + 内存查看器 + + + + Copy selected items to the address table + 将所选项目复制到地址表 + + + + Erase all the table contents + 删除地址表中所有内容 + + + + Refresh the address table[R] + 刷新地址表[R] + + + + Add Address Manually + 手动添加地址 + + + + Create or attach to a process + 创建或附加到进程 + + + + Open a cheat table + 打开一个PCT文件 + + + + Save current table to a file + 将当前表保存到文件 + + + + Wiki + 文档 + + + + About + 关于 + + + + No Process Selected + 未附加到任何进程 + + + + Open a gdb console + 打开一个 GDB 控制台 + + + + Configure options + 配置选项 + + + + Match count: 0 + 匹配次数:0 + + + + Previous + 先前的值 + + + + First Scan + 首次扫描 + + + + Next Scan + 再次扫描 + + + + Undo Scan + 撤销扫描 + + + + Hex + 十六进制 + + + + Scan Type: + 扫描类型: + + + + Value Type: + 值类型: + + + + Scan Scope: + 扫描范围: + + + + Endianness: + 字节序: + + + + Manage Scan Regions + + + + + Pointer Scanner + + + + + Sort + + + + + Clear + 清除 + + + + Fi&le + 文件[&l] + + + + Actio&ns + + + + + &Open + 打开[&O] + + + + &Save As... + + + + + &Scan + + + + + &Filter + + + + + Please select a Process + 请选择一个进程 + + + + Name of the Process: + 进程名称: + + + + PID + PID + + + + Username + 用户名 + + + + Process Name + 进程名称 + + + + Attach to the selected process + 附加到选中进程 + + + + + Open + 打开 + + + + Cancel + 取消 + + + + Open an executable + 打开一个可执行程序 + + + + Create Process[F1] + 创建进程[F1] + + + + Tracer + 追踪器 + + + + File + 文件 + + + + Save + 保存 + + + + Save as a text file + 保存为文本文件 + + + + MainWindow_MemoryView + + + Memory Viewer + 内存查看器 + + + + + + Address + 地址 + + + + Bytes + 字节 + + + + Opcodes + 操作码 + + + + Comment + 注解 + + + + Registers + 寄存器(Registers) + + + + Flags + 状态标志(Flags) + + + + Segment Registers + 段(Segment)寄存器 + + + + Show Float Registers + 显示浮点(Float)寄存器 + + + + Return Address + 返回地址 + + + + Frame Address + 帧地址 + + + + Value + + + + + Points to + 指向 + + + + V&iew + 视图[&i] + + + + &Debug + 调试[&D] + + + + &Tools + 工具[&T] + + + + Fi&le + 文件[&l] + + + + Help + 帮助 + + + + &Bookmarks + 书签[&B] + + + + &StackTrace Info + 堆栈跟踪信息[&S] + + + + &Inject .so file + 注入 .so 文件[&I] + + + + &Break + 中断[&B] + + + + &Run + 运行[&R] + + + + &Step[F7] + 步进[&S][F7] + + + + Step &Over[F8] + 步过[&O][F8] + + + + &Execute Till Return[Shift+F8] + 执行直到返回[&E][Shift+F8] + + + + &Toggle Breakpoint[F5] + 切换断点[&T][F5] + + + + B&reakpoints + 断点[&r] + + + + &Functions + 函数[&F] + + + + Set Address[Shift+F4] + 设置地址[Shift+F4] + + + + &Call Function + 调用函数[&C] + + + + &Load Trace + 加载追踪[&L] + + + + &libpince + &libpince + + + + &GDB Log File + &GDB 日志文件 + + + + &Search Opcode + 搜索操作码[&S] + + + + &Memory Regions + 内存区域[&M] + + + + &Dissect Code + 分析代码[&D] + + + + R&eferenced Strings + 引用的字符串[&e] + + + + Referenced &Calls + 引用的调用[&C] + + + + To&ggle Attach + 切换附加​​[&g] + + + + Restore Instructions + 还原指令 + + + + QPlatformTheme + + + OK + 确认 + + + + &Open + 打开[&O] + + + + &Save + 保存[&S] + + + + Cancel + 取消 + + + + Close + 关闭 + + + + Discard + 撤销 + + + + Apply + 应用 + + + + Reset + 复位 + + + + Restore Defaults + 恢复为默认 + + + + Help + 帮助 + + + + Save All + 全部保存 + + + + &Yes + 是[&Y] + + + + &No + 否[&N] + + + + Abort + 关于 + + + + Retry + 重试 + + + + Ignore + 忽略 + + + + N&o to All + 全部否[&o] + + + + Yes to &All + 全部是[&A] + + + + TabWidget + + + About PINCE + 关于 PINCE + + + + Contributors + 贡献者 + + + + License + 许可 + + + + Breakpoints + 断点 + + + + Interactive + 交互 + + + + No + No + + + + Type + 类型 + + + + Disp + 显示 + + + + Enabled + 启用 + + + + Address + 地址 + + + + Size + 大小 + + + + On Hit + 命中时 + + + + Hit Count + 命中计数 + + + + Condition + 条件 + + + + Raw + 原始 + + + + Floating Point Registers + 浮点寄存器 + + + + + Register + 寄存器 + + + + + Value + + + + + TranslationConstants + + + Pause the process + 暂停进程 + + + + Break the process + 中断进程 + + + + Continue the process + 继续进程 + + + + Toggle attach/detach + 切换附加 / 分离 + + + + Next Scan - Exact + 再次扫描 - 精确 + + + + Next Scan - Increased + 再次扫描 - 增加 + + + + Next Scan - Decreased + 再次扫描 - 减少 + + + + Next Scan - Changed + 再次扫描 - 改变 + + + + Next Scan - Unchanged + 再次扫描 - 未变 + + + + Error + 错误 + + + + Success + 成功 + + + + Information + 信息 + + + + GDB isn't initialized yet + GDB 尚未初始化 + + + + Unable to initialize GDB +You might want to reinstall GDB or use the system GDB +To change the current GDB path, check Settings->Debug + 无法初始化 GDB +你可能想重新安装 GDB 或者使用系统 GDB +要更改当前 GDB 路径,请检查 “设置” -> “调试” + + + + Edit + 编辑 + + + + Show as hexadecimal + 显示为十六进制 + + + + Show as decimal + 显示为十进制 + + + + Show as unsigned + 显示为无符号数 + + + + Show as signed + 显示为有符号数 + + + + Toggle + 切换 + + + + Toggle including children + 切换包括子项 + + + + Freeze + 冻结 + + + + Default + 默认 + + + + Incremental + 递增 + + + + Decremental + 递减 + + + + Browse this memory region + 浏览此内存区域 + + + + Disassemble this address + 反汇编这个地址 + + + + Delete + 删除 + + + + Delete selection + + + + + Cut + 剪切 + + + + Copy + 复制 + + + + Paste + 粘贴 + + + + Paste inside + 粘贴在里 + + + + Pointer scan for this address + + + + + Open pointer scanner + + + + + Find out what writes to this address + 找出是什么写入了这个地址 + + + + Find out what reads this address + 找出是什么读取了这个地址 + + + + Find out what accesses this address + 找出是什么访问了这个地址 + + + + Add to a new group + 加入一个新组 + + + + Create a new group + 创建一个新组 + + + + Group + + + + + Invalid clipboard content + 剪贴板内容无效 + + + + New Scan + 新的扫描 + + + + Match count: {} ({} shown) + 匹配次数:{}(显示了{}个) + + + + Match count: {} + 匹配次数:{} + + + + No Description + 无备注 + + + + Open PCT file(s) + 打开 PCT 文件 + + + + PINCE Cheat Table (*.pct) + PINCE 作弊表 (*.pct) + + + + Shared object library (*.so) + 共享对象库 (*.so) + + + + Trace File (*.trace) + 追踪文件 (*.trace) + + + + Pointer Scan Data (*.scandata) + + + + + Clear address table? + 清除地址表? + + + + File {} is inaccessible or contains invalid content + 文件 {} 无法访问或包含无效的内容 + + + + Save PCT file + 保存 PCT 文件 + + + + Cannot save to file + 无法保存为文件 + + + + Nice try, smartass + 好会试,聪明屁股 + + + + Selected process is not valid + 所选进程无效 + + + + You're debugging this process already + 你已经在调试这个进程了 + + + + That process is already being traced by {}, could not attach to the process + 此进程已被 {} 追踪,无法附加到此进程 + + + + Permission denied, could not attach to the process + 请求被拒,无法附加到此进程 + + + + An error occurred while trying to create process + 尝试创建进程时发生错误 + + + + Scan for + 在这里输入扫描内容 + + + + First Scan + 首次扫描 + + + + No Process Selected + 未附加到任何进程 + + + + [detached] + [已分离] + + + + [stopped] + [已停止] + + + + Process is running + 进程正在运行 + + + + Enter the new value + 输入新值 + + + + Enter the new description + 输入新备注 + + + + Edit Address + 编辑地址 + + + + Please select a process first + 请先选择一个进程 + + + + Select the target binary + 选择目标二进制文件 + + + + Enter the optional arguments + 输入可选参数 + + + + LD_PRELOAD .so path (optional) + LD_PRELOAD .so 路径(可选) + + + + Refresh + 刷新 + + + + Length is not valid + 长度无效 + + + + Length must be greater than 0 + 长度必须大于0 + + + + Can't parse the input + 无法解析输入 + + + + {} isn't a valid regex + {} 非有效正则表达式 + + + + Language settings will take effect upon the next restart + 语言设置会在下次重启时生效 + + + + You have changed the GDB path, reset GDB now? + 你已经更改了 GDB 路径,现在重置 GDB 吗? + + + + This will reset to the default settings +Proceed? + 这将会重置设置为默认 +继续? + + + + Mouse over on this text for examples + 将鼠标悬停在此文本上查看示例 + + + + asdf|qwer --> search for asdf or qwer +[as]df --> search for both adf and sdf +Use the char \ to escape special chars such as [ +\[asdf\] --> search for opcodes that contain [asdf] + asdf|qwer --> 搜索 asdf 或 qwer +[as]df --> 搜索 adf 和 sdf +使用字符 \ 转义特殊字符,如 [ +\[asdf\] --> 搜索包含 [asdf] 的操作码 + + + + Separate processes with {} + 使用 {} 分隔进程 + + + + This setting is unused in AppImage builds + + + + + Select the gdb binary + 选择 GDB 的可执行文件 + + + + Quitting current session will crash PINCE + 退出当前会话会导致 PINCE 崩溃 + + + + Use global hotkeys or the commands 'interrupt' and 'c&' to stop/run the inferior + 使用全局快捷键或者命令 'interrupt' 和 'c&' 停止/运行被调试程序(inferior) + + + + Hotkeys: +----------------------------- +Send: Enter | +Multi-line mode: Ctrl+Enter | +Complete command: Tab | +----------------------------- +Commands: +---------------------------------------------------------- +/clear: Clear the console | +phase-out: Detach from the current process | +phase-in: Attach back to the previously detached process | +--------------------------------------------------------------------------------------------------- +pince-init-so-file so_file_path: Initializes 'lib' variable | +pince-get-so-file-information: Get information about current lib | +pince-execute-from-so-file lib.func(params): Execute a function from lib | +# Check https://github.com/korcankaraokcu/PINCE/wiki#extending-pince-with-so-files for an example | +# CLI output mode doesn't work very well with .so extensions, use MI output mode instead | +--------------------------------------------------------------------------------------------------- +You can change the output mode from bottom right +Changing output mode only affects commands sent. Any other output coming from external sources(e.g async output) will be shown in MI format + 快捷键: +---------------------------- +发送 = Enter +多行模式 = Ctrl+Enter +补全命令 = Tab +---------------------------- +Commands: +---------------------------- +/clear: 清空控制台 +phase-out: 分离当前进程 +phase-in: 重新附加上一个分离的进程 +--------------------------------------------------------------------------------------------------- +pince-init-so-file so_file_path:初始化 'lib' 变量 +pince-get-so-file-information:获得当前 lib 的信息 +pince-execute-from-so-file lib.func(params):运行 lib 中的一个函数 +# 查看 https://github.com/korcankaraokcu/PINCE/wiki#extending-pince-with-so-files 获取示例 +# CLI 输出模式与 .so 扩展兼容不佳, 请改用 MI 输出模式 +--------------------------------------------------------------------------------------------------- +你可以在右下角切换输出模式 +更改输出模式仅影响命令的发送。来自外部源的任何其他输出(例如异步输出)将以 MI 格式显示 + + + + Break[{}] + 中断[{}] + + + + Run[{}] + 运行[{}] + + + + Toggle Attach[{}] + 切换附加[{}] + + + + Failed to set breakpoint at address {} + 在地址 {} 处设置断点失败 + + + + Failed to set watchpoint at address {} + 无法在地址 {} 设置监视点 + + + + Copy to Clipboard + 复制到剪切板 + + + + Go to expression + 跳转到表达式 + + + + Add this address to address list + 添加此地址到地址列表 + + + + Set Watchpoint + 设置监视点 + + + + Write Only + 仅写入 + + + + Read Only + 仅读取 + + + + Both + 两者都 + + + + Add/Change condition for breakpoint + 添加/更改断点条件 + + + + Delete Breakpoint + 删除断点 + + + + Enter the expression + 输入表达式 + + + + {} is invalid + {} 无效 + + + + Protection:{} | Base:{}-{} | Module:{} + 保护:{} | 基础:{}-{} | 模块:{} + + + + Invalid Region + 无效的区域 + + + + Cannot access memory at expression {} + 无法访问表达式 {} 处的内存 + + + + Referenced by: + 引用自: + + + + Press 'Ctrl+E' to see a detailed list of referrers + 按下 'Ctrl+E' 可以查看引用来源的详细列表 + + + + Memory Viewer - Paused + 内存查看器 - 已暂停 + + + + Memory Viewer - Currently debugging {} + 内存查看器 - 正调试 {} + + + + Memory Viewer - Running + 内存查看器 - 运行中 + + + + Enter the expression for condition, for instance: + +$eax==0x523 +$rax>0 && ($rbp<0 || $rsp==0) +printf($r10)==3 + 输入条件表达式,例如: + +$eax==0x523 +$rax>0 && ($rbp<0 || $rsp==0) +printf($r10)==3 + + + + Failed to set condition for address {} +Check terminal for details + 无法设置地址 {} 的条件 +检查终端了解详情 + + + + Full Stack + 全栈 + + + + Copy Return Address + 复制返回地址 + + + + Copy Frame Address + 复制帧地址 + + + + Stacktrace + 堆栈跟踪 + + + + Toggle stack from BP/SP register + + + + + Copy Address + 复制地址 + + + + Copy Value + 复制值 + + + + Copy Points to + 复制指向 + + + + Disassemble 'value' pointer address + 反汇编 '值' 指针地址 + + + + Show 'value' pointer in HexView + 在十六进制视图中显示 '值' 指针 + + + + Back + 后退 + + + + Show this address in HexView + 在十六进制视图中显示这个地址 + + + + Follow + 跟踪 + + + + Examine Referrers + 检查引用来源 + + + + Bookmark this address + 添加此地址到书签 + + + + Delete this bookmark + 删除此书签 + + + + Change comment + 更改注解 + + + + Go to bookmarked address + 跳转到已添加书签的地址 + + + + Toggle Breakpoint + 切换断点 + + + + Edit instruction + 编辑指令 + + + + Replace instruction with NOPs + 用 NOPs 替换指令 + + + + Find out which addresses this instruction accesses + 找出此指令访问的地址 + + + + Break and trace instructions + 中断并跟踪指令 + + + + Dissect this region + 分析此区域 + + + + Copy Bytes + 复制字节 + + + + Copy Opcode + 复制操作码 + + + + Copy Comment + 复制注解 + + + + Copy All + 复制全部 + + + + Enter the register expression(s) you want to track +Register names must start with $ +Each expression must be separated with a comma + +For instance: +Let's say the instruction is mov [rax+rbx],30 +Then you should enter $rax+$rbx +So PINCE can track address [rax+rbx] + +Another example: +If you enter $rax,$rbx*$rcx+4,$rbp +PINCE will track down addresses [rax],[rbx*rcx+4] and [rbp] + 请输入你想要追踪的寄存器表达式 +寄存器名称必须以$开头 +每个表达式必须用逗号分隔 + +例如: +假设指令是 mov [rax+rbx],30 +那么你应该输入 $rax+$rbx +这样 PINCE 就可以追踪地址 [rax+rbx] + +另一个例子: +如果你输入 $rax,$rbx*$rcx+4,$rbp +PINCE 将追踪地址 [rax],[rbx*rcx+4] 和 [rbp] + + + + This address has already been bookmarked + 这个地址已经被添加到书签了 + + + + Enter the comment for bookmarked address + 输入已书签地址的注解 + + + + Select the .so file + 选择 .so 文件 + + + + The file has been injected + 此文件已被注入 + + + + Failed to inject the .so file + 注入 .so 文件失败 + + + + Enter the expression for the function that'll be called from the inferior +You can view functions list from View->Functions + +For instance: +Calling printf("1234") will yield something like this +↓ +$28 = 4 + +$28 is the assigned convenience variable +4 is the result +You can use the assigned variable from the GDB Console + 请输入从被调试程序(inferior)中将要调用的函数表达式 +你可以从 视图 -> 函数 查看函数列表 + +例如: +调用 printf("1234") 将产生类似下面的结果 +↓ +$28 = 4 + +$28 是分配的便利变量 +4 是函数调用的结果 +你可以从 GDB 控制台中使用分配的变量 + + + + Failed to call the expression {} + 调用表达式 {} 失败 + + + + Invalid expression or address + 无效的表达式或地址 + + + + Invalid entries detected, refreshing the page + 检测到无效条目,正在刷新页面 + + + + Add new entry + 添加新条目 + + + + Enter the new value of register {} + 输入寄存器{}的新值 + + + + Enter the new value of flag {} + 输入标志(flag){}的新值 + + + + Restore this instruction + 还原此指令 + + + + Enter the hit count({} or higher) + 输入命中计数({}或更高) + + + + Hit count must be an integer + 命中计数必须是整数 + + + + Hit count can't be lower than {} + 命中计数不能少于 {} + + + + Change condition + 更改条件 + + + + Enable + 开启 + + + + Disable + 关闭 + + + + Disable after hit + 命中后关闭 + + + + Disable after X hits + 命中 X 次后关闭 + + + + Delete after hit + 命中后删除 + + + + Opcodes writing to the address {} + 写入地址 {} 的操作码 + + + + Opcodes reading from the address {} + 读取地址 {} 的操作码 + + + + Opcodes accessing to the address {} + 访问地址 {} 的操作码 + + + + Unable to track watchpoint at expression {} + 无法追踪表达式 {} 处的监视点 + + + + Unable to delete watchpoint at expression {} + 无法删除表达式 {} 处的监视点 + + + + Close + 关闭 + + + + Addresses accessed by instruction {} + 指令 {} 访问的地址 + + + + Unable to track breakpoint at expression {} + 无法跟踪表达式 {} 处的断点 + + + + Accessed by {} + 访问自 {} + + + + Unable to delete breakpoint at expression {} + 无法删除表达式 {} 处的断点 + + + + Max trace count must be greater than or equal to {} + 最大追踪次数必须大或等于 {} + + + + Save trace file + 保存追踪文件 + + + + Open trace file + 打开追踪文件 + + + + Expand All + 展开全部 + + + + Collapse All + 收起全部 + + + + Select a pointer map file + + + + + Scan + + + + + Scanning + + + + + Filter + + + + + Filtering + + + + + DEFINED + 定义 + + + + This symbol is defined. You can use its body as a gdb expression. For instance: + +void func(param) can be used as 'func' as a gdb expression + 该符号已定义。您可以将其作为 GDB 表达式使用其主体。例如: + +void func(param) 可以作为 'func' 作为 GDB 表达式使用 + + + + Copy Symbol + 复制符号 + + + + Here's some useful regex tips: +^quaso --> search for everything that starts with quaso +[ab]cd --> search for both acd and bcd + + How to interpret symbols: +A symbol that looks like 'func(param)@plt' consists of 3 pieces +func, func(param), func(param)@plt +These 3 functions will have different addresses +@plt means this function is a subroutine for the original one +There can be more than one of the same function +It means that the function is overloaded + 以下是一些有用的正则表达式提示: +^quaso --> 搜索以 quaso 开头的所有内容 +[ab]cd --> 搜索同时包含 acd 和 bcd 的内容 + + 如何解释符号: +一个看起来像 'func(param)@plt' 的符号由三个部分组成: +func, func(param), func(param)@plt +这三个函数将有不同的地址 +@plt 表示该函数是原始函数的一个子程序 +同一个函数可能有多个 +这意味着函数是重载的 + + + + New opcode is {} bytes long but old opcode is only {} bytes long +This will cause an overflow, proceed? + 新的操作码长度为 {} 字节,但旧的操作码只有 {} 字节长 +这将导致溢出,是否继续? + + + + {} isn't a valid expression + {} 不是有效的表达式 + + + + Log File of PID {} + PID {} 的日志文件 + + + + Contents of {} (only last {} bytes are shown) + {} 的内容(仅显示最后 {} 字节) + + + + ON + + + + + OFF + + + + + LOGGING: {} + 日志: {} + + + + Unable to read log file at {} + 无法读取位于 {} 的日志文件 + + + + Go to Settings->Debug to enable logging + 请转到“设置”->“调试”以启用日志记录 + + + + Invalid Regex + 无效的正则表达式 + + + + Here's some useful regex examples: +call|rax --> search for opcodes that contain call or rax +[re]cx --> search for both rcx and ecx +Use the char \ to escape special chars such as [ +\[rsp\] --> search for opcodes that contain [rsp] + 这里有一些有用的正则表达式示例: +call|rax --> 搜索包含 call 或 rax 的操作码 +[re]cx --> 搜索 rcx 和 ecx +使用字符 \ 转义特殊字符,如 [ +\[rsp\] --> 搜索包含 [rsp] 的操作码 + + + + Copy Addresses + 复制地址 + + + + Copy Offset + 复制偏移量 + + + + Copy Path + 复制路径 + + + + Start + 开始 + + + + Currently scanning region: + 当前扫描区域: + + + + Cancel + 取消 + + + + Scan finished + 扫描完成 + + + + Scan was canceled + 扫描被取消了 + + + + Select at least one region + 选中至少一个区域 + + + + You need to dissect code first +Proceed? + 你需要先分析代码 +是否继续? + + + + Waiting for breakpoint to trigger + 等待断点被触发 + + + + Tracing has been completed + 追踪已完成 + + + + Not + + + + + Exact + 精确 + + + + Increased + 增加 + + + + Increased by + 增加自 + + + + Decreased + 减少 + + + + Decreased by + 减少自 + + + + Less Than + 少于 + + + + More Than + 多于 + + + + Between + 介于 + + + + Changed + 改变 + + + + Unchanged + 未变 + + + + Unknown Value + 未知值 + + + + Basic + 基础 + + + + Normal + 正常 + + + + Read+Write + 读+写 + + + + Full + 全部 + + + + Host + 主机 + + + + Little + + + + + Big + + + + + Show in HexView + 在十六进制视图中显示 + + + + Show in Disassembler + 在反汇编器中显示 + + + diff --git a/install.sh b/install.sh new file mode 100755 index 00000000..846b7798 --- /dev/null +++ b/install.sh @@ -0,0 +1,234 @@ +#!/bin/bash +: ' +Copyright (C) 2016-2017 Korcan Karaokçu + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +' + +if [ "$(id -u)" = "0" ]; then + echo "Please do not run this script as root!" + exit 1 +fi + +SCRIPTDIR=$(cd -- "$(dirname -- "$0")" && pwd -P) +cd $SCRIPTDIR +if [ ! -d ".git" ]; then + echo "Error! Could not find \".git\" folder!" + echo "This can happen if you downloaded the ZIP file instead of cloning through git." + echo "Please clone the PINCE repository using the \"--recursive\" flag and try again!" + echo "For more information, please follow the installation instructions on GitHub." + exit 1 +fi + +CURRENT_USER="$(whoami)" + +if [ -z "$NUM_MAKE_JOBS" ]; then + NUM_MAKE_JOBS=$(lscpu -p=core | uniq | awk '!/#/' | wc -l) + MAX_NUM_MAKE_JOBS=8 + if [ "$NUM_MAKE_JOBS" -gt "$MAX_NUM_MAKE_JOBS" ]; then # set an upper limit to prevent Out-Of-Memory + NUM_MAKE_JOBS=$MAX_NUM_MAKE_JOBS + fi + if ! echo "$NUM_MAKE_JOBS" | grep -Eq '^[0-9]+$'; then # fallback + NUM_MAKE_JOBS=$MAX_NUM_MAKE_JOBS + fi +fi + +exit_on_error() { + if [ "$?" -ne 0 ]; then + echo + echo "Error occured while installing PINCE, check the output above for more information" + echo "Installation failed." + exit 1 + fi +} + +# assumes you're in libscanmem directory +compile_libscanmem() { + cmake -DCMAKE_BUILD_TYPE=Release . || return 1 + make -j"$NUM_MAKE_JOBS" || return 1 + chown -R "${CURRENT_USER}":"${CURRENT_USER}" . # give permissions for normal user to change file + return 0 +} + +install_libscanmem() { + echo "Downloading libscanmem" + git submodule update --init --recursive || return 1 + + if [ ! -d "libpince/libscanmem" ]; then + mkdir libpince/libscanmem + chown -R "${CURRENT_USER}":"${CURRENT_USER}" libpince/libscanmem + fi + ( + echo "Entering libscanmem directory" + cd libscanmem-PINCE || return 1 + if [ -f "./libscanmem.so" ]; then + echo "Recompile libscanmem? [y/n]" + read -r answer + if echo "$answer" | grep -iq "^[Yy]"; then + make clean + compile_libscanmem || return 1 + fi + else + compile_libscanmem || return 1 + fi + cp --preserve libscanmem.so ../libpince/libscanmem/ + cp --preserve wrappers/scanmem.py ../libpince/libscanmem + echo "Exiting libscanmem directory" + ) || return 1 + return 0 +} + +install_libptrscan() { + echo "Downloading libptrscan" + + if [ ! -d "libpince/libptrscan" ]; then + mkdir libpince/libptrscan + chown -R "${CURRENT_USER}":"${CURRENT_USER}" libpince/libptrscan + fi + ( + cd libpince/libptrscan + # Source code download as we might be forced to distribute it due to licence + curl -L -O https://github.com/kekeimiku/PointerSearcher-X/archive/refs/tags/v0.7.4-dylib.tar.gz || return 1 + # Actual .so and py wrapper + curl -L -o libptrscan.tar.gz https://github.com/kekeimiku/PointerSearcher-X/releases/download/v0.7.4-dylib/libptrscan_pince-x86_64-unknown-linux-gnu.tar.gz || return 1 + tar xf libptrscan.tar.gz --strip-components 1 || return 1 + rm -f libptrscan.tar.gz + cd ../.. + ) || return 1 + return 0 +} + +ask_pkg_mgr() { + echo + echo "Your distro is not officially supported! Trying to install anyway." + echo "Please choose your package manager." + echo "1) APT" + echo "2) Pacman" + echo "3) DNF" + echo "4) Zypper" + echo "5) None of the above" + + read -r -p "Choose: " OPTION + OPTION=$(echo $OPTION | tr '[:lower:]' '[:upper:]') + + case $OPTION in + 1|*APT*) + OS_NAME="Debian" + ;; + 2|*PACMAN*) + OS_NAME="Arch" + ;; + 3|*DNF*) + OS_NAME="Fedora" + ;; + 4|*ZYPPER*) + OS_NAME="SUSE" + ;; + *) + return 1 + ;; + esac + + return 0 +} + +# About xcb packages -> https://github.com/cdgriffith/FastFlix/wiki/Common-questions-and-problems +PKG_NAMES_ALL="python3-pip gdb cmake" +PKG_NAMES_DEBIAN="$PKG_NAMES_ALL python3-dev python3-venv pkg-config qt6-l10n-tools libcairo2-dev libgirepository1.0-dev libxcb-randr0-dev libxcb-xtest0-dev libxcb-xinerama0-dev libxcb-shape0-dev libxcb-xkb-dev libxcb-cursor0" +PKG_NAMES_SUSE="$PKG_NAMES_ALL gcc python3-devel qt6-tools-linguist typelib-1_0-Gtk-3_0 cairo-devel gobject-introspection-devel make" +PKG_NAMES_FEDORA="$PKG_NAMES_ALL python3-devel qt6-linguist redhat-lsb cairo-devel gobject-introspection-devel cairo-gobject-devel" +PKG_NAMES_ARCH="python-pip qt6-tools gdb cmake lsb-release pkgconf gobject-introspection-runtime" # arch defaults to py3 nowadays + +INSTALL_COMMAND="install" + +set_install_vars() { + case $1 in + *SUSE*) + PKG_MGR="zypper" + PKG_NAMES="$PKG_NAMES_SUSE" + LRELEASE_CMD="lrelease6" + ;; + *Arch*) + PKG_MGR="pacman" + PKG_NAMES="$PKG_NAMES_ARCH" + INSTALL_COMMAND="-S --needed" + LRELEASE_CMD="/usr/lib/qt6/bin/lrelease" + ;; + *Fedora*) + PKG_MGR="dnf -y" + PKG_NAMES="$PKG_NAMES_FEDORA" + LRELEASE_CMD="lrelease-qt6" + ;; + *Debian*|*Ubuntu*) + PKG_MGR="apt -y" + PKG_NAMES="$PKG_NAMES_DEBIAN" + LRELEASE_CMD="/usr/lib/qt6/bin/lrelease" + ;; + *) + return 1 + ;; + esac + + return 0 +} + +compile_translations() { + ${LRELEASE_CMD} i18n/ts/* + mkdir -p i18n/qm + mv i18n/ts/*.qm i18n/qm/ +} + +LSB_RELEASE="$(command -v lsb_release)" +if [ -n "$LSB_RELEASE" ]; then + OS_NAME="$(${LSB_RELEASE} -d -s)" +else + # shellcheck disable=SC1091 + . /etc/os-release + OS_NAME="$NAME" +fi + +set_install_vars $OS_NAME + +if [ "$?" -ne 0 ]; then + ask_pkg_mgr + if [ "$?" -ne 0 ]; then + echo + echo "Sorry, your distro is not supported!" + exit 1 + fi + + set_install_vars $OS_NAME +fi + +# shellcheck disable=SC2086 +sudo ${PKG_MGR} ${INSTALL_COMMAND} ${PKG_NAMES} || exit_on_error + +# Prepare Python virtual environment +if [ ! -d ".venv/PINCE" ]; then + python3 -m venv .venv/PINCE +fi +. .venv/PINCE/bin/activate +pip3 install --upgrade pip || exit_on_error + +# shellcheck disable=SC2086 +pip3 install -r requirements.txt || exit_on_error + +install_libscanmem || exit_on_error +install_libptrscan || exit_on_error + +compile_translations || exit_on_error + +echo +echo "PINCE has been installed successfully!" +echo "Now, just run 'sh PINCE.sh' from terminal" diff --git a/install_pince.sh b/install_pince.sh deleted file mode 100755 index c24b9e70..00000000 --- a/install_pince.sh +++ /dev/null @@ -1,102 +0,0 @@ -#!/bin/bash -: ' -Copyright (C) 2016-2017 Korcan Karaokçu - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -' - -# this file cannot (or any file) be named `install.sh` since libtoolize(automake) will not work properly if it does -# it will create the necessary files in PINCEs directory instead of scanmems, which will result in having to run `sh autogen.sh` -# twice, see this link https://github.com/protocolbuffers/protobuf/issues/149#issuecomment-473092810 - - -CURRENT_USER="$(who mom likes | awk '{print $1}')" - -# assumes you're in scanmem directory -compile_scanmem() { - sh autogen.sh - ./configure --prefix=/usr - make -j $(grep -m 1 "cpu cores" /proc/cpuinfo | cut -d: -f 2 | xargs) libscanmem.la - chown -R "${CURRENT_USER}":"${CURRENT_USER}" . # give permissions for normal user to change file -} - -install_scanmem() { - echo "Downloading scanmem" - git submodule update --init --recursive - - if [ ! -d "libpince/libscanmem" ]; then - mkdir libpince/libscanmem - chown -R "${CURRENT_USER}":"${CURRENT_USER}" libpince/libscanmem - fi - ( - echo "Entering scanmem" - cd scanmem || exit - if [ -d "./.libs" ]; then - echo "Recompile scanmem? [y/n]" - read -r answer - if echo "$answer" | grep -iq "^[Yy]"; then - compile_scanmem - fi - else - compile_scanmem - fi - cp --preserve .libs/libscanmem.so ../libpince/libscanmem/libscanmem.so - cp --preserve gui/scanmem.py ../libpince/libscanmem - cp --preserve gui/misc.py ../libpince/libscanmem - echo "Exitting scanmem" - ) - # required for relative import, since it will throw an import error if it's just `import misc` - sed -i 's/import misc/from \. import misc/g' libpince/libscanmem/scanmem.py -} - -OS_NAME="Debian" -PKG_MGR="apt-get" -INSTALL_COMMAND="install" - -PKG_NAMES_ALL="python3-pip gdb" -PKG_NAMES_DEBIAN="$PKG_NAMES_ALL python3-pyqt5 libtool libreadline-dev intltool" -PKG_NAMES_SUSE="$PKG_NAMES_ALL python3-qt5" -PKG_NAMES_ARCH="python-pip python-pyqt5 readline intltool gdb lsb-release" # arch defaults to py3 nowadays -PKG_NAMES="$PKG_NAMES_DEBIAN" -PKG_NAMES_PIP="psutil pexpect distorm3 pygdbmi" -PIP_COMMAND="pip3" - -LSB_RELEASE="$(command -v lsb_release)" -if [ -n "$LSB_RELEASE" ] ; then - OS_NAME="$(${LSB_RELEASE} -d -s)" -else - . /etc/os-release - OS_NAME="$NAME" -fi - -case "$OS_NAME" in -*SUSE*) - PKG_MGR="zypper" - PKG_NAMES="$PKG_NAMES_SUSE" - ;; -*Arch*) - PKG_MGR="pacman" - PKG_NAMES="$PKG_NAMES_ARCH" - INSTALL_COMMAND="-S" - PIP_COMMAND="pip" - ;; -esac - -sudo ${PKG_MGR} ${INSTALL_COMMAND} ${PKG_NAMES} -sudo ${PIP_COMMAND} install ${PKG_NAMES_PIP} - -install_scanmem - -echo "PINCE has been installed successfully!" -echo "Now, just run 'sh PINCE.sh' from terminal" diff --git a/libpince/GuiUtils.py b/libpince/GuiUtils.py deleted file mode 100644 index dfee4781..00000000 --- a/libpince/GuiUtils.py +++ /dev/null @@ -1,323 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Copyright (C) 2016-2017 Korcan Karaokçu - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -""" -# PyQt5 isn't needed to run tests with travis. To reduce the testing time and complexity, we ignore the PyQt5 imports -# There'll be no tests for PyQt5 related functions in GuiUtils.py -try: - from PyQt5.QtWidgets import QDesktopWidget -except ImportError: - pass -from . import SysUtils, type_defs, common_regexes - - -#:tag:GUI -def get_icons_directory(): - """Gets the directory of the icons - - Returns: - str: Path to the icons directory - """ - return SysUtils.get_current_script_directory() + "/media/icons" - - -#:tag:GUI -def center(window): - """Center the given window to desktop - - Args: - window (QMainWindow, QWidget etc.): The window that'll be centered to desktop - """ - window.move(QDesktopWidget().availableGeometry().center() - window.frameGeometry().center()) - - -#:tag:GUI -def center_to_parent(window): - """Center the given window to it's parent - - Args: - window (QMainWindow, QWidget etc.): The window that'll be centered to it's parent - """ - window.move(window.parent().frameGeometry().center() - window.frameGeometry().center()) - - -#:tag:GUI -def center_to_window(window_secondary, window_main): - """Center the given window_secondary to window_main - - Args: - window_secondary (QMainWindow, QWidget etc.): The window that'll be centered to window_main - window_main (QMainWindow, QWidget etc.): The window that window_secondary will centered to - """ - window_secondary.move(window_main.frameGeometry().center() - window_secondary.frameGeometry().center()) - - -#:tag:GUI -def center_scroll_bar(QScrollBar): - """Center the given scrollbar - - Args: - QScrollBar (QScrollbar): The scrollbar that'll be centered - """ - maximum = QScrollBar.maximum() - minimum = QScrollBar.minimum() - QScrollBar.setValue((maximum + minimum) // 2) - - -#:tag:GUI -def fill_value_combobox(QCombobox, current_index=type_defs.VALUE_INDEX.INDEX_4BYTES): - """Fills the given QCombobox with value_index strings - - Args: - QCombobox (QCombobox): The combobox that'll be filled - current_index (int): Can be a member of type_defs.VALUE_INDEX - """ - for key in type_defs.index_to_text_dict: - QCombobox.addItem(type_defs.index_to_text_dict[key]) - QCombobox.setCurrentIndex(current_index) - - -#:tag:GUI -def get_current_row(QObject): - """Returns the currently selected row index for the given QObject - If you try to use only selectionModel().currentIndex().row() for this purpose, you'll get the last selected row even - if it was unselected afterwards. This is why this function exists, it checks the selection state before returning - the selected row - - Args: - QObject (QObject): Self-explanatory - - Returns: - int: Currently selected row. Returns -1 if nothing is selected - - Note: - This function doesn't work properly when used within signals such as currentItemChanged, currentIndexChanged, - currentChanged and currentRowChanged. Use the row, item, QModelIndex or whatever the signal provides instead. - This bug occurs because those signals only update the changed row, not the selectionModel. This causes - selectionModel().selectedRows() to return None and this function to behave improperly - - For developers: You can use the regex \.current.*\.connect to search signals if a cleanup is needed - """ - if QObject.selectionModel().selectedRows(): - return QObject.selectionModel().currentIndex().row() - return -1 - - -#:tag:GUI -def get_current_item(QObject): - """Returns the currently selected item for the given QObject - If you try to use only selectionModel().currentItem() for this purpose, you'll get the last selected item even - if it was unselected afterwards. This is why this function exists, it checks the selection state before returning - the selected item. Unlike get_current_row, this function can be used with QTreeWidget - - Args: - QObject (QObject): Self-explanatory - - Returns: - Any: Currently selected item. Returns None if nothing is selected - - Note: - This function doesn't work properly when used within signals such as currentItemChanged, currentIndexChanged, - currentChanged and currentRowChanged. Use the row, item, QModelIndex or whatever the signal provides instead. - This bug occurs because those signals only update the changed row, not the selectionModel. This causes - selectionModel().selectedRows() to return None and this function to behave improperly - - For developers: You can use the regex \.current.*\.connect to search signals if a cleanup is needed - """ - if QObject.selectionModel().selectedRows(): - return QObject.currentItem() - - -#:tag:GUI -def delete_menu_entries(QMenu, QAction_list): - """Deletes given QActions from the QMenu recursively and cleans up the remaining redundant separators and menus - Doesn't support menus that includes types other than actions, separators and menus - - Args: - QMenu (QMenu): Self-explanatory - QAction_list (list): List of QActions. Leave blank if you just want to clean the redundant separators up - """ - - def remove_entries(menu): - for action in menu.actions(): - try: - QAction_list.index(action) - except ValueError: - if action.menu(): - remove_entries(action.menu()) - else: - menu.removeAction(action) - - def clean_entries(menu): - for action in menu.actions(): - if action.menu(): - clean_entries(action.menu()) - if not action.menu().actions(): - menu.removeAction(action.menu().menuAction()) - elif action.isSeparator(): - actions = menu.actions() - current_index = actions.index(action) - if len(actions) == 1 or (current_index == 0 and actions[1].isSeparator()) or \ - (current_index == -1 and actions[-2].isSeparator()) or \ - (actions[current_index - 1].isSeparator() and actions[current_index + 1].isSeparator()): - menu.removeAction(action) - - remove_entries(QMenu) - clean_entries(QMenu) - - -# TODO: This is a really bad design pattern, remove this function after moving classes to their own files -#:tag:GUI -def search_parents_by_function(qt_object, func_name): - """Search for func_name in the parents of given qt_object. Once function is found, parent that possesses func_name - is returned - - Args: - qt_object (object): The object that'll be searched for it's parents - func_name (str): The name of the function that'll be searched - """ - while qt_object is not None: - qt_object = qt_object.parent() - if func_name in dir(qt_object): - return qt_object - - -#:tag:GUI -def get_layout_widgets(layout): - """Returns the widgets of a layout as a list - - Args: - layout: Self-explanatory - - Returns: - list: A list that contains the widgets of the given layout - """ - return [layout.itemAt(x).widget() for x in range(layout.count())] - - -#:tag:ValueType -def valuetype_to_text(value_index=int, length=0, zero_terminate=True, hex_repr=False): - """Returns a str according to given parameters - - Args: - value_index (int): Determines the type of data. Can be a member of type_defs.VALUE_INDEX - length (int): Length of the data. Only used when the value_index is INDEX_STRING or INDEX_AOB. Ignored otherwise - zero_terminate (bool): If False, ",NZT" will be appended to str. Only used when value_index is INDEX_STRING. - Ignored otherwise. "NZT" stands for "Not Zero Terminate" - hex_repr (bool): If text should be represented as a hexadecimal value - - Returns: - str: A str generated by given parameters - str "out of bounds" is returned if the value_index doesn't match the dictionary - - Examples: - value_index=type_defs.VALUE_INDEX.INDEX_STRING_UTF16, length=15, zero_terminate=False--▼ - returned str="String_UTF16[15],NZT" - value_index=type_defs.VALUE_INDEX.INDEX_AOB, length=42-->returned str="AoB[42]" - """ - returned_string = type_defs.index_to_text_dict.get(value_index, "out of bounds") - if type_defs.VALUE_INDEX.is_string(value_index): - returned_string = returned_string + "[" + str(length) + "]" - if not zero_terminate: - returned_string += ",NZT" - elif value_index is type_defs.VALUE_INDEX.INDEX_AOB: - returned_string += "[" + str(length) + "]" - if hex_repr and not type_defs.VALUE_INDEX.has_length(value_index): - returned_string += "(h)" - return returned_string - - -#:tag:ValueType -def text_to_valuetype(string): - """Returns a tuple of parameters of the function valuetype_to_text evaluated according to given str - - Args: - string (str): String must be generated from the function valuetype_to_text - - Returns: - tuple: A tuple consisting of parameters of the function valuetype_to_text--▼ - value_index, length, zero_terminate, byte_length, hex_repr - - If value_index doesn't contain length, length will be returned as -1 - If value_index is INDEX_STRING, byte_length will be returned as -1 - - Examples: - string="String_UTF8[15],NZT"--▼ - value_index=type_defs.VALUE_INDEX.INDEX_STRING_UTF8, length=15, zero_terminate=False, byte_length=-1 - string="AoB[42]"-->value_index=type_defs.VALUE_INDEX.INDEX_AOB, length=42, None, 42 - string="Double"-->value_index=type_defs.VALUE_INDEX.INDEX_DOUBLE, length=-1, None, 8 - """ - index, length, hex_repr = -1, -1, False - zero_terminate = None - for key in type_defs.text_to_index_dict: - if string.startswith(key): - index = type_defs.text_to_index_dict[key] - break - byte_len = type_defs.index_to_valuetype_dict.get(index, [-1])[0] - if type_defs.VALUE_INDEX.has_length(index): - length = int(common_regexes.valuetype_length.search(string).group(1)) - byte_len = length - if type_defs.VALUE_INDEX.is_string(index): - byte_len = -1 - if common_regexes.valuetype_nzt.search(string): - zero_terminate = False - else: - zero_terminate = True - elif "(h)" in string: - hex_repr = True - return index, length, zero_terminate, byte_len, hex_repr - - -#:tag:GUI -def change_text_length(string, length): - """Changes the length of the given str to the given length - - Args: - string (str): String must be generated from the function valuetype_to_text - length (int,str): New length - - Returns: - str: The changed str - int: -1 is returned if the value_index of the given string isn't INDEX_STRING or INDEX_AOB - """ - index = text_to_valuetype(string)[0] - if type_defs.VALUE_INDEX.has_length(index): - return common_regexes.valuetype_length.sub("[" + str(length) + "]", string) - return -1 - - -#:tag:GUI -def contains_reference_mark(string): - """Checks if given string contains the reference mark - - Args: - string (str): String that'll be checked for the reference mark - - Returns: - bool: True if given string contains the reference mark, False otherwise - """ - return True if common_regexes.reference_mark.search(string) else False - - -#:tag:GUI -def append_shortcut_to_tooltip(QObject, QShortcut): - """Appends key string of the given QShortcut to the toolTip of the given QObject - - Args: - QObject (QObject): Self-explanatory - QShortcut (QShortcut): Self-explanatory - """ - QObject.setToolTip(QObject.toolTip() + "[" + QShortcut.key().toString() + "]") diff --git a/libpince/Notes.txt b/libpince/Notes.txt deleted file mode 100644 index 5b447b7d..00000000 --- a/libpince/Notes.txt +++ /dev/null @@ -1,37 +0,0 @@ -2/5/2018 - All docstrings that has "\" character in them should start with "r" to make themselves interpreted as raw strings. Otherwise SysUtils.get_docstrings() won't be able to escape "\" by itself. Check these functions for examples: -SysUtils.get_comments_of_variables() -GDB_Engine.create_process() -GDB_Engine.attach() -GDB_Engine.init_gdb() - -2/9/2018 - All functions with docstrings should have their subfunctions written after their docstrings. For instance: - - def test(): - """documentation for test""" - def subtest(): - return - return - -If test is declared like above, test.__doc__ will return "documentation for test" correctly. This is the correct documentation - - def test(): - def subtest(): - return - """documentation for test""" - return - -If test is declared like above, test.__doc__ will return a null string because subtest blocks the docstring. This is the wrong documentation -All functions that has a subfunction can be found with the regex def.*:.*\s+def - -2/9/2018 - Seek methods of all file handles that read directly from the memory(/proc/pid/mem etc.) should be wrapped in a try/except block that catches both OSError and ValueError exceptions. For instance: - - try: - self.memory.seek(start_addr) - except (OSError, ValueError): - break - -OSError handles I/O related errors and ValueError handles the off_t limit error that prints "cannot fit 'int' into an offset-sized integer" - -12/9/2018 - All namedtuples must have the same field name with their variable names. This makes the namedtuple transferable via pickle. For instance: - - tuple_examine_expression = collections.namedtuple("tuple_examine_expression", "all address symbol") \ No newline at end of file diff --git a/libpince/PINCEBackend.py b/libpince/PINCEBackend.py deleted file mode 100644 index dd67c7b9..00000000 --- a/libpince/PINCEBackend.py +++ /dev/null @@ -1,114 +0,0 @@ -""" - GameConquerorBackend: communication with libscanmem - - Copyright (C) 2010,2011,2013 Wang Lu - Copyright (C) 2018 Sebastian Parschauer - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -""" - -import ctypes, tempfile, os, sys -from ctypes import byref -import re - -# taken from https://github.com/scanmem/scanmem/blob/6a5e2e86ebacd87bed132dea354433d722081abf/gui/backend.py -# see https://github.com/scanmem/scanmem/issues/225 for future improvements -class PINCEBackend(): - BACKEND_FUNCTIONS = { - "sm_init": (ctypes.c_bool, ), - "sm_cleanup": (None, ), - "sm_set_backend": (None, ), - "sm_backend_exec_cmd" : (None, ctypes.c_char_p), - "sm_get_num_matches" : (ctypes.c_ulong, ), - "sm_get_version" : (ctypes.c_char_p, ), - "sm_get_scan_progress" : (ctypes.c_double, ), - "sm_set_stop_flag" : (None, ctypes.c_bool), - "sm_process_is_dead": (ctypes.c_bool, ) - } - - """ - scans the current dirrectory (PINCE/libpince) for libscanmem - @param libname - """ - def __init__(self, libname="scanmem.so"): - self.lib = ctypes.CDLL(os.path.dirname(__file__) + os.path.sep + libname) - self.libc = ctypes.CDLL("libc.so.6") - self.init_sm_funcs() - self.lib.sm_init() - - def init_sm_funcs(self): - for k, v in PINCEBackend.BACKEND_FUNCTIONS.items(): - f = getattr(self.lib, k) - f.restype = v[0] - f.argtypes = v[1:] - - def sm_cleanup(self): - self.lib.sm_cleanup() - - # Used for most of the things, like searching etc - def sm_exec_cmd(self, cmd, get_output = False): - if get_output: - with tempfile.TemporaryFile() as directed_file: - backup_stdout_fileno = os.dup(sys.stdout.fileno()) - os.dup2(directed_file.fileno(), sys.stdout.fileno()) - - self.lib.sm_backend_exec_cmd(ctypes.c_char_p(cmd.encode("ascii"))) - - os.dup2(backup_stdout_fileno, sys.stdout.fileno()) - os.close(backup_stdout_fileno) - directed_file.seek(0) - return directed_file.read() - else: - self.lib.sm_backend_exec_cmd(ctypes.c_char_p(cmd.encode("ascii"))) - def sm_get_num_matches(self): - return self.lib.sm_get_num_matches() - - def sm_get_version(self): - return self.lib.sm_get_version() - - def sm_get_scan_progress(self): - return self.lib.sm_get_scan_progress() - - def sm_set_stop_flag(self, stop_flag): - self.lib.sm_set_stop_flag(stop_flag) - - def sm_process_is_dead(self, pid): - self.lib.sm_process_is_dead(pid) - - """ - @param string the string that's returned by libscanmem - @returns a dictionary with the key as the n:th match, and value the rest of the structure that's returned - """ - def parse_string(self, string): - # based on information from the scanmem source, the format for a line from scanmem is: - # n address region id* offset region type value type(s) - #[4425] 7fd3ef3cf488, 31 + 8cf488, misc, 12, [I8 ] - # * region id = line number in /proc/pid/maps - # region id can later be probably be used to get like "executable + offset" - if string == None: - return None - ret = dict() - string = string.decode("utf-8").splitlines() - if string == None: - return None - line_match = re.compile(r"^\[ *(\d+)\] +([\da-f]+), +\d+ \+ +([\da-f]+), +(\w+), (.*), +\[([\w ]+)\]$") - for row in string: - (n, address, offset, region_type, value, t) = line_match.match(row).groups() - ret[n] = { - "address": address, - "offset": offset, - "region_type": region_type, - "value": value, - "type": t - } diff --git a/libpince/GDB_Engine.py b/libpince/debugcore.py similarity index 58% rename from libpince/GDB_Engine.py rename to libpince/debugcore.py index d8a947bb..981b0d82 100644 --- a/libpince/GDB_Engine.py +++ b/libpince/debugcore.py @@ -18,45 +18,36 @@ from threading import Lock, Thread, Condition from time import sleep, time from collections import OrderedDict, defaultdict -import pexpect, os, ctypes, pickle, json, shelve, re, struct, io -from . import SysUtils, type_defs, common_regexes +import pexpect, os, sys, ctypes, pickle, shelve, re, struct, io, traceback +from . import utils, typedefs, regexes self_pid = os.getpid() -libc = ctypes.CDLL('libc.so.6') +libc = ctypes.CDLL("libc.so.6") +system_endianness = typedefs.ENDIANNESS.LITTLE if sys.byteorder == "little" else typedefs.ENDIANNESS.BIG -#:tag:GDBInformation -#:doc: # A boolean value. True if gdb is initialized, False if not gdb_initialized = False -#:tag:InferiorInformation -#:doc: -# An integer. Can be a member of type_defs.INFERIOR_ARCH +# An integer. Can be a member of typedefs.INFERIOR_ARCH inferior_arch = int -#:tag:InferiorInformation -#:doc: -# An integer. Can be a member of type_defs.INFERIOR_STATUS +# An integer. Can be a member of typedefs.INFERIOR_STATUS inferior_status = -1 -#:tag:InferiorInformation -#:doc: # An integer. PID of the current attached/created process currentpid = -1 -#:tag:GDBInformation -#:doc: -# An integer. Can be a member of type_defs.STOP_REASON +# An integer. Can be a member of typedefs.STOP_REASON stop_reason = int -#:tag:GDBInformation -#:doc: -# A dictionary. Holds breakpoint addresses and what to do on hit -# Format: {address1:on_hit1, address2:on_hit2, ...} +# A dictionary. Holds breakpoint numbers and what to do on hit +# Format: {bp_num1:on_hit1, bp_num2:on_hit2, ...} breakpoint_on_hit_dict = {} -#:tag:GDBInformation -#:doc: +# A dictionary. Holds address and aob of instructions that were nop'ed out +# Format: {address1:orig_instruction1_aob, address2:orig_instruction2_aob, ...} +modified_instructions_dict = {} + # If an action such as deletion or condition modification happens in one of the breakpoints in a list, others in the # same list will get affected as well # Format: [[[address1, size1], [address2, size2], ...], [[address1, size1], ...], ...] @@ -64,93 +55,78 @@ child = object # this object will be used with pexpect operations -#:tag:ConditionsLocks -#:doc: # This Lock is used by the function send_command to ensure synchronous execution lock_send_command = Lock() -#:tag:ConditionsLocks -#:doc: # This condition is notified whenever status of the inferior changes # Use the variable inferior_status to get information about inferior's status # See PINCE's CheckInferiorStatus class for an example status_changed_condition = Condition() -#:tag:ConditionsLocks -#:doc: # This condition is notified if the current inferior gets terminated # See PINCE's AwaitProcessExit class for an example process_exited_condition = Condition() -#:tag:bool -#:doc: -# Bool for telling CheckInferiorStatus if it's a "real" pause or just for a split -# second that it will continue very soon, used in execute_with_temporary_interruption -temporary_execution_bool = False - -#:tag:ConditionsLocks -#:doc: # This condition is notified if gdb starts to wait for the prompt output # See function send_command for an example gdb_waiting_for_prompt_condition = Condition() -#:tag:GDBInformation -#:doc: # A string. Stores the output of the last command gdb_output = "" -#:tag:GDBInformation -#:doc: -# An instance of type_defs.RegisterQueue. Updated whenever GDB receives an async event such as breakpoint modification +# An instance of typedefs.RegisterQueue. Updated whenever GDB receives an async event such as breakpoint modification # See PINCE's AwaitAsyncOutput class for an example of usage -gdb_async_output = type_defs.RegisterQueue() +gdb_async_output = typedefs.RegisterQueue() -#:tag:GDBInformation -#:doc: # A boolean value. Used to cancel the last gdb command sent # Use the function cancel_last_command to make use of this variable # Return value of the current send_command call will be an empty string cancel_send_command = False -#:tag:GDBInformation -#:doc: +# A boolean value. Used by state_observe_thread to check if a trace session is active +active_trace = False + # A string. Holds the last command sent to gdb last_gdb_command = "" -#:tag:GDBInformation -#:doc: # A list of booleans. Used to adjust gdb output # Use the function set_gdb_output_mode to make use of this variable -gdb_output_mode = type_defs.gdb_output_mode(True, True, True) +gdb_output_mode = typedefs.gdb_output_mode(True, True, True) -#:tag:InferiorInformation -#:doc: # A string. memory file of the currently attached/created process mem_file = "/proc/" + str(currentpid) + "/mem" -''' +# A string. Determines which signal to use to interrupt the process +interrupt_signal = "SIGINT" + +""" When PINCE was first launched, it used gdb 7.7.1, which is a very outdated version of gdb interpreter-exec mi command of gdb showed some buggy behaviour at that time Because of that, PINCE couldn't support gdb/mi commands for a while But PINCE is now updated with the new versions of gdb as much as possible and the interpreter-exec works much better So, old parts of codebase still get their required information by parsing gdb console output New parts can try to rely on gdb/mi output -''' +""" + +""" +Functions that require breakpoint commands, such as track_watchpoint and track_breakpoint, requires process to be +stopped beforehand. If the process is running before we give the breakpoint its commands, there's a chance that the +breakpoint will be triggered before we give it commands. The process must be stopped to avoid this race condition +It's also necessary to stop the process to run commands like "watch" +""" -#:tag:GDBCommunication def set_gdb_output_mode(output_mode_tuple): """Adjusts gdb output Args: - output_mode_tuple (type_defs.gdb_output_mode): Setting any field True will enable the output that's associated + output_mode_tuple (typedefs.gdb_output_mode): Setting any field True will enable the output that's associated with that field. Setting it False will disable the associated output """ global gdb_output_mode gdb_output_mode = output_mode_tuple -#:tag:GDBCommunication def cancel_last_command(): """Cancels the last gdb command sent if it's still present""" if lock_send_command.locked(): @@ -158,18 +134,18 @@ def cancel_last_command(): cancel_send_command = True -#:tag:GDBCommunication -def send_command(command, control=False, cli_output=False, send_with_file=False, file_contents_send=None, - recv_with_file=False): - """Issues the command sent, raises an exception if the inferior is running or no inferior has been selected +def send_command( + command, control=False, cli_output=False, send_with_file=False, file_contents_send=None, recv_with_file=False +): + """Issues the command sent, raises an exception if gdb isn't initiated Args: command (str): The command that'll be sent control (bool): This param should be True if the command sent is ctrl+key instead of the regular command - cli_output (bool): If True, returns the readable parsed cli output instead of gdb/mi garbage - send_with_file (bool): Custom commands declared in GDBCommandExtensions.py requires file communication. If + cli_output (bool): If True, returns a readable cli output instead of gdb/mi output + send_with_file (bool): Custom commands declared in gdbextensions.py requires file communication. If called command has any parameters, pass this as True - file_contents_send (any type that pickle.dump supports): Arguments for the called custom gdb command + file_contents_send (any): Arguments for the custom gdb command called recv_with_file (bool): Pass this as True if the called custom gdb command returns something Examples: @@ -199,15 +175,13 @@ def send_command(command, control=False, cli_output=False, send_with_file=False, if gdb_output_mode.command_info: time0 = time() if not gdb_initialized: - raise type_defs.GDBInitializeException - if inferior_status is type_defs.INFERIOR_STATUS.INFERIOR_RUNNING and not control: - raise type_defs.InferiorRunningException + raise typedefs.GDBInitializeException gdb_output = "" if send_with_file: - send_file = SysUtils.get_IPC_from_PINCE_file(currentpid) + send_file = utils.get_from_pince_file(currentpid) pickle.dump(file_contents_send, open(send_file, "wb")) if recv_with_file or cli_output: - recv_file = SysUtils.get_IPC_to_PINCE_file(currentpid) + recv_file = utils.get_to_pince_file(currentpid) # Truncating the recv_file because we wouldn't like to see output of previous command in case of errors open(recv_file, "w").close() @@ -219,7 +193,7 @@ def send_command(command, control=False, cli_output=False, send_with_file=False, if control: child.sendcontrol(command) else: - command_file = SysUtils.get_gdb_command_file(currentpid) + command_file = utils.get_gdb_command_file(currentpid) command_fd = open(command_file, "r+") command_fd.truncate() command_fd.write(command) @@ -230,7 +204,7 @@ def send_command(command, control=False, cli_output=False, send_with_file=False, child.sendline("cli-output source " + command_file) if not control: while not gdb_output: - sleep(type_defs.CONST_TIME.GDB_INPUT_SLEEP) + sleep(typedefs.CONST_TIME.GDB_INPUT_SLEEP) if cancel_send_command: break if not cancel_send_command: @@ -255,23 +229,6 @@ def send_command(command, control=False, cli_output=False, send_with_file=False, return output -def await_process_exit(): - """ - Checks if the current inferior is alive, uses conditions to inform other functions and threads about inferiors state - Detaches if the current inferior dies while attached - Should be called by creating a thread. Usually called in initialization process by attach function - """ - while True: - if currentpid == -1 or SysUtils.is_process_valid(currentpid): - sleep(0.1) - else: - with process_exited_condition: - print("Process terminated (PID:" + str(currentpid) + ")") - process_exited_condition.notify_all() - detach() - break - - def state_observe_thread(): """ Observes the state of gdb, uses conditions to inform other functions and threads about gdb's state @@ -279,64 +236,67 @@ def state_observe_thread(): Should be called by creating a thread. Usually called in initialization process by attach function """ - def check_inferior_status(cache=None): - if cache: - data = cache - else: - data = child.before - matches = common_regexes.gdb_state_observe.findall(data) + def check_inferior_status(): + matches = regexes.gdb_state_observe.findall(child.before) if len(matches) > 0: global stop_reason global inferior_status - - if matches[-1][0]: # stopped - stop_reason = type_defs.STOP_REASON.DEBUG - inferior_status = type_defs.INFERIOR_STATUS.INFERIOR_STOPPED + old_status = inferior_status + for match in matches: + if match[0].startswith('stopped,reason="exited'): + with process_exited_condition: + detach() + print(f"Process terminated (PID:{currentpid})") + process_exited_condition.notify_all() + return + + # For multiline outputs, only the last async event is important + # Get the last match only to optimize parsing + stop_info = matches[-1][0] + if stop_info: + stop_reason = typedefs.STOP_REASON.DEBUG + inferior_status = typedefs.INFERIOR_STATUS.STOPPED else: - inferior_status = type_defs.INFERIOR_STATUS.INFERIOR_RUNNING - with status_changed_condition: - status_changed_condition.notify_all() + inferior_status = typedefs.INFERIOR_STATUS.RUNNING + bp_num = regexes.breakpoint_number.search(stop_info) + # Return -1 for invalid breakpoints to ignore racing conditions + if not ( + old_status == inferior_status + or (bp_num and breakpoint_on_hit_dict.get(bp_num.group(1), -1) != typedefs.BREAKPOINT_ON_HIT.BREAK) + or active_trace + ): + with status_changed_condition: + status_changed_condition.notify_all() global child global gdb_output - stored_output = "" - while True: - child.expect_exact("\r\n") # A new line for TTY devices - child.before = child.before.strip() - if not child.before: - continue - stored_output += "\n" + child.before - if child.before == "(gdb)": - check_inferior_status(stored_output) - stored_output = "" - continue - command_file = re.escape(SysUtils.get_gdb_command_file(currentpid)) - if common_regexes.gdb_command_source(command_file).search(child.before): - child.expect_exact("(gdb)") + try: + while True: + child.expect_exact("\r\n") # A new line for TTY devices child.before = child.before.strip() + if not child.before: + continue check_inferior_status() - gdb_output = child.before - stored_output = "" - with gdb_waiting_for_prompt_condition: - gdb_waiting_for_prompt_condition.notify_all() - if gdb_output_mode.command_output: - print(child.before) - else: - if gdb_output_mode.async_output: - print(child.before) - gdb_async_output.broadcast_message(child.before) - - -def execute_with_temporary_interruption(func): - """Decorator version of execute_func_temporary_interruption""" - - def wrapper(*args, **kwargs): - execute_func_temporary_interruption(func, *args, **kwargs) - - return wrapper + command_file = re.escape(utils.get_gdb_command_file(currentpid)) + if regexes.gdb_command_source(command_file).search(child.before): + child.expect_exact("(gdb)") + child.before = child.before.strip() + check_inferior_status() + gdb_output = child.before + with gdb_waiting_for_prompt_condition: + gdb_waiting_for_prompt_condition.notify_all() + if gdb_output_mode.command_output: + print(child.before) + else: + if gdb_output_mode.async_output: + print(child.before) + gdb_async_output.broadcast_message(child.before) + except (OSError, ValueError, pexpect.EOF) as e: + if isinstance(e, pexpect.EOF): + print("\nEOF exception caught within pexpect, here's the contents of child.before:\n" + child.before) + print("Exiting state_observe_thread") -#:tag:GDBCommunication def execute_func_temporary_interruption(func, *args, **kwargs): """Interrupts the inferior before executing the given function, continues inferior's execution after calling the given function @@ -351,46 +311,24 @@ def execute_func_temporary_interruption(func, *args, **kwargs): Returns: ???: Result of the given function. Return type depends on the given function """ - global temporary_execution_bool - temporary_execution_bool = True old_status = inferior_status - if old_status == type_defs.INFERIOR_STATUS.INFERIOR_RUNNING: - ___internal_interrupt_inferior(type_defs.STOP_REASON.PAUSE) + if old_status == typedefs.INFERIOR_STATUS.RUNNING: + interrupt_inferior(typedefs.STOP_REASON.PAUSE) result = func(*args, **kwargs) - if old_status == type_defs.INFERIOR_STATUS.INFERIOR_RUNNING: - try: - ___internal_continue_inferior() - except type_defs.InferiorRunningException: - pass + if old_status == typedefs.INFERIOR_STATUS.RUNNING: + continue_inferior() return result -#:tag:Debug -def ___internal_continue_inferior(): - """ - Continue the inferior - DOES NOT TOGGLE temporary_execution_condition - you should always use the real one - if you don't toggle the temporary_execution_condition it will never be able to break - """ - - send_command("c") +def execute_with_temporary_interruption(func): + """Decorator version of execute_func_temporary_interruption""" + def wrapper(*args, **kwargs): + return execute_func_temporary_interruption(func, *args, **kwargs) -#:tag:Debug -def ___internal_interrupt_inferior(interrupt_reason=type_defs.STOP_REASON.DEBUG): - """Interrupt the inferior - see notes on ___internal_continue_inferior - Args: - interrupt_reason (int): Just changes the global variable stop_reason. Can be a member of type_defs.STOP_REASON - """ - global stop_reason - send_command("c", control=True) - wait_for_stop() - stop_reason = interrupt_reason + return wrapper -#:tag:Debug def can_attach(pid): """Check if we can attach to the target @@ -409,80 +347,105 @@ def can_attach(pid): return True -#:tag:Debug -def wait_for_stop(timeout=1): +def wait_for_stop(timeout=0): """Block execution till the inferior stops Args: - timeout (float): Timeout time in seconds + timeout (float): Timeout time in seconds, passing 0 will wait for stop indefinitely """ remaining_time = timeout - while inferior_status == type_defs.INFERIOR_STATUS.INFERIOR_RUNNING: - sleep(type_defs.CONST_TIME.GDB_INPUT_SLEEP) - remaining_time -= type_defs.CONST_TIME.GDB_INPUT_SLEEP + while inferior_status == typedefs.INFERIOR_STATUS.RUNNING: + sleep(0.0001) + if timeout == 0: + continue + remaining_time -= 0.0001 if remaining_time < 0: break -#:tag:Debug -def interrupt_inferior(interrupt_reason=type_defs.STOP_REASON.DEBUG): +def interrupt_inferior(interrupt_reason=typedefs.STOP_REASON.DEBUG): """Interrupt the inferior Args: - interrupt_reason (int): Just changes the global variable stop_reason. Can be a member of type_defs.STOP_REASON + interrupt_reason (int): Just changes the global variable stop_reason. Can be a member of typedefs.STOP_REASON """ + if currentpid == -1: + return global stop_reason - global temporary_execution_bool - temporary_execution_bool = False - send_command("c", control=True) - wait_for_stop() + if interrupt_signal == "SIGINT": + send_command("interrupt") + elif inferior_status == typedefs.INFERIOR_STATUS.RUNNING: + sig_num = interrupt_signal[3:] + if sig_num.isnumeric(): + os.system(f"kill -{sig_num} {currentpid}") + else: + os.system(f"kill -s {interrupt_signal} {currentpid}") + wait_for_stop(1) stop_reason = interrupt_reason -#:tag:Debug def continue_inferior(): """Continue the inferior""" - send_command("c") + if currentpid == -1: + return + send_command("c&") -#:tag:Debug def step_instruction(): """Step one assembly instruction""" - send_command("stepi") + send_command("stepi&") -#:tag:Debug def step_over_instruction(): """Step over one assembly instruction""" - send_command("nexti") + send_command("nexti&") -#:tag:Debug def execute_till_return(): """Continues inferior till current stack frame returns""" - send_command("finish") + send_command("finish&") -#:tag:Debug -def ignore_segfault(ignore): - """Handle the SIGSEGV signal +def set_interrupt_signal(signal_name): + """Decides on what signal to use to stop the process Args: - ignore (bool): Ignores SIGSEGV if True, stops the process otherwise + signal_name (str): Name of the signal """ - if ignore: - send_command("handle SIGSEGV nostop noprint") - else: - send_command("handle SIGSEGV stop print") + global interrupt_signal + handle_signal(signal_name, True, False) + interrupt_signal = signal_name -#:tag:GDBCommunication -def init_gdb(gdb_path=type_defs.PATHS.GDB_PATH, ignore_sigsegv=False): - r"""Spawns gdb and initializes/resets some of the global variables +def handle_signal(signal_name: str, stop: bool, pass_to_program: bool) -> None: + """Decides on what will GDB do when the process recieves a signal + + Args: + signal_name (str): Name of the signal + stop (bool): Stop the program and print to the console + pass_to_program (bool): Pass signal to program + """ + params = [[signal_name, stop, pass_to_program]] + send_command("pince-handle-signals", send_with_file=True, file_contents_send=params) + + +def handle_signals(signal_list): + """Optimized version of handle_signal for multiple signals + + Args: + signal_list (list): A list of the parameters of handle_signal + """ + send_command("pince-handle-signals", send_with_file=True, file_contents_send=signal_list) + + +def init_gdb(gdb_path=utils.get_default_gdb_path()): + """Spawns gdb and initializes/resets some of the global variables Args: gdb_path (str): Path of the gdb binary - ignore_sigsegv (bool): Ignores SIGSEGV signal if True + + Returns: + bool: True if initialization is successful, False otherwise Note: Calling init_gdb() will reset the current session @@ -494,11 +457,12 @@ def init_gdb(gdb_path=type_defs.PATHS.GDB_PATH, ignore_sigsegv=False): global gdb_output global cancel_send_command global last_gdb_command - SysUtils.init_user_files() + utils.init_user_files() detach() # Temporary IPC_PATH, this little hack is needed because send_command requires a valid IPC_PATH - SysUtils.create_PINCE_IPC_PATH(currentpid) + utils.create_ipc_path(currentpid) + utils.create_tmp_path(currentpid) breakpoint_on_hit_dict.clear() chained_breakpoints.clear() @@ -506,48 +470,59 @@ def init_gdb(gdb_path=type_defs.PATHS.GDB_PATH, ignore_sigsegv=False): cancel_send_command = False last_gdb_command = "" - libpince_dir = SysUtils.get_libpince_directory() - child = pexpect.spawn('sudo LC_NUMERIC=C ' + gdb_path + ' --interpreter=mi', cwd=libpince_dir, - encoding="utf-8") + libpince_dir = utils.get_libpince_directory() + is_appimage = os.environ.get("APPDIR") + python_home_env = f"PYTHONHOME={os.environ.get('PYTHONHOME')}" if is_appimage else "" + child = pexpect.spawn( + f"sudo -E --preserve-env=PATH LC_NUMERIC=C {python_home_env} {gdb_path} --nx --interpreter=mi", + cwd=libpince_dir, + env=os.environ, + encoding="utf-8", + ) child.setecho(False) child.delaybeforesend = 0 child.timeout = None - child.expect_exact("(gdb)") + try: + child.expect_exact("(gdb)") + except pexpect.EOF: + print("\nEOF exception caught within pexpect, here's the contents of child.before:\n" + child.before) + return False status_thread = Thread(target=state_observe_thread) status_thread.daemon = True status_thread.start() gdb_initialized = True set_logging(False) - send_command("source " + SysUtils.get_user_path(type_defs.USER_PATHS.GDBINIT_PATH)) - SysUtils.execute_script(SysUtils.get_user_path(type_defs.USER_PATHS.PINCEINIT_PATH)) - ignore_segfault(ignore_sigsegv) + if not is_appimage: + send_command("source ./gdbinit_venv") + set_pince_paths() + send_command("source " + utils.get_user_path(typedefs.USER_PATHS.GDBINIT)) + utils.execute_script(utils.get_user_path(typedefs.USER_PATHS.PINCEINIT)) + return True -#:tag:GDBCommunication def set_logging(state): """Sets logging on or off Args: state (bool): Sets logging on if True, off if False """ - send_command("set logging off") - send_command("set logging file " + SysUtils.get_logging_file(currentpid)) + send_command("set logging enabled off") + send_command("set logging file " + utils.get_logging_file(currentpid)) if state: - send_command("set logging on") + send_command("set logging enabled on") -#:tag:GDBCommunication def set_pince_paths(): - """Initializes $PINCE_PATH and $GDBINIT_AA_PATH convenience variables to make commands in GDBCommandExtensions.py - and ScriptUtils.py work. GDB scripts need to know libpince and .config directories, unfortunately they don't start + """Initializes $PINCE_PATH and $GDBINIT_AA_PATH convenience variables to make commands in gdbextensions.py + and gdbutils.py work. GDB scripts need to know libpince and .config directories, unfortunately they don't start from the place where script exists """ - libpince_dir = SysUtils.get_libpince_directory() + libpince_dir = utils.get_libpince_directory() pince_dir = os.path.dirname(libpince_dir) - gdbinit_aa_dir = SysUtils.get_user_path(type_defs.USER_PATHS.GDBINIT_AA_PATH) - send_command('set $GDBINIT_AA_PATH=' + '"' + gdbinit_aa_dir + '"') - send_command('set $PINCE_PATH=' + '"' + pince_dir + '"') - send_command("source gdb_python_scripts/GDBCommandExtensions.py") + gdbinit_aa_dir = utils.get_user_path(typedefs.USER_PATHS.GDBINIT_AA) + send_command("set $GDBINIT_AA_PATH=" + '"' + gdbinit_aa_dir + '"') + send_command("set $PINCE_PATH=" + '"' + pince_dir + '"') + send_command("source gdb_python_scripts/gdbextensions.py") def init_referenced_dicts(pid): @@ -556,69 +531,56 @@ def init_referenced_dicts(pid): Args: pid (int,str): PID of the attached process """ - shelve.open(SysUtils.get_referenced_strings_file(pid), "c") - shelve.open(SysUtils.get_referenced_jumps_file(pid), "c") - shelve.open(SysUtils.get_referenced_calls_file(pid), "c") + shelve.open(utils.get_referenced_strings_file(pid), "c") + shelve.open(utils.get_referenced_jumps_file(pid), "c") + shelve.open(utils.get_referenced_calls_file(pid), "c") -#:tag:Debug -def attach(pid, gdb_path=type_defs.PATHS.GDB_PATH, ignore_sigsegv=False): - r"""Attaches gdb to the target and initializes some of the global variables +def attach(pid, gdb_path=utils.get_default_gdb_path()): + """Attaches gdb to the target and initializes some of the global variables Args: pid (int,str): PID of the process that'll be attached to gdb_path (str): Path of the gdb binary - ignore_sigsegv (bool): Ignores SIGSEGV signal if True Returns: - tuple: (A member of type_defs.ATTACH_RESULT, result_message) + int: A member of typedefs.ATTACH_RESULT Note: If gdb is already initialized, gdb_path will be ignored """ global currentpid pid = int(pid) - traced_by = SysUtils.is_traced(pid) + traced_by = utils.is_traced(pid) pid_control_list = [ # Attaching PINCE to itself makes PINCE freeze immediately because gdb freezes the target on attach - (lambda: pid == self_pid, type_defs.ATTACH_RESULT.ATTACH_SELF, "Nice try, smartass"), # planned easter egg - (lambda: not SysUtils.is_process_valid(pid), type_defs.ATTACH_RESULT.PROCESS_NOT_VALID, - "Selected process is not valid"), - (lambda: pid == currentpid, type_defs.ATTACH_RESULT.ALREADY_DEBUGGING, "You're debugging this process already"), - (lambda: traced_by is not False, type_defs.ATTACH_RESULT.ALREADY_TRACED, - "That process is already being traced by " + str(traced_by) + ", could not attach to the process"), - (lambda: not can_attach(pid), type_defs.ATTACH_RESULT.PERM_DENIED, - "Permission denied, could not attach to the process") + (lambda: pid == self_pid, typedefs.ATTACH_RESULT.ATTACH_SELF), + (lambda: not utils.is_process_valid(pid), typedefs.ATTACH_RESULT.PROCESS_NOT_VALID), + (lambda: pid == currentpid, typedefs.ATTACH_RESULT.ALREADY_DEBUGGING), + (lambda: traced_by is not None, typedefs.ATTACH_RESULT.ALREADY_TRACED), + (lambda: not can_attach(pid), typedefs.ATTACH_RESULT.PERM_DENIED), ] - for control_func, attach_result, error_message in pid_control_list: + for control_func, attach_result in pid_control_list: if control_func(): - print(error_message) - return attach_result, error_message + return attach_result if currentpid != -1 or not gdb_initialized: - init_gdb(gdb_path, ignore_sigsegv) - else: - ignore_segfault(ignore_sigsegv) + init_gdb(gdb_path) global inferior_arch global mem_file currentpid = pid mem_file = "/proc/" + str(currentpid) + "/mem" - SysUtils.create_PINCE_IPC_PATH(pid) + utils.create_ipc_path(pid) + utils.create_tmp_path(pid) send_command("attach " + str(pid)) set_pince_paths() init_referenced_dicts(pid) inferior_arch = get_inferior_arch() - await_exit_thread = Thread(target=await_process_exit) - await_exit_thread.daemon = True - await_exit_thread.start() - result_message = "Successfully attached to the process with PID " + str(currentpid) - print(result_message) - SysUtils.execute_script(SysUtils.get_user_path(type_defs.USER_PATHS.PINCEINIT_AA_PATH)) - return type_defs.ATTACH_RESULT.ATTACH_SUCCESSFUL, result_message - - -#:tag:Debug -def create_process(process_path, args="", ld_preload_path="", gdb_path=type_defs.PATHS.GDB_PATH, ignore_sigsegv=False): - r"""Creates a new process for debugging and initializes some of the global variables + utils.execute_script(utils.get_user_path(typedefs.USER_PATHS.PINCEINIT_AA)) + return typedefs.ATTACH_RESULT.SUCCESSFUL + + +def create_process(process_path, args="", ld_preload_path="", gdb_path=utils.get_default_gdb_path()): + """Creates a new process for debugging and initializes some of the global variables Current process will be detached even if the create_process call fails Make sure to save your data before calling this monstrosity @@ -627,7 +589,6 @@ def create_process(process_path, args="", ld_preload_path="", gdb_path=type_defs args (str): Arguments of the inferior, optional ld_preload_path (str): Path of the preloaded .so file, optional gdb_path (str): Path of the gdb binary - ignore_sigsegv (bool): Ignores SIGSEGV signal if True Returns: bool: True if the process has been created successfully, False otherwise @@ -639,11 +600,9 @@ def create_process(process_path, args="", ld_preload_path="", gdb_path=type_defs global inferior_arch global mem_file if currentpid != -1 or not gdb_initialized: - init_gdb(gdb_path, ignore_sigsegv) - else: - ignore_segfault(ignore_sigsegv) + init_gdb(gdb_path) output = send_command("file " + process_path) - if common_regexes.gdb_error.search(output): + if regexes.gdb_error.search(output): print("An error occurred while trying to create process from the file at " + process_path) detach() return False @@ -664,18 +623,15 @@ def create_process(process_path, args="", ld_preload_path="", gdb_path=type_defs pid = get_inferior_pid() currentpid = int(pid) mem_file = "/proc/" + str(currentpid) + "/mem" - SysUtils.create_PINCE_IPC_PATH(pid) + utils.create_ipc_path(pid) + utils.create_tmp_path(pid) set_pince_paths() init_referenced_dicts(pid) inferior_arch = get_inferior_arch() - await_exit_thread = Thread(target=await_process_exit) - await_exit_thread.daemon = True - await_exit_thread.start() - SysUtils.execute_script(SysUtils.get_user_path(type_defs.USER_PATHS.PINCEINIT_AA_PATH)) + utils.execute_script(utils.get_user_path(typedefs.USER_PATHS.PINCEINIT_AA)) return True -#:tag:Debug def detach(): """See you, space cowboy""" global gdb_initialized @@ -689,40 +645,39 @@ def detach(): gdb_initialized = False child.close() if old_pid != -1: - SysUtils.delete_PINCE_IPC_PATH(old_pid) + utils.delete_ipc_path(old_pid) print("Detached from the process with PID:" + str(old_pid)) -#:tag:Debug def toggle_attach(): """Detaches from the current process without ending the season if currently attached. Attaches back if detached Returns: - int: The new state of the process as a member of type_defs.TOGGLE_ATTACH + int: The new state of the process as a member of typedefs.TOGGLE_ATTACH None: If detaching or attaching fails """ + if currentpid == -1: + return if is_attached(): - if common_regexes.gdb_error.search(send_command("phase-out")): + if regexes.gdb_error.search(send_command("phase-out")): return - return type_defs.TOGGLE_ATTACH.DETACHED - if common_regexes.gdb_error.search(send_command("phase-in")): + return typedefs.TOGGLE_ATTACH.DETACHED + if regexes.gdb_error.search(send_command("phase-in")): return - return type_defs.TOGGLE_ATTACH.ATTACHED + return typedefs.TOGGLE_ATTACH.ATTACHED -#:tag:Debug def is_attached(): """Checks if gdb is attached to the current process Returns: bool: True if attached, False if not """ - if common_regexes.gdb_error.search(send_command("info proc")): + if regexes.gdb_error.search(send_command("info proc")): return False return True -#:tag:Injection def inject_with_advanced_injection(library_path): """Injects the given .so file to current process @@ -739,7 +694,6 @@ def inject_with_advanced_injection(library_path): raise NotImplementedError -#:tag:Injection def inject_with_dlopen_call(library_path): """Injects the given .so file to current process This is a variant of the function inject_with_advanced_injection @@ -752,6 +706,7 @@ def inject_with_dlopen_call(library_path): Returns: bool: Result of the injection """ + # TODO: Merge injection functions and rename them to inject_so once advanced injection is implemented injectionpath = '"' + library_path + '"' result = call_function_from_inferior("dlopen(" + injectionpath + ", 1)")[1] if result == "0" or not result: @@ -762,40 +717,97 @@ def inject_with_dlopen_call(library_path): return True -#:tag:ValueType -def value_index_to_gdbcommand(index): - """Converts the given value_index to a parameter that'll be used in "x" command of gdb +def read_pointer_chain(pointer_request: typedefs.PointerChainRequest) -> typedefs.PointerChainResult | None: + """Reads the addresses pointed by this pointer chain Args: - index (int): Can be a member of type_defs.VALUE_INDEX + pointer_request (typedefs.PointerChainRequest): class containing a base_address and an offsets list Returns: - str: The str corresponding to the index in type_defs.index_to_gdbcommand_dict + typedefs.PointerChainResult: Class containing every pointer dereference result while walking the chain + None: If an error occurs while reading the given pointer chain """ - return type_defs.index_to_gdbcommand_dict.get(index, "out of bounds") + if not isinstance(pointer_request, typedefs.PointerChainRequest): + raise TypeError("Passed non-PointerChainRequest type to read_pointer_chain!") + + if inferior_arch == typedefs.INFERIOR_ARCH.ARCH_32: + value_index = typedefs.VALUE_INDEX.INT32 + else: + value_index = typedefs.VALUE_INDEX.INT64 + # Simple addresses first, examine_expression takes much longer time, especially for larger tables + try: + start_address = int(pointer_request.base_address, 0) + except (ValueError, TypeError): + start_address = examine_expression(pointer_request.base_address).address -#:tag:MemoryRW -def read_memory(address, value_index, length=None, zero_terminate=True, only_bytes=False, mem_handle=None): + pointer_results: typedefs.PointerChainResult = typedefs.PointerChainResult() + try: + with memory_handle() as mem_handle: + # Dereference the first address which is the base or (base + offset) + deref_address = read_memory(start_address, value_index, mem_handle=mem_handle) + if deref_address is None: + # Simply return None because no point reading further if base is not valid + return None + pointer_results.pointer_chain.append(deref_address) + + for index, offset in enumerate(pointer_request.offsets_list): + # If deref_address is 0, we found an invalid read in the chain + # so we can just keep adding 0 until the end of offsets list + if deref_address == 0: + pointer_results.pointer_chain.append(0) + continue + offset_address = deref_address + offset + if index != len(pointer_request.offsets_list) - 1: # CE derefs every offset except for the last one + deref_address = read_memory(offset_address, value_index, mem_handle=mem_handle) + if deref_address is None: + deref_address = 0 + else: + deref_address = offset_address + pointer_results.pointer_chain.append(deref_address) + except OSError: + return None + return pointer_results + + +def memory_handle(): + """ + Acquire the handle of the currently attached process + + Returns: + BinaryIO: A file handle that points to the memory file of the current process + """ + return open(mem_file, "rb") + + +def read_memory( + address: str | int, + value_index: int, + length: int = 0, + zero_terminate: bool = True, + value_repr: int = typedefs.VALUE_REPR.UNSIGNED, + endian: int = typedefs.ENDIANNESS.HOST, + mem_handle: io.BufferedReader | None = None, +) -> str | float | int | None: """Reads value from the given address Args: address (str, int): Can be a hex string or an integer. - value_index (int): Determines the type of data read. Can be a member of type_defs.VALUE_INDEX + value_index (int): Determines the type of data read. Can be a member of typedefs.VALUE_INDEX length (int): Length of the data that'll be read. Must be greater than 0. Only used when the value_index is - INDEX_STRING or INDEX_AOB. Ignored otherwise + STRING or AOB. Ignored otherwise zero_terminate (bool): If True, data will be split when a null character has been read. Only used when - value_index is INDEX_STRING. Ignored otherwise - only_bytes (bool): Returns only bytes instead of converting it to the according type of value_index - mem_handle (BinaryIO): A file handle that points to the memory file of the current process - This parameter is used for optimization, intended for internal usage. Check read_memory_multiple for an example + value_index is STRING. Ignored otherwise + value_repr (int): Can be a member of typedefs.VALUE_REPR. Only usable with integer types + endian (int): Can be a member of typedefs.ENDIANNESS + mem_handle (io.BufferedReader, None): A file handle that points to the memory file of the current process + This parameter is used for optimization, See memory_handle Don't forget to close the handle after you're done if you use this parameter manually Returns: - str: If the value_index is INDEX_STRING or INDEX_AOB - float: If the value_index is INDEX_FLOAT or INDEX_DOUBLE + str: If the value_index is STRING or AOB, also when value_repr is HEX + float: If the value_index is FLOAT32 or FLOAT64 int: If the value_index is anything else - bytes: If the only_bytes is True None: If an error occurs while reading the given address """ try: @@ -809,8 +821,8 @@ def read_memory(address, value_index, length=None, zero_terminate=True, only_byt except: # print(str(address) + " is not a valid address") return - packed_data = type_defs.index_to_valuetype_dict.get(value_index, -1) - if type_defs.VALUE_INDEX.is_string(value_index): + packed_data = typedefs.index_to_valuetype_dict.get(value_index, -1) + if typedefs.VALUE_INDEX.is_string(value_index): try: length = int(length) except: @@ -819,8 +831,8 @@ def read_memory(address, value_index, length=None, zero_terminate=True, only_byt if not length > 0: # print("length must be greater than 0") return - expected_length = length * type_defs.string_index_to_multiplier_dict.get(value_index, 1) - elif value_index is type_defs.VALUE_INDEX.INDEX_AOB: + expected_length = length * typedefs.string_index_to_multiplier_dict.get(value_index, 1) + elif value_index is typedefs.VALUE_INDEX.AOB: try: expected_length = int(length) except: @@ -837,6 +849,8 @@ def read_memory(address, value_index, length=None, zero_terminate=True, only_byt mem_handle = open(mem_file, "rb") mem_handle.seek(address) data_read = mem_handle.read(expected_length) + if endian != typedefs.ENDIANNESS.HOST and system_endianness != endian: + data_read = data_read[::-1] except (OSError, ValueError): # TODO (read/write error output) # Disabled read error printing. If needed, find a way to implement error logging with this function @@ -844,72 +858,34 @@ def read_memory(address, value_index, length=None, zero_terminate=True, only_byt # Maybe creating a function that toggles logging on and off? Other functions could use it too # print("Can't access the memory at address " + hex(address) + " or offset " + hex(address + expected_length)) return - if only_bytes: - return data_read - if type_defs.VALUE_INDEX.is_string(value_index): - encoding, option = type_defs.string_index_to_encoding_dict[value_index] + if typedefs.VALUE_INDEX.is_string(value_index): + encoding, option = typedefs.string_index_to_encoding_dict[value_index] returned_string = data_read.decode(encoding, option) if zero_terminate: - if returned_string.startswith('\x00'): - returned_string = '\x00' + if returned_string.startswith("\x00"): + returned_string = "\x00" else: - returned_string = returned_string.split('\x00')[0] + returned_string = returned_string.split("\x00")[0] return returned_string[0:length] - elif value_index is type_defs.VALUE_INDEX.INDEX_AOB: - return " ".join(format(n, '02x') for n in data_read) + elif value_index is typedefs.VALUE_INDEX.AOB: + return " ".join(format(n, "02x") for n in data_read) else: - return struct.unpack_from(data_type, data_read)[0] - - -#:tag:MemoryRW -def read_memory_multiple(nested_list): - """Reads multiple values from the given addresses - - Optimized version of the function read_memory. This function is significantly faster after 100 addresses compared - to using read_memory in a for loop. - - Args: - nested_list (list): List of *args of the function read_memory. You don't have to pass all of the parameters for - each list in the nested_list, only parameters address and value_index are obligatory. Defaults of the other - parameters are the same with the function read_memory. - - Examples: - All parameters are passed-->[[address1, value_index1, length1, zero_terminate1, only_bytes], ...] - Parameters are partially passed--▼ - [[address1, value_index1],[address2, value_index2, length2],[address3, value_index3, length3], ...] - - Returns: - list: A list of the values read. - If any errors occurs while reading addresses, it's ignored and the belonging address is returned as None - For instance; If 4 addresses has been read and 3rd one is problematic, the returned list will be - [returned_value1,returned_value2,None,returned_value4] - """ - data_read_list = [] - mem_handle = open(mem_file, "rb") - - for item in nested_list: - address = item[0] - index = item[1] - try: - length = item[2] - except IndexError: - length = 0 - try: - zero_terminate = item[3] - except IndexError: - zero_terminate = True - try: - only_bytes = item[4] - except IndexError: - only_bytes = False - data_read = read_memory(address, index, length, zero_terminate, only_bytes, mem_handle) - data_read_list.append(data_read) - mem_handle.close() - return data_read_list - - -#:tag:MemoryRW -def write_memory(address, value_index, value): + is_integer = typedefs.VALUE_INDEX.is_integer(value_index) + if is_integer and value_repr == typedefs.VALUE_REPR.SIGNED: + data_type = data_type.lower() + result = struct.unpack_from(data_type, data_read)[0] + if is_integer and value_repr == typedefs.VALUE_REPR.HEX: + return hex(result) + return result + + +def write_memory( + address: str | int, + value_index: int, + value: str | int | float | list[int], + zero_terminate=True, + endian=typedefs.ENDIANNESS.HOST, +): """Sets the given value to the given address If any errors occurs while setting value to the according address, it'll be ignored but the information about @@ -917,8 +893,10 @@ def write_memory(address, value_index, value): Args: address (str, int): Can be a hex string or an integer - value_index (int): Can be a member of type_defs.VALUE_INDEX - value (str): The value that'll be written to the given address + value_index (int): Can be a member of typedefs.VALUE_INDEX + value (str, int, float, list): The value that'll be written to the given address + zero_terminate (bool): If True, appends a null byte to the value. Only used when value_index is STRING + endian (int): Can be a member of typedefs.ENDIANNESS Notes: TODO: Implement a mem_handle parameter for optimization, check read_memory for an example @@ -931,18 +909,25 @@ def write_memory(address, value_index, value): except: # print(str(address) + " is not a valid address") return - write_data = SysUtils.parse_string(value, value_index) - if write_data is None: - return - encoding, option = type_defs.string_index_to_encoding_dict.get(value_index, (None, None)) + if isinstance(value, str): + write_data = utils.parse_string(value, value_index) + if write_data is None: + return + else: + write_data = value + encoding, option = typedefs.string_index_to_encoding_dict.get(value_index, (None, None)) if encoding is None: - if value_index is type_defs.VALUE_INDEX.INDEX_AOB: + if value_index is typedefs.VALUE_INDEX.AOB: write_data = bytearray(write_data) else: - data_type = type_defs.index_to_struct_pack_dict.get(value_index, -1) + data_type = typedefs.index_to_struct_pack_dict.get(value_index, -1) write_data = struct.pack(data_type, write_data) else: write_data = write_data.encode(encoding, option) + if zero_terminate: + write_data += b"\x00" + if endian != typedefs.ENDIANNESS.HOST and system_endianness != endian: + write_data = write_data[::-1] FILE = open(mem_file, "rb+") try: FILE.seek(address) @@ -954,46 +939,46 @@ def write_memory(address, value_index, value): return -#:tag:MemoryRW -def write_memory_multiple(nested_list, value): - """Sets the given value to the given addresses - - If any errors occurs while setting values to the according addresses, it'll be ignored but the information about - error will be printed to the terminal. +def disassemble(expression, offset_or_address): + """Disassembles the address evaluated by the given expression Args: - nested_list (list): List of the address and value_index parameters of the function write_memory - Both parameters address and value_index are necessary. - value (str): The value that'll be written to the given addresses + expression (str): Any gdb expression + offset_or_address (str): If you pass this parameter as an offset, you should add "+" in front of it + (e.g "+42" or "+0x42"). If you pass this parameter as an hex address, the address range between the expression + and the secondary address is disassembled + If the second parameter is an address, it always should be bigger than the first address - Examples: - nested_list-->[[address1, value_index1],[address2, value_index2], ...] + Returns: + list: A list of str values in this format-->[(address1, bytes1, opcodes1), (address2, ...), ...] """ - for item in nested_list: - address = item[0] - index = item[1] - write_memory(address, index, value) + output = send_command("disas /r " + expression + "," + offset_or_address) + disas_data = [] + for line in output.splitlines(): + result = regexes.disassemble_output.search(line) + if result: + disas_data.append(result.groups()) + return disas_data -#:tag:Assembly -def disassemble(expression, offset_or_address): - """Disassembles the address evaluated by the given expression +def convert_to_hex(expression): + """Converts numeric values in the expression into their hex equivalents + Respects edge cases like indexed maps and keeps indexes as decimals Args: expression (str): Any gdb expression - offset_or_address (str): If you pass this parameter as an offset, you should add "+" in front of it - (e.g "+42" or "+0x42"). If you pass this parameter as an hex address, the address range between the expression - and the secondary address is disassembled. - If the second parameter is an address. it always should be bigger than the first address. Returns: - list: A list of str values in this format-->[[address1,bytes1,opcodes1],[address2, ...], ...] + str: Converted str """ - output = send_command("disas /r " + expression + "," + offset_or_address) - return [list(item) for item in common_regexes.disassemble_output.findall(output)] + # TODO (lldb): We'll most likely write our own expression parser once we switch to lldb + # Merge this function with examine_expression and gdbutils.examine_expression once that happens + return regexes.expression_with_hex.sub( + lambda m: "0x" + m.group(1) if m.group(1) and not examine_expression(m.group(1)).symbol else m.group(0), + expression, + ) -#:tag:GDBExpressions def examine_expression(expression): """Evaluates the given expression and returns evaluated value, address and symbol @@ -1001,11 +986,14 @@ def examine_expression(expression): expression (str): Any gdb expression Returns: - type_defs.tuple_examine_expression: Evaluated value, address and symbol in a tuple + typedefs.tuple_examine_expression: Evaluated value, address and symbol in a tuple Any erroneous field will be returned as None instead of str """ - return send_command("pince-examine-expressions", send_with_file=True, file_contents_send=[expression], - recv_with_file=True)[0] + if currentpid == -1: + return typedefs.tuple_examine_expression(None, None, None) + return send_command( + "pince-examine-expressions", send_with_file=True, file_contents_send=[expression], recv_with_file=True + )[0] def examine_expressions(expression_list): @@ -1015,21 +1003,26 @@ def examine_expressions(expression_list): expression_list (list): List of gdb expressions as str Returns: - list: List of type_defs.tuple_examine_expression + list: List of typedefs.tuple_examine_expression """ - return send_command("pince-examine-expressions", send_with_file=True, file_contents_send=expression_list, - recv_with_file=True) + if not expression_list: + return [] + if currentpid == -1: + return [typedefs.tuple_examine_expression(None, None, None) for _ in range(len(expression_list))] + return send_command( + "pince-examine-expressions", send_with_file=True, file_contents_send=expression_list, recv_with_file=True + ) -#:tag:GDBExpressions def parse_and_eval(expression, cast=str): """Calls gdb.parse_and_eval with the given expression and returns the value after casting with the given type Use examine_expression if your data can be expressed as an address or a symbol, use this function otherwise Unlike examine_expression, this function can read data that has void type or multiple type representations For instance: - $eflags has both str and int reprs - $_siginfo is a struct with many fields - x64 register convenience vars such as $rax are void if the process is x86 + + - $eflags has both str and int reprs + - $_siginfo is a struct with many fields + - x64 register convenience vars such as $rax are void if the process is x86 Args: expression (str): Any gdb expression @@ -1039,12 +1032,12 @@ def parse_and_eval(expression, cast=str): cast: Self-explanatory None: If casting fails """ - return send_command("pince-parse-and-eval", send_with_file=True, file_contents_send=(expression, cast), - recv_with_file=True) + return send_command( + "pince-parse-and-eval", send_with_file=True, file_contents_send=(expression, cast), recv_with_file=True + ) -#:tag:Threads -def get_current_thread_information(): +def get_thread_info(): """Invokes "info threads" command and returns the line corresponding to the current thread Returns: @@ -1052,11 +1045,10 @@ def get_current_thread_information(): None: If the output doesn't fit the regex """ thread_info = send_command("info threads") - return re.sub(r'\\"', r'"', common_regexes.thread_info.search(thread_info).group(1)) + return re.sub(r'\\"', r'"', regexes.thread_info.search(thread_info).group(1)) -#:tag:Assembly -def find_address_of_closest_instruction(address, instruction_location="next", instruction_count=1): +def find_closest_instruction_address(address, instruction_location="next", instruction_count=1): """Finds address of the closest instruction next to the given address, assuming that the given address is valid Args: @@ -1084,24 +1076,23 @@ def find_address_of_closest_instruction(address, instruction_location="next", in disas_data = disassemble(address + offset, address) if not disas_data: if instruction_location != "next": - start_address = hex(SysUtils.get_region_info(currentpid, address).start) + start_address = hex(utils.get_region_info(currentpid, address).start) disas_data = disassemble(start_address, address) if instruction_location == "next": try: - return SysUtils.extract_address(disas_data[instruction_count][0]) + return utils.extract_address(disas_data[instruction_count][0]) except IndexError: - return hex(SysUtils.get_region_info(currentpid, address).end) + return hex(utils.get_region_info(currentpid, address).end) else: try: - return SysUtils.extract_address(disas_data[-instruction_count][0]) + return utils.extract_address(disas_data[-instruction_count][0]) except IndexError: try: return start_address except UnboundLocalError: - return hex(SysUtils.get_region_info(currentpid, address).start) + return hex(utils.get_region_info(currentpid, address).start) -#:tag:GDBExpressions def get_address_info(expression): """Runs the gdb command "info symbol" for given expression and returns the result of it @@ -1114,7 +1105,6 @@ def get_address_info(expression): return send_command("info symbol " + expression, cli_output=True) -#:tag:GDBExpressions def get_symbol_info(expression): """Runs the gdb command "info address" for given expression and returns the result of it @@ -1127,7 +1117,6 @@ def get_symbol_info(expression): return send_command("info address " + expression, cli_output=True) -#:tag:Tools def search_functions(expression, case_sensitive=False): """Runs the gdb command "info functions" for given expression and returns the result of it @@ -1148,11 +1137,14 @@ def search_functions(expression, case_sensitive=False): Please don't try to write a symbol parser for every single language out there, it's an overkill https://sourceware.org/bugzilla/show_bug.cgi?id=23899 """ - return send_command("pince-search-functions", send_with_file=True, file_contents_send=(expression, case_sensitive), - recv_with_file=True) + return send_command( + "pince-search-functions", + send_with_file=True, + file_contents_send=(expression, case_sensitive), + recv_with_file=True, + ) -#:tag:InferiorInformation def get_inferior_pid(): """Get pid of the current inferior @@ -1160,37 +1152,34 @@ def get_inferior_pid(): str: pid """ output = send_command("info inferior") - return common_regexes.inferior_pid.search(output).group(1) + return regexes.inferior_pid.search(output).group(1) -#:tag:InferiorInformation def get_inferior_arch(): """Returns the architecture of the current inferior Returns: - int: A member of type_defs.INFERIOR_ARCH + int: A member of typedefs.INFERIOR_ARCH """ if parse_and_eval("$rax") == "void": - return type_defs.INFERIOR_ARCH.ARCH_32 - return type_defs.INFERIOR_ARCH.ARCH_64 + return typedefs.INFERIOR_ARCH.ARCH_32 + return typedefs.INFERIOR_ARCH.ARCH_64 -#:tag:Registers def read_registers(): """Returns the current registers Returns: - dict: A dict that holds general, flag and segment registers. Check type_defs.REGISTERS for the full list + dict: A dict that holds general, flag and segment registers. Check typedefs.REGISTERS for the full list """ return send_command("pince-read-registers", recv_with_file=True) -#:tag:Registers -def read_float_registers(): +def read_float_registers() -> OrderedDict[str, str]: """Returns the current floating point registers Returns: - dict: A dict that holds floating point registers. Check type_defs.REGISTERS.FLOAT for the full list + OrderedDict[str, str]: A dict that holds floating point registers. Check typedefs.REGISTERS.FLOAT for the full list Note: Returned xmm values are based on xmm.v4_float @@ -1198,8 +1187,6 @@ def read_float_registers(): return send_command("pince-read-float-registers", recv_with_file=True) -#:tag:GDBExpressions -#:tag:Registers def set_convenience_variable(variable, value): """Sets given convenience variable to given value Can be also used for modifying registers directly @@ -1211,12 +1198,11 @@ def set_convenience_variable(variable, value): send_command("set $" + variable + "=" + value) -#:tag:Registers def set_register_flag(flag, value): """Sets given register flag to given value Args: - flag (str): A member of type_defs.REGISTERS.FLAG + flag (str): A member of typedefs.REGISTERS.FLAG value (Union[int,str]): 0 or 1 """ registers = read_registers() @@ -1224,15 +1210,28 @@ def set_register_flag(flag, value): registers[flag] = value if value != "0" and value != "1": raise Exception(value + " isn't valid value. It can be only 0 or 1") - if flag not in type_defs.REGISTERS.FLAG: - raise Exception(flag + " isn't a valid flag, must be a member of type_defs.REGISTERS.FLAG") - eflags_hex_value = hex(int( - registers["of"] + registers["df"] + registers["if"] + registers["tf"] + registers["sf"] + registers[ - "zf"] + "0" + registers["af"] + "0" + registers["pf"] + "0" + registers["cf"], 2)) + if flag not in typedefs.REGISTERS.FLAG: + raise Exception(flag + " isn't a valid flag, must be a member of typedefs.REGISTERS.FLAG") + eflags_hex_value = hex( + int( + registers["of"] + + registers["df"] + + registers["if"] + + registers["tf"] + + registers["sf"] + + registers["zf"] + + "0" + + registers["af"] + + "0" + + registers["pf"] + + "0" + + registers["cf"], + 2, + ) + ) set_convenience_variable("eflags", eflags_hex_value) -#:tag:Stack def get_stacktrace_info(): """Returns information about current stacktrace @@ -1245,9 +1244,9 @@ def get_stacktrace_info(): return send_command("pince-get-stack-trace-info", recv_with_file=True) -#:tag:Stack -def get_stack_info(): +def get_stack_info(from_base_pointer: bool = False) -> list[str]: """Returns information about current stack + Also can view stack from EBP or RBP register Returns: list: A list of str values in this format--▼ @@ -1260,10 +1259,12 @@ def get_stack_info(): if points to a symbol-->(ptr) pointer_info becomes a null string if pointer isn't valid """ - return send_command("pince-get-stack-info", recv_with_file=True) + if from_base_pointer: + return send_command("pince-get-stack-info from-base-pointer", recv_with_file=True) + else: + return send_command("pince-get-stack-info", recv_with_file=True) -#:tag:Stack def get_stack_frame_return_addresses(): """Returns return addresses of stack frames @@ -1275,7 +1276,6 @@ def get_stack_frame_return_addresses(): return send_command("pince-get-frame-return-addresses", recv_with_file=True) -#:tag:Stack def get_stack_frame_info(index): """Returns information about stack by the given index @@ -1283,7 +1283,8 @@ def get_stack_frame_info(index): index (int,str): Index of the frame Returns: - str: Information that looks like this--▼ + str: Information that looks like this:: + Stack level 0, frame at 0x7ffc5f87f6a0: rip = 0x7fd1d639412d in poll (../sysdeps/unix/syscall-template.S:81); saved rip = 0x7fd1d27fcfe4 called by frame at 0x7ffc5f87f700 @@ -1296,7 +1297,6 @@ def get_stack_frame_info(index): return send_command("pince-get-frame-info", send_with_file=True, file_contents_send=str(index), recv_with_file=True) -#:tag:MemoryRW def hex_dump(address, offset): """Returns hex dump of range (address to address+offset) @@ -1319,23 +1319,85 @@ def hex_dump(address, offset): pass for item in range(offset): try: - current_item = " ".join(format(n, '02x') for n in FILE.read(1)) + current_item = " ".join(format(n, "02x") for n in FILE.read(1)) except OSError: current_item = "??" try: FILE.seek(1, io.SEEK_CUR) # Necessary since read() failed to execute except (OSError, ValueError): pass - hex_byte_list.append(current_item) + hex_byte_list.append(utils.upper_hex(current_item)) return hex_byte_list -#:tag:BreakWatchpoints -def get_breakpoint_info(): +def get_modified_instructions(): + """Returns currently modified instructions + + Returns: + dict: A dictionary where the key is the start address of instruction and value is the aob before modifying + + """ + global modified_instructions_dict + return modified_instructions_dict + + +def nop_instruction(start_address, length_of_instr): + """Replaces an instruction's opcodes with NOPs + + Args: + start_address (int): Self-explanatory + length_of_instr (int): Length of the instruction that'll be NOP'ed + + Returns: + None + """ + old_aob = " ".join(hex_dump(start_address, length_of_instr)) + global modified_instructions_dict + if start_address not in modified_instructions_dict: + modified_instructions_dict[start_address] = old_aob + + nop_aob = "90 " * length_of_instr + write_memory(start_address, typedefs.VALUE_INDEX.AOB, nop_aob) + + +def modify_instruction(start_address, array_of_bytes): + """Replaces an instruction's opcodes with a new AOB + + Args: + start_address (int): Self-explanatory + array_of_bytes (str): String that contains the replacement bytes of the instruction + + Returns: + None + """ + length = len(array_of_bytes.split()) + old_aob = " ".join(hex_dump(start_address, length)) + + global modified_instructions_dict + if start_address not in modified_instructions_dict: + modified_instructions_dict[start_address] = old_aob + write_memory(start_address, typedefs.VALUE_INDEX.AOB, array_of_bytes) + + +def restore_instruction(start_address): + """Restores a modified instruction to it's original opcodes + + Args: + start_address (int): Self-explanatory + + Returns: + None + """ + global modified_instructions_dict + array_of_bytes = modified_instructions_dict.pop(start_address) + write_memory(start_address, typedefs.VALUE_INDEX.AOB, array_of_bytes) + + +def get_breakpoint_info() -> list[typedefs.tuple_breakpoint_info]: """Returns current breakpoint/watchpoint list Returns: - list: A list of type_defs.tuple_breakpoint_info where; + list: A list of typedefs.tuple_breakpoint_info where; number is the gdb breakpoint number breakpoint_type is the breakpoint type disp shows what will be done after breakpoint hits @@ -1358,12 +1420,20 @@ def get_breakpoint_info(): raw_info = send_command("-break-list") # Temporary fix for https://sourceware.org/bugzilla/show_bug.cgi?id=9659 # TODO:Delete this line when gdb or pygdbmi fixes the problem - raw_info = re.sub("script={(.*?)}", "script=[\g<1>]", raw_info) # Please refer to issue #53 - for item in SysUtils.parse_response(raw_info)['payload']['BreakpointTable']['body']: + raw_info = re.sub(r"script={(.*?)}", r"script=[\g<1>]", raw_info) # Please refer to issue #53 + for item in utils.parse_response(raw_info)["payload"]["BreakpointTable"]["body"]: item = defaultdict(lambda: "", item) - number, breakpoint_type, disp, enabled, address, what, condition, hit_count, enable_count = \ - item['number'], item['type'], item['disp'], item['enabled'], item['addr'], item['what'], item['cond'], \ - item['times'], item['enable'] + number, breakpoint_type, disp, enabled, address, what, condition, hit_count, enable_count = ( + item["number"], + item["type"], + item["disp"], + item["enabled"], + item["addr"], + item["what"], + item["cond"], + item["times"], + item["enable"], + ) if address == "": multiple_break_data[number] = (breakpoint_type, disp, condition, hit_count) continue @@ -1371,56 +1441,52 @@ def get_breakpoint_info(): number = number.split(".")[0] breakpoint_type, disp, condition, hit_count = multiple_break_data[number] if what: - address = SysUtils.extract_address(what) + address = utils.extract_address(what) if not address: address = examine_expression(what).address - try: - int_address = int(address, 16) - except ValueError: - on_hit = type_defs.on_hit_to_text_dict.get(type_defs.BREAKPOINT_ON_HIT.BREAK) - else: - on_hit_dict_value = breakpoint_on_hit_dict.get(int_address, type_defs.BREAKPOINT_ON_HIT.BREAK) - on_hit = type_defs.on_hit_to_text_dict.get(on_hit_dict_value, "Unknown") + on_hit_dict_value = breakpoint_on_hit_dict.get(number, typedefs.BREAKPOINT_ON_HIT.BREAK) + on_hit = typedefs.on_hit_to_text_dict.get(on_hit_dict_value, "Unknown") if breakpoint_type.find("breakpoint") >= 0: size = 1 else: - possible_size = common_regexes.breakpoint_size.search(what) + possible_size = regexes.breakpoint_size.search(what) if possible_size: size = int(possible_size.group(1)) else: size = 1 returned_list.append( - type_defs.tuple_breakpoint_info(number, breakpoint_type, disp, enabled, address, size, on_hit, hit_count, - enable_count, condition)) + typedefs.tuple_breakpoint_info( + number, breakpoint_type, disp, enabled, address, size, on_hit, hit_count, enable_count, condition + ) + ) return returned_list -#:tag:BreakWatchpoints -def check_address_in_breakpoints(address, range_offset=0): +def get_breakpoints_in_range(address: str | int, length: int = 1) -> list[typedefs.tuple_breakpoint_info]: """Checks if given address exists in breakpoint list Args: - address (int,str): Hex address or an int - range_offset (int): If this parameter is different than 0, the range between address and address+offset is - checked instead of just address itself + address (str,int): Start address of the range, hex address or an int + length (int): If this parameter is bigger than 1, the range between address and address+length-1 will be + checked instead of just the address itself Returns: - type_defs.tuple_breakpoint_info: Info of the existing breakpoint for given address range - None: If it doesn't exist + list: A list of typedefs.tuple_breakpoint_info, info of the existing breakpoints for given address range """ + breakpoint_list = [] if type(address) != int: address = int(address, 0) - max_address = max(address, address + range_offset) - min_address = min(address, address + range_offset) + max_address = max(address, address + length - 1) + min_address = min(address, address + length - 1) breakpoint_info = get_breakpoint_info() for item in breakpoint_info: breakpoint_address = int(item.address, 16) if not (max_address < breakpoint_address or min_address > breakpoint_address + item.size - 1): - return item + breakpoint_list.append(item) + return breakpoint_list -#:tag:BreakWatchpoints -def hardware_breakpoint_available(): +def hardware_breakpoint_available() -> bool: """Checks if there is an available hardware breakpoint slot Returns: @@ -1428,28 +1494,28 @@ def hardware_breakpoint_available(): Todo: Check debug registers to determine hardware breakpoint state rather than relying on gdb output because inferior - might modify it's own debug registers + might modify its own debug registers """ breakpoint_info = get_breakpoint_info() hw_bp_total = 0 for item in breakpoint_info: - if common_regexes.hw_breakpoint_count.search(item.breakpoint_type): + if regexes.hw_breakpoint_count.search(item.breakpoint_type): hw_bp_total += 1 # Maximum number of hardware breakpoints is limited to 4 in x86 architecture return hw_bp_total < 4 -#:tag:BreakWatchpoints -def add_breakpoint(expression, breakpoint_type=type_defs.BREAKPOINT_TYPE.HARDWARE_BP, - on_hit=type_defs.BREAKPOINT_ON_HIT.BREAK): +def add_breakpoint( + expression, breakpoint_type=typedefs.BREAKPOINT_TYPE.HARDWARE, on_hit=typedefs.BREAKPOINT_ON_HIT.BREAK +): """Adds a breakpoint at the address evaluated by the given expression. Uses a software breakpoint if all hardware breakpoint slots are being used Args: expression (str): Any gdb expression - breakpoint_type (int): Can be a member of type_defs.BREAKPOINT_TYPE - on_hit (int): Can be a member of type_defs.BREAKPOINT_ON_HIT + breakpoint_type (int): Can be a member of typedefs.BREAKPOINT_TYPE + on_hit (int): Can be a member of typedefs.BREAKPOINT_ON_HIT Returns: str: Number of the breakpoint set @@ -1460,35 +1526,40 @@ def add_breakpoint(expression, breakpoint_type=type_defs.BREAKPOINT_TYPE.HARDWAR if not str_address: print("expression for breakpoint is not valid") return - if check_address_in_breakpoints(str_address): + if get_breakpoints_in_range(str_address): print("breakpoint/watchpoint for address " + str_address + " is already set") return - if breakpoint_type == type_defs.BREAKPOINT_TYPE.HARDWARE_BP: + if breakpoint_type == typedefs.BREAKPOINT_TYPE.HARDWARE: if hardware_breakpoint_available(): output = send_command("hbreak *" + str_address) else: print("All hardware breakpoint slots are being used, using a software breakpoint instead") output = send_command("break *" + str_address) - elif breakpoint_type == type_defs.BREAKPOINT_TYPE.SOFTWARE_BP: + elif breakpoint_type == typedefs.BREAKPOINT_TYPE.SOFTWARE: output = send_command("break *" + str_address) - if common_regexes.breakpoint_created.search(output): + if regexes.breakpoint_created.search(output): global breakpoint_on_hit_dict - breakpoint_on_hit_dict[int(str_address, 16)] = on_hit - return common_regexes.breakpoint_number.search(output).group(1) + number = regexes.breakpoint_number.search(output).group(1) + breakpoint_on_hit_dict[number] = on_hit + return number else: return -#:tag:BreakWatchpoints -def add_watchpoint(expression, length=4, watchpoint_type=type_defs.WATCHPOINT_TYPE.BOTH, - on_hit=type_defs.BREAKPOINT_ON_HIT.BREAK): +@execute_with_temporary_interruption +def add_watchpoint( + expression: str, + length: int = 4, + watchpoint_type: int = typedefs.WATCHPOINT_TYPE.BOTH, + on_hit: int = typedefs.BREAKPOINT_ON_HIT.BREAK, +) -> list[str]: """Adds a watchpoint at the address evaluated by the given expression Args: expression (str): Any gdb expression length (int): Length of the watchpoint - watchpoint_type (int): Can be a member of type_defs.WATCHPOINT_TYPE - on_hit (int): Can be a member of type_defs.BREAKPOINT_ON_HIT + watchpoint_type (int): Can be a member of typedefs.WATCHPOINT_TYPE + on_hit (int): Can be a member of typedefs.BREAKPOINT_ON_HIT Returns: list: Numbers of the successfully set breakpoints as strings @@ -1497,42 +1568,43 @@ def add_watchpoint(expression, length=4, watchpoint_type=type_defs.WATCHPOINT_TY if not str_address: print("expression for watchpoint is not valid") return - if watchpoint_type == type_defs.WATCHPOINT_TYPE.WRITE_ONLY: + if watchpoint_type == typedefs.WATCHPOINT_TYPE.WRITE_ONLY: watch_command = "watch" - elif watchpoint_type == type_defs.WATCHPOINT_TYPE.READ_ONLY: + elif watchpoint_type == typedefs.WATCHPOINT_TYPE.READ_ONLY: watch_command = "rwatch" - elif watchpoint_type == type_defs.WATCHPOINT_TYPE.BOTH: + elif watchpoint_type == typedefs.WATCHPOINT_TYPE.BOTH: watch_command = "awatch" remaining_length = length breakpoints_set = [] arch = get_inferior_arch() str_address_int = int(str_address, 16) breakpoint_addresses = [] - if arch == type_defs.INFERIOR_ARCH.ARCH_64: + if arch == typedefs.INFERIOR_ARCH.ARCH_64: max_length = 8 else: max_length = 4 while remaining_length > 0: - if check_address_in_breakpoints(str_address_int): + if remaining_length >= max_length: + breakpoint_length = max_length + else: + breakpoint_length = remaining_length + if get_breakpoints_in_range(str_address_int, breakpoint_length): print("breakpoint/watchpoint for address " + hex(str_address_int) + " is already set. Bailing out...") break if not hardware_breakpoint_available(): print("All hardware breakpoint slots are being used, unable to set a new watchpoint. Bailing out...") break - if remaining_length >= max_length: - breakpoint_length = max_length - else: - breakpoint_length = remaining_length - output = send_command(watch_command + " * (char[" + str(breakpoint_length) + "] *) " + hex(str_address_int)) - if common_regexes.breakpoint_created.search(output): + cmd = f"{watch_command} * (char[{breakpoint_length}] *) {hex(str_address_int)}" + output = execute_func_temporary_interruption(send_command, cmd) + if regexes.breakpoint_created.search(output): breakpoint_addresses.append([str_address_int, breakpoint_length]) else: print("Failed to create a watchpoint at address " + hex(str_address_int) + ". Bailing out...") break - breakpoint_number = common_regexes.breakpoint_number.search(output).group(1) + breakpoint_number = regexes.breakpoint_number.search(output).group(1) breakpoints_set.append(breakpoint_number) global breakpoint_on_hit_dict - breakpoint_on_hit_dict[str_address_int] = on_hit + breakpoint_on_hit_dict[breakpoint_number] = on_hit remaining_length -= max_length str_address_int += max_length global chained_breakpoints @@ -1540,13 +1612,12 @@ def add_watchpoint(expression, length=4, watchpoint_type=type_defs.WATCHPOINT_TY return breakpoints_set -#:tag:BreakWatchpoints def modify_breakpoint(expression, modify_what, condition=None, count=None): """Adds a condition to the breakpoint at the address evaluated by the given expression Args: expression (str): Any gdb expression - modify_what (int): Can be a member of type_defs.BREAKPOINT_MODIFY_TYPES + modify_what (int): Can be a member of typedefs.BREAKPOINT_MODIFY_TYPES This function modifies condition of the breakpoint if CONDITION, enables the breakpoint if ENABLE, disables the breakpoint if DISABLE, enables once then disables after hit if ENABLE_ONCE, enables for specified count then disables after the count is reached if ENABLE_COUNT, enables once then deletes the breakpoint if ENABLE_DELETE @@ -1557,12 +1628,12 @@ def modify_breakpoint(expression, modify_what, condition=None, count=None): bool: True if the condition has been set successfully, False otherwise Examples: - modify_what-->type_defs.BREAKPOINT_MODIFY_TYPES.CONDITION + modify_what-->typedefs.BREAKPOINT_MODIFY_TYPES.CONDITION condition-->$eax==0x523 condition-->$rax>0 && ($rbp<0 || $rsp==0) condition-->printf($r10)==3 - modify_what-->type_defs.BREAKPOINT_MODIFY_TYPES.ENABLE_COUNT + modify_what-->typedefs.BREAKPOINT_MODIFY_TYPES.ENABLE_COUNT count-->10 """ str_address = examine_expression(expression).address @@ -1577,24 +1648,24 @@ def modify_breakpoint(expression, modify_what, condition=None, count=None): modification_list = item break for breakpoint in modification_list: - found_breakpoint = check_address_in_breakpoints(breakpoint[0]) + found_breakpoint = get_breakpoints_in_range(breakpoint[0]) if not found_breakpoint: print("no such breakpoint exists for address " + str_address) continue else: - breakpoint_number = found_breakpoint.number - if modify_what == type_defs.BREAKPOINT_MODIFY.CONDITION: + breakpoint_number = found_breakpoint[0].number + if modify_what == typedefs.BREAKPOINT_MODIFY.CONDITION: if condition is None: print("Please set condition first") return False send_command("condition " + breakpoint_number + " " + condition) - elif modify_what == type_defs.BREAKPOINT_MODIFY.ENABLE: + elif modify_what == typedefs.BREAKPOINT_MODIFY.ENABLE: send_command("enable " + breakpoint_number) - elif modify_what == type_defs.BREAKPOINT_MODIFY.DISABLE: + elif modify_what == typedefs.BREAKPOINT_MODIFY.DISABLE: send_command("disable " + breakpoint_number) - elif modify_what == type_defs.BREAKPOINT_MODIFY.ENABLE_ONCE: + elif modify_what == typedefs.BREAKPOINT_MODIFY.ENABLE_ONCE: send_command("enable once " + breakpoint_number) - elif modify_what == type_defs.BREAKPOINT_MODIFY.ENABLE_COUNT: + elif modify_what == typedefs.BREAKPOINT_MODIFY.ENABLE_COUNT: if count is None: print("Please set count first") return False @@ -1602,7 +1673,7 @@ def modify_breakpoint(expression, modify_what, condition=None, count=None): print("Count can't be lower than 1") return False send_command("enable count " + str(count) + " " + breakpoint_number) - elif modify_what == type_defs.BREAKPOINT_MODIFY.ENABLE_DELETE: + elif modify_what == typedefs.BREAKPOINT_MODIFY.ENABLE_DELETE: send_command("enable delete " + breakpoint_number) else: print("Parameter modify_what is not valid") @@ -1610,7 +1681,6 @@ def modify_breakpoint(expression, modify_what, condition=None, count=None): return True -#:tag:BreakWatchpoints def delete_breakpoint(expression): """Deletes a breakpoint at the address evaluated by the given expression @@ -1634,22 +1704,22 @@ def delete_breakpoint(expression): del chained_breakpoints[n] break for breakpoint in deletion_list: - found_breakpoint = check_address_in_breakpoints(breakpoint[0]) + found_breakpoint = get_breakpoints_in_range(breakpoint[0]) if not found_breakpoint: print("no such breakpoint exists for address " + str_address) continue else: - breakpoint_number = found_breakpoint.number + breakpoint_number = found_breakpoint[0].number global breakpoint_on_hit_dict try: - del breakpoint_on_hit_dict[breakpoint[0]] + del breakpoint_on_hit_dict[breakpoint_number] except KeyError: pass - send_command("delete " + str(breakpoint_number)) + send_command("delete " + breakpoint_number) return True -#:tag:BreakWatchpoints +@execute_with_temporary_interruption def track_watchpoint(expression, length, watchpoint_type): """Starts tracking a value by setting a watchpoint at the address holding it Use get_track_watchpoint_info() to get info about the watchpoint you set @@ -1657,24 +1727,22 @@ def track_watchpoint(expression, length, watchpoint_type): Args: expression (str): Any gdb expression length (int): Length of the watchpoint - watchpoint_type (int): Can be a member of type_defs.WATCHPOINT_TYPE + watchpoint_type (int): Can be a member of typedefs.WATCHPOINT_TYPE Returns: list: Numbers of the successfully set breakpoints as strings None: If fails to set any watchpoint """ - breakpoints = add_watchpoint(expression, length, watchpoint_type, type_defs.BREAKPOINT_ON_HIT.FIND_CODE) + breakpoints = add_watchpoint(expression, length, watchpoint_type, typedefs.BREAKPOINT_ON_HIT.FIND_CODE) if not breakpoints: return for breakpoint in breakpoints: - send_command("commands " + breakpoint - + "\npince-get-track-watchpoint-info " + str(breakpoints) - + "\nc" - + "\nend") + send_command( + "commands " + breakpoint + "\npince-get-track-watchpoint-info " + str(breakpoints) + "\nc&" + "\nend" + ) return breakpoints -#:tag:BreakWatchpoints def get_track_watchpoint_info(watchpoint_list): """Gathers the information for the tracked watchpoint(s) @@ -1692,7 +1760,7 @@ def get_track_watchpoint_info(watchpoint_list): float_info-->(dict) Same dict returned from read_float_registers() disas_info-->(str) A small section that's disassembled just after previous_pc_counter """ - track_watchpoint_file = SysUtils.get_track_watchpoint_file(currentpid, watchpoint_list) + track_watchpoint_file = utils.get_track_watchpoint_file(currentpid, watchpoint_list) try: output = pickle.load(open(track_watchpoint_file, "rb")) except: @@ -1700,7 +1768,7 @@ def get_track_watchpoint_info(watchpoint_list): return output -#:tag:BreakWatchpoints +@execute_with_temporary_interruption def track_breakpoint(expression, register_expressions): """Starts tracking a value by setting a breakpoint at the address holding it Use get_track_breakpoint_info() to get info about the breakpoint you set @@ -1715,17 +1783,24 @@ def track_breakpoint(expression, register_expressions): str: Number of the breakpoint set None: If fails to set any breakpoint """ - breakpoint = add_breakpoint(expression, on_hit=type_defs.BREAKPOINT_ON_HIT.FIND_ADDR) + breakpoint = add_breakpoint(expression, on_hit=typedefs.BREAKPOINT_ON_HIT.FIND_ADDR) if not breakpoint: return - send_command("commands " + breakpoint - + "\npince-get-track-breakpoint-info " + register_expressions.replace(" ", "") + "," + breakpoint - + "\nc" - + "\nend") + # TODO (lldb): When we switch to LLDB, remove c& and only continue if there isn't an active trace + # Apply the same for track_watchpoint + send_command( + "commands " + + breakpoint + + "\npince-get-track-breakpoint-info " + + register_expressions.replace(" ", "") + + "," + + breakpoint + + "\nc&" + + "\nend" + ) return breakpoint -#:tag:BreakWatchpoints def get_track_breakpoint_info(breakpoint): """Gathers the information for the tracked breakpoint @@ -1740,7 +1815,7 @@ def get_track_breakpoint_info(breakpoint): value-->(str) Value calculated by given register expression as hex str count-->(int) How many times this expression has been reached """ - track_breakpoint_file = SysUtils.get_track_breakpoint_file(currentpid, breakpoint) + track_breakpoint_file = utils.get_track_breakpoint_file(currentpid, breakpoint) try: output = pickle.load(open(track_breakpoint_file, "rb")) except: @@ -1748,116 +1823,144 @@ def get_track_breakpoint_info(breakpoint): return output -#:tag:Tools -def trace_instructions(expression, max_trace_count=1000, trigger_condition="", stop_condition="", - step_mode=type_defs.STEP_MODE.SINGLE_STEP, - stop_after_trace=False, collect_general_registers=True, collect_flag_registers=True, - collect_segment_registers=True, collect_float_registers=True): - """Starts tracing instructions at the address evaluated by the given expression - There can be only one tracing process at a time, calling this function without waiting the first tracing process - meet an end may cause bizarre behaviour - Use get_trace_instructions_info() to get info about the breakpoint you set - - Args: - expression (str): Any gdb expression - max_trace_count (int): Maximum number of steps will be taken while tracing. Must be greater than or equal to 1 - trigger_condition (str): Optional, any gdb expression. Tracing will start if the condition is met - stop_condition (str): Optional, any gdb expression. Tracing will stop whenever the condition is met - step_mode (int): Can be a member of type_defs.STEP_MODE - stop_after_trace (bool): Inferior won't be continuing after the tracing process - collect_general_registers (bool): Collect general registers while stepping - collect_flag_registers (bool): Collect flag registers while stepping - collect_segment_registers (bool): Collect segment registers while stepping - collect_float_registers (bool): Collect float registers while stepping - - Returns: - str: Number of the breakpoint set - None: If fails to set any breakpoint or if max_trace_count is not valid - """ - if max_trace_count < 1: - print("max_trace_count must be greater than or equal to 1") - return - if type(max_trace_count) != int: - print("max_trace_count must be an integer") - return - breakpoint = add_breakpoint(expression, on_hit=type_defs.BREAKPOINT_ON_HIT.TRACE) - if not breakpoint: - return - modify_breakpoint(expression, type_defs.BREAKPOINT_MODIFY.CONDITION, condition=trigger_condition) - contents_send = (type_defs.TRACE_STATUS.STATUS_IDLE, "Waiting for breakpoint to trigger") - trace_status_file = SysUtils.get_trace_instructions_status_file(currentpid, breakpoint) - pickle.dump(contents_send, open(trace_status_file, "wb")) - param_str = ( - breakpoint, max_trace_count, stop_condition, step_mode, stop_after_trace, collect_general_registers, - collect_flag_registers, collect_segment_registers, collect_float_registers) - send_command("commands " + breakpoint - + "\npince-trace-instructions " + str(param_str) - + "\nend") - return breakpoint - - -#:tag:Tools -def get_trace_instructions_info(breakpoint): - """Gathers the information of the tracing process for the given breakpoint - - Args: - breakpoint (str): breakpoint number, must be returned from trace_instructions() - - Returns: - list: [node1, node2, node3, ...] - node-->[(line_info, register_dict), parent_index, child_index_list] - If an error occurs while reading, an empty list returned instead - - Check PINCE.TraceInstructionsWindowForm.show_trace_info() to see how to traverse the tree - If you just want to search something in the trace data, you can enumerate the tree instead of traversing - Root always be an empty node, it's up to you to use or delete it - Any "call" instruction creates a node in SINGLE_STEP mode - Any "ret" instruction creates a parent regardless of the mode - """ - trace_instructions_file = SysUtils.get_trace_instructions_file(currentpid, breakpoint) - try: - output = json.load(open(trace_instructions_file, "r"), object_pairs_hook=OrderedDict) - except: - output = [] - return output - - -#:tag:Tools -def get_trace_instructions_status(breakpoint): - """Returns the current state of tracing process for given breakpoint - - Args: - breakpoint (str): breakpoint number, must be returned from trace_instructions() - - Returns: - tuple:(status_id, status_str) - - status_id-->(int) A member of type_defs.TRACE_STATUS - status_str-->(str) Status string - - Returns a tuple of (False, "") if fails to gather info - """ - trace_status_file = SysUtils.get_trace_instructions_status_file(currentpid, breakpoint) - try: - output = pickle.load(open(trace_status_file, "rb")) - except: - output = False, "" - return output - - -#:tag:Tools -def cancel_trace_instructions(breakpoint): - """Finishes the trace instruction process early on for the given breakpoint +class Tracer: + def __init__(self) -> None: + """Use set_breakpoint after init and if it succeeds, use tracer_loop within a thread + There can be only one trace session at a time. Don't create new trace sessions before finishing the last one""" + self.expression = "" + self.max_trace_count = 1000 + self.stop_condition = "" + self.step_mode = typedefs.STEP_MODE.SINGLE_STEP + self.stop_after_trace = False + self.collect_registers = True + self.trace_status = typedefs.TRACE_STATUS.IDLE + self.current_trace_count = 0 + self.trace_data = ([], None) + self.cancel = False + utils.change_trace_status(currentpid, self.trace_status) + + @execute_with_temporary_interruption + def set_breakpoint( + self, + expression: str, + max_trace_count: int = 1000, + trigger_condition: str = "", + stop_condition: str = "", + step_mode: typedefs.STEP_MODE = typedefs.STEP_MODE.SINGLE_STEP, + stop_after_trace: bool = False, + collect_registers: bool = True, + ) -> str: + """Sets the breakpoint for tracing instructions at the address evaluated by the given expression + + Args: + expression (str): Any gdb expression + max_trace_count (int): Maximum number of steps taken while tracing. Must be greater than or equal to 1 + trigger_condition (str): Optional, any gdb expression. Tracing will start if the condition is met + stop_condition (str): Optional, any gdb expression. Tracing will stop whenever the condition is met + step_mode (int): Can be a member of typedefs.STEP_MODE + stop_after_trace (bool): Inferior won't be continuing after the tracing process + collect_registers (bool): Collect registers while stepping + + Returns: + str: Number of the breakpoint set + None: If fails to set any breakpoint or if max_trace_count is not valid + """ + if max_trace_count < 1: + print("max_trace_count must be greater than or equal to 1") + return + if type(max_trace_count) != int: + print("max_trace_count must be an integer") + return + breakpoint = add_breakpoint(expression, on_hit=typedefs.BREAKPOINT_ON_HIT.TRACE) + if not breakpoint: + return + modify_breakpoint(expression, typedefs.BREAKPOINT_MODIFY.CONDITION, condition=trigger_condition) + ( + self.expression, + self.max_trace_count, + self.stop_condition, + self.step_mode, + self.stop_after_trace, + self.collect_registers, + ) = (expression, max_trace_count, stop_condition, step_mode, stop_after_trace, collect_registers) + send_command("commands " + breakpoint + "\npince-trace-instructions\nend") + return breakpoint + + def tracer_loop(self): + """The main tracer loop, call within a thread""" + self.current_trace_count = 0 + trace_status_file = utils.get_trace_status_file(currentpid) + while not (self.trace_status != typedefs.TRACE_STATUS.IDLE or self.cancel or currentpid == -1): + try: + with open(trace_status_file, "r") as trace_file: + self.trace_status = int(trace_file.read()) + except (ValueError, FileNotFoundError): + pass + sleep(0.1) + global active_trace + active_trace = True + delete_breakpoint(self.expression) + self.trace_status = typedefs.TRACE_STATUS.TRACING + + # The reason we don't use a tree class is to make the tree json-compatible + # tree format-->[node1, node2, node3, ...] + # node-->[(line_info, register_dict), parent_index, child_index_list] + tree = [] + current_index = 0 # Avoid calling len() + current_root_index = 0 + root_index = 0 + + # Root always be an empty node, it's up to you to use or delete it + tree.append([("", None), None, []]) + try: # In case process exits during the trace session + for x in range(self.max_trace_count): + if self.cancel or currentpid == -1: + break + line_info = send_command("x/i $pc", cli_output=True).splitlines()[0].split(maxsplit=1)[1] + collect_dict = OrderedDict() + if self.collect_registers: + collect_dict.update(read_registers()) + collect_dict.update(read_float_registers()) + current_index += 1 + tree.append([(line_info, collect_dict), current_root_index, []]) + tree[current_root_index][2].append(current_index) # Add a child + self.current_trace_count = x + 1 + if regexes.trace_instructions_ret.search(line_info): + if tree[current_root_index][1] is None: # If no parents exist + current_index += 1 + tree.append([("", None), None, [current_root_index]]) + tree[current_root_index][1] = current_index # Set new parent + current_root_index = current_index # current_node=current_node.parent + root_index = current_root_index # set new root + else: + current_root_index = tree[current_root_index][1] # current_node=current_node.parent + elif self.step_mode == typedefs.STEP_MODE.SINGLE_STEP: + if regexes.trace_instructions_call.search(line_info): + current_root_index = current_index + if self.stop_condition: + try: + if str(parse_and_eval(self.stop_condition)) == "1": + break + except: + pass + if self.step_mode == typedefs.STEP_MODE.SINGLE_STEP: + step_instruction() + elif self.step_mode == typedefs.STEP_MODE.STEP_OVER: + step_over_instruction() + wait_for_stop() + except: + traceback.print_exc() + self.trace_data = (tree, root_index) + self.trace_status = typedefs.TRACE_STATUS.FINISHED + active_trace = False + if not self.stop_after_trace: + continue_inferior() - Args: - breakpoint (str): breakpoint number, must be returned from trace_instructions() - """ - status_info = (type_defs.TRACE_STATUS.STATUS_CANCELED, "Tracing has been canceled") - trace_status_file = SysUtils.get_trace_instructions_status_file(currentpid, breakpoint) - pickle.dump(status_info, open(trace_status_file, "wb")) + def cancel_trace(self): + """Prematurely ends the trace session, trace data will still be collected""" + self.cancel = True -#:tag:Tools def call_function_from_inferior(expression): """Calls the given function expression from the inferior @@ -1871,14 +1974,13 @@ def call_function_from_inferior(expression): Examples: call_function_from_inferior("printf('123')") returns ("$26","3") """ - result = send_command("call (char *) " + expression) - filtered_result = common_regexes.convenience_variable.search(result) + result = execute_func_temporary_interruption(send_command, f"call (void*(*)(char*, int)) {expression}") + filtered_result = regexes.convenience_variable.search(result) if filtered_result: return filtered_result.group(1), filtered_result.group(2) return False, False -#:tag:InferiorInformation def find_entry_point(): """Finds entry point of the inferior @@ -1887,12 +1989,11 @@ def find_entry_point(): None: If fails to find an entry point """ result = send_command("info file") - filtered_result = common_regexes.entry_point.search(result) + filtered_result = regexes.entry_point.search(result) if filtered_result: return filtered_result.group(1) -#:tag:Tools def search_opcode(searched_str, starting_address, ending_address_or_offset, case_sensitive=False, enable_regex=False): """Searches for the given str in the disassembled output @@ -1938,20 +2039,18 @@ def search_opcode(searched_str, starting_address, ending_address_or_offset, case return returned_list -#:tag:Tools def dissect_code(region_list, discard_invalid_strings=True): """Searches given regions for jumps, calls and string references Use function get_dissect_code_data() to gather the results Args: - region_list (list): A list of psutil._pslinux.pmmap_ext objects - Can be returned from functions like SysUtils.get_memory_regions_by_perms + region_list (list): A list of (start_address, end_address) -> (str, str) + Can be returned from functions like utils.filter_regions discard_invalid_strings (bool): Entries that can't be decoded as utf-8 won't be included in referenced strings """ send_command("pince-dissect-code", send_with_file=True, file_contents_send=(region_list, discard_invalid_strings)) -#:tag:Tools def get_dissect_code_status(): """Returns the current state of dissect code process @@ -1968,7 +2067,7 @@ def get_dissect_code_status(): Returns a tuple of ("", "", "", 0, 0, 0) if fails to gather info """ - dissect_code_status_file = SysUtils.get_dissect_code_status_file(currentpid) + dissect_code_status_file = utils.get_dissect_code_status_file(currentpid) try: output = pickle.load(open(dissect_code_status_file, "rb")) except: @@ -1976,14 +2075,12 @@ def get_dissect_code_status(): return output -#:tag:Tools def cancel_dissect_code(): """Finishes the current dissect code process early on""" if last_gdb_command.find("pince-dissect-code") != -1: cancel_last_command() -#:tag:Tools def get_dissect_code_data(referenced_strings=True, referenced_jumps=True, referenced_calls=True): """Returns shelve.DbfilenameShelf objects of referenced dicts @@ -2013,22 +2110,22 @@ def get_dissect_code_data(referenced_strings=True, referenced_jumps=True, refere """ dict_list = [] if referenced_strings: - dict_list.append(shelve.open(SysUtils.get_referenced_strings_file(currentpid), "r")) + dict_list.append(shelve.open(utils.get_referenced_strings_file(currentpid), "r")) if referenced_jumps: - dict_list.append(shelve.open(SysUtils.get_referenced_jumps_file(currentpid), "r")) + dict_list.append(shelve.open(utils.get_referenced_jumps_file(currentpid), "r")) if referenced_calls: - dict_list.append(shelve.open(SysUtils.get_referenced_calls_file(currentpid), "r")) + dict_list.append(shelve.open(utils.get_referenced_calls_file(currentpid), "r")) return dict_list -#:tag:Tools -def search_referenced_strings(searched_str, value_index=type_defs.VALUE_INDEX.INDEX_STRING_UTF8, case_sensitive=False, - enable_regex=False): +def search_referenced_strings( + searched_str, value_index=typedefs.VALUE_INDEX.STRING_UTF8, case_sensitive=False, enable_regex=False +): """Searches for given str in the referenced strings Args: searched_str (str): String that will be searched - value_index (int): Can be a member of type_defs.VALUE_INDEX + value_index (int): Can be a member of typedefs.VALUE_INDEX case_sensitive (bool): If True, search will be case sensitive enable_regex (bool): If True, searched_str will be treated as a regex expression @@ -2046,14 +2143,10 @@ def search_referenced_strings(searched_str, value_index=type_defs.VALUE_INDEX.IN print("An exception occurred while trying to compile the given regex\n", str(e)) return str_dict = get_dissect_code_data(True, False, False)[0] - nested_list = [] - referenced_list = [] + mem_handle = memory_handle() returned_list = [] - for item in str_dict: - nested_list.append((int(item, 16), value_index, 100)) - referenced_list.append(item) - value_list = read_memory_multiple(nested_list) - for index, value in enumerate(value_list): + for address, refs in str_dict.items(): + value = read_memory(int(address, 16), value_index, 100, mem_handle=mem_handle) value_str = "" if value is None else str(value) if not value_str: continue @@ -2067,13 +2160,12 @@ def search_referenced_strings(searched_str, value_index=type_defs.VALUE_INDEX.IN else: if value_str.lower().find(searched_str.lower()) == -1: continue - ref_addr = referenced_list[index] - returned_list.append((ref_addr, len(str_dict[ref_addr]), value)) + returned_list.append((address, len(refs), value)) str_dict.close() + mem_handle.close() return returned_list -#:tag:Tools def search_referenced_calls(searched_str, case_sensitive=True, enable_regex=False): """Searches for given str in the referenced calls @@ -2101,6 +2193,6 @@ def complete_command(gdb_command): """ returned_list = [] for item in send_command("complete " + gdb_command, cli_output=True).splitlines(): - if not common_regexes.max_completions_reached.search(item): + if not regexes.max_completions_reached.search(item): returned_list.append(item) return returned_list diff --git a/libpince/gdb_python_scripts/GDBCommandExtensions.py b/libpince/gdb_python_scripts/gdbextensions.py similarity index 67% rename from libpince/gdb_python_scripts/GDBCommandExtensions.py rename to libpince/gdb_python_scripts/gdbextensions.py index 46c21f12..9a89444a 100644 --- a/libpince/gdb_python_scripts/GDBCommandExtensions.py +++ b/libpince/gdb_python_scripts/gdbextensions.py @@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ -import gdb, pickle, json, sys, re, struct, ctypes, os, shelve, distorm3 +import gdb, pickle, json, sys, re, struct, ctypes, os, shelve, distorm3, importlib from collections import OrderedDict # This is some retarded hack @@ -23,14 +23,13 @@ PINCE_PATH = gdbvalue.string() sys.path.append(PINCE_PATH) # Adds the PINCE directory to PYTHONPATH to import libraries from PINCE -from libpince.gdb_python_scripts import ScriptUtils -from libpince import SysUtils, type_defs, common_regexes - -inferior = gdb.selected_inferior() -pid = inferior.pid -recv_file = SysUtils.get_IPC_from_PINCE_file(pid) -send_file = SysUtils.get_IPC_to_PINCE_file(pid) +from libpince.gdb_python_scripts import gdbutils +from libpince import utils, typedefs, regexes +importlib.reload(gdbutils) +pid = gdbutils.pid +recv_file = utils.get_from_pince_file(pid) +send_file = utils.get_to_pince_file(pid) lib = None # Format of info_list: [count, previous_pc_address, register_info, float_info, disas_info] @@ -53,7 +52,7 @@ def send_to_pince(contents_send): pickle.dump(contents_send, open(send_file, "wb")) -ScriptUtils.gdbinit() +gdbutils.gdbinit() class IgnoreErrors(gdb.Command): @@ -80,6 +79,18 @@ def invoke(self, arg, from_tty): send_to_pince(contents_send) +class HandleSignals(gdb.Command): + def __init__(self): + super().__init__("pince-handle-signals", gdb.COMMAND_USER) + + def invoke(self, arg, from_tty): + signal_data = receive_from_pince() + for signal, stop, pass_to_program in signal_data: + stop = "stop print" if stop else "nostop noprint" + pass_to_program = "pass" if pass_to_program else "nopass" + gdb.execute(f"handle {signal} {stop} {pass_to_program}", from_tty, to_string=True) + + class ParseAndEval(gdb.Command): def __init__(self): super(ParseAndEval, self).__init__("pince-parse-and-eval", gdb.COMMAND_USER) @@ -102,9 +113,11 @@ def __init__(self): super(ReadRegisters, self).__init__("pince-read-registers", gdb.COMMAND_USER) def invoke(self, arg, from_tty): - registers = ScriptUtils.get_general_registers() - registers.update(ScriptUtils.get_flag_registers()) - registers.update(ScriptUtils.get_segment_registers()) + registers = gdbutils.get_general_registers() + for key, value in registers.items(): + registers[key] = utils.upper_hex(value) + registers.update(gdbutils.get_flag_registers()) + registers.update(gdbutils.get_segment_registers()) send_to_pince(registers) @@ -113,7 +126,7 @@ def __init__(self): super(ReadFloatRegisters, self).__init__("pince-read-float-registers", gdb.COMMAND_USER) def invoke(self, arg, from_tty): - send_to_pince(ScriptUtils.get_float_registers()) + send_to_pince(gdbutils.get_float_registers()) class GetStackTraceInfo(gdb.Command): @@ -122,25 +135,33 @@ def __init__(self): def invoke(self, arg, from_tty): stacktrace_info_list = [] - if ScriptUtils.current_arch == type_defs.INFERIOR_ARCH.ARCH_64: + if gdbutils.current_arch == typedefs.INFERIOR_ARCH.ARCH_64: sp_register = "rsp" else: sp_register = "esp" - stack_pointer_int = int(ScriptUtils.examine_expression("$" + sp_register).address, 16) + stack_pointer = gdbutils.examine_expression(f"${sp_register}").address + if not stack_pointer: + print(f"Cannot get the value of ${sp_register}") + send_to_pince(stacktrace_info_list) + return + stack_pointer = int(stack_pointer, 16) result = gdb.execute("bt", from_tty, to_string=True) - max_frame = common_regexes.max_frame_count.findall(result)[-1] + max_frame = regexes.max_frame_count.findall(result)[-1] # +1 because frame numbers start from 0 for item in range(int(max_frame) + 1): - result = gdb.execute("info frame " + str(item), from_tty, to_string=True) - frame_address = common_regexes.frame_address.search(result).group(1) - difference = hex(int(frame_address, 16) - stack_pointer_int) + try: + result = gdb.execute(f"info frame {item}", from_tty, to_string=True) + except: + break + frame_address = regexes.frame_address.search(result).group(1) + difference = hex(int(frame_address, 16) - stack_pointer) frame_address_with_difference = frame_address + "(" + sp_register + "+" + difference + ")" - return_address = common_regexes.return_address.search(result) + return_address = regexes.return_address.search(result) if return_address: - return_address_with_info = ScriptUtils.examine_expression(return_address.group(1)).all + return_address_with_info = gdbutils.examine_expression(return_address.group(1)).all else: - return_address_with_info = "" + return_address_with_info = "<>" stacktrace_info_list.append([return_address_with_info, frame_address_with_difference]) send_to_pince(stacktrace_info_list) @@ -151,24 +172,39 @@ def __init__(self): def invoke(self, arg, from_tty): stack_info_list = [] - if ScriptUtils.current_arch == type_defs.INFERIOR_ARCH.ARCH_64: + if gdbutils.current_arch == typedefs.INFERIOR_ARCH.ARCH_64: chunk_size = 8 int_format = "Q" - sp_register = "rsp" + + if arg == "from-base-pointer": + stack_register = "rbp" + else: + stack_register = "rsp" else: chunk_size = 4 int_format = "I" - sp_register = "esp" - sp_address = int(ScriptUtils.examine_expression("$" + sp_register).address, 16) - with open(ScriptUtils.mem_file, "rb") as FILE: + + if arg == "from-base-pointer": + stack_register = "ebp" + else: + stack_register = "esp" + + sp_address = gdbutils.examine_expression(f"${stack_register}").address + if not sp_address: + print(f"Cannot get the value of ${stack_register}") + send_to_pince(stack_info_list) + return + sp_address = int(sp_address, 16) + with open(gdbutils.mem_file, "rb") as FILE: try: old_position = FILE.seek(sp_address) except (OSError, ValueError): + print(f"Cannot accesss the memory at {hex(sp_address)}") send_to_pince(stack_info_list) return for index in range(int(4096 / chunk_size)): current_offset = chunk_size * index - stack_indicator = hex(sp_address + current_offset) + "(" + sp_register + "+" + hex(current_offset) + ")" + stack_indicator = hex(sp_address + current_offset) + "(" + stack_register + "+" + hex(current_offset) + ")" try: FILE.seek(old_position) read = FILE.read(chunk_size) @@ -184,7 +220,7 @@ def invoke(self, arg, from_tty): except (OSError, ValueError): pointer_data = "" else: - symbol = ScriptUtils.examine_expression(hex_repr).symbol + symbol = gdbutils.examine_expression(hex_repr).symbol if not symbol: pointer_data = "(str)" + read_pointer.decode("utf-8", "ignore") else: @@ -200,16 +236,19 @@ def __init__(self): def invoke(self, arg, from_tty): return_address_list = [] result = gdb.execute("bt", from_tty, to_string=True) - max_frame = common_regexes.max_frame_count.findall(result)[-1] + max_frame = regexes.max_frame_count.findall(result)[-1] # +1 because frame numbers start from 0 for item in range(int(max_frame) + 1): - result = gdb.execute("info frame " + str(item), from_tty, to_string=True) - return_address = common_regexes.return_address.search(result) + try: + result = gdb.execute(f"info frame {item}", from_tty, to_string=True) + except: + break + return_address = regexes.return_address.search(result) if return_address: - return_address_with_info = ScriptUtils.examine_expression(return_address.group(1)).all + return_address_with_info = gdbutils.examine_expression(return_address.group(1)).all else: - return_address_with_info = "" + return_address_with_info = "<>" return_address_list.append(return_address_with_info) send_to_pince(return_address_list) @@ -221,7 +260,7 @@ def __init__(self): def invoke(self, arg, from_tty): frame_number = receive_from_pince() result = gdb.execute("bt", from_tty, to_string=True) - max_frame = common_regexes.max_frame_count.findall(result)[-1] + max_frame = regexes.max_frame_count.findall(result)[-1] if 0 <= int(frame_number) <= int(max_frame): frame_info = gdb.execute("info frame " + frame_number, from_tty, to_string=True) else: @@ -236,13 +275,13 @@ def __init__(self): def invoke(self, arg, from_tty): breakpoints = arg - current_pc_int = int(SysUtils.extract_address(str(gdb.parse_and_eval("$pc"))), 16) + current_pc_int = int(utils.extract_address(str(gdb.parse_and_eval("$pc"))), 16) try: disas_output = gdb.execute("disas $pc-30,$pc", to_string=True) # Just before the line "End of assembler dump" last_instruction = disas_output.splitlines()[-2] - previous_pc_address = SysUtils.extract_address(last_instruction) + previous_pc_address = utils.extract_address(last_instruction) except: previous_pc_address = hex(current_pc_int) global track_watchpoint_dict @@ -252,14 +291,19 @@ def invoke(self, arg, from_tty): if breakpoints not in track_watchpoint_dict: track_watchpoint_dict[breakpoints] = OrderedDict() count = 1 - register_info = ScriptUtils.get_general_registers() - register_info.update(ScriptUtils.get_flag_registers()) - register_info.update(ScriptUtils.get_segment_registers()) - float_info = ScriptUtils.get_float_registers() + register_info = gdbutils.get_general_registers() + register_info.update(gdbutils.get_flag_registers()) + register_info.update(gdbutils.get_segment_registers()) + float_info = gdbutils.get_float_registers() disas_info = gdb.execute("disas " + previous_pc_address + ",+40", to_string=True).replace("=>", " ") - track_watchpoint_dict[breakpoints][current_pc_int] = [count, previous_pc_address, register_info, float_info, - disas_info] - track_watchpoint_file = SysUtils.get_track_watchpoint_file(pid, breakpoints) + track_watchpoint_dict[breakpoints][current_pc_int] = [ + count, + previous_pc_address, + register_info, + float_info, + disas_info, + ] + track_watchpoint_file = utils.get_track_watchpoint_file(pid, breakpoints) pickle.dump(track_watchpoint_dict[breakpoints], open(track_watchpoint_file, "wb")) @@ -280,7 +324,7 @@ def invoke(self, arg, from_tty): if not register_expression in track_breakpoint_dict[breakpoint_number]: track_breakpoint_dict[breakpoint_number][register_expression] = OrderedDict() try: - address = ScriptUtils.examine_expression(register_expression).address + address = gdbutils.examine_expression(register_expression).address except: address = None if address: @@ -288,7 +332,7 @@ def invoke(self, arg, from_tty): track_breakpoint_dict[breakpoint_number][register_expression][address] = 1 else: track_breakpoint_dict[breakpoint_number][register_expression][address] += 1 - track_breakpoint_file = SysUtils.get_track_breakpoint_file(pid, breakpoint_number) + track_breakpoint_file = utils.get_track_breakpoint_file(pid, breakpoint_number) pickle.dump(track_breakpoint_dict[breakpoint_number], open(track_breakpoint_file, "wb")) @@ -315,74 +359,7 @@ def __init__(self): super(TraceInstructions, self).__init__("pince-trace-instructions", gdb.COMMAND_USER) def invoke(self, arg, from_tty): - (breakpoint, max_trace_count, stop_condition, step_mode, stop_after_trace, collect_general_registers, - collect_flag_registers, collect_segment_registers, collect_float_registers) = eval(arg) - gdb.execute("delete " + breakpoint) - trace_status_file = SysUtils.get_trace_instructions_status_file(pid, breakpoint) - - # The reason we don't use a tree class is to make the tree json-compatible - # tree format-->[node1, node2, node3, ...] - # node-->[(line_info, register_dict), parent_index, child_index_list] - tree = [] - current_index = 0 # Avoid calling len() - current_root_index = 0 - root_index = 0 - - # Root always be an empty node, it's up to you to use or delete it - tree.append([("", None), None, []]) - for x in range(max_trace_count): - try: - output = pickle.load(open(trace_status_file, "rb")) - if output[0] == type_defs.TRACE_STATUS.STATUS_CANCELED: - break - except: - pass - line_info = gdb.execute("x/i $pc", to_string=True).split(maxsplit=1)[1] - collect_dict = OrderedDict() - if collect_general_registers: - collect_dict.update(ScriptUtils.get_general_registers()) - if collect_flag_registers: - collect_dict.update(ScriptUtils.get_flag_registers()) - if collect_segment_registers: - collect_dict.update(ScriptUtils.get_segment_registers()) - if collect_float_registers: - collect_dict.update(ScriptUtils.get_float_registers()) - current_index += 1 - tree.append([(line_info, collect_dict), current_root_index, []]) - tree[current_root_index][2].append(current_index) # Add a child - status_info = (type_defs.TRACE_STATUS.STATUS_TRACING, - line_info + " (" + str(x + 1) + "/" + str(max_trace_count) + ")") - pickle.dump(status_info, open(trace_status_file, "wb")) - if common_regexes.trace_instructions_ret.search(line_info): - if tree[current_root_index][1] is None: # If no parents exist - current_index += 1 - tree.append([("", None), None, [current_root_index]]) - tree[current_root_index][1] = current_index # Set new parent - current_root_index = current_index # current_node=current_node.parent - root_index = current_root_index # set new root - else: - current_root_index = tree[current_root_index][1] # current_node=current_node.parent - elif step_mode == type_defs.STEP_MODE.SINGLE_STEP: - if common_regexes.trace_instructions_call.search(line_info): - current_root_index = current_index - if stop_condition: - try: - if str(gdb.parse_and_eval(stop_condition)) == "1": - break - except: - pass - if step_mode == type_defs.STEP_MODE.SINGLE_STEP: - gdb.execute("stepi", to_string=True) - elif step_mode == type_defs.STEP_MODE.STEP_OVER: - gdb.execute("nexti", to_string=True) - status_info = (type_defs.TRACE_STATUS.STATUS_PROCESSING, "Processing the collected data") - pickle.dump(status_info, open(trace_status_file, "wb")) - trace_instructions_file = SysUtils.get_trace_instructions_file(pid, breakpoint) - json.dump((tree, root_index), open(trace_instructions_file, "w")) - status_info = (type_defs.TRACE_STATUS.STATUS_FINISHED, "Tracing has been completed") - pickle.dump(status_info, open(trace_status_file, "wb")) - if not stop_after_trace: - gdb.execute("c") + utils.change_trace_status(pid, typedefs.TRACE_STATUS.TRACING) class InitSoFile(gdb.Command): @@ -444,26 +421,23 @@ def is_memory_valid(self, int_address, discard_invalid_strings=False): return True def invoke(self, arg, from_tty): - if ScriptUtils.current_arch == type_defs.INFERIOR_ARCH.ARCH_64: + if gdbutils.current_arch == typedefs.INFERIOR_ARCH.ARCH_64: disas_option = distorm3.Decode64Bits else: disas_option = distorm3.Decode32Bits - referenced_strings_dict = shelve.open(SysUtils.get_referenced_strings_file(pid), writeback=True) - referenced_jumps_dict = shelve.open(SysUtils.get_referenced_jumps_file(pid), writeback=True) - referenced_calls_dict = shelve.open(SysUtils.get_referenced_calls_file(pid), writeback=True) + referenced_strings_dict = shelve.open(utils.get_referenced_strings_file(pid), writeback=True) + referenced_jumps_dict = shelve.open(utils.get_referenced_jumps_file(pid), writeback=True) + referenced_calls_dict = shelve.open(utils.get_referenced_calls_file(pid), writeback=True) region_list, discard_invalid_strings = receive_from_pince() - dissect_code_status_file = SysUtils.get_dissect_code_status_file(pid) + dissect_code_status_file = utils.get_dissect_code_status_file(pid) region_count = len(region_list) - self.memory = open(ScriptUtils.mem_file, "rb") - - # Has the best record of 111 secs. Tested on Torchlight 2 with Intel i7-4702MQ CPU and 8GB RAM - buffer = 0x10000 # Aligned to 2**16 + self.memory = open(gdbutils.mem_file, "rb") + buffer = 0x100000 ref_str_count = len(referenced_strings_dict) ref_jmp_count = len(referenced_jumps_dict) ref_call_count = len(referenced_calls_dict) - for region_index, region in enumerate(region_list): - region_info = region.addr, "Region " + str(region_index + 1) + " of " + str(region_count) - start_addr, end_addr = region.addr.split("-") + for region_index, (start_addr, end_addr) in enumerate(region_list): + region_info = start_addr + "-" + end_addr, str(region_index + 1) + " / " + str(region_count) start_addr = int(start_addr, 16) # Becomes address of the last disassembled instruction later on end_addr = int(end_addr, 16) region_finished = False @@ -474,8 +448,12 @@ def invoke(self, arg, from_tty): region_finished = True else: offset = buffer - status_info = region_info + (hex(start_addr) + "-" + hex(start_addr + offset), - ref_str_count, ref_jmp_count, ref_call_count) + status_info = region_info + ( + hex(start_addr)[2:] + "-" + hex(start_addr + offset)[2:], + ref_str_count, + ref_jmp_count, + ref_call_count, + ) pickle.dump(status_info, open(dissect_code_status_file, "wb")) try: self.memory.seek(start_addr) @@ -489,16 +467,16 @@ def invoke(self, arg, from_tty): del disas_data[-1] # Get rid of last 4 instructions to ensure correct bytecode translation else: last_disas_addr = 0 - for (instruction_offset, size, instruction, hexdump) in disas_data: + for instruction_offset, size, instruction, hexdump in disas_data: if isinstance(instruction, bytes): instruction = instruction.decode() if instruction.startswith("J") or instruction.startswith("LOOP"): - found = common_regexes.dissect_code_valid_address.search(instruction) + found = regexes.dissect_code_valid_address.search(instruction) if found: - referenced_address_str = common_regexes.hex_number.search(found.group(0)).group(0) + referenced_address_str = regexes.hex_number.search(found.group(0)).group(0) referenced_address_int = int(referenced_address_str, 16) if self.is_memory_valid(referenced_address_int): - instruction_only = common_regexes.alphanumerics.search(instruction).group(0).casefold() + instruction_only = regexes.alphanumerics.search(instruction).group(0).casefold() try: referenced_jumps_dict[referenced_address_str][instruction_offset] = instruction_only except KeyError: @@ -506,9 +484,9 @@ def invoke(self, arg, from_tty): referenced_jumps_dict[referenced_address_str][instruction_offset] = instruction_only ref_jmp_count += 1 elif instruction.startswith("CALL"): - found = common_regexes.dissect_code_valid_address.search(instruction) + found = regexes.dissect_code_valid_address.search(instruction) if found: - referenced_address_str = common_regexes.hex_number.search(found.group(0)).group(0) + referenced_address_str = regexes.hex_number.search(found.group(0)).group(0) referenced_address_int = int(referenced_address_str, 16) if self.is_memory_valid(referenced_address_int): try: @@ -518,9 +496,9 @@ def invoke(self, arg, from_tty): referenced_calls_dict[referenced_address_str].add(instruction_offset) ref_call_count += 1 else: - found = common_regexes.dissect_code_valid_address.search(instruction) + found = regexes.dissect_code_valid_address.search(instruction) if found: - referenced_address_str = common_regexes.hex_number.search(found.group(0)).group(0) + referenced_address_str = regexes.hex_number.search(found.group(0)).group(0) referenced_address_int = int(referenced_address_str, 16) if self.is_memory_valid(referenced_address_int, discard_invalid_strings): try: @@ -548,10 +526,10 @@ def invoke(self, arg, from_tty): except Exception as e: print("An exception occurred while trying to compile the given regex\n", str(e)) return - str_dict = shelve.open(SysUtils.get_referenced_calls_file(pid), "r") + str_dict = shelve.open(utils.get_referenced_calls_file(pid), "r") returned_list = [] for index, item in enumerate(str_dict): - symbol = ScriptUtils.examine_expression(item).all + symbol = gdbutils.examine_expression(item).all if not symbol: continue if enable_regex: @@ -577,8 +555,10 @@ def invoke(self, arg, from_tty): data_read_list = [] contents_recv = receive_from_pince() # contents_recv format: [expression1, expression2, ...] + + regions = utils.get_region_dict(pid) for expression in contents_recv: - result_tuple = ScriptUtils.examine_expression(expression) + result_tuple = gdbutils.examine_expression(expression, regions) data_read_list.append(result_tuple) send_to_pince(data_read_list) @@ -594,10 +574,14 @@ def invoke(self, arg, from_tty): gdb.execute("set case-sensitive on") else: gdb.execute("set case-sensitive off") - output = gdb.execute("info functions " + expression, to_string=True).splitlines() + try: + output = gdb.execute("info functions " + expression, to_string=True) + except Exception as e: + print("An exception occurred while trying to search functions:\n", e) + output = "" gdb.execute("set case-sensitive auto") - for line in output: - non_debugging = common_regexes.info_functions_non_debugging.search(line) + for line in output.splitlines(): + non_debugging = regexes.info_functions_non_debugging.search(line) if non_debugging: function_list.append((non_debugging.group(1), non_debugging.group(2))) else: @@ -608,6 +592,7 @@ def invoke(self, arg, from_tty): IgnoreErrors() CLIOutput() +HandleSignals() ParseAndEval() ReadRegisters() ReadFloatRegisters() diff --git a/libpince/gdb_python_scripts/ScriptUtils.py b/libpince/gdb_python_scripts/gdbutils.py similarity index 60% rename from libpince/gdb_python_scripts/ScriptUtils.py rename to libpince/gdb_python_scripts/gdbutils.py index 762a52f8..a452d87c 100644 --- a/libpince/gdb_python_scripts/ScriptUtils.py +++ b/libpince/gdb_python_scripts/gdbutils.py @@ -23,18 +23,18 @@ GDBINIT_AA_PATH = gdb.parse_and_eval("$GDBINIT_AA_PATH").string() sys.path.append(PINCE_PATH) # Adds the PINCE directory to PYTHONPATH to import libraries from PINCE -from libpince import type_defs, common_regexes +from libpince import typedefs, regexes inferior = gdb.selected_inferior() -pid = inferior.pid +pid = -1 if inferior.pid == 0 else inferior.pid mem_file = "/proc/" + str(pid) + "/mem" void_ptr = gdb.lookup_type("void").pointer() if str(gdb.parse_and_eval("$rax")) == "void": - current_arch = type_defs.INFERIOR_ARCH.ARCH_32 + current_arch = typedefs.INFERIOR_ARCH.ARCH_32 else: - current_arch = type_defs.INFERIOR_ARCH.ARCH_64 + current_arch = typedefs.INFERIOR_ARCH.ARCH_64 # Use this function instead of the .gdbinit file @@ -65,10 +65,10 @@ def wrapper(*args, **kwargs): def get_general_registers(): contents_send = OrderedDict() - if current_arch == type_defs.INFERIOR_ARCH.ARCH_64: - general_register_list = type_defs.REGISTERS.GENERAL_64 + if current_arch == typedefs.INFERIOR_ARCH.ARCH_64: + general_register_list = typedefs.REGISTERS.GENERAL_64 else: - general_register_list = type_defs.REGISTERS.GENERAL_32 + general_register_list = typedefs.REGISTERS.GENERAL_32 for item in general_register_list: contents_send[item] = examine_expression("$" + item).address return contents_send @@ -78,8 +78,17 @@ def get_flag_registers(): contents_send = OrderedDict() bitwise_flags = bin(int(gdb.parse_and_eval("$eflags")))[2:] reversed_bitwise_flags = "".join(reversed(bitwise_flags)) - (contents_send["cf"], contents_send["pf"], contents_send["af"], contents_send["zf"], contents_send["sf"], - contents_send["tf"], contents_send["if"], contents_send["df"], contents_send["of"]) = ["0"] * 9 + ( + contents_send["cf"], + contents_send["pf"], + contents_send["af"], + contents_send["zf"], + contents_send["sf"], + contents_send["tf"], + contents_send["if"], + contents_send["df"], + contents_send["of"], + ) = ["0"] * 9 try: contents_send["cf"] = reversed_bitwise_flags[0] contents_send["pf"] = reversed_bitwise_flags[2] @@ -97,27 +106,55 @@ def get_flag_registers(): def get_segment_registers(): contents_send = OrderedDict() - for item in type_defs.REGISTERS.SEGMENT: + for item in typedefs.REGISTERS.SEGMENT: contents_send[item] = examine_expression("$" + item).address return contents_send def get_float_registers(): contents_send = OrderedDict() - for register in type_defs.REGISTERS.FLOAT.ST: + for register in typedefs.REGISTERS.FLOAT.ST: value = gdb.parse_and_eval("$" + register) contents_send[register] = str(value) - for register in type_defs.REGISTERS.FLOAT.XMM: + if current_arch == typedefs.INFERIOR_ARCH.ARCH_64: + xmm_registers = typedefs.REGISTERS.FLOAT.XMM_64 + else: + xmm_registers = typedefs.REGISTERS.FLOAT.XMM_32 + for register in xmm_registers: value = gdb.parse_and_eval("$" + register + ".v4_float") contents_send[register] = str(value) return contents_send -def examine_expression(expression): +def examine_expression(expression: str, regions=None): try: value = gdb.parse_and_eval(expression).cast(void_ptr) except Exception as e: - print(e, "for expression " + expression) - return type_defs.tuple_examine_expression(None, None, None) - result = common_regexes.address_with_symbol.search(str(value)) - return type_defs.tuple_examine_expression(*result.groups()) + if regions: # this check comes first for optimization + offset = regexes.offset_expression.search(expression) + if offset: + offset = offset.group(0) + expression = expression.split(offset[0])[0] + else: + offset = "+0" + index = regexes.index.search(expression) + if index: + expression = expression[: index.start()] + index = int(index.group(1)) + else: + index = 0 + if expression in regions: + start_address_list = regions[expression] + if len(start_address_list) > index: + address = start_address_list[index] + try: + address = hex(eval(address + offset)) + except Exception as e: + print(e) + return typedefs.tuple_examine_expression(None, None, None) + return typedefs.tuple_examine_expression(f"{address} {expression}", address, expression) + return typedefs.tuple_examine_expression(None, None, None) + print(e) + return typedefs.tuple_examine_expression(None, None, None) + result = regexes.address_with_symbol.search(str(value)) + return typedefs.tuple_examine_expression(*result.groups()) diff --git a/libpince/gdbinit_venv b/libpince/gdbinit_venv new file mode 100644 index 00000000..c19bc2ad --- /dev/null +++ b/libpince/gdbinit_venv @@ -0,0 +1,14 @@ +# Update GDB's Python paths with the `sys.path` values of the local +# Python installation, whether that is brew'ed Python, a virtualenv, +# or another system python. + +# Convert GDB to interpret in Python +python +import os,subprocess,sys + +# Execute a Python using the user's shell and pull out the sys.path (for site-packages) +paths = subprocess.check_output('python3 -c "import os,sys;print(os.linesep.join(sys.path).strip())"',shell=True).decode("utf-8").split() + +# Extend GDB's Python's search path +sys.path.extend(paths) +end diff --git a/libpince/Injection/.gitignore b/libpince/injection/.gitignore similarity index 100% rename from libpince/Injection/.gitignore rename to libpince/injection/.gitignore diff --git a/libpince/Injection/Notes.txt b/libpince/injection/Notes.txt similarity index 100% rename from libpince/Injection/Notes.txt rename to libpince/injection/Notes.txt diff --git a/libpince/Injection/example.c b/libpince/injection/example.c similarity index 100% rename from libpince/Injection/example.c rename to libpince/injection/example.c diff --git a/libpince/common_regexes.py b/libpince/regexes.py similarity index 55% rename from libpince/common_regexes.py rename to libpince/regexes.py index f9747113..d1cb2cb5 100644 --- a/libpince/common_regexes.py +++ b/libpince/regexes.py @@ -15,14 +15,13 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ -from re import compile +from re import compile, VERBOSE, MULTILINE # The comments near regular expressions shows the expected gdb output, hope it helps to the future developers -# --------------------------------------------GDB_Engine---------------------------------------------------------------- +# --------------------------------------------debugcore---------------------------------------------------------------- -# stopped-threads="all" # *running,thread-id="all" -gdb_state_observe = compile(r"(stopped)-threads=\"all\"|\*(running),thread-id=\"all\"") +gdb_state_observe = compile(r"^\*(stopped.+)|^\*(running)", MULTILINE) gdb_error = compile(r"\^error") hex_plain = compile(r"[0-9a-fA-F]+") hex_number = compile(r"0x" + hex_plain.pattern) @@ -30,39 +29,73 @@ address_with_symbol = compile(r"(" + hex_number_grouped.pattern + r"\s*(<.+>)?)") # 0x7f3067f1174d \n thread_info = compile(r"\*\s+\d+\s+(.*)\\n") inferior_pid = compile(r"process\s+(\d+)") -numbers = compile(r"\d+") hw_breakpoint_count = compile(r"(hw|read|acc)") breakpoint_size = compile(r"char\[(\d+)\]") breakpoint_created = compile(r"breakpoint-created") -breakpoint_number = compile(r"number=\"(\d+)\"") +breakpoint_number = compile(r"(?:number|bkptno)=\"(\d+)\"") convenience_variable = compile(r'"(\$\d+)\s+=\s+(.*)"') # "$26 = 3" entry_point = compile(r"Entry\s+point:\s+" + hex_number_grouped.pattern) -# The command will always start with the word "source", check GDB_Engine.send_command function for the cause +# The command will always start with the word "source", check debugcore.send_command function for the cause gdb_command_source = lambda command_file: compile(r"&\".*source\s" + command_file + r"\\n\"") # &"command\n" +# This will only match hex patterns without 0x and ignore the ones below: +# Hex patterns with 0x such as 0x5123 +# Map symbols of PINCE, such as kmines[2] +expression_with_hex = compile(r"\b0x[0-9a-fA-F]+|[a-zA-Z_]\w*\[\d+\]|\b([0-9a-fA-F]+)\b") # 0x00007fd81d4c7400 <__printf+0>:\t48 81 ec d8 00 00 00\tsub rsp,0xd8\n -disassemble_output = compile(r"(" + hex_number.pattern + r".*)\\t(.+)\\t(.+)\\n") +disassemble_output = compile( + r""" + ([0-9a-fA-F]+.*)\\t # Address with symbol + (.*?[0-9a-fA-F]{2})\s*\\t # Bytes, ignore padding + (.+)\\n # Opcode +""", + VERBOSE, +) info_functions_non_debugging = compile(hex_number_grouped.pattern + r"\s+(.*)") max_completions_reached = compile(r"\*\*\*\s+List\s+may\s+be\s+truncated,\s+max-completions\s+reached\.\s+\*\*\*") -# --------------------------------------------SysUtils------------------------------------------------------------------ +# --------------------------------------------utils------------------------------------------------------------------ instruction_follow = compile(r"(j|call|loop).*\s+" + hex_number_grouped.pattern) docstring_variable = compile(r"(\w+)\s*=") docstring_function_or_variable = compile(r"def\s+(\w+)|" + docstring_variable.pattern) whitespaces = compile(r"\s+") +ps = compile( + r""" + \s+(\d+)\s+ # PID + (\S+)\s+ # Username + (.*)$ # Process name +""", + VERBOSE, +) +maps = compile( + r""" + ([0-9a-f]+)-([0-9a-f]+)\s+ # Address (start-end) + (\S+)\s+ # Permissions + ([0-9a-f]+)\s+ # Map offset + (\S+)\s+ # Device node + (\d+)\s+ # Inode + (.*)$ # Pathname +""", + VERBOSE, +) -# --------------------------------------------GuiUtils------------------------------------------------------------------ +# --------------------------------------------guiutils------------------------------------------------------------------ -valuetype_length = compile(r"\[(\d+)\]") -valuetype_nzt = compile(r",NZT") reference_mark = compile(r"\{\d*\}") +float_number = compile(r"-?[0-9]+[.,]?[0-9]*") +bytearray_input = compile(r"^(([A-Fa-f0-9?]{2} +)+)$") +decimal_number = compile(r"-?\d+") +hex_number_gui = compile(r"-?(0x)?[0-9a-fA-F]*") # contains optional 0x prefix -# --------------------------------------------GDBCommandExtensions------------------------------------------------------ +# --------------------------------------------gdbextensions------------------------------------------------------ max_frame_count = compile(r"#(\d+)\s+.*") frame_address = compile(r"frame\s+at\s+" + hex_number_grouped.pattern) # frame at 0x7ffe1e989950 return_address = compile(r"saved.*=\s+" + hex_number_grouped.pattern) # saved rip = 0x7f633a853fe4 trace_instructions_ret = compile(r":\s+ret") # 0x7f71a4dc5ff8 : ret trace_instructions_call = compile(r":\s+call") # 0x7f71a4dc5fe4 : call 0x7f71a4de1100 -dissect_code_valid_address = compile(r"(\s+|\[|,)" + hex_number.pattern + "(\s+|\]|,|$)") +dissect_code_valid_address = compile(r"(\s+|\[|,)" + hex_number.pattern + r"(\s+|\]|,|$)") alphanumerics = compile(r"\w+") +file_with_extension = compile(r".+?\.\w+") +offset_expression = compile(r"[/*+\-][0-9a-fA-FxX/*+\-\[\]]+$") +index = compile(r"\[(\d+)\]$") diff --git a/libpince/type_defs.py b/libpince/type_defs.py deleted file mode 100644 index df4160e9..00000000 --- a/libpince/type_defs.py +++ /dev/null @@ -1,383 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Copyright (C) 2016-2017 Korcan Karaokçu - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -""" - -# IMPORTANT: Any constant involving only PINCE.py should be declared in PINCE.py - -import collections.abc, queue, sys - - -class CONST_TIME: - GDB_INPUT_SLEEP = sys.float_info.min - - -class PATHS: - GDB_PATH = "/bin/gdb" - - -class IPC_PATHS: - PINCE_IPC_PATH = "/dev/shm/PINCE-connection/" # Use SysUtils.get_PINCE_IPC_directory() - IPC_FROM_PINCE_PATH = "/from_PINCE_file" # Use SysUtils.get_IPC_from_PINCE_file() - IPC_TO_PINCE_PATH = "/to_PINCE_file" # Use SysUtils.get_IPC_to_PINCE_file() - - -class USER_PATHS: - # Use SysUtils.get_user_path() to make use of these - - CONFIG_PATH = ".config/" - ROOT_PATH = CONFIG_PATH + "PINCE/PINCE_USER_FILES/" - TRACE_INSTRUCTIONS_PATH = ROOT_PATH + "TraceInstructions/" - CHEAT_TABLES_PATH = ROOT_PATH + "CheatTables/" - GDBINIT_PATH = ROOT_PATH + "gdbinit" - GDBINIT_AA_PATH = ROOT_PATH + "gdbinit_after_attach" - PINCEINIT_PATH = ROOT_PATH + "pinceinit.py" - PINCEINIT_AA_PATH = ROOT_PATH + "pinceinit_after_attach.py" - - @staticmethod - def get_init_directories(): - return USER_PATHS.ROOT_PATH, USER_PATHS.TRACE_INSTRUCTIONS_PATH, USER_PATHS.CHEAT_TABLES_PATH - - @staticmethod - def get_init_files(): - return USER_PATHS.GDBINIT_PATH, USER_PATHS.GDBINIT_AA_PATH, USER_PATHS.PINCEINIT_PATH, \ - USER_PATHS.PINCEINIT_AA_PATH - - -class INFERIOR_STATUS: - INFERIOR_RUNNING = 1 - INFERIOR_STOPPED = 2 - - -class INFERIOR_ARCH: - ARCH_32 = 1 - ARCH_64 = 2 - - -class INJECTION_METHOD: - SIMPLE_DLOPEN_CALL = 1 - ADVANCED_INJECTION = 2 - - -class BREAKPOINT_TYPE: - HARDWARE_BP = 1 - SOFTWARE_BP = 2 - - -class WATCHPOINT_TYPE: - WRITE_ONLY = 1 - READ_ONLY = 2 - BOTH = 3 - - -class BREAKPOINT_ON_HIT: - BREAK = 1 - FIND_CODE = 2 - FIND_ADDR = 3 - TRACE = 4 - - -class BREAKPOINT_MODIFY: - CONDITION = 1 - ENABLE = 2 - DISABLE = 3 - ENABLE_ONCE = 4 - ENABLE_COUNT = 5 - ENABLE_DELETE = 6 - - -class STEP_MODE: - SINGLE_STEP = 1 - STEP_OVER = 2 - - -class TRACE_STATUS: - STATUS_IDLE = 1 - STATUS_TRACING = 2 - STATUS_CANCELED = 3 - STATUS_PROCESSING = 4 - STATUS_FINISHED = 5 - - -class STOP_REASON: - PAUSE = 1 - DEBUG = 2 - - -class ATTACH_RESULT: - ATTACH_SELF = 1 - ATTACH_SUCCESSFUL = 2 - PROCESS_NOT_VALID = 3 - ALREADY_DEBUGGING = 4 - ALREADY_TRACED = 5 - PERM_DENIED = 6 - - -class TOGGLE_ATTACH: - ATTACHED = 1 - DETACHED = 2 - - -class REGISTERS: - GENERAL_32 = ["eax", "ebx", "ecx", "edx", "esi", "edi", "ebp", "esp", "eip"] - GENERAL_64 = ["rax", "rbx", "rcx", "rdx", "rsi", "rdi", "rbp", "rsp", "rip", "r8", "r9", "r10", "r11", "r12", - "r13", "r14", "r15"] - SEGMENT = ["cs", "ss", "ds", "es", "fs", "gs"] - FLAG = ["cf", "pf", "af", "zf", "sf", "tf", "if", "df", "of"] - - class FLOAT: - ST = ["st" + str(i) for i in range(8)] - XMM = ["xmm" + str(i) for i in range(8)] - - -# represents the indexes of value types -# Also used in PINCE's value comboboxes -class VALUE_INDEX: - INDEX_BYTE = 0 - INDEX_2BYTES = 1 - INDEX_4BYTES = 2 - INDEX_8BYTES = 3 - INDEX_FLOAT = 4 - INDEX_DOUBLE = 5 - - # Beginning of the string indexes, new string indexes should be added between 6 and 9 - INDEX_STRING_ASCII = 6 - INDEX_STRING_UTF8 = 7 - INDEX_STRING_UTF16 = 8 - INDEX_STRING_UTF32 = 9 - # Ending of the string indexes, 69... not on purpose tho - - INDEX_AOB = 10 # Array of Bytes - - @staticmethod - def is_string(value_index): - return VALUE_INDEX.INDEX_STRING_ASCII <= value_index <= VALUE_INDEX.INDEX_STRING_UTF32 - - @staticmethod - def has_length(value_index): - return VALUE_INDEX.INDEX_STRING_ASCII <= value_index <= VALUE_INDEX.INDEX_STRING_UTF32 or \ - value_index == VALUE_INDEX.INDEX_AOB - - -on_hit_to_text_dict = { - BREAKPOINT_ON_HIT.BREAK: "Break", - BREAKPOINT_ON_HIT.FIND_CODE: "Find Code", - BREAKPOINT_ON_HIT.FIND_ADDR: "Find Address", - BREAKPOINT_ON_HIT.TRACE: "Trace" -} - -# Represents the texts at indexes in combobox -index_to_text_dict = collections.OrderedDict([ - (VALUE_INDEX.INDEX_BYTE, "Byte"), - (VALUE_INDEX.INDEX_2BYTES, "2 Bytes"), - (VALUE_INDEX.INDEX_4BYTES, "4 Bytes"), - (VALUE_INDEX.INDEX_8BYTES, "8 Bytes"), - (VALUE_INDEX.INDEX_FLOAT, "Float"), - (VALUE_INDEX.INDEX_DOUBLE, "Double"), - (VALUE_INDEX.INDEX_STRING_ASCII, "String_ASCII"), - (VALUE_INDEX.INDEX_STRING_UTF8, "String_UTF8"), - (VALUE_INDEX.INDEX_STRING_UTF16, "String_UTF16"), - (VALUE_INDEX.INDEX_STRING_UTF32, "String_UTF32"), - (VALUE_INDEX.INDEX_AOB, "Array of Bytes") -]) - -text_to_index_dict = collections.OrderedDict() -for key in index_to_text_dict: - text_to_index_dict[index_to_text_dict[key]] = key - - -class SCAN_TYPE: - EXACT = 0 - INCREASED = 1 - DECREASED = 2 - LESS = 3 - MORE = 4 - BETWEEN = 5 - CHANGED = 6 - UNCHANGED = 7 - UNKNOWN = 8 - - -# Represents the texts at indexes in combobox -scan_type_to_text_dict = collections.OrderedDict([ - (SCAN_TYPE.EXACT, "Exact Scan"), - (SCAN_TYPE.INCREASED, "Increased"), - (SCAN_TYPE.DECREASED, "Decreased"), - (SCAN_TYPE.LESS, "Less Than"), - (SCAN_TYPE.MORE, "More Than"), - (SCAN_TYPE.BETWEEN, "Between"), - (SCAN_TYPE.CHANGED, "Changed"), - (SCAN_TYPE.UNCHANGED, "Unchanged"), - (SCAN_TYPE.UNKNOWN, "Unknown Value") -]) - - -class SCAN_MODE: - NEW = 0 - ONGOING = 1 - - -class SCAN_SCOPE: - BASIC = 1 - NORMAL = 2 - FULL = 3 - - -scan_scope_to_text_dict = collections.OrderedDict([ - (SCAN_SCOPE.BASIC, "Basic"), - (SCAN_SCOPE.NORMAL, "Normal"), - (SCAN_SCOPE.FULL, "Full") -]) - -string_index_to_encoding_dict = { - VALUE_INDEX.INDEX_STRING_UTF8: ["utf-8", "surrogateescape"], - VALUE_INDEX.INDEX_STRING_UTF16: ["utf-16", "replace"], - VALUE_INDEX.INDEX_STRING_UTF32: ["utf-32", "replace"], - VALUE_INDEX.INDEX_STRING_ASCII: ["ascii", "replace"], -} - -string_index_to_multiplier_dict = { - VALUE_INDEX.INDEX_STRING_UTF8: 2, - VALUE_INDEX.INDEX_STRING_UTF16: 4, - VALUE_INDEX.INDEX_STRING_UTF32: 8, -} - -# A dictionary used to convert value_combobox index to gdb/mi x command -# Check GDB_Engine for an exemplary usage -index_to_gdbcommand_dict = { - VALUE_INDEX.INDEX_BYTE: "db", - VALUE_INDEX.INDEX_2BYTES: "dh", - VALUE_INDEX.INDEX_4BYTES: "dw", - VALUE_INDEX.INDEX_8BYTES: "dg", - VALUE_INDEX.INDEX_FLOAT: "fw", - VALUE_INDEX.INDEX_DOUBLE: "fg", - VALUE_INDEX.INDEX_STRING_ASCII: "xb", - VALUE_INDEX.INDEX_STRING_UTF8: "xb", - VALUE_INDEX.INDEX_STRING_UTF16: "xb", - VALUE_INDEX.INDEX_STRING_UTF32: "xb", - VALUE_INDEX.INDEX_AOB: "xb" -} - -# first value is the length and the second one is the type -# Check ScriptUtils for an exemplary usage -index_to_valuetype_dict = { - VALUE_INDEX.INDEX_BYTE: [1, "b"], - VALUE_INDEX.INDEX_2BYTES: [2, "h"], - VALUE_INDEX.INDEX_4BYTES: [4, "i"], - VALUE_INDEX.INDEX_8BYTES: [8, "q"], - VALUE_INDEX.INDEX_FLOAT: [4, "f"], - VALUE_INDEX.INDEX_DOUBLE: [8, "d"], - VALUE_INDEX.INDEX_STRING_ASCII: [None, None], - VALUE_INDEX.INDEX_STRING_UTF8: [None, None], - VALUE_INDEX.INDEX_STRING_UTF16: [None, None], - VALUE_INDEX.INDEX_STRING_UTF32: [None, None], - VALUE_INDEX.INDEX_AOB: [None, None] -} - -# Check ScriptUtils for an exemplary usage -index_to_struct_pack_dict = { - VALUE_INDEX.INDEX_BYTE: "B", - VALUE_INDEX.INDEX_2BYTES: "H", - VALUE_INDEX.INDEX_4BYTES: "I", - VALUE_INDEX.INDEX_8BYTES: "Q", - VALUE_INDEX.INDEX_FLOAT: "f", - VALUE_INDEX.INDEX_DOUBLE: "d" -} - -# Format: {tag:tag_description} -tag_to_string = collections.OrderedDict([ - ("MemoryRW", "Memory Read/Write"), - ("ValueType", "Value Type"), - ("Injection", "Injection"), - ("Debug", "Debugging"), - ("BreakWatchpoints", "Breakpoints&Watchpoints"), - ("Threads", "Threads"), - ("Registers", "Registers"), - ("Stack", "Stack&StackTrace"), - ("Assembly", "Disassemble&Assemble"), - ("GDBExpressions", "GDB Expressions"), - ("GDBCommunication", "GDB Communication"), - ("Tools", "Tools"), - ("Utilities", "Utilities"), - ("Processes", "Processes"), - ("GUI", "GUI"), - ("ConditionsLocks", "Conditions&Locks"), - ("GDBInformation", "GDB Information"), - ("InferiorInformation", "Inferior Information"), -]) - -# size-->int, any other field-->str -tuple_breakpoint_info = collections.namedtuple("tuple_breakpoint_info", "number breakpoint_type \ - disp enabled address size on_hit hit_count enable_count condition") - -# start, end-->int, region-->psutil.Process.memory_maps()[item] -tuple_region_info = collections.namedtuple("tuple_region_info", "start end region") - -# all fields-->str/None -tuple_examine_expression = collections.namedtuple("tuple_examine_expression", "all address symbol") - -# all fields-->bool -gdb_output_mode = collections.namedtuple("gdb_output_mode", "async_output command_output command_info") - - -class InferiorRunningException(Exception): - def __init__(self, message="Inferior is running"): - super(InferiorRunningException, self).__init__(message) - - -class GDBInitializeException(Exception): - def __init__(self, message="GDB not initialized"): - super(GDBInitializeException, self).__init__(message) - - -class RegisterQueue: - def __init__(self): - self.queue_list = [] - - def register_queue(self): - new_queue = queue.Queue() - self.queue_list.append(new_queue) - return new_queue - - def broadcast_message(self, message): - for item in self.queue_list: - item.put(message) - - def delete_queue(self, queue_instance): - try: - self.queue_list.remove(queue_instance) - except ValueError: - pass - - -class KeyboardModifiersTupleDict(collections.abc.Mapping): - def _convert_to_int(self, tuple_key): - return tuple(int(x) for x in tuple_key) - - def __init__(self, OrderedDict_like_list): - new_dict = {} - for tuple_key, value in OrderedDict_like_list: - new_dict[self._convert_to_int(tuple_key)] = value - self._storage = new_dict - - def __getitem__(self, tuple_key): - return self._storage[self._convert_to_int(tuple_key)] - - def __iter__(self): - return iter(self._storage) - - def __len__(self): - return len(self._storage) diff --git a/libpince/typedefs.py b/libpince/typedefs.py new file mode 100644 index 00000000..f61dd6e1 --- /dev/null +++ b/libpince/typedefs.py @@ -0,0 +1,611 @@ +# -*- coding: utf-8 -*- +""" +Copyright (C) 2016-2017 Korcan Karaokçu + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +# IMPORTANT: Any constant involving only PINCE.py should be declared in PINCE.py + +import collections.abc, queue, sys + + +class CONST_TIME: + GDB_INPUT_SLEEP = sys.float_info.min + + +class PATHS: + GDB = "/bin/gdb" # Use utils.get_default_gdb_path() + TMP = "/tmp/PINCE/" # Use utils.get_tmp_path() + IPC = "/dev/shm/PINCE_IPC/" # Use utils.get_ipc_path() + FROM_PINCE = "/from_PINCE" # Use utils.get_from_pince_file() + TO_PINCE = "/to_PINCE" # Use utils.get_to_pince_file() + + +class USER_PATHS: + # Use utils.get_user_path() to make use of these + CONFIG = ".config/" + ROOT = CONFIG + "PINCE/" + GDBINIT = ROOT + "gdbinit" + GDBINIT_AA = ROOT + "gdbinit_after_attach" + PINCEINIT = ROOT + "pinceinit.py" + PINCEINIT_AA = ROOT + "pinceinit_after_attach.py" + + @staticmethod + def get_init_files(): + return ( + USER_PATHS.GDBINIT, + USER_PATHS.GDBINIT_AA, + USER_PATHS.PINCEINIT, + USER_PATHS.PINCEINIT_AA, + ) + + +class INFERIOR_STATUS: + RUNNING = 1 + STOPPED = 2 + + +class INFERIOR_ARCH: + ARCH_32 = 1 + ARCH_64 = 2 + + +class INJECTION_METHOD: + DLOPEN = 1 + ADVANCED = 2 + + +class BREAKPOINT_TYPE: + HARDWARE = 1 + SOFTWARE = 2 + + +class WATCHPOINT_TYPE: + WRITE_ONLY = 1 + READ_ONLY = 2 + BOTH = 3 + + +class BREAKPOINT_ON_HIT: + BREAK = 1 + FIND_CODE = 2 + FIND_ADDR = 3 + TRACE = 4 + + +class BREAKPOINT_MODIFY: + CONDITION = 1 + ENABLE = 2 + DISABLE = 3 + ENABLE_ONCE = 4 + ENABLE_COUNT = 5 + ENABLE_DELETE = 6 + + +class STEP_MODE: + SINGLE_STEP = 1 + STEP_OVER = 2 + + +class TRACE_STATUS: + IDLE = 1 + TRACING = 2 + CANCELED = 3 + FINISHED = 4 + + +class STOP_REASON: + PAUSE = 1 + DEBUG = 2 + + +class ATTACH_RESULT: + ATTACH_SELF = 1 + SUCCESSFUL = 2 + PROCESS_NOT_VALID = 3 + ALREADY_DEBUGGING = 4 + ALREADY_TRACED = 5 + PERM_DENIED = 6 + + +class TOGGLE_ATTACH: + ATTACHED = 1 + DETACHED = 2 + + +class REGISTERS: + GENERAL_32 = ["eax", "ebx", "ecx", "edx", "esi", "edi", "ebp", "esp", "eip"] + GENERAL_64 = [ + "rax", + "rbx", + "rcx", + "rdx", + "rsi", + "rdi", + "rbp", + "rsp", + "rip", + "r8", + "r9", + "r10", + "r11", + "r12", + "r13", + "r14", + "r15", + ] + SEGMENT = ["cs", "ss", "ds", "es", "fs", "gs"] + FLAG = ["cf", "pf", "af", "zf", "sf", "tf", "if", "df", "of"] + + class FLOAT: + ST = ["st" + str(i) for i in range(8)] + XMM_32 = ["xmm" + str(i) for i in range(8)] + XMM_64 = ["xmm" + str(i) for i in range(16)] + + +class FREEZE_TYPE: + DEFAULT = 0 + INCREMENT = 1 + DECREMENT = 2 + + +class VALUE_REPR: + UNSIGNED = 0 + SIGNED = 1 + HEX = 2 + + +class VALUE_INDEX: + # Beginning of the integer indexes, new integer indexes should be added between 0 and 3 + INT8 = 0 + INT16 = 1 + INT32 = 2 + INT64 = 3 + # Ending of the integer indexes + + FLOAT32 = 4 + FLOAT64 = 5 + + # Beginning of the string indexes, new string indexes should be added between 6 and 9 + STRING_ASCII = 6 + STRING_UTF8 = 7 + STRING_UTF16 = 8 + STRING_UTF32 = 9 + # Ending of the string indexes + + AOB = 10 # Array of Bytes + + @staticmethod + def is_integer(value_index: int): + return VALUE_INDEX.INT8 <= value_index <= VALUE_INDEX.INT64 + + @staticmethod + def is_float(value_index: int): + return VALUE_INDEX.FLOAT32 <= value_index <= VALUE_INDEX.FLOAT64 + + @staticmethod + def is_number(value_index: int): + return VALUE_INDEX.INT8 <= value_index <= VALUE_INDEX.FLOAT64 + + @staticmethod + def is_string(value_index: int): + return VALUE_INDEX.STRING_ASCII <= value_index <= VALUE_INDEX.STRING_UTF32 + + @staticmethod + def has_length(value_index: int): + return VALUE_INDEX.STRING_ASCII <= value_index <= VALUE_INDEX.AOB + + +class SCAN_INDEX: + INT_ANY = 0 + INT8 = 1 + INT16 = 2 + INT32 = 3 + INT64 = 4 + FLOAT_ANY = 5 + FLOAT32 = 6 + FLOAT64 = 7 + ANY = 8 + STRING = 9 + AOB = 10 # Array of Bytes + + +# GDB already provides breakpoint info in english, no need to make these translatable +on_hit_to_text_dict = { + BREAKPOINT_ON_HIT.BREAK: "Break", + BREAKPOINT_ON_HIT.FIND_CODE: "Find Code", + BREAKPOINT_ON_HIT.FIND_ADDR: "Find Address", + BREAKPOINT_ON_HIT.TRACE: "Trace", +} + +# Represents the texts at indexes in the address table +# TODO: This class is mostly an UI helper, maybe integrate it into the the UI completely in the future? +index_to_text_dict = collections.OrderedDict( + [ + (VALUE_INDEX.INT8, "Int8"), + (VALUE_INDEX.INT16, "Int16"), + (VALUE_INDEX.INT32, "Int32"), + (VALUE_INDEX.INT64, "Int64"), + (VALUE_INDEX.FLOAT32, "Float32"), + (VALUE_INDEX.FLOAT64, "Float64"), + (VALUE_INDEX.STRING_ASCII, "String_ASCII"), + (VALUE_INDEX.STRING_UTF8, "String_UTF8"), + (VALUE_INDEX.STRING_UTF16, "String_UTF16"), + (VALUE_INDEX.STRING_UTF32, "String_UTF32"), + (VALUE_INDEX.AOB, "ByteArray"), + ] +) + +text_to_index_dict = collections.OrderedDict() +for key in index_to_text_dict: + text_to_index_dict[index_to_text_dict[key]] = key + +scanmem_result_to_index_dict = collections.OrderedDict( + [ + ("I8", VALUE_INDEX.INT8), + ("I8u", VALUE_INDEX.INT8), + ("I8s", VALUE_INDEX.INT8), + ("I16", VALUE_INDEX.INT16), + ("I16u", VALUE_INDEX.INT16), + ("I16s", VALUE_INDEX.INT16), + ("I32", VALUE_INDEX.INT32), + ("I32u", VALUE_INDEX.INT32), + ("I32s", VALUE_INDEX.INT32), + ("I64", VALUE_INDEX.INT64), + ("I64u", VALUE_INDEX.INT64), + ("I64s", VALUE_INDEX.INT64), + ("F32", VALUE_INDEX.FLOAT32), + ("F64", VALUE_INDEX.FLOAT64), + ("string", VALUE_INDEX.STRING_UTF8), + ("bytearray", VALUE_INDEX.AOB), + ] +) + +# Represents the texts at indexes in scan combobox +# TODO: Same as index_to_text_dict, consider integrating into UI completely +scan_index_to_text_dict = collections.OrderedDict( + [ + (SCAN_INDEX.INT_ANY, "Int(any)"), + (SCAN_INDEX.INT8, "Int8"), + (SCAN_INDEX.INT16, "Int16"), + (SCAN_INDEX.INT32, "Int32"), + (SCAN_INDEX.INT64, "Int64"), + (SCAN_INDEX.FLOAT_ANY, "Float(any)"), + (SCAN_INDEX.FLOAT32, "Float32"), + (SCAN_INDEX.FLOAT64, "Float64"), + (SCAN_INDEX.ANY, "Any(int, float)"), + (SCAN_INDEX.STRING, "String"), + (VALUE_INDEX.AOB, "ByteArray"), + ] +) + +# Used in scan_data_type option of scanmem +scan_index_to_scanmem_dict = collections.OrderedDict( + [ + (SCAN_INDEX.INT_ANY, "int"), + (SCAN_INDEX.INT8, "int8"), + (SCAN_INDEX.INT16, "int16"), + (SCAN_INDEX.INT32, "int32"), + (SCAN_INDEX.INT64, "int64"), + (SCAN_INDEX.FLOAT_ANY, "float"), + (SCAN_INDEX.FLOAT32, "float32"), + (SCAN_INDEX.FLOAT64, "float64"), + (SCAN_INDEX.ANY, "number"), + (SCAN_INDEX.STRING, "string"), + (VALUE_INDEX.AOB, "bytearray"), + ] +) + + +# TODO: Same as index_to_text_dict, consider integrating into UI completely +class SCAN_TYPE: + EXACT = 0 + INCREASED = 1 + INCREASED_BY = 2 + DECREASED = 3 + DECREASED_BY = 4 + LESS = 5 + MORE = 6 + BETWEEN = 7 + CHANGED = 8 + UNCHANGED = 9 + UNKNOWN = 10 + NOT = 11 + + @staticmethod + def get_list(scan_mode, value_type): + if scan_mode == SCAN_MODE.NEW: + if value_type == SCAN_INDEX.STRING or value_type == SCAN_INDEX.AOB: + list = [ + SCAN_TYPE.EXACT, + SCAN_TYPE.UNKNOWN, + ] + else: + list = [ + SCAN_TYPE.EXACT, + SCAN_TYPE.NOT, + SCAN_TYPE.LESS, + SCAN_TYPE.MORE, + SCAN_TYPE.BETWEEN, + SCAN_TYPE.UNKNOWN, + ] + else: + if value_type == SCAN_INDEX.STRING or value_type == SCAN_INDEX.AOB: + list = [SCAN_TYPE.EXACT] + else: + list = [ + SCAN_TYPE.EXACT, + SCAN_TYPE.NOT, + SCAN_TYPE.INCREASED, + SCAN_TYPE.INCREASED_BY, + SCAN_TYPE.DECREASED, + SCAN_TYPE.DECREASED_BY, + SCAN_TYPE.LESS, + SCAN_TYPE.MORE, + SCAN_TYPE.BETWEEN, + SCAN_TYPE.CHANGED, + SCAN_TYPE.UNCHANGED, + ] + + return list + + +class SCAN_MODE: + NEW = 0 + ONGOING = 1 + + +class SCAN_SCOPE: + BASIC = 1 + NORMAL = 2 + FULL_RW = 3 + FULL = 4 + + +class ENDIANNESS: + HOST = 0 + LITTLE = 1 + BIG = 2 + + +string_index_to_encoding_dict = { + VALUE_INDEX.STRING_UTF8: ["utf-8", "surrogateescape"], + VALUE_INDEX.STRING_UTF16: ["utf-16", "replace"], + VALUE_INDEX.STRING_UTF32: ["utf-32", "replace"], + VALUE_INDEX.STRING_ASCII: ["ascii", "replace"], +} + +string_index_to_multiplier_dict = { + VALUE_INDEX.STRING_UTF8: 2, + VALUE_INDEX.STRING_UTF16: 4, + VALUE_INDEX.STRING_UTF32: 8, +} + +# first value is the length and the second one is the type +# Check gdbutils for an exemplary usage +index_to_valuetype_dict = { + VALUE_INDEX.INT8: [1, "B"], + VALUE_INDEX.INT16: [2, "H"], + VALUE_INDEX.INT32: [4, "I"], + VALUE_INDEX.INT64: [8, "Q"], + VALUE_INDEX.FLOAT32: [4, "f"], + VALUE_INDEX.FLOAT64: [8, "d"], + VALUE_INDEX.STRING_ASCII: [None, None], + VALUE_INDEX.STRING_UTF8: [None, None], + VALUE_INDEX.STRING_UTF16: [None, None], + VALUE_INDEX.STRING_UTF32: [None, None], + VALUE_INDEX.AOB: [None, None], +} + +# Check gdbutils for an exemplary usage +index_to_struct_pack_dict = { + VALUE_INDEX.INT8: "B", + VALUE_INDEX.INT16: "H", + VALUE_INDEX.INT32: "I", + VALUE_INDEX.INT64: "Q", + VALUE_INDEX.FLOAT32: "f", + VALUE_INDEX.FLOAT64: "d", +} + +# Format: {tag:tag_description} +tag_to_string = collections.OrderedDict( + [ + ("MemoryRW", "Memory Read/Write"), + ("ValueType", "Value Type"), + ("Injection", "Injection"), + ("Debug", "Debugging"), + ("BreakWatchpoints", "Breakpoints&Watchpoints"), + ("Threads", "Threads"), + ("Registers", "Registers"), + ("Stack", "Stack&StackTrace"), + ("Assembly", "Disassemble&Assemble"), + ("GDBExpressions", "GDB Expressions"), + ("GDBCommunication", "GDB Communication"), + ("Tools", "Tools"), + ("Utilities", "Utilities"), + ("Processes", "Processes"), + ("GUI", "GUI"), + ("ConditionsLocks", "Conditions&Locks"), + ("GDBInformation", "GDB Information"), + ("InferiorInformation", "Inferior Information"), + ] +) + +# size-->int, any other field-->str +tuple_breakpoint_info = collections.namedtuple( + "tuple_breakpoint_info", + "number breakpoint_type disp enabled address size on_hit hit_count enable_count condition", +) + +# start, end-->int, perms-->str, file_name-->str +tuple_region_info = collections.namedtuple("tuple_region_info", "start end perms file_name") + +# all fields-->str/None +tuple_examine_expression = collections.namedtuple("tuple_examine_expression", "all address symbol") + +# all fields-->bool +gdb_output_mode = collections.namedtuple("gdb_output_mode", "async_output command_output command_info") + + +class GDBInitializeException(Exception): + def __init__(self, message="GDB not initialized"): + super(GDBInitializeException, self).__init__(message) + + +class Frozen: + def __init__(self, value, freeze_type=FREEZE_TYPE.DEFAULT): + self.value = value + self.freeze_type = freeze_type + self.enabled = False + + +class ValueType: + def __init__( + self, + value_index=VALUE_INDEX.INT32, + length=10, + zero_terminate=True, + value_repr=VALUE_REPR.UNSIGNED, + endian=ENDIANNESS.HOST, + ): + """ + Args: + value_index (int): Determines the type of data. Can be a member of VALUE_INDEX + length (int): Length of the data. Only used when the value_index is STRING or AOB + zero_terminate (bool): If False, ",NZT" will be appended to the text representation + Only used when value_index is STRING. Ignored otherwise. "NZT" stands for "Non-Zero Terminate" + value_repr (int): Determines how the data is represented. Can be a member of VALUE_REPR + endian (int): Determines the endianness. Can be a member of ENDIANNESS + """ + self.value_index = value_index + self.length = length + self.zero_terminate = zero_terminate + self.value_repr = value_repr + self.endian = endian + + def serialize(self): + return ( + self.value_index, + self.length, + self.zero_terminate, + self.value_repr, + self.endian, + ) + + def text(self): + """Returns the text representation according to its members + + Returns: + str: A str generated by given parameters + + Examples: + value_index=VALUE_INDEX.STRING_UTF16, length=15, zero_terminate=False--▼ + returned str="String_UTF16[15],NZT" + value_index=VALUE_INDEX.AOB, length=42-->returned str="AoB[42]" + """ + returned_string = index_to_text_dict[self.value_index] + if VALUE_INDEX.is_string(self.value_index): + returned_string += f"[{self.length}]" + if not self.zero_terminate: + returned_string += ",NZT" + elif self.value_index == VALUE_INDEX.AOB: + returned_string += f"[{self.length}]" + if VALUE_INDEX.is_integer(self.value_index): + if self.value_repr == VALUE_REPR.SIGNED: + returned_string += "(s)" + elif self.value_repr == VALUE_REPR.HEX: + returned_string += "(h)" + if self.endian == ENDIANNESS.LITTLE: + returned_string += "" + elif self.endian == ENDIANNESS.BIG: + returned_string += "" + return returned_string + + +class PointerChainResult: + def __init__(self): + self.pointer_chain: list[int] = [] + + def get_pointer_by_index(self, index) -> int | None: + if index >= len(self.pointer_chain): + return None + return self.pointer_chain[index] + + def get_final_address(self) -> int | None: + return self.pointer_chain[-1] if self.pointer_chain else None + + def get_final_address_as_hex(self) -> str | None: + """ + Returns the hex representation of this pointer chain's final/destination address + """ + return hex(self.pointer_chain[-1]) if self.pointer_chain else None + + +class PointerChainRequest: + def __init__(self, base_address: str | int, offsets_list: list[int] = None): + """ + Args: + base_address (str, int): The base address of where this pointer chain starts from. Can be str expression or int. + offsets_list (list): List of offsets to reach the final pointed data. Can be None for no offsets. + Last offset in list won't be dereferenced to emulate CE behaviour. + """ + self.base_address: str | int = base_address + self.offsets_list: list[int] = [] if not offsets_list else offsets_list + + def serialize(self) -> tuple[str | int, list[int]]: + return self.base_address, self.offsets_list + + def get_base_address_as_str(self) -> str: + """ + Returns the text representation of this pointer's base address + """ + return hex(self.base_address) if type(self.base_address) != str else self.base_address + + +class RegisterQueue: + def __init__(self): + self.queue_list = [] + + def register_queue(self): + new_queue = queue.Queue() + self.queue_list.append(new_queue) + return new_queue + + def broadcast_message(self, message): + for item in self.queue_list: + item.put(message) + + def delete_queue(self, queue_instance): + try: + self.queue_list.remove(queue_instance) + except ValueError: + pass + + +class KeyboardModifiersTupleDict(collections.abc.Mapping): + def __init__(self, OrderedDict_like_list): + new_dict = {} + for keycomb, value in OrderedDict_like_list: + new_dict[keycomb] = value + self._storage = new_dict + + def __getitem__(self, keycomb): + return self._storage[keycomb] + + def __iter__(self): + return iter(self._storage) + + def __len__(self): + return len(self._storage) diff --git a/libpince/SysUtils.py b/libpince/utils.py similarity index 51% rename from libpince/SysUtils.py rename to libpince/utils.py index 09f853a7..def25baa 100644 --- a/libpince/SysUtils.py +++ b/libpince/utils.py @@ -15,90 +15,111 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ - -# Fixes the ImportError problem in GDBCommandExtensions.py for Archlinux -# This makes any psutil based function that's called from GDB unusable for Archlinux -# Currently there's none but we can't take it for granted, can we? -# TODO: Research the reason behind it or at least find a workaround -try: - import psutil -except ImportError: - print("WARNING: GDB couldn't locate the package psutil, psutil based user-defined functions won't work\n" + - "If you are getting this message without invoking GDB, it means that installation has failed, well, sort of") -import os, shutil, sys, binascii, pickle, json, traceback, re, pwd, pathlib -from . import type_defs, common_regexes +import os, shutil, sys, binascii, pickle, json, traceback, re, pwd, pathlib, distorm3 +from . import typedefs, regexes +from keystone import Ks, KsError, KS_ARCH_X86, KS_MODE_32, KS_MODE_64 from collections import OrderedDict from importlib.machinery import SourceFileLoader from pygdbmi import gdbmiparser +# Keystone initialization +ks_32 = Ks(KS_ARCH_X86, KS_MODE_32) +ks_64 = Ks(KS_ARCH_X86, KS_MODE_64) -#:tag:Processes -def iterate_processes(): - """Returns a generator of psutil.Process objects corresponding to currently running processes - Returns: - generator: Generator of psutil.Process objects +def get_process_list() -> list[str, str, str]: + """Returns a list of processes - Note: - Calling any function from the iterated processes will give the psutil.NoSuchProcess exception if the iterated - process doesn't exist anymore. Use functions in a try/except block for safety + Returns: + list: List of (pid, user, process_name) -> (str, str, str) """ - return psutil.process_iter() + process_list = [] + for line in os.popen("ps -eo pid:11,user,comm").read().splitlines(): + info = regexes.ps.match(line) + if info: + process_list.append(info.groups()) + return process_list -#:tag:Processes -def get_process_information(pid): - """Returns a psutil.Process object corresponding to given pid +def get_process_name(pid: int | str) -> str: + """Returns the process name of given pid Args: - pid (int): PID of the process + pid (int, str): PID of the process Returns: - psutil.Process: psutil.Process object corresponding to the given pid + str: Process name """ - return psutil.Process(pid) + with open(f"/proc/{pid}/comm") as f: + return f.read().splitlines()[0] -#:tag:Processes -def search_in_processes_by_name(process_name): - """Searches currently running processes and returns a list of psutil.Process objects corresponding to processes that - has the str process_name in them +def search_processes(process_name): + """Searches processes and returns a list of the ones that contain process_name Args: process_name (str): Name of the process that'll be searched for Returns: - list: List of psutil.Process objects corresponding to the filtered processes - - Note: - Calling any function from the iterated processes will give the psutil.NoSuchProcess exception if the iterated - process doesn't exist anymore. Use functions in a try/except block for safety + list: List of (pid, user, process_name) -> (str, str, str) """ processlist = [] - for p in psutil.process_iter(): - try: - name = p.name() - except psutil.NoSuchProcess: - continue - if re.search(process_name, name, re.IGNORECASE): - processlist.append(p) + for pid, user, name in get_process_list(): + if process_name.lower() in name.lower(): + processlist.append((pid, user, name)) return processlist -#:tag:Processes -def get_memory_regions(pid): - """Returns memory regions as a list of psutil._pslinux.pmmap_ext objects +def get_regions(pid): + """Returns memory regions of a process Args: pid (int): PID of the process Returns: - list: List of psutil._pslinux.pmmap_ext objects corresponding to the given pid + list: List of (start_address, end_address, permissions, map_offset, device_node, inode, path) -> all str """ - return psutil.Process(pid).memory_maps(grouped=False) + with open("/proc/" + str(pid) + "/maps") as f: + regions = [] + for line in f.read().splitlines(): + regions.append(regexes.maps.match(line).groups()) + return regions + + +def get_region_dict(pid: int) -> dict[str, list[str]]: + """Returns memory regions of a process as a dictionary where key is the path tail and value is the list of the + corresponding start addresses of the tail, empty paths will be ignored. Also adds shortcuts for file extensions, + returned dict will include both sonames, with and without version information + + Args: + pid (int): PID of the process + + Returns: + dict: {file_name:start_address_list} + """ + region_dict: dict[str, list[str]] = {} + for item in get_regions(pid): + start_addr, _, _, _, _, _, path = item + if not path: + continue + _, tail = os.path.split(path) + start_addr = "0x" + start_addr + short_name = regexes.file_with_extension.search(tail) + if short_name: + short_name = short_name.group(0) + if short_name == tail: + short_name = None + if tail in region_dict: + region_dict[tail].append(start_addr) + if short_name: + region_dict[short_name].append(start_addr) + else: + region_dict[tail] = [start_addr] + if short_name: + region_dict[short_name] = [start_addr] + return region_dict -#:tag:Processes def get_region_info(pid, address): """Finds the closest valid starting/ending address and region to given address, assuming given address is in the valid address range @@ -108,55 +129,51 @@ def get_region_info(pid, address): address (int,str): Can be an int or a hex str Returns: - type_defs.tuple_region_info: Starting address as int, ending address as int and region corresponding to - the given address as psutil._pslinux.pmmap_ext object + list: List of (start_address, end_address, permissions, file_name) -> (int, int, str, str) None: If the given address isn't in any valid address range - - Note: - This function is very slow because of the poor performance on psutil's part. You might want to optimize your - code while using this function. Check MemoryViewWindowForm.hex_dump_address() for an optimization example """ if type(pid) != int: pid = int(pid) if type(address) != int: address = int(address, 0) - region_list = get_memory_regions(pid) - for item in region_list: - splitted_address = item.addr.split("-") - start = int(splitted_address[0], 16) - end = int(splitted_address[1], 16) + region_list = get_regions(pid) + for start, end, perms, _, _, _, path in region_list: + start = int(start, 16) + end = int(end, 16) + file_name = os.path.split(path)[1] if start <= address < end: - return type_defs.tuple_region_info(start, end, item) + return typedefs.tuple_region_info(start, end, perms, file_name) -#:tag:Processes -def filter_memory_regions(pid, attribute, regex, case_sensitive=False): +def filter_regions(pid, attribute, regex, case_sensitive=False): """Filters memory regions by searching for the given regex within the given attribute Args: pid (int): PID of the process - attribute (str): The attribute that'll be filtered. Can be "addr", "perms" or "path" + attribute (str): The attribute that'll be filtered. Can be one of the below + start_address, end_address, permissions, map_offset, device_node, inode, path regex (str): Regex statement that'll be searched case_sensitive (bool): If True, search will be case sensitive Returns: - list: A list of psutil._pslinux.pmmap_ext objects + list: List of (start_address, end_address, permissions, map_offset, device_node, inode, path) -> all str """ - assert attribute in ["addr", "perms", "path"], "invalid attribute" + index = ["start_address", "end_address", "permissions", "map_offset", "device_node", "inode", "path"].index( + attribute + ) + if index == -1: + raise Exception("Invalid attribute") if case_sensitive: compiled_regex = re.compile(regex) else: compiled_regex = re.compile(regex, re.IGNORECASE) filtered_regions = [] - p = psutil.Process(pid) - for m in p.memory_maps(grouped=False): - current_attribute = getattr(m, attribute) - if compiled_regex.search(current_attribute): - filtered_regions.append(m) + for region in get_regions(pid): + if compiled_regex.search(region[index]): + filtered_regions.append(region) return filtered_regions -#:tag:Processes def is_traced(pid): """Check if the process corresponding to given pid traced by any other process @@ -165,22 +182,19 @@ def is_traced(pid): Returns: str: Name of the tracer if the specified process is being traced - bool: False, if the specified process is not being traced or the process doesn't exist anymore + None: if the specified process is not being traced or the process doesn't exist anymore """ try: status_file = open("/proc/%d/status" % pid) except FileNotFoundError: - return False + return for line in status_file.readlines(): if line.startswith("TracerPid:"): tracer_pid = line.split(":", 1)[1].strip() - if tracer_pid == "0": - return False - else: - return psutil.Process(int(tracer_pid)).name() + if tracer_pid != "0": + return get_process_name(tracer_pid) -#:tag:Processes def is_process_valid(pid): """Check if the process corresponding to given pid is valid @@ -190,40 +204,36 @@ def is_process_valid(pid): Returns: bool: True if the process is still running, False if not """ - return is_path_valid("/proc/%d" % pid) + return os.path.exists("/proc/%d" % pid) -#:tag:Utilities -def get_current_script_directory(): - """Get current working directory +def get_script_directory(): + """Get main script directory Returns: - str: A string pointing to the current working directory + str: A string pointing to the main script directory """ return sys.path[0] -#:tag:Utilities def get_media_directory(): """Get media directory Returns: str: A string pointing to the media directory """ - return sys.path[0] + "/media" + return get_script_directory() + "/media" -#:tag:Utilities def get_logo_directory(): """Get logo directory Returns: str: A string pointing to the logo directory """ - return sys.path[0] + "/media/logo" + return get_script_directory() + "/media/logo" -#:tag:Utilities def get_libpince_directory(): """Get libpince directory @@ -231,63 +241,54 @@ def get_libpince_directory(): str: A string pointing to the libpince directory Note: - In fact this function returns the directory where SysUtils in and considering the fact that SysUtils resides in - libpince, it works. So, please don't move out SysUtils outside of libpince folder! + In fact this function returns the directory where utils in and considering the fact that utils resides in + libpince, it works. So, please don't move out utils outside of libpince folder! """ return os.path.dirname(os.path.realpath(__file__)) -#:tag:Utilities -def is_path_valid(dest_path, issue_path=""): - """Check if the given path is valid - - Args: - dest_path (str): Path - issue_path (str): If this parameter is passed as "delete", given path will be deleted if it's valid. - If this parameter is passed as "create", given path path will be created if it's not valid. - - Returns: - bool: True if path is valid, False if not - """ - if os.path.exists(dest_path): - if issue_path == "delete": - shutil.rmtree(dest_path) - return True - else: - if issue_path == "create": - os.makedirs(dest_path) - return False - - -#:tag:GDBCommunication -def delete_PINCE_IPC_PATH(pid): +def delete_ipc_path(pid): """Deletes the IPC directory of given pid Args: pid (int,str): PID of the process """ - is_path_valid(get_PINCE_IPC_directory(pid), "delete") + path = get_ipc_path(pid) + if os.path.exists(path): + shutil.rmtree(path) -#:tag:GDBCommunication -def create_PINCE_IPC_PATH(pid): +def create_ipc_path(pid): """Creates the IPC directory of given pid Args: pid (int,str): PID of the process """ - delete_PINCE_IPC_PATH(pid) - is_path_valid(get_PINCE_IPC_directory(pid), "create") + path = get_ipc_path(pid) + if os.path.exists(path): + shutil.rmtree(path) + os.makedirs(path) - # Opening the command file with 'w' each time GDB_Engine.send_command() gets invoked slows down the process + # Opening the command file with 'w' each time debugcore.send_command() gets invoked slows down the process # Instead, here we create the command file for only once when IPC path gets initialized - # Then, open the command file with 'r' in GDB_Engine.send_command() to get a better performance + # Then, open the command file with 'r' in debugcore.send_command() to get a better performance command_file = get_gdb_command_file(pid) open(command_file, "w").close() -#:tag:GDBCommunication -def get_PINCE_IPC_directory(pid): +def create_tmp_path(pid): + """Creates the tmp directory of given pid + + Args: + pid (int,str): PID of the process + """ + path = get_tmp_path(pid) + if os.path.exists(path): + shutil.rmtree(path) + os.makedirs(path) + + +def get_ipc_path(pid): """Get the IPC directory of given pid Args: @@ -296,10 +297,21 @@ def get_PINCE_IPC_directory(pid): Returns: str: Path of IPC directory """ - return type_defs.IPC_PATHS.PINCE_IPC_PATH + str(pid) + return typedefs.PATHS.IPC + str(pid) + + +def get_tmp_path(pid): + """Get the tmp directory of given pid + + Args: + pid (int): PID of the process + + Returns: + str: Path of tmp directory + """ + return typedefs.PATHS.TMP + str(pid) -#:tag:GDBCommunication def get_logging_file(pid): """Get the path of gdb logfile of given pid @@ -309,10 +321,9 @@ def get_logging_file(pid): Returns: str: Path of gdb logfile """ - return get_PINCE_IPC_directory(pid) + "/gdb_log.txt" + return get_tmp_path(pid) + "/gdb_log.txt" -#:tag:GDBCommunication def get_gdb_command_file(pid): """Get the path of gdb command file of given pid @@ -322,10 +333,9 @@ def get_gdb_command_file(pid): Returns: str: Path of gdb command file """ - return get_PINCE_IPC_directory(pid) + "/gdb_command.txt" + return get_ipc_path(pid) + "/gdb_command.txt" -#:tag:BreakWatchpoints def get_track_watchpoint_file(pid, watchpoint_list): """Get the path of track watchpoint file for given pid and watchpoint @@ -336,10 +346,9 @@ def get_track_watchpoint_file(pid, watchpoint_list): Returns: str: Path of track watchpoint file """ - return get_PINCE_IPC_directory(pid) + "/" + str(watchpoint_list) + "_track_watchpoint.txt" + return get_ipc_path(pid) + "/" + str(watchpoint_list) + "_track_watchpoint.txt" -#:tag:BreakWatchpoints def get_track_breakpoint_file(pid, breakpoint): """Get the path of track breakpoint file for given pid and breakpoint @@ -350,24 +359,9 @@ def get_track_breakpoint_file(pid, breakpoint): Returns: str: Path of track breakpoint file """ - return get_PINCE_IPC_directory(pid) + "/" + breakpoint + "_track_breakpoint.txt" + return get_ipc_path(pid) + "/" + breakpoint + "_track_breakpoint.txt" -#:tag:Tools -def get_trace_instructions_file(pid, breakpoint): - """Get the path of trace instructions file for given pid and breakpoint - - Args: - pid (int,str): PID of the process - breakpoint (str): breakpoint number - - Returns: - str: Path of trace instructions file - """ - return get_PINCE_IPC_directory(pid) + "/" + breakpoint + "_trace.txt" - - -#:tag:Utilities def append_file_extension(string, extension): """Appends the given extension to the given string if it doesn't end with the given extension @@ -382,7 +376,6 @@ def append_file_extension(string, extension): return string if string.endswith("." + extension) else string + "." + extension -#:tag:Utilities def save_file(data, file_path, save_method="json"): """Saves the specified data to given path @@ -415,7 +408,6 @@ def save_file(data, file_path, save_method="json"): return False -#:tag:Utilities def load_file(file_path, load_method="json"): """Loads data from the given path @@ -445,21 +437,30 @@ def load_file(file_path, load_method="json"): return output -#:tag:Tools -def get_trace_instructions_status_file(pid, breakpoint): - """Get the path of trace instructions status file for given pid and breakpoint +def get_trace_status_file(pid): + """Get the path of trace status file for given pid Args: pid (int,str): PID of the process - breakpoint (str): breakpoint number Returns: - str: Path of trace instructions status file + str: Path of trace status file """ - return get_PINCE_IPC_directory(pid) + "/" + breakpoint + "_trace_status.txt" + return get_ipc_path(pid) + "/_trace_status.txt" + + +def change_trace_status(pid: int | str, trace_status: int): + """Change trace status for given pid + + Args: + pid (int,str): PID of the process + trace_status (int): New trace status, can be a member of typedefs.TRACE_STATUS + """ + trace_status_file = get_trace_status_file(pid) + with open(trace_status_file, "w") as trace_file: + trace_file.write(str(trace_status)) -#:tag:Tools def get_dissect_code_status_file(pid): """Get the path of dissect code status file for given pid @@ -469,10 +470,9 @@ def get_dissect_code_status_file(pid): Returns: str: Path of dissect code status file """ - return get_PINCE_IPC_directory(pid) + "/dissect_code_status.txt" + return get_ipc_path(pid) + "/dissect_code_status.txt" -#:tag:Tools def get_referenced_strings_file(pid): """Get the path of referenced strings dict file for given pid @@ -482,10 +482,9 @@ def get_referenced_strings_file(pid): Returns: str: Path of referenced strings dict file """ - return get_PINCE_IPC_directory(pid) + "/referenced_strings_dict.txt" + return get_tmp_path(pid) + "/referenced_strings_dict.txt" -#:tag:Tools def get_referenced_jumps_file(pid): """Get the path of referenced jumps dict file for given pid @@ -495,10 +494,9 @@ def get_referenced_jumps_file(pid): Returns: str: Path of referenced jumps dict file """ - return get_PINCE_IPC_directory(pid) + "/referenced_jumps_dict.txt" + return get_tmp_path(pid) + "/referenced_jumps_dict.txt" -#:tag:Tools def get_referenced_calls_file(pid): """Get the path of referenced strings dict file for given pid @@ -508,11 +506,10 @@ def get_referenced_calls_file(pid): Returns: str: Path of referenced calls dict file """ - return get_PINCE_IPC_directory(pid) + "/referenced_calls_dict.txt" + return get_tmp_path(pid) + "/referenced_calls_dict.txt" -#:tag:GDBCommunication -def get_IPC_from_PINCE_file(pid): +def get_from_pince_file(pid): """Get the path of IPC file sent to custom gdb commands from PINCE for given pid Args: @@ -521,11 +518,10 @@ def get_IPC_from_PINCE_file(pid): Returns: str: Path of IPC file """ - return get_PINCE_IPC_directory(pid) + type_defs.IPC_PATHS.IPC_FROM_PINCE_PATH + return get_ipc_path(pid) + typedefs.PATHS.FROM_PINCE -#:tag:GDBCommunication -def get_IPC_to_PINCE_file(pid): +def get_to_pince_file(pid): """Get the path of IPC file sent to PINCE from custom gdb commands for given pid Args: @@ -534,29 +530,27 @@ def get_IPC_to_PINCE_file(pid): Returns: str: Path of IPC file """ - return get_PINCE_IPC_directory(pid) + type_defs.IPC_PATHS.IPC_TO_PINCE_PATH + return get_ipc_path(pid) + typedefs.PATHS.TO_PINCE -#:tag:ValueType -def parse_string(string, value_index): +def parse_string(string: str, value_index: int): """Parses the string according to the given value_index Args: string (str): String that'll be parsed - value_index (int): Determines the type of data. Can be a member of type_defs.VALUE_INDEX + value_index (int): Determines the type of data. Can be a member of typedefs.VALUE_INDEX Returns: - str: If the value_index is INDEX_STRING - list: If the value_index is INDEX_AOB. A list of ints is returned - float: If the value_index is INDEX_FLOAT or INDEX_DOUBLE + str: If the value_index is STRING + list: If the value_index is AOB. A list of ints is returned + float: If the value_index is FLOAT32 or FLOAT64 int: If the value_index is anything else None: If the string is not parsable by using the parameter value_index Examples: - string="42 DE AD BE EF 24",value_index=type_defs.VALUE_INDEX.INDEX_AOB--▼ + string="42 DE AD BE EF 24",value_index=typedefs.VALUE_INDEX.AOB--▼ returned_list=[66, 222, 173, 190, 239, 36] """ - string = str(string) if not string: print("please enter a string first") return @@ -565,12 +559,12 @@ def parse_string(string, value_index): except: print(str(value_index) + " can't be converted to int") return - if type_defs.VALUE_INDEX.is_string(value_index): + if typedefs.VALUE_INDEX.is_string(value_index): return string string = string.strip() - if value_index is type_defs.VALUE_INDEX.INDEX_AOB: + if value_index == typedefs.VALUE_INDEX.AOB: try: - string_list = common_regexes.whitespaces.split(string) + string_list = regexes.whitespaces.split(string) for item in string_list: if len(item) > 2: print(string + " can't be parsed as array of bytes") @@ -580,7 +574,7 @@ def parse_string(string, value_index): except: print(string + " can't be parsed as array of bytes") return - elif value_index is type_defs.VALUE_INDEX.INDEX_FLOAT or value_index is type_defs.VALUE_INDEX.INDEX_DOUBLE: + elif typedefs.VALUE_INDEX.is_float(value_index): try: string = float(string) except: @@ -599,18 +593,17 @@ def parse_string(string, value_index): except: print(string + " can't be parsed as integer or hexadecimal") return - if value_index is type_defs.VALUE_INDEX.INDEX_BYTE: + if value_index == typedefs.VALUE_INDEX.INT8: string = string % 0x100 # 256 - elif value_index is type_defs.VALUE_INDEX.INDEX_2BYTES: + elif value_index == typedefs.VALUE_INDEX.INT16: string = string % 0x10000 # 65536 - elif value_index is type_defs.VALUE_INDEX.INDEX_4BYTES: + elif value_index == typedefs.VALUE_INDEX.INT32: string = string % 0x100000000 # 4294967296 - elif value_index is type_defs.VALUE_INDEX.INDEX_8BYTES: + elif value_index == typedefs.VALUE_INDEX.INT64: string = string % 0x10000000000000000 # 18446744073709551616 return string -#:tag:Assembly def instruction_follow_address(string): """Searches for the location changing instructions such as Jcc, CALL and LOOPcc in the given string. Returns the hex address the instruction jumps to @@ -622,12 +615,11 @@ def instruction_follow_address(string): str: Hex address None: If no hex address is found or no location changing instructions found """ - result = common_regexes.instruction_follow.search(string) + result = regexes.instruction_follow.search(string) if result: return result.group(2) -#:tag:Utilities def extract_address(string): """Extracts hex address from the given string @@ -638,47 +630,110 @@ def extract_address(string): str: Hex address None: If no hex address is found """ - result = common_regexes.hex_number.search(string) + result = regexes.hex_number.search(string) if result: return result.group(0) -#:tag:Utilities def modulo_address(int_address, arch_type): """Calculates the modulo of the given integer based on the given architecture type to make sure that it doesn't exceed the borders of the given architecture type (0xffffffff->x86, 0xffffffffffffffff->x64) Args: int_address (int): Self-explanatory - arch_type (int): Architecture type (x86, x64). Can be a member of type_defs.INFERIOR_ARCH + arch_type (int): Architecture type (x86, x64). Can be a member of typedefs.INFERIOR_ARCH Returns: int: Modulo of the given integer based on the given architecture type """ - if arch_type == type_defs.INFERIOR_ARCH.ARCH_32: + if arch_type == typedefs.INFERIOR_ARCH.ARCH_32: return int_address % 0x100000000 - elif arch_type == type_defs.INFERIOR_ARCH.ARCH_64: + elif arch_type == typedefs.INFERIOR_ARCH.ARCH_64: return int_address % 0x10000000000000000 - raise Exception("arch_type must be a member of type_defs.INFERIOR_ARCH") + raise Exception("arch_type must be a member of typedefs.INFERIOR_ARCH") + + +def get_opcodes(address, aob, inferior_arch): + """Returns the instructions from the given array of bytes + + Args: + address (int): The address where the opcode starts from + aob (str): Bytes of the opcode as an array of bytes + inferior_arch (int): Architecture type (x86, x64). Can be a member of typedefs.INFERIOR_ARCH + + Returns: + str: Opcodes, multiple entries are separated with ; + None: If there was an error + """ + if inferior_arch == typedefs.INFERIOR_ARCH.ARCH_64: + disas_option = distorm3.Decode64Bits + elif inferior_arch == typedefs.INFERIOR_ARCH.ARCH_32: + disas_option = distorm3.Decode32Bits + try: + bytecode = bytes.fromhex(aob.replace(" ", "")) + except ValueError: + return + disas_data = distorm3.Decode(address, bytecode, disas_option) + return "; ".join([data[2] for data in disas_data]) + + +def assemble(instructions, address, inferior_arch): + """Assembles the given instructions + Args: + instructions (str): A string of instructions, multiple entries separated by ; + address (int): Address of the instruction + inferior_arch (int): Can be a member of typedefs.INFERIOR_ARCH -#:tag:ValueType -def aob_to_str(list_of_bytes, encoding="ascii"): + Returns: + tuple: A tuple of (list, int) --> Assembled bytes (list of int) and instruction count (int) + None: If there was an error + """ + try: + if inferior_arch == typedefs.INFERIOR_ARCH.ARCH_64: + return ks_64.asm(instructions, address) + else: + return ks_32.asm(instructions, address) + except KsError as e: + print(e) + + +def aob_to_str(list_of_bytes, encoding="ascii", replace_unprintable=True): """Converts given array of hex strings to str Args: - list_of_bytes (list): Must be returned from GDB_Engine.hex_dump() + list_of_bytes (list): Must be returned from debugcore.hex_dump() encoding (str): See here-->https://docs.python.org/3/library/codecs.html#standard-encodings + replace_unprintable (bool): If True, replaces non-printable characters with a period (.) Returns: str: str equivalent of array """ - # 3f is ascii hex representation of char "?" - return bytes.fromhex("".join(list_of_bytes).replace("??", "3f")).decode(encoding, "surrogateescape") + ### make an actual list of bytes + hexString = "" + byteList = list_of_bytes + if isinstance(list_of_bytes, list): + byteList = list_of_bytes + else: + byteList = [] + byteList.append(list_of_bytes) + for sByte in byteList: + if sByte == "??": + hexString += f"{63:02x}" # replace ?? with a single ? + else: + if isinstance(sByte, int): + byte = sByte + else: + byte = int(sByte, 16) + if replace_unprintable and ((byte < 32) or (byte > 126)): + hexString += f"{46:02x}" # replace non-printable chars with a period (.) + else: + hexString += f"{byte:02x}" + hexBytes = bytes.fromhex(hexString) + return hexBytes.decode(encoding, "surrogateescape") -#:tag:ValueType def str_to_aob(string, encoding="ascii"): """Converts given string to aob string @@ -689,17 +744,16 @@ def str_to_aob(string, encoding="ascii"): Returns: str: AoB equivalent of the given string """ - s = str(binascii.hexlify(string.encode(encoding, "surrogateescape")), "ascii") - return " ".join(s[i:i + 2] for i in range(0, len(s), 2)) + s = str(binascii.hexlify(string.encode(encoding, "surrogateescape")), encoding).upper() + return " ".join(s[i : i + 2] for i in range(0, len(s), 2)) -#:tag:GDBExpressions def split_symbol(symbol_string): - """Splits symbol part of type_defs.tuple_function_info into smaller fractions + """Splits symbol part of typedefs.tuple_function_info into smaller fractions Fraction count depends on the the symbol_string. See Examples section for demonstration Args: - symbol_string (str): symbol part of type_defs.tuple_function_info + symbol_string (str): symbol part of typedefs.tuple_function_info Returns: list: A list containing parts of the splitted symbol @@ -721,9 +775,9 @@ def split_symbol(symbol_string): # searching for balanced parentheses works because apparently no demangled symbol can finish with <.*> # XXX: run this code to test while attached to a process and open a detailed issue if you get a result """ - from libpince import GDB_Engine + from libpince import debugcore import re - result=GDB_Engine.search_functions("") + result=debugcore.search_functions("") for address, symbol in result: if re.search("<.*>[^()]+$", symbol): print(symbol) @@ -734,10 +788,12 @@ def split_symbol(symbol_string): elif letter == "(": p_count -= 1 if p_count == 0: - returned_list.append((symbol_string[:-(index + 1)])) + returned_list.append((symbol_string[: -(index + 1)])) break - assert p_count >= 0, symbol_string + " contains unhealthy amount of left parentheses\nGotta give him some" \ - ' right parentheses. Like Bob always says "everyone needs a friend"' + assert p_count >= 0, ( + symbol_string + " contains unhealthy amount of left parentheses\nGotta give him some" + ' right parentheses. Like Bob always says "everyone needs a friend"' + ) assert p_count == 0, symbol_string + " contains unbalanced parentheses" if "@plt" in symbol_string: returned_list.append(symbol_string.rsplit("@plt", maxsplit=1)[0]) @@ -745,192 +801,14 @@ def split_symbol(symbol_string): return returned_list -#:tag:Utilities -def execute_shell_command_as_user(command): +def execute_command_as_user(command): """Executes given command as user Args: command (str): Command that'll be invoked from the shell """ uid, gid = get_user_ids() - os.system("sudo -u '#" + uid + "' " + command) - - -#:tag:Utilities -def get_docstrings(modules, search_for=""): - """Gathers docstrings from a list of modules - For now, this function only supports variables and functions - See get_comments_of_variables function to learn documenting variables in PINCE style - - Args: - modules (list): A list of modules - search_for (str): String that will be searched in variables and functions - - Returns: - dict: A dict containing docstrings for documented variables and functions - Format-->{variable1:docstring1, variable2:docstring2, ...} - """ - element_dict = {} - variable_comment_dict = get_comments_of_variables(modules) - for item in modules: - for key, value in item.__dict__.items(): - name_with_module = get_module_name(item) + "." + key - if name_with_module in variable_comment_dict: - element_dict[name_with_module] = variable_comment_dict[name_with_module] - else: - element_dict[name_with_module] = value.__doc__ - for item in list(element_dict): - if item.split(".")[-1].find(search_for) == -1: - del element_dict[item] - return element_dict - - -#:tag:Utilities -def get_comments_of_variables(modules, search_for=""): - r"""Gathers comments from a list of modules - Python normally doesn't allow modifying __doc__ variable of the variables - This function is designed to bring a solution to this problem - The documentation must be PINCE style. It must start with this--> "#:doc:" - See examples for more details - - Args: - modules (list): A list of modules - search_for (str): String that will be searched in variables - - Returns: - dict: A dict containing docstrings for documented variables - Format-->{variable1:docstring1, variable2:docstring2, ...} - - Example for single line comments: - Code--▼ - #:doc: - #Documentation for the variable - some_variable = blablabla - Returns--▼ - {"some_variable":"Documentation for the variable"} - - Example for multi line comments: - Code--▼ - #:doc: - '''Some Header - Documentation for the variable - Some Ending Word''' - some_variable = blablabla - Returns--▼ - {"some_variable":"Some Header\nDocumentation for the variable\nSome Ending Word"} - """ - comment_dict = {} - source_files = [] - for module in modules: - source_files.append(module.__file__) - for index, file_path in enumerate(source_files): - source_file = open(file_path, "r") - lines = source_file.readlines() - for row, line in enumerate(lines): - stripped_line = line.strip() - if stripped_line.startswith("#:doc:"): - docstring_list = [] - while True: - row += 1 - current_line = lines[row].strip() - if current_line.startswith("#"): - docstring_list.append(current_line.replace("#", "", 1)) - elif current_line.startswith("'''"): - current_line = current_line.replace("'''", "", 1) - if current_line.endswith("'''"): - current_line = current_line.replace("'''", "") - docstring_list.append(current_line) - continue - docstring_list.append(current_line) - while True: - row += 1 - current_line = lines[row].strip() - if current_line.endswith("'''"): - current_line = current_line.replace("'''", "") - docstring_list.append(current_line) - break - docstring_list.append(current_line) - else: - while True: - stripped_current_line = common_regexes.docstring_variable.search(current_line) - if stripped_current_line: - variable = stripped_current_line.group(1) - break - row += 1 - current_line = lines[row].strip() - break - if variable.find(search_for) == -1: - continue - comment_dict[get_module_name(modules[index]) + "." + variable] = "\n".join(docstring_list) - return comment_dict - - -#:tag:Utilities -def get_tags(modules, tag_to_string, search_for=""): - """Gathers tags from a python source file - The documentation must be PINCE style. It must start like this--> "#:tag:tag_name" - For now, tagging system only supports variables and functions - See examples for more details - - Args: - modules (list): A list of modules - tag_to_string (dict): A dictionary that holds tag descriptions in this format-->{tag:tag_description} - Check type_defs.tag_to_string for an example - search_for (str): String that will be searched in tags - - Returns: - dict: A dict containing tag keys for tagged variables - Format-->{tag1_desc:variable_list1, tag2_desc:variable_list2, ...} - - Examples: - Code--▼ - #:tag:tag_name - #Documentation for the variable - some_variable = blablabla - - or - - #:tag:tag_name - def func_name(...) - Returns--▼ - {tag_to_string["tag_name"]:list of some_variables or func_names that have the tag tag_name} - """ - tag_dict = {} - source_files = [] - for module in modules: - source_files.append(module.__file__) - for index, file_path in enumerate(source_files): - source_file = open(file_path, "r") - lines = source_file.readlines() - for row, line in enumerate(lines): - stripped_line = line.strip() - if stripped_line.startswith("#:tag:"): - tag = stripped_line.replace("#:tag:", "", 1) - while True: - row += 1 - current_line = lines[row].strip() - stripped_current_line = common_regexes.docstring_function_or_variable.search(current_line) - if stripped_current_line: - for item in stripped_current_line.groups(): - if item: - if item.find(search_for) == -1: - break - item = get_module_name(modules[index]) + "." + item - try: - tag_dict[tag].append(item) - except KeyError: - tag_dict[tag] = [item] - break - else: - continue - break - ordered_tag_dict = OrderedDict() - for tag, desc in tag_to_string.items(): - if tag in tag_dict: - ordered_tag_dict[desc] = tag_dict[tag] - else: - continue - return ordered_tag_dict + os.system("sudo -Eu '#" + uid + "' " + command) def get_module_name(module): @@ -945,12 +823,12 @@ def get_module_name(module): return module.__name__.replace(module.__package__ + ".", "", 1) -#:tag:Utilities def init_user_files(): """Initializes user files""" - for directory in type_defs.USER_PATHS.get_init_directories(): - is_path_valid(get_user_path(directory), "create") - for file in type_defs.USER_PATHS.get_init_files(): + root_path = get_user_path(typedefs.USER_PATHS.ROOT) + if not os.path.exists(root_path): + os.makedirs(root_path) + for file in typedefs.USER_PATHS.get_init_files(): file = get_user_path(file) try: open(file).close() @@ -958,7 +836,6 @@ def init_user_files(): open(file, "w").close() -#:tag:Utilities def get_user_ids(): """Gets uid and gid of the current user @@ -970,7 +847,6 @@ def get_user_ids(): return uid, gid -#:tag:Utilities def get_user_home_dir(): """Returns the home directory of the current user @@ -981,12 +857,11 @@ def get_user_home_dir(): return pwd.getpwuid(int(uid)).pw_dir -#:tag:Utilities def get_user_path(user_path): """Returns the specified user path for the current user Args: - user_path (str): Can be a member of type_defs.USER_PATHS + user_path (str): Can be a member of typedefs.USER_PATHS Returns: str: Specified user path of the current user @@ -996,7 +871,13 @@ def get_user_path(user_path): return os.path.join(homedir, user_path) -#:tag:Tools +def get_default_gdb_path(): + appdir = os.environ.get("APPDIR") + if appdir: + return appdir + "/usr/bin/gdb" + return typedefs.PATHS.GDB + + def execute_script(file_path): """Loads and executes the script in the given path @@ -1025,10 +906,9 @@ def execute_script(file_path): return module, None -#:tag:Utilities def parse_response(response, line_num=0): """Parses the given GDB/MI output. Wraps gdbmiparser.parse_response - GDB_Engine.send_command returns an additional "^done" output because of the "source" command + debugcore.send_command returns an additional "^done" output because of the "source" command This function is used to get rid of that output before parsing Args: @@ -1041,7 +921,6 @@ def parse_response(response, line_num=0): return gdbmiparser.parse_response(response.splitlines()[line_num]) -#:tag:Utilities def search_files(directory, regex): """Searches the files in given directory for given regex recursively @@ -1058,3 +937,27 @@ def search_files(directory, regex): if result: file_list.append(str(file.relative_to(directory))) return sorted(file_list) + + +def ignore_exceptions(func): + """A decorator to ignore exceptions""" + + def wrapper(*args, **kwargs): + try: + func(*args, **kwargs) + except: + traceback.print_exc() + + return wrapper + + +def upper_hex(hex_str: str): + """Converts the given hex string to uppercase while keeping the 'x' character lowercase""" + # check if the given string is a hex string, if not return the string as is + if not regexes.hex_number_gui.match(hex_str): + return hex_str + return hex_str.upper().replace("X", "x") + + +def return_optional_int(val: int) -> int | None: + return None if val == 0 else val diff --git a/libscanmem-PINCE b/libscanmem-PINCE new file mode 160000 index 00000000..e04f7963 --- /dev/null +++ b/libscanmem-PINCE @@ -0,0 +1 @@ +Subproject commit e04f7963af5c4aa3d3d32270eb1b1c1137af6637 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..25323d3c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,4 @@ +[tool.black] +line-length = 120 +[tool.isort] +skip_glob = ["*"] diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..8f0c4ee2 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,8 @@ +PyQt6==6.6.0 +PyQt6-Qt6==6.6.0 +pexpect==4.9.0 +distorm3==3.5.2 +keystone-engine==0.9.2 +pygdbmi==0.11.0.0 +keyboard==0.13.5 +pygobject==3.46.0 diff --git a/run_tests.py b/run_tests.py index b5928680..0062a80f 100644 --- a/run_tests.py +++ b/run_tests.py @@ -16,47 +16,54 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ -import unittest, argparse, psutil -from libpince import GDB_Engine, SysUtils +import unittest, argparse +from libpince import debugcore, utils -desc = 'Runs all unit tests by creating or attaching to a process' -ex = 'Example of Usage:' \ - + '\n\tsudo python3 run_tests.py -a kmines' \ - + '\n\tsudo python3 run_tests.py -c /usr/games/kmines -o="-v"' +desc = "Runs all unit tests by creating or attaching to a process" +ex = ( + "Example of Usage:" + + "\n\tsudo python3 run_tests.py -a kmines" + + '\n\tsudo python3 run_tests.py -c /usr/games/kmines -o="-v"' +) parser = argparse.ArgumentParser(description=desc, epilog=ex, formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument("-a", metavar="process_name", type=str, help="Attaches to the process with given name") parser.add_argument("-c", metavar="file_path", type=str, help="Creates a new process with given path") -parser.add_argument("-o", metavar="options", type=str, default="", - help="Arguments that'll be passed to the inferior, only can be used with -c, optional") -parser.add_argument("-l", metavar="ld_preload_path", type=str, default="", - help="Path of the preloaded .so file, only can be used with -c, optional") +parser.add_argument( + "-o", + metavar="options", + type=str, + default="", + help="Arguments that'll be passed to the inferior, only can be used with -c, optional", +) +parser.add_argument( + "-l", + metavar="ld_preload_path", + type=str, + default="", + help="Path of the preloaded .so file, only can be used with -c, optional", +) args = parser.parse_args() if args.a: - process_list = SysUtils.search_in_processes_by_name(args.a) + process_list = utils.search_processes(args.a) if not process_list: parser.error("There's no process with the name " + args.a) if len(process_list) > 1: - for p in process_list: - try: - name = p.name() - except psutil.NoSuchProcess: - print("Process with pid", p.pid, "does not exist anymore") - continue + for pid, user, name in process_list: print(name) print("There are more than one process with the name " + args.a) exit() - pid = process_list[0].pid - if not GDB_Engine.can_attach(pid): - parser.error("Failed to attach to the process with pid " + str(pid)) - GDB_Engine.attach(pid) + pid = process_list[0][0] + if not debugcore.can_attach(pid): + parser.error("Failed to attach to the process with pid " + pid) + debugcore.attach(pid) elif args.c: - if not GDB_Engine.create_process(args.c, args.o, args.l): + if not debugcore.create_process(args.c, args.o, args.l): parser.error("Couldn't create the process with current args") else: parser.error("Provide at least one of these arguments: -a or -c") -unittest.main(module="tests.GDB_Engine_tests", exit=False, argv=[""]) -unittest.main(module="tests.SysUtils_tests", exit=False, argv=[""]) -unittest.main(module="tests.GuiUtils_tests", exit=False, argv=[""]) -GDB_Engine.detach() +unittest.main(module="tests.debugcore_tests", exit=False, argv=[""]) +unittest.main(module="tests.utils_tests", exit=False, argv=[""]) +unittest.main(module="tests.guiutils_tests", exit=False, argv=[""]) +debugcore.detach() diff --git a/scanmem b/scanmem deleted file mode 160000 index 3a7f1199..00000000 --- a/scanmem +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3a7f11991b9794a16e9dc0fe01b2ec34027312fa diff --git a/tests/GDB_Engine_tests.py b/tests/debugcore_tests.py similarity index 72% rename from tests/GDB_Engine_tests.py rename to tests/debugcore_tests.py index d2447abe..4f82a04a 100644 --- a/tests/GDB_Engine_tests.py +++ b/tests/debugcore_tests.py @@ -15,14 +15,14 @@ along with this program. If not, see . """ import unittest -from libpince import GDB_Engine, type_defs, common_regexes +from libpince import debugcore, typedefs, regexes -class GDB_Engine_tests(unittest.TestCase): +class debugcore_tests(unittest.TestCase): def test_read_registers(self): - register_dict = GDB_Engine.read_registers() - if GDB_Engine.inferior_arch == type_defs.INFERIOR_ARCH.ARCH_64: + register_dict = debugcore.read_registers() + if debugcore.inferior_arch == typedefs.INFERIOR_ARCH.ARCH_64: test_register = "rax" else: test_register = "eax" - self.assertRegex(register_dict[test_register], common_regexes.hex_number.pattern) + self.assertRegex(register_dict[test_register], regexes.hex_number.pattern) diff --git a/tests/GuiUtils_tests.py b/tests/guiutils_tests.py similarity index 70% rename from tests/GuiUtils_tests.py rename to tests/guiutils_tests.py index ad339c4f..6d272cb5 100644 --- a/tests/GuiUtils_tests.py +++ b/tests/guiutils_tests.py @@ -15,9 +15,13 @@ along with this program. If not, see . """ import unittest -from libpince import GuiUtils -class GuiUtils_tests(unittest.TestCase): +# from GUI.Utils import guiutils + + +class guiutils_tests(unittest.TestCase): def test_change_text_length(self): - self.assertEqual(GuiUtils.change_text_length("AoB[42]", 30), "AoB[30]") + self.assertEqual(True, True) + # The function below was removed during refactorization, thus making this test just a placeholder for now + # self.assertEqual(guiutils.change_text_length("AoB[42]", 30), "AoB[30]") diff --git a/tests/SysUtils_tests.py b/tests/utils_tests.py similarity index 79% rename from tests/SysUtils_tests.py rename to tests/utils_tests.py index 75350cf7..fae912c8 100644 --- a/tests/SysUtils_tests.py +++ b/tests/utils_tests.py @@ -15,9 +15,9 @@ along with this program. If not, see . """ import unittest -from libpince import SysUtils +from libpince import utils -class SysUtils_tests(unittest.TestCase): +class utils_tests(unittest.TestCase): def test_split_symbol(self): - self.assertListEqual(SysUtils.split_symbol("func(param)@plt"), ["func", "func(param)", "func(param)@plt"]) + self.assertListEqual(utils.split_symbol("func(param)@plt"), ["func", "func(param)", "func(param)@plt"]) diff --git a/tr/tr.py b/tr/tr.py new file mode 100644 index 00000000..6c3c8c0c --- /dev/null +++ b/tr/tr.py @@ -0,0 +1,363 @@ +from PyQt6.QtCore import QObject, QLocale, QT_TR_NOOP, QT_TRANSLATE_NOOP +from collections import OrderedDict + +language_list = OrderedDict([("en_US", "English"), ("it_IT", "Italiano"), ("zh_CN", "简体中文")]) + + +def get_locale(): + system_locale = QLocale.system().name() + return system_locale if system_locale in language_list else "en_US" + + +# This handles the default translations for QDialogButtonBox.StandardButton +# Some of the standard button labels have an ampersand (&). It's used to denote an access key or keyboard shortcut +# Translate the text as you wish, then put the ampersand (&) where the shortcut should be +QT_TRANSLATE_NOOP("QPlatformTheme", "OK") +QT_TRANSLATE_NOOP("QPlatformTheme", "&Open") +QT_TRANSLATE_NOOP("QPlatformTheme", "&Save") +QT_TRANSLATE_NOOP("QPlatformTheme", "Cancel") +QT_TRANSLATE_NOOP("QPlatformTheme", "Close") +QT_TRANSLATE_NOOP("QPlatformTheme", "Discard") +QT_TRANSLATE_NOOP("QPlatformTheme", "Apply") +QT_TRANSLATE_NOOP("QPlatformTheme", "Reset") +QT_TRANSLATE_NOOP("QPlatformTheme", "Restore Defaults") +QT_TRANSLATE_NOOP("QPlatformTheme", "Help") +QT_TRANSLATE_NOOP("QPlatformTheme", "Save All") +QT_TRANSLATE_NOOP("QPlatformTheme", "&Yes") +QT_TRANSLATE_NOOP("QPlatformTheme", "&No") +QT_TRANSLATE_NOOP("QPlatformTheme", "Abort") +QT_TRANSLATE_NOOP("QPlatformTheme", "Retry") +QT_TRANSLATE_NOOP("QPlatformTheme", "Ignore") +QT_TRANSLATE_NOOP("QPlatformTheme", "N&o to All") +QT_TRANSLATE_NOOP("QPlatformTheme", "Yes to &All") + + +class TranslationConstants(QObject): + @staticmethod + def translate(): + for key, value in vars(TranslationConstants).items(): + if not key.startswith("__") and isinstance(value, str): + setattr(TranslationConstants, key, TranslationConstants.tr(value)) + + PAUSE_HOTKEY = QT_TR_NOOP("Pause the process") + BREAK_HOTKEY = QT_TR_NOOP("Break the process") + CONTINUE_HOTKEY = QT_TR_NOOP("Continue the process") + TOGGLE_ATTACH_HOTKEY = QT_TR_NOOP("Toggle attach/detach") + EXACT_SCAN_HOTKEY = QT_TR_NOOP("Next Scan - Exact") + INC_SCAN_HOTKEY = QT_TR_NOOP("Next Scan - Increased") + DEC_SCAN_HOTKEY = QT_TR_NOOP("Next Scan - Decreased") + CHANGED_SCAN_HOTKEY = QT_TR_NOOP("Next Scan - Changed") + UNCHANGED_SCAN_HOTKEY = QT_TR_NOOP("Next Scan - Unchanged") + ERROR = QT_TR_NOOP("Error") + SUCCESS = QT_TR_NOOP("Success") + INFO = QT_TR_NOOP("Information") + GDB_INIT = QT_TR_NOOP("GDB isn't initialized yet") + GDB_INIT_ERROR = QT_TR_NOOP( + "Unable to initialize GDB\n" + "You might want to reinstall GDB or use the system GDB\n" + "To change the current GDB path, check Settings->Debug" + ) + EDIT = QT_TR_NOOP("Edit") + SHOW_HEX = QT_TR_NOOP("Show as hexadecimal") + SHOW_DEC = QT_TR_NOOP("Show as decimal") + SHOW_UNSIGNED = QT_TR_NOOP("Show as unsigned") + SHOW_SIGNED = QT_TR_NOOP("Show as signed") + TOGGLE = QT_TR_NOOP("Toggle") + TOGGLE_CHILDREN = QT_TR_NOOP("Toggle including children") + FREEZE = QT_TR_NOOP("Freeze") + DEFAULT = QT_TR_NOOP("Default") + INCREMENTAL = QT_TR_NOOP("Incremental") + DECREMENTAL = QT_TR_NOOP("Decremental") + BROWSE_MEMORY_REGION = QT_TR_NOOP("Browse this memory region") + DISASSEMBLE_ADDRESS = QT_TR_NOOP("Disassemble this address") + DELETE = QT_TR_NOOP("Delete") + DELETE_SELECTION = QT_TR_NOOP("Delete selection") + CUT = QT_TR_NOOP("Cut") + COPY = QT_TR_NOOP("Copy") + PASTE = QT_TR_NOOP("Paste") + PASTE_INSIDE = QT_TR_NOOP("Paste inside") + POINTER_SCAN = QT_TR_NOOP("Pointer scan for this address") + POINTER_SCANNER = QT_TR_NOOP("Open pointer scanner") + WHAT_WRITES = QT_TR_NOOP("Find out what writes to this address") + WHAT_READS = QT_TR_NOOP("Find out what reads this address") + WHAT_ACCESSES = QT_TR_NOOP("Find out what accesses this address") + ADD_GROUP = QT_TR_NOOP("Add to a new group") + CREATE_GROUP = QT_TR_NOOP("Create a new group") + GROUP = QT_TR_NOOP("Group") + INVALID_CLIPBOARD = QT_TR_NOOP("Invalid clipboard content") + NEW_SCAN = QT_TR_NOOP("New Scan") + MATCH_COUNT_LIMITED = QT_TR_NOOP("Match count: {} ({} shown)") + MATCH_COUNT = QT_TR_NOOP("Match count: {}") + NO_DESCRIPTION = QT_TR_NOOP("No Description") + OPEN_PCT_FILE = QT_TR_NOOP("Open PCT file(s)") + + # Keep file extensions such as (*.pct) while translating, it doesn't matter where it stays within the sentence + # For instance, you can keep (*.pct) in the beginning of the sentence for right-to-left languages like arabic + # Apply the same to similar entries below + FILE_TYPES_PCT = QT_TR_NOOP("PINCE Cheat Table (*.pct)") + SHARED_OBJECT_TYPE = QT_TR_NOOP("Shared object library (*.so)") + FILE_TYPES_TRACE = QT_TR_NOOP("Trace File (*.trace)") + FILE_TYPES_SCANDATA = QT_TR_NOOP("Pointer Scan Data (*.scandata)") + CLEAR_TABLE = QT_TR_NOOP("Clear address table?") + FILE_LOAD_ERROR = QT_TR_NOOP("File {} is inaccessible or contains invalid content") + SAVE_PCT_FILE = QT_TR_NOOP("Save PCT file") + FILE_SAVE_ERROR = QT_TR_NOOP("Cannot save to file") + SMARTASS = QT_TR_NOOP("Nice try, smartass") + PROCESS_NOT_VALID = QT_TR_NOOP("Selected process is not valid") + ALREADY_DEBUGGING = QT_TR_NOOP("You're debugging this process already") + ALREADY_TRACED = QT_TR_NOOP("That process is already being traced by {}, could not attach to the process") + PERM_DENIED = QT_TR_NOOP("Permission denied, could not attach to the process") + CREATE_PROCESS_ERROR = QT_TR_NOOP("An error occurred while trying to create process") + SCAN_FOR = QT_TR_NOOP("Scan for") + FIRST_SCAN = QT_TR_NOOP("First Scan") + NO_PROCESS_SELECTED = QT_TR_NOOP("No Process Selected") + STATUS_DETACHED = QT_TR_NOOP("[detached]") + STATUS_STOPPED = QT_TR_NOOP("[stopped]") + PROCESS_RUNNING = QT_TR_NOOP("Process is running") + ENTER_VALUE = QT_TR_NOOP("Enter the new value") + ENTER_DESCRIPTION = QT_TR_NOOP("Enter the new description") + EDIT_ADDRESS = QT_TR_NOOP("Edit Address") + SELECT_PROCESS = QT_TR_NOOP("Please select a process first") + SELECT_BINARY = QT_TR_NOOP("Select the target binary") + ENTER_OPTIONAL_ARGS = QT_TR_NOOP("Enter the optional arguments") + LD_PRELOAD_OPTIONAL = QT_TR_NOOP("LD_PRELOAD .so path (optional)") + REFRESH = QT_TR_NOOP("Refresh") + LENGTH_NOT_VALID = QT_TR_NOOP("Length is not valid") + LENGTH_GT = QT_TR_NOOP("Length must be greater than 0") + PARSE_ERROR = QT_TR_NOOP("Can't parse the input") + IS_INVALID_REGEX = QT_TR_NOOP("{} isn't a valid regex") + LANG_RESET = QT_TR_NOOP("Language settings will take effect upon the next restart") + GDB_RESET = QT_TR_NOOP("You have changed the GDB path, reset GDB now?") + RESET_DEFAULT_SETTINGS = QT_TR_NOOP("This will reset to the default settings\n" "Proceed?") + MOUSE_OVER_EXAMPLES = QT_TR_NOOP("Mouse over on this text for examples") + AUTO_ATTACH_TOOLTIP = QT_TR_NOOP( + "asdf|qwer --> search for asdf or qwer\n" + "[as]df --> search for both adf and sdf\n" + "Use the char \\ to escape special chars such as [\n" + r"\[asdf\] --> search for opcodes that contain [asdf]" + ) + SEPARATE_PROCESSES_WITH = QT_TR_NOOP("Separate processes with {}") + UNUSED_APPIMAGE_SETTING = QT_TR_NOOP("This setting is unused in AppImage builds") + SELECT_GDB_BINARY = QT_TR_NOOP("Select the gdb binary") + QUIT_SESSION_CRASH = QT_TR_NOOP("Quitting current session will crash PINCE") + CONT_SESSION_CRASH = QT_TR_NOOP("Use global hotkeys or the commands 'interrupt' and 'c&' to stop/run the inferior") + + # For some languages, it might be hard to keep the pipe characters balanced + # You are free to modify pipes and dashes as you like when translating + # Check Chinese translation for an example + GDB_CONSOLE_INIT = QT_TR_NOOP( + "Hotkeys:\n" + "-----------------------------\n" + "Send: Enter |\n" + "Multi-line mode: Ctrl+Enter |\n" + "Complete command: Tab |\n" + "-----------------------------\n" + "Commands:\n" + "----------------------------------------------------------\n" + "/clear: Clear the console |\n" + "phase-out: Detach from the current process |\n" + "phase-in: Attach back to the previously detached process |\n" + "---------------------------------------------------------------------------------------------------\n" + "pince-init-so-file so_file_path: Initializes 'lib' variable |\n" + "pince-get-so-file-information: Get information about current lib |\n" + "pince-execute-from-so-file lib.func(params): Execute a function from lib |\n" + "# Check https://github.com/korcankaraokcu/PINCE/wiki#extending-pince-with-so-files for an example |\n" + "# CLI output mode doesn't work very well with .so extensions, use MI output mode instead |\n" + "---------------------------------------------------------------------------------------------------\n" + "You can change the output mode from bottom right\n" + "Changing output mode only affects commands sent. Any other output coming from external sources" + "(e.g async output) will be shown in MI format" + ) + BREAK = QT_TR_NOOP("Break[{}]") + RUN = QT_TR_NOOP("Run[{}]") + TOGGLE_ATTACH = QT_TR_NOOP("Toggle Attach[{}]") + BREAKPOINT_FAILED = QT_TR_NOOP("Failed to set breakpoint at address {}") + WATCHPOINT_FAILED = QT_TR_NOOP("Failed to set watchpoint at address {}") + COPY_CLIPBOARD = QT_TR_NOOP("Copy to Clipboard") + GO_TO_EXPRESSION = QT_TR_NOOP("Go to expression") + ADD_TO_ADDRESS_LIST = QT_TR_NOOP("Add this address to address list") + SET_WATCHPOINT = QT_TR_NOOP("Set Watchpoint") + WRITE_ONLY = QT_TR_NOOP("Write Only") + READ_ONLY = QT_TR_NOOP("Read Only") + BOTH = QT_TR_NOOP("Both") + CHANGE_BREAKPOINT_CONDITION = QT_TR_NOOP("Add/Change condition for breakpoint") + DELETE_BREAKPOINT = QT_TR_NOOP("Delete Breakpoint") + ENTER_EXPRESSION = QT_TR_NOOP("Enter the expression") + INVALID = QT_TR_NOOP("{} is invalid") + REGION_INFO = QT_TR_NOOP("Protection:{} | Base:{}-{} | Module:{}") + INVALID_REGION = QT_TR_NOOP("Invalid Region") + EXPRESSION_ACCESS_ERROR = QT_TR_NOOP("Cannot access memory at expression {}") + REFERENCED_BY = QT_TR_NOOP("Referenced by:") + SEE_REFERRERS = QT_TR_NOOP("Press 'Ctrl+E' to see a detailed list of referrers") + MV_PAUSED = QT_TR_NOOP("Memory Viewer - Paused") + MV_DEBUGGING = QT_TR_NOOP("Memory Viewer - Currently debugging {}") + MV_RUNNING = QT_TR_NOOP("Memory Viewer - Running") + ENTER_BP_CONDITION = QT_TR_NOOP( + "Enter the expression for condition, for instance:\n\n" + "$eax==0x523\n" + "$rax>0 && ($rbp<0 || $rsp==0)\n" + "printf($r10)==3" + ) + BP_CONDITION_FAILED = QT_TR_NOOP("Failed to set condition for address {}\n" "Check terminal for details") + FULL_STACK = QT_TR_NOOP("Full Stack") + COPY_RETURN_ADDRESS = QT_TR_NOOP("Copy Return Address") + COPY_FRAME_ADDRESS = QT_TR_NOOP("Copy Frame Address") + STACKTRACE = QT_TR_NOOP("Stacktrace") + TOGGLE_STACK_FROM_SP_BP = QT_TR_NOOP("Toggle stack from BP/SP register") + COPY_ADDRESS = QT_TR_NOOP("Copy Address") + COPY_VALUE = QT_TR_NOOP("Copy Value") + COPY_POINTS_TO = QT_TR_NOOP("Copy Points to") + DISASSEMBLE_VALUE_POINTER = QT_TR_NOOP("Disassemble 'value' pointer address") + HEXVIEW_VALUE_POINTER = QT_TR_NOOP("Show 'value' pointer in HexView") + BACK = QT_TR_NOOP("Back") + HEXVIEW_ADDRESS = QT_TR_NOOP("Show this address in HexView") + FOLLOW = QT_TR_NOOP("Follow") + EXAMINE_REFERRERS = QT_TR_NOOP("Examine Referrers") + BOOKMARK_ADDRESS = QT_TR_NOOP("Bookmark this address") + DELETE_BOOKMARK = QT_TR_NOOP("Delete this bookmark") + CHANGE_COMMENT = QT_TR_NOOP("Change comment") + GO_TO_BOOKMARK_ADDRESS = QT_TR_NOOP("Go to bookmarked address") + TOGGLE_BREAKPOINT = QT_TR_NOOP("Toggle Breakpoint") + EDIT_INSTRUCTION = QT_TR_NOOP("Edit instruction") + REPLACE_WITH_NOPS = QT_TR_NOOP("Replace instruction with NOPs") + WHAT_ACCESSES_INSTRUCTION = QT_TR_NOOP("Find out which addresses this instruction accesses") + TRACE_INSTRUCTION = QT_TR_NOOP("Break and trace instructions") + DISSECT_REGION = QT_TR_NOOP("Dissect this region") + COPY_BYTES = QT_TR_NOOP("Copy Bytes") + COPY_OPCODE = QT_TR_NOOP("Copy Opcode") + COPY_COMMENT = QT_TR_NOOP("Copy Comment") + COPY_ALL = QT_TR_NOOP("Copy All") + ENTER_TRACK_BP_EXPRESSION = QT_TR_NOOP( + "Enter the register expression(s) you want to track\n" + "Register names must start with $\n" + "Each expression must be separated with a comma\n\n" + "For instance:\n" + "Let's say the instruction is mov [rax+rbx],30\n" + "Then you should enter $rax+$rbx\n" + "So PINCE can track address [rax+rbx]\n\n" + "Another example:\n" + "If you enter $rax,$rbx*$rcx+4,$rbp\n" + "PINCE will track down addresses [rax],[rbx*rcx+4] and [rbp]" + ) + ALREADY_BOOKMARKED = QT_TR_NOOP("This address has already been bookmarked") + ENTER_BOOKMARK_COMMENT = QT_TR_NOOP("Enter the comment for bookmarked address") + SELECT_SO_FILE = QT_TR_NOOP("Select the .so file") + FILE_INJECTED = QT_TR_NOOP("The file has been injected") + FILE_INJECT_FAILED = QT_TR_NOOP("Failed to inject the .so file") + ENTER_CALL_EXPRESSION = QT_TR_NOOP( + "Enter the expression for the function that'll be called from the inferior\n" + "You can view functions list from View->Functions\n\n" + "For instance:\n" + 'Calling printf("1234") will yield something like this\n' + "↓\n" + "$28 = 4\n\n" + "$28 is the assigned convenience variable\n" + "4 is the result\n" + "You can use the assigned variable from the GDB Console" + ) + CALL_EXPRESSION_FAILED = QT_TR_NOOP("Failed to call the expression {}") + INVALID_EXPRESSION = QT_TR_NOOP("Invalid expression or address") + INVALID_ENTRY = QT_TR_NOOP("Invalid entries detected, refreshing the page") + ADD_ENTRY = QT_TR_NOOP("Add new entry") + ENTER_REGISTER_VALUE = QT_TR_NOOP("Enter the new value of register {}") + ENTER_FLAG_VALUE = QT_TR_NOOP("Enter the new value of flag {}") + RESTORE_INSTRUCTION = QT_TR_NOOP("Restore this instruction") + ENTER_HIT_COUNT = QT_TR_NOOP("Enter the hit count({} or higher)") + HIT_COUNT_ASSERT_INT = QT_TR_NOOP("Hit count must be an integer") + HIT_COUNT_ASSERT_LT = QT_TR_NOOP("Hit count can't be lower than {}") + CHANGE_CONDITION = QT_TR_NOOP("Change condition") + ENABLE = QT_TR_NOOP("Enable") + DISABLE = QT_TR_NOOP("Disable") + DISABLE_AFTER_HIT = QT_TR_NOOP("Disable after hit") + DISABLE_AFTER_COUNT = QT_TR_NOOP("Disable after X hits") + DELETE_AFTER_HIT = QT_TR_NOOP("Delete after hit") + OPCODE_WRITING_TO = QT_TR_NOOP("Opcodes writing to the address {}") + OPCODE_READING_FROM = QT_TR_NOOP("Opcodes reading from the address {}") + OPCODE_ACCESSING_TO = QT_TR_NOOP("Opcodes accessing to the address {}") + TRACK_WATCHPOINT_FAILED = QT_TR_NOOP("Unable to track watchpoint at expression {}") + DELETE_WATCHPOINT_FAILED = QT_TR_NOOP("Unable to delete watchpoint at expression {}") + CLOSE = QT_TR_NOOP("Close") + ACCESSED_BY_INSTRUCTION = QT_TR_NOOP("Addresses accessed by instruction {}") + TRACK_BREAKPOINT_FAILED = QT_TR_NOOP("Unable to track breakpoint at expression {}") + ACCESSED_BY = QT_TR_NOOP("Accessed by {}") + DELETE_BREAKPOINT_FAILED = QT_TR_NOOP("Unable to delete breakpoint at expression {}") + MAX_TRACE_COUNT_ASSERT_GT = QT_TR_NOOP("Max trace count must be greater than or equal to {}") + SAVE_TRACE_FILE = QT_TR_NOOP("Save trace file") + OPEN_TRACE_FILE = QT_TR_NOOP("Open trace file") + EXPAND_ALL = QT_TR_NOOP("Expand All") + COLLAPSE_ALL = QT_TR_NOOP("Collapse All") + SELECT_POINTER_MAP = QT_TR_NOOP("Select a pointer map file") + SCAN = QT_TR_NOOP("Scan") + SCANNING = QT_TR_NOOP("Scanning") + FILTER = QT_TR_NOOP("Filter") + FILTERING = QT_TR_NOOP("Filtering") + DEFINED = QT_TR_NOOP("DEFINED") + DEFINED_SYMBOL = QT_TR_NOOP( + "This symbol is defined. You can use its body as a gdb expression. For instance:\n\n" + "void func(param) can be used as 'func' as a gdb expression" + ) + COPY_SYMBOL = QT_TR_NOOP("Copy Symbol") + FUNCTIONS_INFO_HELPER = QT_TR_NOOP( + "\tHere's some useful regex tips:\n" + "^quaso --> search for everything that starts with quaso\n" + "[ab]cd --> search for both acd and bcd\n\n" + "\tHow to interpret symbols:\n" + "A symbol that looks like 'func(param)@plt' consists of 3 pieces\n" + "func, func(param), func(param)@plt\n" + "These 3 functions will have different addresses\n" + "@plt means this function is a subroutine for the original one\n" + "There can be more than one of the same function\n" + "It means that the function is overloaded" + ) + NEW_OPCODE = QT_TR_NOOP( + "New opcode is {} bytes long but old opcode is only {} bytes long\n" "This will cause an overflow, proceed?" + ) + IS_INVALID_EXPRESSION = QT_TR_NOOP("{} isn't a valid expression") + LOG_FILE = QT_TR_NOOP("Log File of PID {}") + LOG_CONTENTS = QT_TR_NOOP("Contents of {} (only last {} bytes are shown)") + ON = QT_TR_NOOP("ON") + OFF = QT_TR_NOOP("OFF") + LOG_STATUS = QT_TR_NOOP("LOGGING: {}") + LOG_READ_ERROR = QT_TR_NOOP("Unable to read log file at {}") + SETTINGS_ENABLE_LOG = QT_TR_NOOP("Go to Settings->Debug to enable logging") + INVALID_REGEX = QT_TR_NOOP("Invalid Regex") + SEARCH_OPCODE_HELPER = QT_TR_NOOP( + "\tHere's some useful regex examples:\n" + "call|rax --> search for opcodes that contain call or rax\n" + "[re]cx --> search for both rcx and ecx\n" + "Use the char \\ to escape special chars such as [\n" + r"\[rsp\] --> search for opcodes that contain [rsp]" + ) + COPY_ADDRESSES = QT_TR_NOOP("Copy Addresses") + COPY_OFFSET = QT_TR_NOOP("Copy Offset") + COPY_PATH = QT_TR_NOOP("Copy Path") + START = QT_TR_NOOP("Start") + CURRENT_SCAN_REGION = QT_TR_NOOP("Currently scanning region:") + CANCEL = QT_TR_NOOP("Cancel") + SCAN_FINISHED = QT_TR_NOOP("Scan finished") + SCAN_CANCELED = QT_TR_NOOP("Scan was canceled") + SELECT_ONE_REGION = QT_TR_NOOP("Select at least one region") + DISSECT_CODE = QT_TR_NOOP("You need to dissect code first\n" "Proceed?") + WAITING_FOR_BREAKPOINT = QT_TR_NOOP("Waiting for breakpoint to trigger") + TRACING_COMPLETED = QT_TR_NOOP("Tracing has been completed") + NOT = QT_TR_NOOP("Not") + EXACT = QT_TR_NOOP("Exact") + INCREASED = QT_TR_NOOP("Increased") + INCREASED_BY = QT_TR_NOOP("Increased by") + DECREASED = QT_TR_NOOP("Decreased") + DECREASED_BY = QT_TR_NOOP("Decreased by") + LESS_THAN = QT_TR_NOOP("Less Than") + MORE_THAN = QT_TR_NOOP("More Than") + BETWEEN = QT_TR_NOOP("Between") + CHANGED = QT_TR_NOOP("Changed") + UNCHANGED = QT_TR_NOOP("Unchanged") + UNKNOWN_VALUE = QT_TR_NOOP("Unknown Value") + BASIC = QT_TR_NOOP("Basic") + NORMAL = QT_TR_NOOP("Normal") + RW = QT_TR_NOOP("Read+Write") + FULL = QT_TR_NOOP("Full") + HOST = QT_TR_NOOP("Host") + LITTLE = QT_TR_NOOP("Little") + BIG = QT_TR_NOOP("Big") + SHOW_HEXVIEW = QT_TR_NOOP("Show in HexView") + SHOW_DISASSEMBLER = QT_TR_NOOP("Show in Disassembler")