forked from Keelrleek/Harbor
Add shared reverse-engineering lookups (re_disassemble, re_xrefs, re_bytes, re_lookup)
Read the project's flat image, facts.jsonl and annotations with the team's current names; exposed as MCP tools and as 'harbor re'. Configured by a [reverse] table in the project file.
This commit is contained in:
1 parent
dbc0c45ef2
commit
3f6df88f95
5 files changed
+410
-2
No files matched your search
@@ -0,0 +1,59 @@
|
||||
"""The shared reverse-engineering lookups, against a small synthetic image and annotation set."""
|
||||
import json
|
||||
import shutil
|
||||
import struct
|
||||
import unittest
|
||||
|
||||
from helpers import temp_dir
|
||||
from harbor.reverse import Reverse
|
||||
from harbor.util import HarborError
|
||||
|
||||
|
||||
class ReverseTools(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.d = temp_dir()
|
||||
base = 0x401000
|
||||
image = bytearray(0x100)
|
||||
# 0x401000: mov ecx,[0x401080]; call 0x401040; fmul dword [0x401090]; ret
|
||||
code = bytes.fromhex("8b0d80104000") + b"\xe8" + struct.pack("<i", 0x401040 - 0x40100B) \
|
||||
+ bytes.fromhex("d80d90104000") + b"\xc3"
|
||||
image[0:len(code)] = code
|
||||
image[0x40] = 0xC3 # 0x401040: ret
|
||||
image[0x90:0x94] = struct.pack("<f", 100.0) # 0x401090: 100.0f
|
||||
image[0xA0:0xA5] = b".cue\0"
|
||||
(self.d / "image.bin").write_bytes(bytes(image))
|
||||
facts = [{"address": "0x401000", "size": len(code), "name": "FUN_00401000", "calls": ["0x401040"],
|
||||
"data": ["0x401080"], "imports": [], "strings": []},
|
||||
{"address": "0x401040", "size": 1, "name": "FUN_00401040", "calls": [], "data": [],
|
||||
"imports": [], "strings": []}]
|
||||
(self.d / "facts.jsonl").write_text("\n".join(json.dumps(f) for f in facts), encoding="utf-8")
|
||||
ann = self.d / "annotations"
|
||||
ann.mkdir()
|
||||
(ann / "names.csv").write_text("address,name,category,note\n0x00401000,GetLevel,game,\"Reads it.\"\n"
|
||||
"0x00401040,Helper,game,x\n", encoding="utf-8")
|
||||
(ann / "globals.csv").write_text("address,name,type,note\n0x00401080,g_root,int,x\n", encoding="utf-8")
|
||||
(ann / "functions.csv").write_text("address,signature,note\n", encoding="utf-8")
|
||||
(ann / "prototypes.h").write_text("int GetLevel(void); /* plain ret */\n", encoding="utf-8")
|
||||
self.r = Reverse({"image": str(self.d / "image.bin"), "base": "0x401000", "facts": str(self.d / "facts.jsonl"),
|
||||
"annotations": str(ann)})
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.d, ignore_errors=True)
|
||||
|
||||
def test_disassembly_names_calls_globals_and_constants(self):
|
||||
text = self.r.disassemble("GetLevel")
|
||||
self.assertIn("0x401080 g_root", text)
|
||||
self.assertIn("0x401040 Helper", text)
|
||||
self.assertIn("= 100.0f", text)
|
||||
|
||||
def test_xrefs_bytes_and_lookup(self):
|
||||
x = self.r.xrefs("0x401040")
|
||||
self.assertEqual([c["name"] for c in x["callers"]], ["GetLevel"])
|
||||
self.assertEqual(self.r.bytes_at("0x401090", 4, "float")["float"], ["100.0"])
|
||||
self.assertEqual(self.r.bytes_at("0x4010A0", as_type="string")["string"], ".cue")
|
||||
info = self.r.lookup("GetLevel")
|
||||
self.assertIn("plain ret", info["prototype"])
|
||||
with self.assertRaises(HarborError):
|
||||
self.r.bytes_at("0x500000")
|
||||
with self.assertRaises(HarborError):
|
||||
Reverse({})
|
||||
Reference in new issue
Block a user