From efd415a5d49fc8a6d2e421cd7dd31c4f8d6da592 Mon Sep 17 00:00:00 2001 From: edward Date: Thu, 6 Oct 2022 19:38:57 -0400 Subject: [PATCH 01/13] add angr and no backend as options for GUI, fixup some frontend dev tools --- .../ofrak_angr/ofrak_angr/serializer.py | 21 +++++++++++++++++++ frontend/Makefile | 4 ++-- frontend/backend/ofrak_server.Dockerfile | 7 +++---- frontend/backend/ofrak_server.py | 13 ++++++++++-- 4 files changed, 37 insertions(+), 8 deletions(-) create mode 100644 disassemblers/ofrak_angr/ofrak_angr/serializer.py diff --git a/disassemblers/ofrak_angr/ofrak_angr/serializer.py b/disassemblers/ofrak_angr/ofrak_angr/serializer.py new file mode 100644 index 000000000..380a9af90 --- /dev/null +++ b/disassemblers/ofrak_angr/ofrak_angr/serializer.py @@ -0,0 +1,21 @@ +from typing import Any + +from angr import Project + +from ofrak.service.serialization.pjson_types import PJSONType +from ofrak.service.serialization.serializers.serializer_i import SerializerInterface + + +class AngrAnalysisSerializer(SerializerInterface): + """ + Dummy serializer to silently pass serialization attempts, and hard fail on attempting to + deserialize. + """ + + targets = (Project,) + + def obj_to_pjson(self, obj: Project, type_hint: Any) -> PJSONType: + return None + + def pjson_to_obj(self, pjson_obj: Any, type_hint: Any) -> Project: + raise NotImplementedError("Deserialization for Angr projects is not yet supported!") diff --git a/frontend/Makefile b/frontend/Makefile index 7cfa079e4..db9ba8b30 100644 --- a/frontend/Makefile +++ b/frontend/Makefile @@ -3,7 +3,7 @@ ################################################################################ PORT ?= 8888 -OFRAK_SOURCE_DIR ?= ~/ofrak +OFRAK_SOURCE_DIR ?= $(CURDIR)/.. ################################################################################ @@ -56,7 +56,7 @@ images: app-image ofrak-server-image proxy-image .PHONY: ofrak-server-image ofrak-server-image: - cp --recursive $(OFRAK_SOURCE_DIR)/docs/ backend/ + cp -r $(OFRAK_SOURCE_DIR)/docs/ backend/ cp $(OFRAK_SOURCE_DIR)/mkdocs.yml backend/mkdocs.yml docker build -t rbs/ofrak-server:latest -f backend/ofrak_server.Dockerfile backend diff --git a/frontend/backend/ofrak_server.Dockerfile b/frontend/backend/ofrak_server.Dockerfile index 30332f432..2d56be209 100644 --- a/frontend/backend/ofrak_server.Dockerfile +++ b/frontend/backend/ofrak_server.Dockerfile @@ -1,12 +1,11 @@ FROM redballoonsecurity/ofrak/dev:latest -COPY ofrak_server.py /ofrak_server.py -COPY docs /docs -COPY mkdocs.yml /mkdocs.yml +ARG BACKEND_DIR=. +COPY $BACKEND_DIR/ofrak_server.py /ofrak_server.py RUN python3 -m pip install aiohttp~=3.8.1 ENTRYPOINT python3 -m ofrak_ghidra.server start \ & mkdocs serve --dev-addr 0.0.0.0:8000 \ - & python3 /ofrak_server.py 0.0.0.0 8877 ghidra \ + & python3 /ofrak_server.py 0.0.0.0 8877 \ & sleep infinity diff --git a/frontend/backend/ofrak_server.py b/frontend/backend/ofrak_server.py index 88377b949..2ed19c542 100644 --- a/frontend/backend/ofrak_server.py +++ b/frontend/backend/ofrak_server.py @@ -476,7 +476,7 @@ def get_query_string_as_pjson(request: Request) -> Dict[str, PJSONType]: if len(sys.argv) == 4: backend = sys.argv[3] else: - backend = "ghidra" + backend = None ofrak = OFRAK(logging.INFO) @@ -486,9 +486,18 @@ def get_query_string_as_pjson(request: Request) -> Dict[str, PJSONType]: ofrak.injector.discover(ofrak_capstone) ofrak.injector.discover(ofrak_binary_ninja) - else: + + elif backend == "ghidra": import ofrak_ghidra # type: ignore ofrak.injector.discover(ofrak_ghidra) + elif backend == "angr": + import ofrak_angr # type: ignore + + ofrak.injector.discover(ofrak_angr) + + else: + LOGGER.warning("No disassembler backend specified, so no disassembly will be possible") + ofrak.run(main, _host, _port) # type: ignore From 62c6e5819db497696a7d6527993b97b6d3e6c477 Mon Sep 17 00:00:00 2001 From: edward Date: Fri, 7 Oct 2022 12:28:16 -0400 Subject: [PATCH 02/13] remove docs from development frontend image (they are hosted on ofrak.com anyway) --- frontend/Makefile | 2 -- 1 file changed, 2 deletions(-) diff --git a/frontend/Makefile b/frontend/Makefile index db9ba8b30..2df14a12d 100644 --- a/frontend/Makefile +++ b/frontend/Makefile @@ -56,8 +56,6 @@ images: app-image ofrak-server-image proxy-image .PHONY: ofrak-server-image ofrak-server-image: - cp -r $(OFRAK_SOURCE_DIR)/docs/ backend/ - cp $(OFRAK_SOURCE_DIR)/mkdocs.yml backend/mkdocs.yml docker build -t rbs/ofrak-server:latest -f backend/ofrak_server.Dockerfile backend .PHONY: proxy-image From 37140cdc9d8d97b93ce8de8fd43de5b74b9d3c41 Mon Sep 17 00:00:00 2001 From: edward Date: Fri, 7 Oct 2022 14:12:35 -0400 Subject: [PATCH 03/13] add test for Angr project serializer --- .../ofrak_angr_test/test_serializer.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 disassemblers/ofrak_angr/ofrak_angr_test/test_serializer.py diff --git a/disassemblers/ofrak_angr/ofrak_angr_test/test_serializer.py b/disassemblers/ofrak_angr/ofrak_angr_test/test_serializer.py new file mode 100644 index 000000000..668e61126 --- /dev/null +++ b/disassemblers/ofrak_angr/ofrak_angr_test/test_serializer.py @@ -0,0 +1,27 @@ +import pytest +from angr import Project + +from ofrak import OFRAKContext +from ofrak.core import File +from ofrak.service.serialization.serializers.serializer_i import SerializerInterface +from ofrak_angr.model import AngrAnalysis +from ofrak_angr.serializer import AngrAnalysisSerializer + + +@pytest.fixture +def serializer(): + return AngrAnalysisSerializer() + + +async def test_angr_project_serializer( + ofrak_context: OFRAKContext, hello_world_elf: bytes, serializer: SerializerInterface +): + root = await ofrak_context.create_root_resource("hello_world", hello_world_elf, (File,)) + await root.identify() + angr_analysis = await root.analyze(AngrAnalysis) + + serialized_project = serializer.obj_to_pjson(angr_analysis.project, Project) + assert serialized_project is None + + with pytest.raises(NotImplementedError): + _ = serializer.pjson_to_obj(serialized_project, Project) From d1d73ca9b73f2903ed6064037809e629bcaa8b6c Mon Sep 17 00:00:00 2001 From: edward Date: Fri, 7 Oct 2022 14:13:58 -0400 Subject: [PATCH 04/13] remove mkdocs serve command --- frontend/backend/ofrak_server.Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/backend/ofrak_server.Dockerfile b/frontend/backend/ofrak_server.Dockerfile index 2d56be209..3af935b0d 100644 --- a/frontend/backend/ofrak_server.Dockerfile +++ b/frontend/backend/ofrak_server.Dockerfile @@ -6,6 +6,5 @@ COPY $BACKEND_DIR/ofrak_server.py /ofrak_server.py RUN python3 -m pip install aiohttp~=3.8.1 ENTRYPOINT python3 -m ofrak_ghidra.server start \ - & mkdocs serve --dev-addr 0.0.0.0:8000 \ & python3 /ofrak_server.py 0.0.0.0 8877 \ & sleep infinity From 0ac82611d5790606273d1d63c2b682e67486b3dc Mon Sep 17 00:00:00 2001 From: edward Date: Fri, 30 Sep 2022 19:23:12 -0400 Subject: [PATCH 05/13] add new route to search for resources matching a vaddr or range of vaddrs --- frontend/backend/ofrak_server.py | 37 ++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/frontend/backend/ofrak_server.py b/frontend/backend/ofrak_server.py index 2ed19c542..ce6e6a79b 100644 --- a/frontend/backend/ofrak_server.py +++ b/frontend/backend/ofrak_server.py @@ -25,8 +25,14 @@ from ofrak_type.error import NotFoundError from ofrak_type.range import Range -from ofrak import OFRAKContext, OFRAK -from ofrak.core import File +from ofrak import ( + OFRAKContext, + OFRAK, + ResourceFilter, + ResourceAttributeRangeFilter, + ResourceAttributeValueFilter, +) +from ofrak.core import File, Addressable from ofrak.core import ( GenericBinary, AddCommentModifier, @@ -131,6 +137,7 @@ def __init__( web.post("/{resource_id}/find_and_replace", self.find_and_replace), web.post("/{resource_id}/add_comment", self.add_comment), web.post("/{resource_id}/delete_comment", self.delete_comment), + web.post("/{resource_id}/search_for_vaddr", self.search_for_vaddr), ] ) @@ -330,6 +337,32 @@ async def delete_comment(self, request: Request) -> Response: ) return web.json_response(await self._serialize_component_result(result)) + @exceptions_to_http(SerializedError) + async def search_for_vaddr(self, request: Request) -> Response: + resource = await self._get_resource_for_request(request) + vaddr_start, vaddr_end = self._serializer.from_pjson( + await request.json(), Tuple[int, Optional[int]] + ) + if vaddr_end is not None: + matching_resources = await resource.get_descendants( + r_filter=ResourceFilter( + attribute_filters=( + ResourceAttributeRangeFilter( + Addressable.VirtualAddress, vaddr_start, vaddr_end + ) + ) + ) + ) + else: + matching_resources = await resource.get_descendants( + r_filter=ResourceFilter( + attribute_filters=( + ResourceAttributeValueFilter(Addressable.VirtualAddress, vaddr_start) + ) + ) + ) + return web.json_response(list(map(self._serialize_resource, matching_resources))) + async def _get_resource_by_id(self, resource_id: bytes, job_id: bytes) -> Resource: resource = await self._ofrak_context.resource_factory.create( job_id, From 2b8e8dc8dabb71cfb341553bd5d87d676f2c0b25 Mon Sep 17 00:00:00 2001 From: edward Date: Fri, 7 Oct 2022 14:18:56 -0400 Subject: [PATCH 06/13] initial implementation of search feature in frontend --- frontend/src/ResourceTreeToolbar.svelte | 9 ++ frontend/src/SearchView.svelte | 201 ++++++++++++++++++++++++ 2 files changed, 210 insertions(+) create mode 100644 frontend/src/SearchView.svelte diff --git a/frontend/src/ResourceTreeToolbar.svelte b/frontend/src/ResourceTreeToolbar.svelte index 7dfd0b567..725b3a083 100644 --- a/frontend/src/ResourceTreeToolbar.svelte +++ b/frontend/src/ResourceTreeToolbar.svelte @@ -5,6 +5,7 @@ import Toolbar from "./Toolbar.svelte"; import { selectedResource, selected } from "./stores.js"; + import SearchView from "./SearchView.svelte"; export let resourceNodeDataMap, modifierView; $: rootResource = $selectedResource; @@ -187,6 +188,14 @@ modifierView = CommentView; }, }, + + { + text: "Search", + iconUrl: "/icons/modify.svg", + onclick: async (e) => { + modifierView = SearchView; + }, + }, ]; } diff --git a/frontend/src/SearchView.svelte b/frontend/src/SearchView.svelte new file mode 100644 index 000000000..3dd376695 --- /dev/null +++ b/frontend/src/SearchView.svelte @@ -0,0 +1,201 @@ + + + + +
+
+

+ Search for resources whose virtual address matches a specific address or + lies in a range of addresses. +

+ {#if rangeSearch} + + + {:else} + + {/if} +
+ +
+ {#if errorMessage} +

+ Error: + {errorMessage} +

+ {/if} +
+
+ + +
+
From 901d2f2b5b2e6337de4163274ef1b465c30c0152 Mon Sep 17 00:00:00 2001 From: edward Date: Fri, 7 Oct 2022 16:37:21 -0400 Subject: [PATCH 07/13] working search frontend-backend implementation (results not displayed) --- frontend/backend/ofrak_server.py | 31 ++++++++++--------- frontend/src/SearchView.svelte | 44 +++++++++++++++++---------- frontend/src/ofrak/remote_resource.js | 16 ++++++++++ frontend/src/ofrak/resource.js | 4 +++ 4 files changed, 65 insertions(+), 30 deletions(-) diff --git a/frontend/backend/ofrak_server.py b/frontend/backend/ofrak_server.py index ce6e6a79b..fb7711e8a 100644 --- a/frontend/backend/ofrak_server.py +++ b/frontend/backend/ofrak_server.py @@ -343,25 +343,28 @@ async def search_for_vaddr(self, request: Request) -> Response: vaddr_start, vaddr_end = self._serializer.from_pjson( await request.json(), Tuple[int, Optional[int]] ) - if vaddr_end is not None: - matching_resources = await resource.get_descendants( - r_filter=ResourceFilter( - attribute_filters=( - ResourceAttributeRangeFilter( - Addressable.VirtualAddress, vaddr_start, vaddr_end + try: + if vaddr_end is not None: + matching_resources = await resource.get_descendants( + r_filter=ResourceFilter( + attribute_filters=( + ResourceAttributeRangeFilter( + Addressable.VirtualAddress, vaddr_start, vaddr_end + ), ) ) ) - ) - else: - matching_resources = await resource.get_descendants( - r_filter=ResourceFilter( - attribute_filters=( - ResourceAttributeValueFilter(Addressable.VirtualAddress, vaddr_start) + else: + matching_resources = await resource.get_descendants( + r_filter=ResourceFilter( + attribute_filters=( + ResourceAttributeValueFilter(Addressable.VirtualAddress, vaddr_start), + ) ) ) - ) - return web.json_response(list(map(self._serialize_resource, matching_resources))) + return web.json_response(list(map(self._serialize_resource, matching_resources))) + except NotFoundError: + return web.json_response([]) async def _get_resource_by_id(self, resource_id: bytes, job_id: bytes) -> Resource: resource = await self._ofrak_context.resource_factory.create( diff --git a/frontend/src/SearchView.svelte b/frontend/src/SearchView.svelte index 3dd376695..98bc7f992 100644 --- a/frontend/src/SearchView.svelte +++ b/frontend/src/SearchView.svelte @@ -123,14 +123,15 @@
@@ -217,7 +226,7 @@
- +

Found {results.length} results

From b054ef283d3a6000c288cde1965d88bbcebf32c9 Mon Sep 17 00:00:00 2001 From: edward Date: Mon, 10 Oct 2022 16:51:29 -0400 Subject: [PATCH 11/13] apply suggestions from PR --- frontend/backend/ofrak_server.py | 25 ++++++++----------------- frontend/src/SearchView.svelte | 31 +------------------------------ 2 files changed, 9 insertions(+), 47 deletions(-) diff --git a/frontend/backend/ofrak_server.py b/frontend/backend/ofrak_server.py index fa063d61f..58b84714d 100644 --- a/frontend/backend/ofrak_server.py +++ b/frontend/backend/ofrak_server.py @@ -346,26 +346,17 @@ async def search_for_vaddr(self, request: Request) -> Response: ) try: if vaddr_end is not None: - matching_resources = await resource.get_descendants( - r_filter=ResourceFilter( - attribute_filters=( - ResourceAttributeRangeFilter( - Addressable.VirtualAddress, vaddr_start, vaddr_end - ), - ) - ), - r_sort=ResourceSort(Addressable.VirtualAddress), + vaddr_filter = ResourceAttributeRangeFilter( + Addressable.VirtualAddress, vaddr_start, vaddr_end ) else: - matching_resources = await resource.get_descendants( - r_filter=ResourceFilter( - attribute_filters=( - ResourceAttributeValueFilter(Addressable.VirtualAddress, vaddr_start), - ) - ), - r_sort=ResourceSort(Addressable.VirtualAddress), - ) + vaddr_filter = ResourceAttributeValueFilter(Addressable.VirtualAddress, vaddr_start) + matching_resources = await resource.get_descendants( + r_filter=ResourceFilter(attribute_filters=(vaddr_filter,)), + r_sort=ResourceSort(Addressable.VirtualAddress), + ) return web.json_response(list(map(self._serialize_resource, matching_resources))) + except NotFoundError: return web.json_response([]) diff --git a/frontend/src/SearchView.svelte b/frontend/src/SearchView.svelte index 923752ae4..8a4e82e12 100644 --- a/frontend/src/SearchView.svelte +++ b/frontend/src/SearchView.svelte @@ -64,38 +64,8 @@ input[type="checkbox"] { flex-grow: 0; margin-left: 1ch; - /* - appearance: none; - background-color: var(--main-bg-color); - margin: 0; - color: currentColor; - width: 1em; - height: 1em; - border: 1px solid currentColor; - display: grid; - place-content: center; - */ } - /* - input[type="checkbox"]:focus { - box-shadow: none; - } - - input[type="checkbox"]::before { - content: ""; - width: 0.45em; - height: 0.45em; - transform: scale(0); - transition: 120ms transform ease-in-out; - box-shadow: inset 1em 1em var(--main-fg-color); - } - - input[type="checkbox"]:checked::before { - transform: scale(1); - } - */ - label { margin-bottom: 1em; display: flex; @@ -179,6 +149,7 @@ } } } + async function leave_view() { if ($selectedResource !== undefined) { const ancestors = await $selectedResource.get_ancestors(null); From 9edb3e71bf4b9d7764ffca23ff822da7b095158a Mon Sep 17 00:00:00 2001 From: edward Date: Mon, 17 Oct 2022 16:49:42 -0400 Subject: [PATCH 12/13] add resource caption to SearchView as well as the unique ID --- frontend/src/SearchView.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/SearchView.svelte b/frontend/src/SearchView.svelte index 8a4e82e12..023421320 100644 --- a/frontend/src/SearchView.svelte +++ b/frontend/src/SearchView.svelte @@ -164,8 +164,8 @@

- Searching for descendants of {searchTarget.get_id()} whose virtual address - matches a specific address or lies in a range of addresses. + Searching for descendants of {searchTarget.get_caption()} ({searchTarget.get_id()}) whose + virtual address matches a specific address or lies in a range of addresses.

{#if rangeSearch}