├── .gitignore ├── BRUTAL-ICONS ├── BRUTAL_A.png ├── BRUTAL_B.png ├── BRUTAL_IDA5.png ├── BRUTAL_IDA6.png ├── BRUTAL_L.png ├── BRUTAL_R.png ├── BRUTAL_T.png └── BRUTAL_U.png ├── LICENSE ├── README.md ├── brutal_ida.py └── brutal_toolbar.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /BRUTAL-ICONS/BRUTAL_A.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmr232/BRUTAL-IDA/0e2c7fa9ae25d365922d255c7956ab89575fcfbb/BRUTAL-ICONS/BRUTAL_A.png -------------------------------------------------------------------------------- /BRUTAL-ICONS/BRUTAL_B.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmr232/BRUTAL-IDA/0e2c7fa9ae25d365922d255c7956ab89575fcfbb/BRUTAL-ICONS/BRUTAL_B.png -------------------------------------------------------------------------------- /BRUTAL-ICONS/BRUTAL_IDA5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmr232/BRUTAL-IDA/0e2c7fa9ae25d365922d255c7956ab89575fcfbb/BRUTAL-ICONS/BRUTAL_IDA5.png -------------------------------------------------------------------------------- /BRUTAL-ICONS/BRUTAL_IDA6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmr232/BRUTAL-IDA/0e2c7fa9ae25d365922d255c7956ab89575fcfbb/BRUTAL-ICONS/BRUTAL_IDA6.png -------------------------------------------------------------------------------- /BRUTAL-ICONS/BRUTAL_L.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmr232/BRUTAL-IDA/0e2c7fa9ae25d365922d255c7956ab89575fcfbb/BRUTAL-ICONS/BRUTAL_L.png -------------------------------------------------------------------------------- /BRUTAL-ICONS/BRUTAL_R.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmr232/BRUTAL-IDA/0e2c7fa9ae25d365922d255c7956ab89575fcfbb/BRUTAL-ICONS/BRUTAL_R.png -------------------------------------------------------------------------------- /BRUTAL-ICONS/BRUTAL_T.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmr232/BRUTAL-IDA/0e2c7fa9ae25d365922d255c7956ab89575fcfbb/BRUTAL-ICONS/BRUTAL_T.png -------------------------------------------------------------------------------- /BRUTAL-ICONS/BRUTAL_U.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmr232/BRUTAL-IDA/0e2c7fa9ae25d365922d255c7956ab89575fcfbb/BRUTAL-ICONS/BRUTAL_U.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Tamir Bahar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BRUTAL IDA 2 | ## Block Redo & Undo To Achieve Legacy IDA 3 | 4 | ## The Problem 5 | 6 | As we all know, IDA has no undo. 7 | No undo - no surrender. 8 | 9 | So naturally, IDA 7.3 adding undo functionality has ruined everyone's workflow. 10 | 11 | 12 | ### In The Media 13 | 14 | - [IDA 7.3 Adds Undo][undo-added] 15 | - [Undo Causes Massive Outcry][undo-outcry] 16 | - [T-Shirt Line Destroyed][tshirts] 17 | 18 | 19 | This will not stand. 20 | 21 | ## The Solution 22 | 23 | BRUTAL IDA restores your original workflow by blocking the `undo` and `redo` keyboard shortcuts. 24 | 25 | It comes in 2 modes: 26 | 27 | - ![][ida6]: Block `undo` and `redo` 28 | - ![][ida5]: Crash IDA on `undo` and `redo` 29 | 30 | ## Installation & Usage 31 | 32 | It is recommended to install the plugin using the [IDA Plugin Loader](https://github.com/tmr232/ida-plugin-loader). 33 | Just point it to the `brutal_ida.py` script. 34 | 35 | Once installed, the plugin will add a toolbar to IDA: 36 | 37 | ![][toolbar] 38 | 39 | Clicking the IDA icon will toggle between ![][ida5] and ![][ida6] modes. The default is ![][ida6]. 40 | 41 | 42 | [ida5]: BRUTAL-ICONS/BRUTAL_IDA5.png "5.x" 43 | [ida6]: BRUTAL-ICONS/BRUTAL_IDA6.png "6.x" 44 | [toolbar]: brutal_toolbar.png "BRUTAL IDA Toolbar" 45 | 46 | [undo-added]: https://twitter.com/newsoft/status/1135417060907991040 47 | [undo-outcry]: https://twitter.com/tmr232/status/1135869035806101505 48 | [tshirts]: https://twitter.com/IgorSkochinsky/status/1135912157973893120 -------------------------------------------------------------------------------- /brutal_ida.py: -------------------------------------------------------------------------------- 1 | ''' 2 | BRUTAL IDA 3 | 4 | Block Redo & Undo To Achieve Legacy IDA 5 | ''' 6 | import os 7 | 8 | import idaapi 9 | 10 | 11 | def get_brutal_icon_path(brutal_icon_name): 12 | return idaapi.load_custom_icon(os.path.join(os.path.dirname(__file__), 'BRUTAL-ICONS', brutal_icon_name)) 13 | 14 | 15 | BRUTAL5_ICON = get_brutal_icon_path('BRUTAL_IDA5.png') 16 | BRUTAL6_ICON = get_brutal_icon_path('BRUTAL_IDA6.png') 17 | BRUTAL_LETTERS = { 18 | brutal_letter: get_brutal_icon_path('BRUTAL_{}.png'.format(brutal_letter)) for brutal_letter in 'BRUTAL' 19 | } 20 | 21 | 22 | class BrutalLetterHandler(idaapi.action_handler_t): 23 | def __init__(brutal_self): 24 | idaapi.action_handler_t.__init__(brutal_self) 25 | 26 | def activate(brutal_self, brutal_context): 27 | pass 28 | 29 | def update(brutal_self, brutal_context): 30 | return idaapi.AST_DISABLE_ALWAYS 31 | 32 | 33 | class BrutalActionHandler(idaapi.action_handler_t): 34 | def __init__(brutal_self): 35 | idaapi.action_handler_t.__init__(brutal_self) 36 | brutal_self.brutal_mode = '6.x' 37 | 38 | def activate(brutal_self, brutal_context): 39 | brutal_self.brutal_mode = { 40 | '5.x': '6.x', 41 | '6.x': '5.x', 42 | }[brutal_self.brutal_mode] 43 | 44 | @property 45 | def icon(brutal_self): 46 | return { 47 | '5.x': BRUTAL5_ICON, 48 | '6.x': BRUTAL6_ICON, 49 | }[brutal_self.brutal_mode] 50 | 51 | def update(brutal_self, brutal_context): 52 | idaapi.update_action_icon(brutal_context.action, brutal_self.icon) 53 | return idaapi.AST_ENABLE 54 | 55 | 56 | class BrutalIDA(idaapi.plugin_t): 57 | flags = idaapi.PLUGIN_FIX 58 | comment = 'BRUTAL IDA' 59 | help = 'Block Redo & Undo To Achieve Legacy IDA' 60 | wanted_name = 'BRUTAL-IDA' 61 | wanted_hotkey = '' 62 | 63 | def handle_5x(brutal_self): 64 | idaapi.error('bTree error: Brutal. Just like the good ol\' days!') 65 | 66 | def handle_6x(brutal_self): 67 | pass 68 | 69 | def dispatch_brutality(brutal_self): 70 | { 71 | '5.x': brutal_self.handle_5x, 72 | '6.x': brutal_self.handle_6x, 73 | }[brutal_self.brutal_action_handler.brutal_mode]() 74 | 75 | def init(brutal_self): 76 | idaapi.unregister_action('Undo') 77 | idaapi.unregister_action('Redo') 78 | 79 | brutal_self.brutal_action_handler = BrutalActionHandler() 80 | brutal_action_desc = idaapi.action_desc_t('BRUTAL', 'BRUTAL IDA', brutal_self.brutal_action_handler, '', 81 | 'IDA', BRUTAL6_ICON) 82 | idaapi.register_action(brutal_action_desc) 83 | idaapi.create_toolbar('BRUTAL IDA', 'BRUTAL IDA') 84 | 85 | brutal_self.brutal_letter_handlers = [] 86 | 87 | for brutal_letter in 'BRUTAL': 88 | brutal_letter_handler = BrutalLetterHandler() 89 | brutal_self.brutal_letter_handlers.append(brutal_letter_handler) 90 | 91 | brutal_label = 'BRUTAL {}'.format(brutal_letter) 92 | brutal_letter_desc = idaapi.action_desc_t(brutal_label, 93 | brutal_label, 94 | brutal_letter_handler, 95 | '', 96 | brutal_letter, 97 | BRUTAL_LETTERS[brutal_letter]) 98 | idaapi.register_action(brutal_letter_desc) 99 | idaapi.attach_action_to_toolbar('BRUTAL IDA', brutal_label) 100 | 101 | idaapi.attach_action_to_toolbar('BRUTAL IDA', 'BRUTAL') 102 | 103 | brutal_self.brutal_hotkey = idaapi.add_hotkey('Ctrl+Z', brutal_self.dispatch_brutality) 104 | 105 | return idaapi.PLUGIN_KEEP 106 | 107 | def term(brutal_self): 108 | pass 109 | 110 | def run(brutal_self, arg): 111 | pass 112 | 113 | 114 | def PLUGIN_ENTRY(): 115 | return BrutalIDA() 116 | -------------------------------------------------------------------------------- /brutal_toolbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmr232/BRUTAL-IDA/0e2c7fa9ae25d365922d255c7956ab89575fcfbb/brutal_toolbar.png --------------------------------------------------------------------------------