diff --git a/frontend/package.json b/frontend/package.json index a9de48068..92b027677 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "ofrak-app", - "version": "3.1.0", + "version": "3.2.0", "description": "The graphical front-end for OFRAK.", "homepage": "https://ofrak.com", "private": true, diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte index c5ba22244..e925b26a1 100644 --- a/frontend/src/App.svelte +++ b/frontend/src/App.svelte @@ -275,5 +275,7 @@ Answer by running riddle.answer('your answer here') from the console.`); {/if}
-

v3.1.0

+

+ v3.2.0 +

diff --git a/frontend/src/ProjectManager/ProjectManagerBinaryOptions.svelte b/frontend/src/ProjectManager/ProjectManagerBinaryOptions.svelte index e75ebaacf..bf40fa739 100644 --- a/frontend/src/ProjectManager/ProjectManagerBinaryOptions.svelte +++ b/frontend/src/ProjectManager/ProjectManagerBinaryOptions.svelte @@ -32,7 +32,7 @@ import { selectedProject, settings, selected } from "../stores"; import Icon from "../Icon.svelte"; - export let name; + export let args, selectedBinaryName, forceRefreshProject; async function deleteBinary() { await fetch(`${$settings.backendUrl}/delete_binary_from_project`, { @@ -42,12 +42,14 @@ }, body: JSON.stringify({ id: $selectedProject.session_id, - binary: name, + binary: args.name, }), }).then(async (r) => { if (!r.ok) { throw Error(JSON.stringify(await r.json(), undefined, 2)); } + selectedBinaryName = undefined; + forceRefreshProject = {}; $selectedProject = await fetch( `${$settings.backendUrl}/get_project_by_id?id=${$selectedProject.session_id}` ).then((r) => { @@ -63,6 +65,6 @@
Delete {args.name} from project.
diff --git a/frontend/src/ProjectManager/ProjectManagerOptions.svelte b/frontend/src/ProjectManager/ProjectManagerOptions.svelte index e503844fd..764b59e42 100644 --- a/frontend/src/ProjectManager/ProjectManagerOptions.svelte +++ b/frontend/src/ProjectManager/ProjectManagerOptions.svelte @@ -12,15 +12,18 @@
- {#if focus && typeof focus == "string"} - "{focus}" - {:else if focus && typeof focus == "object"} - + {#if focus} + {:else} Click anywhere to see its options. {/if} diff --git a/frontend/src/ProjectManager/ProjectManagerScriptOptions.svelte b/frontend/src/ProjectManager/ProjectManagerScriptOptions.svelte index e616ada8d..f8d65d793 100644 --- a/frontend/src/ProjectManager/ProjectManagerScriptOptions.svelte +++ b/frontend/src/ProjectManager/ProjectManagerScriptOptions.svelte @@ -31,7 +31,7 @@ import { selectedProject, settings, selected } from "../stores"; import Icon from "../Icon.svelte"; - export let name; + export let args; async function deleteScript() { await fetch(`${$settings.backendUrl}/delete_script_from_project`, { @@ -41,7 +41,7 @@ }, body: JSON.stringify({ id: $selectedProject.session_id, - script: name, + script: args.name, }), }).then(async (r) => { if (!r.ok) { @@ -55,7 +55,6 @@ } return r.json(); }); - console.log($selectedProject); return await r.json(); }); } @@ -63,6 +62,6 @@
Delete {args.name} from project.
diff --git a/frontend/src/ProjectManager/ProjectManagerToolbar.svelte b/frontend/src/ProjectManager/ProjectManagerToolbar.svelte index 28700ba9b..d8c2eccd4 100644 --- a/frontend/src/ProjectManager/ProjectManagerToolbar.svelte +++ b/frontend/src/ProjectManager/ProjectManagerToolbar.svelte @@ -65,9 +65,9 @@ } $selectedProject = await fetch( `${$settings.backendUrl}/get_project_by_id?id=${$selectedProject.session_id}` - ).then((r) => { + ).then(async (r) => { if (!r.ok) { - throw Error(r.statusText); + throw Error(JSON.stringify(await r.json(), undefined, 2)); } return r.json(); }); diff --git a/frontend/src/ProjectManager/ProjectManagerView.svelte b/frontend/src/ProjectManager/ProjectManagerView.svelte index f2ea11c31..02fe6c04a 100644 --- a/frontend/src/ProjectManager/ProjectManagerView.svelte +++ b/frontend/src/ProjectManager/ProjectManagerView.svelte @@ -75,11 +75,6 @@ scriptCheckboxHoverInfo = {}; let binariesForProject = []; - for (let binaryName in $selectedProject.binaries) { - if ($selectedProject.binaries.hasOwnProperty(binaryName)) { - binariesForProject.push(binaryName); - } - } export let resources, rootResourceLoadPromise, @@ -88,6 +83,9 @@ showProjectManager; async function openProject() { + if (!selectedBinaryName) { + throw Error("Select a binary to launch!"); + } let rootModel = await fetch(`${$settings.backendUrl}/open_project`, { method: "POST", headers: { @@ -98,7 +96,12 @@ binary: selectedBinaryName, script: $selectedProject.binaries[selectedBinaryName].init_script, }), - }).then((r) => r.json()); + }).then(async (r) => { + if (!r.ok) { + throw Error(JSON.stringify(await r.json(), undefined, 2)); + } + return await r.json(); + }); rootResource = remote_model_to_resource(rootModel, resources); $selected = rootModel.id; showProjectManager = false; @@ -131,6 +134,14 @@ }; focusScript = undefined; } + $: { + binariesForProject = []; + for (let binaryName in $selectedProject.binaries) { + if ($selectedProject.binaries.hasOwnProperty(binaryName)) { + binariesForProject.push(binaryName); + } + } + }
OFRAK Project Manager
@@ -149,20 +160,25 @@
- {#each binariesForProject as binaryName} -
- -
- {/each} + {#key forceRefreshProject} + {#each binariesForProject as binaryName} +
+ +
+ {/each} + {/key}
@@ -171,7 +187,10 @@
@@ -191,10 +210,10 @@

{/if}
- {#each $selectedProject.scripts as script} -
- {#if selectedBinaryName} - {#key forceRefreshProject} + {#key forceRefreshProject} + {#each $selectedProject.scripts as script} +
+ {#if selectedBinaryName} - {/key} - {:else} - - {/if} -
- {/each} + {:else} + + {/if} +
+ {/each} + {/key} - + diff --git a/ofrak_core/CHANGELOG.md b/ofrak_core/CHANGELOG.md index 4b9b57ee9..005ad3e3b 100644 --- a/ofrak_core/CHANGELOG.md +++ b/ofrak_core/CHANGELOG.md @@ -4,17 +4,21 @@ All notable changes to `ofrak` will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased](https://github.com/redballoonsecurity/ofrak/tree/master) + +## [3.2.0](https://github.com/redballoonsecurity/ofrak/compare/ofrak-v3.1.0...ofrak-v3.2.0) ### Added - Add a JFFS2 packer and unpacker. ([#326](https://github.com/redballoonsecurity/ofrak/pull/326)) - Add method to Resource and data service to search for patterns in its data ([#333](https://github.com/redballoonsecurity/ofrak/pull/333)) - Add search bars to GUI in order to search for a string or bytes within a resource. ([#345](https://github.com/redballoonsecurity/ofrak/pull/345)) - Add Identifier, Unpacker, Packer for Intel Hex format. ([#349](https://github.com/redballoonsecurity/ofrak/pull/349)) +- Add unpackers for EXT filesystems (versions 2 through 4). ([#337](https://github.com/redballoonsecurity/ofrak/pull/337)) - A new feature that allows users to create an OFRAK "project" that contains a collection of scripts and binaries. ([#360](https://github.com/redballoonsecurity/ofrak/pull/360)) ### Changed - Support uploading files in chunks to handle files larger than 2GB from the GUI ([#324](https://github.com/redballoonsecurity/ofrak/pull/324)) ### Fixed +- Save resources affected by data patches and dependency updates on a resource being saved ([#355](https://github.com/redballoonsecurity/ofrak/pull/355)) ## [3.1.0](https://github.com/redballoonsecurity/ofrak/compare/ofrak-v3.0.0...ofrak-v3.1.0) ### Added diff --git a/ofrak_core/ofrak/project/project.py b/ofrak_core/ofrak/project/project.py index b6ac43da4..b41f10bc1 100644 --- a/ofrak_core/ofrak/project/project.py +++ b/ofrak_core/ofrak/project/project.py @@ -48,6 +48,14 @@ def metadata_path(self): def readme_path(self): return os.path.join(self.path, "README.md") + @property + def trashed_binaries(self): + return os.path.join(self.path, ".Trash", "binaries") + + @property + def trashed_scripts(self): + return os.path.join(self.path, ".Trash", "binaries") + @staticmethod def create(name: str, path: str) -> "OfrakProject": new_project = OfrakProject( @@ -130,6 +138,7 @@ def init_from_path(path: str) -> "OfrakProject": scripts, ) + project._restore_trashed_files() return project def script_path(self, script_name, check: bool = True) -> str: @@ -231,26 +240,27 @@ def add_script(self, name: str, script_contents: str): f.write(script_contents) def delete_binary(self, name: str): - if not os.path.isdir(os.path.join(self.path, ".Trash")): - os.mkdir(os.path.join(self.path, ".Trash")) - if not os.path.isdir(os.path.join(os.path.join(self.path, ".Trash"), "binaries")): - os.mkdir(os.path.join(os.path.join(self.path, ".Trash"), "binaries")) + os.makedirs(self.trashed_binaries, exist_ok=True) os.rename( self.binary_path(name), - os.path.join(os.path.join(os.path.join(self.path, ".Trash"), "binaries"), name), + os.path.join(self.trashed_binaries, name), ) self.binaries.pop(name) def delete_script(self, name: str): - if not os.path.isdir(os.path.join(self.path, ".Trash")): - os.mkdir(os.path.join(self.path, ".Trash")) - if not os.path.isdir(os.path.join(os.path.join(self.path, ".Trash"), "scripts")): - os.mkdir(os.path.join(os.path.join(self.path, ".Trash"), "scripts")) + os.makedirs(self.trashed_scripts, exist_ok=True) os.rename( self.script_path(name), - os.path.join(os.path.join(os.path.join(self.path, ".Trash"), "scripts"), name), + os.path.join(self.trashed_scripts, name), ) self.scripts.remove(name) + for binary_metadata in self.binaries.values(): + try: + binary_metadata.associated_scripts.remove(name) + except ValueError: + pass + if binary_metadata.init_script == name: + binary_metadata.init_script = None def reset_project(self): path = self.path @@ -277,48 +287,46 @@ def reset_project(self): raw_metadata = json.load(f) self.scripts = [script["name"] for script in raw_metadata["scripts"]] - for script in self.scripts: - if not os.path.exists(self.script_path(script, check=False)): - if not os.path.exists( - os.path.join(os.path.join(os.path.join(self.path, ".Trash"), "scripts"), script) - ): - raise AttributeError( - f"Trying to restore script {script} but the file is missing from .Trash" - ) - else: - os.rename( - os.path.join( - os.path.join(os.path.join(self.path, ".Trash"), "scripts"), script - ), - self.script_path(script, check=False), - ) - self.binaries = {} for binaryName, info in raw_metadata["binaries"].items(): self.binaries[binaryName] = _OfrakProjectBinary( info["associated_scripts"], info.get("init_script") ) - for binary in self.binaries.keys(): - if not os.path.exists(self.binary_path(binary, check=False)): - if not os.path.exists( - os.path.join( - os.path.join(os.path.join(self.path, ".Trash"), "binaries"), binary + + self.name = raw_metadata["name"] + self.project_id = binascii.unhexlify(raw_metadata["project_id"]) + + self._restore_trashed_files() + + def _restore_trashed_files(self): + for script in self.scripts: + try: + self.script_path(script) + except ValueError: + try: + os.rename( + os.path.join(self.trashed_scripts, script), + self.script_path(script, check=False), ) - ): + except OSError: raise AttributeError( - f"Trying to restore binary {binary} but the file is missing from .Trash" + f"Trying to restore script {script} but the file is missing from .Trash" ) - else: + + for binary in self.binaries.keys(): + try: + self.binary_path(binary) + except ValueError: + try: os.rename( - os.path.join( - os.path.join(os.path.join(self.path, ".Trash"), "scripts"), binary - ), + os.path.join(self.trashed_binaries, binary), self.binary_path(binary, check=False), ) - - self.name = raw_metadata["name"] - self.project_id = binascii.unhexlify(raw_metadata["project_id"]) + except OSError: + raise AttributeError( + f"Trying to restore binary {binary} but the file is missing from .Trash" + ) def _get_binary(self, name): if not name in self.binaries.keys(): diff --git a/ofrak_core/setup.py b/ofrak_core/setup.py index ef3268314..925d62f5c 100644 --- a/ofrak_core/setup.py +++ b/ofrak_core/setup.py @@ -40,7 +40,7 @@ def read_requirements(requirements_path): setuptools.setup( name="ofrak", - version="3.1.0", + version="3.2.0", description="A binary analysis and modification platform", packages=setuptools.find_packages(exclude=["test_ofrak", "test_ofrak.*"]), package_data={ @@ -49,7 +49,7 @@ def read_requirements(requirements_path): install_requires=[ "ofrak_io>=1.0,==1.*", "ofrak_type>=2.2.0rc0,==2.*", - "ofrak_patch_maker>=4.0.1rc0,==4.*", + "ofrak_patch_maker>=4.0.2rc0,==4.*", ] + read_requirements("requirements.txt"), extras_require={ diff --git a/ofrak_patch_maker/CHANGELOG.md b/ofrak_patch_maker/CHANGELOG.md index e24e2aaa7..26ebf58bd 100644 --- a/ofrak_patch_maker/CHANGELOG.md +++ b/ofrak_patch_maker/CHANGELOG.md @@ -4,6 +4,8 @@ All notable changes to `ofrak-patch-maker` will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased](https://github.com/redballoonsecurity/ofrak/tree/master) + +## [4.0.2](https://github.com/redballoonsecurity/ofrak/compare/ofrak-patch-maker-v.4.0.1...ofrak-patch-maker-v.4.0.2) ### Fixed - Remove option to read or install toolchain.conf from/to "/etc/toolchain.conf" ([#342](https://github.com/redballoonsecurity/ofrak/pull/342)) diff --git a/ofrak_patch_maker/setup.py b/ofrak_patch_maker/setup.py index e99f15180..d135259fb 100644 --- a/ofrak_patch_maker/setup.py +++ b/ofrak_patch_maker/setup.py @@ -31,7 +31,7 @@ def read_requirements(requirements_path): setuptools.setup( name="ofrak_patch_maker", - version="4.0.1", + version="4.0.2", description="PatchMaker tool for applying source-code patches to binaries", packages=setuptools.find_packages(exclude=("ofrak_patch_maker_test",)), package_data={"ofrak_patch_maker": ["py.typed"]},