summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrandon Miller <brandon@vector35.com>2024-11-22 08:20:14 -0500
committerBrandon Miller <brandon@vector35.com>2024-11-22 08:20:53 -0500
commitc74fa0d5f974efa4ccd569a821701956063a31e9 (patch)
treeed20c1e13e22df9c5d3867294654a28931f4fbe7
parent3617086969462676272786827089388047502883 (diff)
C++ and Python API for Firmware Ninja
-rw-r--r--binaryninjaapi.h123
-rw-r--r--binaryninjacore.h66
-rw-r--r--firmwareninja.cpp292
-rw-r--r--python/__init__.py1
-rw-r--r--python/firmwareninja.py468
-rw-r--r--python/variable.py4
6 files changed, 921 insertions, 33 deletions
diff --git a/binaryninjaapi.h b/binaryninjaapi.h
index 2de2aa63..762cd275 100644
--- a/binaryninjaapi.h
+++ b/binaryninjaapi.h
@@ -18369,6 +18369,129 @@ namespace BinaryNinja {
bool IsAborted();
};
+
+ /*!
+ \ingroup firmwareninja
+ */
+ struct FirmwareNinjaDevice
+ {
+ std::string name;
+ uint64_t start;
+ uint64_t end;
+ std::string info;
+ };
+
+ /*!
+ \ingroup firmwareninja
+ */
+ struct FirmwareNinjaFunctionMemoryAccesses
+ {
+ uint64_t start;
+ size_t count;
+ std::vector<BNFirmwareNinjaMemoryAccess> accesses;
+ };
+
+ /*!
+ \ingroup firmwareninja
+ */
+ struct FirmwareNinjaDeviceAccesses
+ {
+ std::string name;
+ size_t total;
+ size_t unique;
+ };
+
+ /*! FirmwareNinja is a class containing features specific to embedded firmware analysis. This class is only
+ available in the Ultimate Edition of Binary Ninja.
+
+ \ingroup firmwareninja
+ */
+ class FirmwareNinja
+ {
+ Ref<BinaryView> m_view;
+ BNFirmwareNinja* m_object;
+
+ public:
+ FirmwareNinja(Ref<BinaryView> view);
+ ~FirmwareNinja();
+
+ /*! Store a user-defined Firmware Ninja device to the binary view metadata
+
+ \param device Hardware device information
+ \return true on success, false otherwise
+ */
+ bool StoreCustomDevice(FirmwareNinjaDevice& device);
+
+ /*! Remove a user-defined Firmware Ninja device from the binary view metadata
+
+ \param name Name of the device to remove
+ \return true on success, false otherwise
+ */
+ bool RemoveCustomDevice(const std::string& name);
+
+ /*! Query all user-defined Firmware Ninja devices from the binary view metadata
+
+ \return Vector of user-defined Firmware Ninja devices
+ */
+ std::vector<FirmwareNinjaDevice> QueryCustomDevices();
+
+ /*! Query names of all boards that are compatable with the current binary view and contain bundled device
+ definitions
+
+ \return Vector of board names
+ */
+ std::vector<std::string> QueryBoardNames();
+
+ /*! Query Firmware Ninja device definitions for the specified board
+
+ \param board Name of the board to query devices for
+ \return Vector of Firmware Ninja device definitions
+ */
+ std::vector<FirmwareNinjaDevice> QueryDevicesForBoard(const std::string& board);
+
+ /*! Find sections in the binary with Firmware Ninja heuristics and entropy analysis
+
+ \param board highCodeEntropyThreshold High threshold for code entropy value range
+ \param board lowCodeEntropyThreshold Low threshold for code entropy value range
+ \param blockSize Size of blocks to analyze
+ \param mode Analysis mode of operation
+ \return Vector of Firmware Ninja section information
+ */
+ std::vector<BNFirmwareNinjaSection> FindSections(float highCodeEntropyThreshold, float lowCodeEntropyThreshold,
+ size_t blockSize, BNFirmwareNinjaSectionAnalysisMode mode);
+
+ /*! Find functions that access memory-mapped I/O and other non-file backed memory regions
+
+ \param progress Progress callback function
+ \param progressContext Progress context
+ \return Vector of Firmware Ninja function memory accesses information
+ */
+ std::vector<FirmwareNinjaFunctionMemoryAccesses> GetFunctionMemoryAccesses(BNProgressFunction progress,
+ void* progressContext);
+
+ /*! Store Firmware Ninja function memory accesses information in the binary view metadata
+
+ \param fma Vector of Firmware Ninja function memory accesses information
+ */
+ void StoreFunctionMemoryAccesses(const std::vector<FirmwareNinjaFunctionMemoryAccesses>& fma);
+
+ /*! Query cached Firmware Ninja function memory accesses information from the binary view metadata
+
+ \return Vector of Firmware Ninja memory analyis information
+ */
+ std::vector<FirmwareNinjaFunctionMemoryAccesses> QueryFunctionMemoryAccesses();
+
+ /*! Compute number of accesses mad to memory-mapped hardware devices for each board that is compatible with the
+ current architecture
+
+ \param fma Vector of Firmware Ninja function memory accesses information
+ \return Vector of Firmware Ninja device accesses information for each board
+ */
+ std::vector<FirmwareNinjaDeviceAccesses> GetBoardDeviceAccesses(
+ const std::vector<FirmwareNinjaFunctionMemoryAccesses>& fma);
+ };
+
+
/*!
\ingroup demangler
*/
diff --git a/binaryninjacore.h b/binaryninjacore.h
index ff1f3241..bb60b931 100644
--- a/binaryninjacore.h
+++ b/binaryninjacore.h
@@ -37,14 +37,14 @@
// Current ABI version for linking to the core. This is incremented any time
// there are changes to the API that affect linking, including new functions,
// new types, or modifications to existing functions or types.
-#define BN_CURRENT_CORE_ABI_VERSION 83
+#define BN_CURRENT_CORE_ABI_VERSION 84
// Minimum ABI version that is supported for loading of plugins. Plugins that
// are linked to an ABI version less than this will not be able to load and
// will require rebuilding. The minimum version is increased when there are
// incompatible changes that break binary compatibility, such as changes to
// existing types or functions.
-#define BN_MINIMUM_CORE_ABI_VERSION 81
+#define BN_MINIMUM_CORE_ABI_VERSION 82
#ifdef __GNUC__
#ifdef BINARYNINJACORE_LIBRARY
@@ -3497,37 +3497,38 @@ extern "C"
float entropy;
} BNFirmwareNinjaSection;
- typedef enum BNFirmwareNinjaMMIOHeuristic
+ typedef enum BNFirmwareNinjaMemoryHeuristic
{
- NoMMIOHeuristic,
- HasReadBarrierMMIOHeuristic,
- HasWriteBarrierMMIOHeuristic,
- StoreToOOBMemoryMMIOHeuristic,
- LoadFromOOBMemoryMMIOHeuristic,
- RepeatLoadStoreMMIOHeuristic,
- } BNFirmwareNinjaMMIOHeuristic;
+ NoMemoryHeuristic,
+ HasReadBarrierMemoryHeuristic,
+ HasWriteBarrierMemoryHeuristic,
+ StoreToOOBMemoryMemoryHeuristic,
+ LoadFromOOBMemoryMemoryHeuristic,
+ RepeatLoadStoreMemoryHeuristic,
+ } BNFirmwareNinjaMemoryHeuristic;
- typedef enum BNFirmwareNinjaMMIOAccessType
+ typedef enum BNFirmwareNinjaMemoryAccessType
{
- NoMMIOAccessType,
- ReadMMIOAccessType,
- WriteMMIOAccessType,
- } BNFirmwareNinjaMMIOAccessType;
+ NoMemoryAccessType,
+ ReadMemoryAccessType,
+ WriteMemoryAccessType,
+ } BNFirmwareNinjaMemoryAccessType;
- typedef struct BNFirmwareNinjaMMIOAccess
+ typedef struct BNFirmwareNinjaMemoryAccess
{
uint64_t instrAddress;
- BNRegisterValue reg;
- BNFirmwareNinjaMMIOHeuristic heuristic;
- BNFirmwareNinjaMMIOAccessType type;
- } BNFirmwareNinjaMMIOAccess;
+ BNRegisterValue memAddress;
+ BNFirmwareNinjaMemoryHeuristic heuristic;
+ BNFirmwareNinjaMemoryAccessType type;
+ BNRegisterValue value;
+ } BNFirmwareNinjaMemoryAccess;
- typedef struct BNFirmwareNinjaFunctionMMIOInfo
+ typedef struct BNFirmwareNinjaFunctionMemoryAccesses
{
uint64_t start;
size_t count;
- BNFirmwareNinjaMMIOAccess** accesses;
- } BNFirmwareNinjaFunctionMMIOInfo;
+ BNFirmwareNinjaMemoryAccess** accesses;
+ } BNFirmwareNinjaFunctionMemoryAccesses;
typedef struct BNFirmwareNinjaDeviceAccesses
{
@@ -8025,22 +8026,21 @@ extern "C"
// FirmwareNinja
BINARYNINJACOREAPI BNFirmwareNinja* BNCreateFirmwareNinja(BNBinaryView *view);
- BINARYNINJACOREAPI bool BNFirmwareNinjaAddCustomDevice(BNFirmwareNinja* fn, const char* name, uint64_t start, uint64_t end, const char* info);
- BINARYNINJACOREAPI bool BNFirmwareNinjaDeleteCustomDevice(BNFirmwareNinja* fn, const char* name);
+ BINARYNINJACOREAPI void BNFreeFirmwareNinja(BNFirmwareNinja* fn);
+ BINARYNINJACOREAPI bool BNFirmwareNinjaStoreCustomDevice(BNFirmwareNinja* fn, const char* name, uint64_t start, uint64_t end, const char* info);
+ BINARYNINJACOREAPI bool BNFirmwareNinjaRemoveCustomDevice(BNFirmwareNinja* fn, const char* name);
BINARYNINJACOREAPI int BNFirmwareNinjaQueryCustomDevices(BNFirmwareNinja* fn, BNFirmwareNinjaDevice** devices);
BINARYNINJACOREAPI void BNFirmwareNinjaFreeDevices(BNFirmwareNinjaDevice *devices, int size);
BINARYNINJACOREAPI int BNFirmwareNinjaQueryBoardNamesForArchitecture(BNFirmwareNinja* fn, BNArchitecture* arch, char ***boards);
BINARYNINJACOREAPI void BNFirmwareNinjaFreeBoardNames(char **boards, int size);
BINARYNINJACOREAPI int BNFirmwareNinjaQueryBoardDevices(BNFirmwareNinja* fn, BNArchitecture* arch, const char* board, BNFirmwareNinjaDevice** devices);
- BINARYNINJACOREAPI int BNFirmwareNinjaFindSectionsWithEntropy(BNFirmwareNinja* fn, BNFirmwareNinjaSection** sections,
- float highCodeEntropyThreshold, float lowCodeEntropyThreshold, size_t blockSize, BNFirmwareNinjaSectionAnalysisMode mode);
+ BINARYNINJACOREAPI int BNFirmwareNinjaFindSectionsWithEntropy(BNFirmwareNinja* fn, BNFirmwareNinjaSection** sections, float highCodeEntropyThreshold, float lowCodeEntropyThreshold, size_t blockSize, BNFirmwareNinjaSectionAnalysisMode mode);
BINARYNINJACOREAPI void BNFirmwareNinjaFreeSections(BNFirmwareNinjaSection *sections, int size);
- BINARYNINJACOREAPI int BNFirmwareNinjaFindMMIOAccesses(BNFirmwareNinja* fn, BNFirmwareNinjaFunctionMMIOInfo*** mmio, BNProgressFunction progress, void* progressContext);
- BINARYNINJACOREAPI void BNFirmwareNinjaFreeMMIOAccesses(BNFirmwareNinjaFunctionMMIOInfo **mmio, int size);
- BINARYNINJACOREAPI void BNFirmwareNinjaSaveMMIOAccessesToMetadata(BNFirmwareNinja* fn, BNFirmwareNinjaFunctionMMIOInfo** mmio, int size);
- BINARYNINJACOREAPI int BNFirmwareNinjaLoadMMIOAccessesFromMetadata(BNFirmwareNinja* fn, BNFirmwareNinjaFunctionMMIOInfo*** mmio);
- BINARYNINJACOREAPI int BNFirmwareNinjaGetBoardDeviceAccesses(BNFirmwareNinja* fn, BNFirmwareNinjaFunctionMMIOInfo** mmio, int size,
- BNFirmwareNinjaDeviceAccesses** accesses, BNArchitecture* arch);
+ BINARYNINJACOREAPI int BNFirmwareNinjaGetFunctionMemoryAccesses(BNFirmwareNinja* fn, BNFirmwareNinjaFunctionMemoryAccesses*** mmio, BNProgressFunction progress, void* progressContext);
+ BINARYNINJACOREAPI void BNFirmwareNinjaFreeFunctionMemoryAccesses(BNFirmwareNinjaFunctionMemoryAccesses **mmio, int size);
+ BINARYNINJACOREAPI void BNFirmwareNinjaStoreFunctionMemoryAccessesToMetadata(BNFirmwareNinja* fn, BNFirmwareNinjaFunctionMemoryAccesses** mmio, int size);
+ BINARYNINJACOREAPI int BNFirmwareNinjaQueryFunctionMemoryAccessesFromMetadata(BNFirmwareNinja* fn, BNFirmwareNinjaFunctionMemoryAccesses*** mmio);
+ BINARYNINJACOREAPI int BNFirmwareNinjaGetBoardDeviceAccesses(BNFirmwareNinja* fn, BNFirmwareNinjaFunctionMemoryAccesses** mmio, int size, BNFirmwareNinjaDeviceAccesses** accesses, BNArchitecture* arch);
BINARYNINJACOREAPI void BNFirmwareNinjaFreeBoardDeviceAccesses(BNFirmwareNinjaDeviceAccesses *accesses, int size);
#ifdef __cplusplus
}
diff --git a/firmwareninja.cpp b/firmwareninja.cpp
new file mode 100644
index 00000000..5bf623bd
--- /dev/null
+++ b/firmwareninja.cpp
@@ -0,0 +1,292 @@
+// Copyright (c) 2015-2024 Vector 35 Inc
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "binaryninjaapi.h"
+#include "binaryninjacore.h"
+
+using namespace BinaryNinja;
+
+
+static BNFirmwareNinjaFunctionMemoryAccesses** MemoryInfoVectorToArray(
+ const std::vector<FirmwareNinjaFunctionMemoryAccesses>& fma)
+{
+ BNFirmwareNinjaFunctionMemoryAccesses** result = new BNFirmwareNinjaFunctionMemoryAccesses*[fma.size()];
+ for (size_t i = 0; i < fma.size(); i++)
+ {
+ result[i] = new BNFirmwareNinjaFunctionMemoryAccesses;
+ result[i]->start = fma[i].start;
+ result[i]->count = fma[i].count;
+ result[i]->accesses = new BNFirmwareNinjaMemoryAccess*[fma[i].count];
+ for (size_t j = 0; j < fma[i].count; j++)
+ {
+ result[i]->accesses[j] = new BNFirmwareNinjaMemoryAccess;
+ std::memcpy(result[i]->accesses[j], &fma[i].accesses[j], sizeof(BNFirmwareNinjaMemoryAccess));
+ }
+ }
+
+ return result;
+}
+
+
+static void FreeMemoryInfoArray(BNFirmwareNinjaFunctionMemoryAccesses** fma, size_t count)
+{
+ for (size_t i = 0; i < count; i++)
+ {
+ for (size_t j = 0; j < fma[i]->count; j++)
+ delete fma[i]->accesses[j];
+
+ delete[] fma[i]->accesses;
+ delete fma[i];
+ }
+}
+
+
+FirmwareNinja::FirmwareNinja(Ref<BinaryView> view)
+{
+ m_view = view;
+ m_object = BNCreateFirmwareNinja(view->GetObject());
+}
+
+
+FirmwareNinja::~FirmwareNinja()
+{
+ BNFreeFirmwareNinja(m_object);
+}
+
+
+bool FirmwareNinja::StoreCustomDevice(FirmwareNinjaDevice& device)
+{
+ return BNFirmwareNinjaStoreCustomDevice(m_object, device.name.c_str(),
+ device.start, device.end, device.info.c_str());
+}
+
+
+bool FirmwareNinja::RemoveCustomDevice(const std::string& name)
+{
+ return BNFirmwareNinjaRemoveCustomDevice(m_object, name.c_str());
+}
+
+
+std::vector<FirmwareNinjaDevice> FirmwareNinja::QueryCustomDevices()
+{
+ std::vector<FirmwareNinjaDevice> result;
+ BNFirmwareNinjaDevice* devices;
+ int count = BNFirmwareNinjaQueryCustomDevices(m_object, &devices);
+ if (count <= 0)
+ return result;
+
+ result.reserve(count);
+ for (size_t i = 0; i < count; i++)
+ result.push_back({
+ devices[i].name,
+ devices[i].start,
+ devices[i].end,
+ devices[i].info
+ });
+
+ BNFirmwareNinjaFreeDevices(devices, count);
+ return result;
+}
+
+
+std::vector<std::string> FirmwareNinja::QueryBoardNames()
+{
+ std::vector<std::string> result;
+ char** boards;
+ auto platform = m_view->GetDefaultPlatform();
+ if (!platform)
+ return result;
+
+ auto arch = platform->GetArchitecture();
+ if (!arch)
+ return result;
+
+ int count = BNFirmwareNinjaQueryBoardNamesForArchitecture(m_object, arch->GetObject(), &boards);
+ if (count <= 0)
+ return result;
+
+ result.reserve(count);
+ for (size_t i = 0; i < count; i++)
+ result.push_back(boards[i]);
+
+ BNFirmwareNinjaFreeBoardNames(boards, count);
+ sort(result.begin(), result.end());
+ return result;
+}
+
+
+std::vector<FirmwareNinjaDevice> FirmwareNinja::QueryDevicesForBoard(const std::string& board)
+{
+ std::vector<FirmwareNinjaDevice> result;
+ BNFirmwareNinjaDevice* devices;
+ auto platform = m_view->GetDefaultPlatform();
+ if (!platform)
+ return result;
+
+ auto arch = platform->GetArchitecture();
+ if (!arch)
+ return result;
+
+ int count = BNFirmwareNinjaQueryBoardDevices(m_object, arch->GetObject(), board.c_str(), &devices);
+ if (count <= 0)
+ return result;
+
+ result.reserve(count);
+ for (size_t i = 0; i < count; i++)
+ result.push_back({
+ devices[i].name,
+ devices[i].start,
+ devices[i].end,
+ devices[i].info
+ });
+
+ BNFirmwareNinjaFreeDevices(devices, count);
+ return result;
+}
+
+
+std::vector<BNFirmwareNinjaSection> FirmwareNinja::FindSections(float highCodeEntropyThreshold,
+ float lowCodeEntropyThreshold, size_t blockSize, BNFirmwareNinjaSectionAnalysisMode mode)
+{
+ std::vector<BNFirmwareNinjaSection> result;
+ BNFirmwareNinjaSection* sections;
+ int count = BNFirmwareNinjaFindSectionsWithEntropy(m_object, &sections, highCodeEntropyThreshold,
+ lowCodeEntropyThreshold, blockSize, mode);
+ if (count <= 0)
+ return result;
+
+ result.reserve(count);
+ for (size_t i = 0; i < count; i++)
+ result.push_back(sections[i]);
+
+ BNFirmwareNinjaFreeSections(sections, count);
+ return result;
+}
+
+
+std::vector<FirmwareNinjaFunctionMemoryAccesses> FirmwareNinja::GetFunctionMemoryAccesses(BNProgressFunction progress,
+ void* progressContext)
+{
+ std::vector<FirmwareNinjaFunctionMemoryAccesses> result;
+ BNFirmwareNinjaFunctionMemoryAccesses** fma;
+ int count = BNFirmwareNinjaGetFunctionMemoryAccesses(m_object, &fma, progress, progressContext);
+ if (count <= 0)
+ return result;
+
+ result.reserve(count);
+ for (size_t i = 0; i < count; i++)
+ {
+ FirmwareNinjaFunctionMemoryAccesses info;
+ info.start = fma[i]->start;
+ info.count = fma[i]->count;
+ for (size_t j = 0; j < info.count; j++)
+ {
+ BNFirmwareNinjaMemoryAccess access;
+ std::memcpy(&access, fma[i]->accesses[j], sizeof(BNFirmwareNinjaMemoryAccess));
+ info.accesses.push_back(access);
+ }
+
+ result.push_back(info);
+ }
+
+ BNFirmwareNinjaFreeFunctionMemoryAccesses(fma, count);
+ std::sort(result.begin(), result.end(), [](const FirmwareNinjaFunctionMemoryAccesses& a,
+ const FirmwareNinjaFunctionMemoryAccesses& b) {
+ return a.count > b.count;
+ });
+
+ return result;
+}
+
+
+void FirmwareNinja::StoreFunctionMemoryAccesses(const std::vector<FirmwareNinjaFunctionMemoryAccesses>& fma)
+{
+ BNFirmwareNinjaFunctionMemoryAccesses** fmaArray = MemoryInfoVectorToArray(fma);
+ BNFirmwareNinjaStoreFunctionMemoryAccessesToMetadata(m_object, fmaArray, fma.size());
+ FreeMemoryInfoArray(fmaArray, fma.size());
+}
+
+
+std::vector<FirmwareNinjaFunctionMemoryAccesses> FirmwareNinja::QueryFunctionMemoryAccesses()
+{
+ std::vector<FirmwareNinjaFunctionMemoryAccesses> result;
+ BNFirmwareNinjaFunctionMemoryAccesses** fma;
+ int count = BNFirmwareNinjaQueryFunctionMemoryAccessesFromMetadata(m_object, &fma);
+ if (count <= 0)
+ return result;
+
+ result.reserve(count);
+ for (size_t i = 0; i < count; i++)
+ {
+ FirmwareNinjaFunctionMemoryAccesses info;
+ info.start = fma[i]->start;
+ info.count = fma[i]->count;
+ for (size_t j = 0; j < info.count; j++)
+ {
+ BNFirmwareNinjaMemoryAccess access;
+ std::memcpy(&access, fma[i]->accesses[j], sizeof(BNFirmwareNinjaMemoryAccess));
+ info.accesses.push_back(access);
+ }
+
+ result.push_back(info);
+ }
+
+ BNFirmwareNinjaFreeFunctionMemoryAccesses(fma, count);
+ std::sort(result.begin(), result.end(), [](const FirmwareNinjaFunctionMemoryAccesses& a,
+ const FirmwareNinjaFunctionMemoryAccesses& b) {
+ return a.count > b.count;
+ });
+
+ return result;
+}
+
+
+std::vector<FirmwareNinjaDeviceAccesses> FirmwareNinja::GetBoardDeviceAccesses(
+ const std::vector<FirmwareNinjaFunctionMemoryAccesses>& fma)
+{
+ std::vector<FirmwareNinjaDeviceAccesses> result;
+ auto platform = m_view->GetDefaultPlatform();
+ if (!platform)
+ return result;
+
+ auto arch = platform->GetArchitecture();
+ if (!arch)
+ return result;
+
+ BNFirmwareNinjaFunctionMemoryAccesses** fmaArray = MemoryInfoVectorToArray(fma);
+ BNFirmwareNinjaDeviceAccesses* accesses;
+ int count = BNFirmwareNinjaGetBoardDeviceAccesses(m_object, fmaArray, fma.size(), &accesses, arch->GetObject());
+ if (count <= 0)
+ {
+ FreeMemoryInfoArray(fmaArray, fma.size());
+ return result;
+ }
+
+ result.reserve(count);
+ for (size_t i = 0; i < count; i++)
+ result.push_back({accesses[i].name, accesses[i].total, accesses[i].unique});
+
+ BNFirmwareNinjaFreeBoardDeviceAccesses(accesses, count);
+ sort(result.begin(), result.end(), [](const FirmwareNinjaDeviceAccesses& a, const FirmwareNinjaDeviceAccesses& b) {
+ return a.total > b.total;
+ });
+
+ return result;
+}
diff --git a/python/__init__.py b/python/__init__.py
index b6a3de97..9b5a394e 100644
--- a/python/__init__.py
+++ b/python/__init__.py
@@ -96,6 +96,7 @@ warnings.filterwarnings('once', '', DeprecatedWarning)
# Only load Enterprise Client support on Ultimate builds
if core.BNGetProduct() == "Binary Ninja Enterprise Client" or core.BNGetProduct() == "Binary Ninja Ultimate":
from .enterprise import *
+ from .firmwareninja import *
def shutdown():
diff --git a/python/firmwareninja.py b/python/firmwareninja.py
new file mode 100644
index 00000000..b000ecc9
--- /dev/null
+++ b/python/firmwareninja.py
@@ -0,0 +1,468 @@
+# coding=utf-8
+# Copyright (c) 2015-2024 Vector 35 Inc
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+import ctypes
+from dataclasses import dataclass
+from typing import Callable
+from .binaryview import BinaryView
+from .variable import RegisterValue
+from .enums import (
+ FirmwareNinjaMemoryHeuristic,
+ FirmwareNinjaMemoryAccessType,
+ FirmwareNinjaSectionAnalysisMode,
+ FirmwareNinjaSectionType,
+)
+from .function import Function
+from . import _binaryninjacore as core
+
+
+@dataclass
+class FirmwareNinjaDevice:
+ """
+ ``class FirmwareNinjaDevice`` is a class that stores information about a hardware device, including the device
+ name, start address, size, and information about the device.
+ """
+
+ name: str
+ start: int
+ size: int
+ info: str
+
+
+@dataclass
+class FirmwareNinjaSection:
+ """
+ ``class FirmwareNinjaSection`` is a class that stores information about a section identified with Firmware Ninja
+ analysis, including the section type, start address, size, and entropy of the section.
+ """
+
+ type: FirmwareNinjaSectionType
+ start: int
+ size: int
+ entropy: float
+
+
+@dataclass
+class FirmwareNinjaMemoryAccess:
+ """
+ ``class FirmwareNinjaMemoryAccess`` is a class that stores information on instructions that access regions of
+ memory that are not file-backed, such as memory-mapped I/O and RAM.
+ """
+
+ instr_address: int
+ mem_address: RegisterValue
+ heuristic: FirmwareNinjaMemoryHeuristic
+ type: FirmwareNinjaMemoryAccessType
+ value: RegisterValue
+
+ @classmethod
+ def from_BNFirmwareNinjaMemoryAccess(cls, access: core.BNFirmwareNinjaMemoryAccess) -> "FirmwareNinjaMemoryAccess":
+ return cls(
+ instr_address=access.instrAddress,
+ mem_address=RegisterValue.from_BNRegisterValue(access.memAddress),
+ heuristic=FirmwareNinjaMemoryHeuristic(access.heuristic),
+ type=FirmwareNinjaMemoryAccessType(access.type),
+ value=RegisterValue.from_BNRegisterValue(access.value),
+ )
+
+ @classmethod
+ def to_BNFirmwareNinjaMemoryAccess(cls, access: "FirmwareNinjaMemoryAccess") -> core.BNFirmwareNinjaMemoryAccess:
+ return core.BNFirmwareNinjaMemoryAccess(
+ instrAddress=access.instr_address,
+ memAddress=RegisterValue.to_BNRegisterValue(access.mem_address),
+ heuristic=access.heuristic,
+ type=access.type,
+ value=RegisterValue.to_BNRegisterValue(access.value),
+ )
+
+
+@dataclass
+class FirmwareNinjaFunctionMemoryAccesses:
+ """
+ ``class FirmwareNinjaFunctionMemoryAccesses`` is a class that stores information on accesses made by a function
+ to memory regions that are not file-backed, such as memory-mapped I/O and RAM.
+ """
+
+ function: Function
+ accesses: list[FirmwareNinjaMemoryAccess]
+
+ @classmethod
+ def from_BNFirmwareNinjaFunctionMemoryAccesses(
+ cls,
+ info: core.BNFirmwareNinjaFunctionMemoryAccesses,
+ view: BinaryView,
+ ) -> "FirmwareNinjaFunctionMemoryAccesses":
+ accesses = []
+ for i in range(info.count):
+ access = info.accesses[i]
+ accesses.append(FirmwareNinjaMemoryAccess.from_BNFirmwareNinjaMemoryAccess(access.contents))
+
+ return cls(
+ function=view.get_function_at(info.start),
+ accesses=accesses,
+ )
+
+
+@dataclass
+class FirmwareNinjaDeviceAccesses:
+ """
+ ``class FirmwareNinjaDeviceAccesses`` is a class that stores information on the number of accesses to hardware
+ devices for each board that is compatible with the current architecture. This information can be used to identify
+ a board based on the number of accesses to hardware devices.
+ """
+
+ board_name: str
+ total: int
+ unique: int
+
+
+class FirmwareNinja:
+ """
+ ``class FirmwareNinja`` is a class that aids in analysis of embedded firmware images. This class is only available
+ in the Ultimate Edition of Binary Ninja.
+
+ :Example:
+
+ >>> from binaryninja import *
+ >>> view = load("path/to/firmware.bin", options={"loader.imageBase": 0x100000})
+ >>> fwn = FirmwareNinja(view)
+ >>> fwn.get_function_memory_accesses()[0].accesses[0].mem_address
+ <const ptr 0x40090028>
+ """
+
+ def __init__(self, view: BinaryView) -> None:
+ self._view = view
+ self._handle = core.BNCreateFirmwareNinja(view.handle)
+
+ def __del__(self):
+ if core is not None:
+ core.BNFreeFirmwareNinja(self._handle)
+
+ def store_custom_device(self, name: str, start: int, size: int, info: str) -> bool:
+ """
+ ``store_custom_device`` store a user-defined Firmware Ninja device in the binary view metadata
+
+ :param str name: Name of the device
+ :param int start: Start address of the device
+ :param int size: Size of the device memory region
+ :param str info: Information about the device
+ :return: True on success, False on failure
+ :rtype: bool
+ """
+
+ return core.BNFirmwareNinjaStoreCustomDevice(self._handle, name, start, start + size, info)
+
+ def remove_custom_device(self, name: str) -> bool:
+ """
+ ``remove_custom_device`` removes a user-defined Firmware Ninja device from the binary view metadata by device
+ name
+
+ :param str name: Name of the device
+ :return: True on success, False on failure
+ :rtype: bool
+ """
+
+ return core.BNFirmwareNinjaRemoveCustomDevice(self._handle, name)
+
+ def query_custom_devices(self) -> list[FirmwareNinjaDevice]:
+ """
+ ``query_custom_devices`` queries user-defined Firmware Ninja devices from the binary view metadata
+
+ :return: List of Firmware Ninja device objects
+ :rtype: list[FirmwareNinjaDevice]
+ """
+
+ devices = ctypes.POINTER(core.BNFirmwareNinjaDevice)()
+ count = core.BNFirmwareNinjaQueryCustomDevices(self._handle, ctypes.byref(devices))
+ if count == -1:
+ raise RuntimeError("BNFirmwareNinjaQueryCustomDevices")
+
+ try:
+ device_list = []
+ for i in range(count):
+ device_list.append(
+ FirmwareNinjaDevice(
+ name=devices[i].name,
+ start=devices[i].start,
+ size=devices[i].end - devices[i].start,
+ info=devices[i].info,
+ )
+ )
+
+ return device_list
+ finally:
+ core.BNFirmwareNinjaFreeDevices(devices, count)
+
+ def query_board_names(self) -> list[str]:
+ """
+ ``query_board_names`` queries the name of all boards that are compatible with the current architecture
+
+ :return: List of board names
+ :rtype: list[str]
+ """
+
+ boards = ctypes.POINTER(ctypes.c_char_p)()
+ count = core.BNFirmwareNinjaQueryBoardNamesForArchitecture(
+ self._handle, self._view.arch.handle, ctypes.byref(boards)
+ )
+ if count == -1:
+ raise RuntimeError("BNFirmwareNinjaQueryBoardNamesForArchitecture")
+
+ try:
+ board_list = []
+ for i in range(count):
+ board_list.append(boards[i].decode("utf-8"))
+
+ return board_list
+ finally:
+ core.BNFirmwareNinjaFreeBoardNames(boards, count)
+
+ def query_devices_by_board_name(self, name: str) -> list[FirmwareNinjaDevice]:
+ """
+ ``query_devices_by_board_name`` queries the hardware device information for a specific board
+
+ :Example:
+
+ >>> fwn = FirmwareNinja(bv)
+ >>> fwn.query_devices_by_board_name(fwn.query_board_names()[0])[0]
+ FirmwareNinjaDevice(name='nand@12f', start=303, size=1024, info='marvell,orion-nand')
+
+ :param str name: Name of the board
+ :return: List of Firmware Ninja device objects
+ :rtype: list[FirmwareNinjaDevice]
+ """
+
+ devices = ctypes.POINTER(core.BNFirmwareNinjaDevice)()
+ count = core.BNFirmwareNinjaQueryBoardDevices(self._handle, self._view.arch.handle, name, ctypes.byref(devices))
+ if count == -1:
+ raise RuntimeError("BNFirmwareNinjaQueryBoardDevices")
+
+ try:
+ device_list = []
+ for i in range(count):
+ device_list.append(
+ FirmwareNinjaDevice(
+ name=devices[i].name,
+ start=devices[i].start,
+ size=devices[i].end - devices[i].start,
+ info=devices[i].info,
+ )
+ )
+
+ return device_list
+ finally:
+ core.BNFirmwareNinjaFreeDevices(devices, count)
+
+ def find_sections(
+ self,
+ high_code_entropy_threshold: float = 0.910,
+ low_code_entropy_threshold: float = 0.500,
+ block_size: int = 4096,
+ mode: FirmwareNinjaSectionAnalysisMode = FirmwareNinjaSectionAnalysisMode.DetectStringsSectionAnalysisMode,
+ ) -> list[FirmwareNinjaSection]:
+ """
+ ``find_sections`` finds sections with Firmware Ninja entropy analysis and heuristics
+
+ :Example:
+
+ >>> fwn = FirmwareNinja(bv)
+ >>> fwn.find_sections(block_size=2048)[0].entropy
+ 0.48716872930526733
+ >>> fwn.find_sections(block_size=2048)[0].type
+ <FirmwareNinjaSectionType.DataSectionType: 1>
+
+ :param float high_code_entropy_threshold: High code entropy threshold
+ :param float low_code_entropy_threshold: Low code entropy threshold
+ :param int block_size: Block size
+ :param str mode: Analysis mode
+ :return: List of sections
+ :rtype: list[FirmwareNinjaSection]
+ """
+
+ sections = ctypes.POINTER(core.BNFirmwareNinjaSection)()
+ count = core.BNFirmwareNinjaFindSectionsWithEntropy(
+ self._handle,
+ ctypes.byref(sections),
+ high_code_entropy_threshold,
+ low_code_entropy_threshold,
+ block_size,
+ mode,
+ )
+ if count == -1:
+ raise RuntimeError("BNFirmwareNinjaFindSectionsWithEntropy")
+
+ try:
+ section_list = []
+ for i in range(count):
+ section_list.append(
+ FirmwareNinjaSection(
+ type=FirmwareNinjaSectionType(sections[i].type),
+ start=sections[i].start,
+ size=sections[i].end - sections[i].start,
+ entropy=sections[i].entropy,
+ )
+ )
+
+ return section_list
+ finally:
+ core.BNFirmwareNinjaFreeSections(sections, count)
+
+ def get_function_memory_accesses(self, progress_func: Callable = None) -> list[FirmwareNinjaFunctionMemoryAccesses]:
+ """
+ ``get_function_memory_accesses`` runs analysis to find accesses to memory regions that are not file-backed, such
+ as memory-mapped I/O and RAM.
+
+ :param callback progress_func: optional function to be called with the current progress and total count.
+ :return: List of function memory accesses objects
+ :rtype: list[FirmwareNinjaFunctionMemoryAccesses]
+ """
+
+ fma_info = ctypes.POINTER((ctypes.POINTER(core.BNFirmwareNinjaFunctionMemoryAccesses)))()
+ if progress_func is None:
+ progress_cfunc = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.c_ulonglong)(
+ lambda ctxt, cur, total: True
+ )
+ else:
+ progress_cfunc = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.c_ulonglong)(
+ lambda ctxt, cur, total: progress_func(cur, total)
+ )
+
+ count = core.BNFirmwareNinjaGetFunctionMemoryAccesses(
+ self._handle, ctypes.byref(fma_info), progress_cfunc, None
+ )
+ if count == -1:
+ raise RuntimeError("BNFirmwareNinjaGetFunctionMemoryAccesses")
+
+ try:
+ fma_info_list = []
+ for i in range(count):
+ fma_info_list.append(
+ FirmwareNinjaFunctionMemoryAccesses.from_BNFirmwareNinjaFunctionMemoryAccesses(
+ fma_info[i].contents, self._view
+ )
+ )
+
+ return fma_info_list
+ finally:
+ core.BNFirmwareNinjaFreeFunctionMemoryAccesses(fma_info, count)
+
+ def _fma_info_list_to_array(self, fma: list[FirmwareNinjaFunctionMemoryAccesses]) -> ctypes.POINTER:
+ fma_info_ptr_array = (ctypes.POINTER(core.BNFirmwareNinjaFunctionMemoryAccesses) * len(fma))()
+ for i, info in enumerate(fma):
+ accesses_ptr_array = (ctypes.POINTER(core.BNFirmwareNinjaMemoryAccess) * len(info.accesses))()
+ for j, access in enumerate(info.accesses):
+ accesses_ptr_array[j] = ctypes.pointer(FirmwareNinjaMemoryAccess.to_BNFirmwareNinjaMemoryAccess(access))
+
+ fma_info_struct = core.BNFirmwareNinjaFunctionMemoryAccesses(
+ function=info.function.handle,
+ accesses=accesses_ptr_array,
+ count=len(info.accesses),
+ )
+
+ fma_info_ptr_array[i] = ctypes.pointer(fma_info_struct)
+
+ return fma_info_ptr_array
+
+ def store_function_memory_accesses(self, fma: list[FirmwareNinjaFunctionMemoryAccesses]) -> None:
+ """
+ ``store_function_memory_accesses`` saves information on function memory accesses to binary view metadata
+
+ :Example:
+
+ >>> fwn = FirmwareNinja(bv)
+ >>> fma = fwn.get_function_memory_accesses()
+ >>> fwn.store_function_memory_accesses(fma)
+
+ :param list[FirmwareNinjaFunctionMemoryAccesses] fma: List of function memory accesses objects
+ :return: None
+ :rtype: None
+ """
+
+ fma_info_ptr_array = self._fma_info_list_to_array(fma)
+ core.BNFirmwareNinjaStoreFunctionMemoryAccessesToMetadata(self._handle, fma_info_ptr_array, len(fma))
+
+ def query_function_memory_accesses(self) -> list[FirmwareNinjaFunctionMemoryAccesses]:
+ """
+ ``query_function_memory_accesses`` queries information on function memory accesses from binary view metadata
+
+ :return: List of function memory accesses objects
+ :rtype: list[FirmwareNinjaFunctionMemoryAccesses]
+ """
+
+ fma = ctypes.POINTER((ctypes.POINTER(core.BNFirmwareNinjaFunctionMemoryAccesses)))()
+ count = core.BNFirmwareNinjaQueryFunctionMemoryAccessesFromMetadata(self._handle, ctypes.byref(fma))
+ if count == -1:
+ return None
+
+ try:
+ fma_info_list = []
+ for i in range(count):
+ fma_info_list.append(
+ FirmwareNinjaFunctionMemoryAccesses.from_BNFirmwareNinjaFunctionMemoryAccesses(
+ fma[i].contents, self._view
+ )
+ )
+
+ return fma_info_list
+ finally:
+ core.BNFirmwareNinjaFreeFunctionMemoryAccesses(fma, count)
+
+ def get_board_device_accesses(
+ self, fma: list[FirmwareNinjaFunctionMemoryAccesses]
+ ) -> list[FirmwareNinjaDeviceAccesses]:
+ """
+ ``get_board_device_accesses`` counts accesses made to memory-mapped hardware devices for each board that is
+ compatible with the current architecture. This function can be used to help identify a board.
+
+ :Example:
+
+ >>> fwn = FirmwareNinja(bv)
+ >>> fma = fwn.get_function_memory_accesses()
+ >>> fwn.get_board_device_accesses(fma)[0]
+ FirmwareNinjaDeviceAccesses(board_name='stm32mp157c-dhcom-picoitx', total=414, unique=2)
+
+ :param list[FirmwareNinjaFunctionMemoryAccesses] fma: List of function memory accesses objects
+ :return: List of device accesses objects
+ :rtype: list[FirmwareNinjaDeviceAccesses]
+ """
+
+ fma_info_ptr_array = self._fma_info_list_to_array(fma)
+ device_accesses = ctypes.POINTER(core.BNFirmwareNinjaDeviceAccesses)()
+ count = core.BNFirmwareNinjaGetBoardDeviceAccesses(
+ self._handle, fma_info_ptr_array, len(fma), ctypes.byref(device_accesses), self._view.arch.handle
+ )
+ if count == -1:
+ raise RuntimeError("BNFirmwareNinjaGetBoardDeviceAccesses")
+
+ try:
+ device_accesses_list = []
+ for i in range(count):
+ device_accesses_list.append(
+ FirmwareNinjaDeviceAccesses(
+ board_name=device_accesses[i].name,
+ total=device_accesses[i].total,
+ unique=device_accesses[i].unique,
+ )
+ )
+
+ return device_accesses_list
+ finally:
+ core.BNFirmwareNinjaFreeBoardDeviceAccesses(device_accesses, count)
diff --git a/python/variable.py b/python/variable.py
index 7b4121bf..ec8f050d 100644
--- a/python/variable.py
+++ b/python/variable.py
@@ -114,6 +114,10 @@ class RegisterValue:
return ConstantDataRegisterValue(reg_value.value, 0, RegisterValueType(reg_value.state), confidence=confidence, size=reg_value.size)
assert False, f"RegisterValueType {reg_value.state} not handled"
+ @classmethod
+ def to_BNRegisterValue(cls, reg_value: 'RegisterValue') -> core.BNRegisterValue:
+ return reg_value._to_core_struct()
+
@dataclass(frozen=True, eq=False)
class Undetermined(RegisterValue):