Skip to content

Commit 5037372

Browse files
committed
Allow opening databases without loading a file
1 parent 8884749 commit 5037372

6 files changed

Lines changed: 59 additions & 0 deletions

File tree

binaryninjaapi.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3384,6 +3384,8 @@ namespace BinaryNinja {
33843384
public:
33853385
Database(BNDatabase* database);
33863386

3387+
static Ref<Database> OpenExisting(const std::string& path);
3388+
33873389
bool SnapshotHasData(int64_t id);
33883390
Ref<Snapshot> GetSnapshot(int64_t id);
33893391
std::vector<Ref<Snapshot>> GetSnapshots();

binaryninjacore.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4639,6 +4639,8 @@ extern "C"
46394639
// Database object
46404640
BINARYNINJACOREAPI BNDatabase* BNNewDatabaseReference(BNDatabase* database);
46414641
BINARYNINJACOREAPI void BNFreeDatabase(BNDatabase* database);
4642+
BINARYNINJACOREAPI BNDatabase* BNCreateDatabaseInstance(void);
4643+
BINARYNINJACOREAPI bool BNDatabaseOpenExisting(BNDatabase* database, const char* path);
46424644
BINARYNINJACOREAPI void BNSetDatabaseCurrentSnapshot(BNDatabase* database, int64_t id);
46434645
BINARYNINJACOREAPI BNSnapshot* BNGetDatabaseCurrentSnapshot(BNDatabase* database);
46444646
BINARYNINJACOREAPI BNSnapshot** BNGetDatabaseSnapshots(BNDatabase* database, size_t* count);

database.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,15 @@ Database::Database(BNDatabase* database)
380380
}
381381

382382

383+
Ref<Database> Database::OpenExisting(const std::string& path)
384+
{
385+
Ref<Database> db = new Database(BNCreateDatabaseInstance());
386+
if (!BNDatabaseOpenExisting(db->GetObject(), path.c_str()))
387+
throw DatabaseException("BNDatabaseOpenExisting");
388+
return db;
389+
}
390+
391+
383392
Ref<Snapshot> Database::GetSnapshot(int64_t id)
384393
{
385394
BNSnapshot* snap = BNGetDatabaseSnapshot(m_object, id);

python/database.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,18 @@ def __init__(self, handle):
246246
def __del__(self):
247247
core.BNFreeDatabase(self.handle)
248248

249+
@classmethod
250+
def open_existing(cls, path: str) -> 'Database':
251+
"""
252+
Open a Database from a file
253+
:param path: Path to file containing database
254+
:return: Database instance
255+
"""
256+
db = Database(handle=core.BNCreateDatabaseInstance())
257+
if not core.BNDatabaseOpenExisting(db.handle, path):
258+
raise RuntimeError("BNDatabaseOpenExisting returned False")
259+
return db
260+
249261
def __getitem__(self, item: int) -> Optional[Snapshot]:
250262
return self.get_snapshot(item)
251263

rust/src/database.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use binaryninjacore_sys::*;
66
use std::collections::HashMap;
77
use std::ffi::c_void;
88
use std::fmt::Debug;
9+
use std::path::Path;
910
use std::ptr::NonNull;
1011

1112
use crate::binary_view::BinaryView;
@@ -29,6 +30,17 @@ impl Database {
2930
Ref::new(Self { handle })
3031
}
3132

33+
/// Open a database with the given file path
34+
pub fn open_existing(path: impl AsRef<Path>) -> Result<Ref<Self>, ()> {
35+
let db = unsafe { Self::ref_from_raw(NonNull::new(BNCreateDatabaseInstance()).ok_or(())?) };
36+
let path_raw = path.as_ref().to_cstr();
37+
if unsafe { BNDatabaseOpenExisting(db.handle.as_ptr(), path_raw.as_ptr()) } {
38+
Ok(db)
39+
} else {
40+
Err(())
41+
}
42+
}
43+
3244
/// Get a [`Snapshot`] by its `id`, or `None` if no snapshot with that `id` exists.
3345
pub fn snapshot_by_id(&self, id: SnapshotId) -> Option<Ref<Snapshot>> {
3446
let result = unsafe { BNGetDatabaseSnapshot(self.handle.as_ptr(), id.0) };

rust/tests/database.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
use binaryninja::database::Database;
2+
use binaryninja::file_metadata::SaveSettings;
3+
use binaryninja::headless::Session;
4+
use std::path::PathBuf;
5+
6+
#[test]
7+
fn test_open_existing() {
8+
let _session = Session::new().expect("Failed to initialize session");
9+
let out_dir = env!("OUT_DIR").parse::<PathBuf>().unwrap();
10+
let view = binaryninja::load(out_dir.join("atox.obj")).expect("Failed to create view");
11+
// Save the modified database.
12+
let temp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
13+
let temp_path = temp_dir.path().join("atox.obj.bndb");
14+
assert!(view
15+
.file()
16+
.create_database(&temp_path, &SaveSettings::new()));
17+
// Verify that the file exists and is modified.
18+
drop(view);
19+
let db = Database::open_existing(&temp_path).unwrap();
20+
// Make sure the database has data
21+
assert!(db.snapshots().len() > 0);
22+
}

0 commit comments

Comments
 (0)