summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--binaryninjaapi.h80
-rw-r--r--binaryninjacore.h81
-rw-r--r--filemetadata.cpp15
-rw-r--r--python/filemetadata.py9
-rw-r--r--python/transform.py31
-rw-r--r--transform.cpp46
-rw-r--r--transformcontext.cpp186
-rw-r--r--transformsession.cpp150
-rw-r--r--ui/containerbrowser.h106
-rw-r--r--ui/options.h2
-rw-r--r--ui/uitypes.h2
-rw-r--r--view/kernelcache/core/KernelCacheView.cpp149
-rw-r--r--view/kernelcache/core/transformers/KernelCacheTransforms.cpp210
13 files changed, 895 insertions, 172 deletions
diff --git a/binaryninjaapi.h b/binaryninjaapi.h
index fd926ebd..9da9f677 100644
--- a/binaryninjaapi.h
+++ b/binaryninjaapi.h
@@ -3816,6 +3816,18 @@ namespace BinaryNinja {
*/
void SetFilename(const std::string& name);
+ /*! Get the path to the container file if the current file is inside a container (e.g. ZIP, TAR, etc.)
+
+ \return The path to the container file if the current file is inside a container, otherwise an empty string
+ */
+ std::string GetVirtualPath() const;
+
+ /*! Set the path to the container file if the current file is inside a container (e.g. ZIP, TAR, etc.)
+
+ \param path The path to the container file if the current file is inside a container
+ */
+ void SetVirtualPath(const std::string& path);
+
/*! Whether the file has unsaved modifications
\return Whether the file has unsaved modifications
@@ -8947,6 +8959,7 @@ namespace BinaryNinja {
{
protected:
BNTransformType m_typeForRegister;
+ BNTransformCapabilities m_capabilitiesForRegister;
std::string m_nameForRegister, m_longNameForRegister, m_groupForRegister;
Transform(BNTransform* xform);
@@ -8957,6 +8970,7 @@ namespace BinaryNinja {
void* ctxt, BNDataBuffer* input, BNDataBuffer* output, BNTransformParameter* params, size_t paramCount);
static bool EncodeCallback(
void* ctxt, BNDataBuffer* input, BNDataBuffer* output, BNTransformParameter* params, size_t paramCount);
+ static bool CanDecodeCallback(void* ctxt, BNBinaryView* input);
static std::vector<TransformParameter> EncryptionKeyParameters(size_t fixedKeyLength = 0);
static std::vector<TransformParameter> EncryptionKeyAndIVParameters(
@@ -8964,22 +8978,24 @@ namespace BinaryNinja {
public:
Transform(BNTransformType type, const std::string& name, const std::string& longName, const std::string& group);
+ Transform(BNTransformType type, BNTransformCapabilities capabilities, const std::string& name, const std::string& longName, const std::string& group);
static void Register(Transform* xform);
static Ref<Transform> GetByName(const std::string& name);
static std::vector<Ref<Transform>> GetTransformTypes();
BNTransformType GetType() const;
+ BNTransformCapabilities GetCapabilities() const;
+ bool SupportsDetection() const;
std::string GetName() const;
std::string GetLongName() const;
std::string GetGroup() const;
virtual std::vector<TransformParameter> GetParameters() const;
- virtual bool Decode(const DataBuffer& input, DataBuffer& output,
- const std::map<std::string, DataBuffer>& params = std::map<std::string, DataBuffer>());
- virtual bool Encode(const DataBuffer& input, DataBuffer& output,
- const std::map<std::string, DataBuffer>& params = std::map<std::string, DataBuffer>());
+ virtual bool Decode(const DataBuffer& input, DataBuffer& output, const std::map<std::string, DataBuffer>& params = std::map<std::string, DataBuffer>());
+ virtual bool Encode(const DataBuffer& input, DataBuffer& output, const std::map<std::string, DataBuffer>& params = std::map<std::string, DataBuffer>());
+ virtual bool CanDecode(Ref<BinaryView> input) const;
};
/*!
@@ -8995,8 +9011,64 @@ namespace BinaryNinja {
const std::map<std::string, DataBuffer>& params = std::map<std::string, DataBuffer>()) override;
virtual bool Encode(const DataBuffer& input, DataBuffer& output,
const std::map<std::string, DataBuffer>& params = std::map<std::string, DataBuffer>()) override;
+ virtual bool CanDecode(Ref<BinaryView> input) const override;
};
+ class TransformContext : public CoreRefCountObject<BNTransformContext, BNNewTransformContextReference, BNFreeTransformContext>
+ {
+ public:
+ TransformContext(BNTransformContext* context);
+ virtual ~TransformContext();
+
+ std::string GetTransformName() const;
+ std::string GetFileName() const;
+ Ref<BinaryView> GetInput() const;
+ Ref<Metadata> GetMetadata() const;
+ Ref<TransformContext> GetParent() const;
+ size_t GetChildCount() const;
+ std::vector<Ref<TransformContext>> GetChildren() const;
+ Ref<TransformContext> GetChild(const std::string& filename) const;
+ Ref<TransformContext> CreateChild(const DataBuffer& data, const std::string& filename);
+ bool IsLeaf() const;
+ bool IsRoot() const;
+ std::vector<std::string> GetAvailableFiles() const;
+ void SetAvailableFiles(const std::vector<std::string>& files);
+ bool HasAvailableFiles() const;
+ std::vector<std::string> GetRequestedFiles() const;
+ void SetRequestedFiles(const std::vector<std::string>& files);
+ bool HasRequestedFiles() const;
+ bool IsDatabase() const;
+ };
+
+ class TransformSession : public CoreRefCountObject<BNTransformSession, BNNewTransformSessionReference, BNFreeTransformSession>
+ {
+ public:
+ TransformSession(const std::string& filename);
+ TransformSession(const std::string& filename, BNTransformSessionMode mode);
+ TransformSession(Ref<BinaryView> initialView);
+ TransformSession(Ref<BinaryView> initialView, BNTransformSessionMode mode);
+ TransformSession(BNTransformSession* session);
+ virtual ~TransformSession();
+
+ Ref<BinaryView> GetCurrentView() const;
+ Ref<TransformContext> GetRootContext() const;
+ Ref<TransformContext> GetCurrentContext() const;
+ bool Process();
+ bool HasAnyStages() const;
+ bool HasSinglePath() const;
+
+ std::vector<Ref<TransformContext>> GetSelectedContexts() const;
+ void SetSelectedContexts(const std::vector<Ref<TransformContext>>& contexts);
+
+ // UI interaction support
+ bool RequiresUserInput() const;
+ bool HasMultipleFileChoices() const;
+ std::vector<std::string> GetAvailableFileChoices() const;
+ bool SelectFiles(const std::vector<std::string>& selectedFiles);
+ bool ProcessWithUserInput();
+ };
+
+
struct InstructionInfo : public BNInstructionInfo
{
InstructionInfo();
diff --git a/binaryninjacore.h b/binaryninjacore.h
index 3c2386cd..6299618d 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 132
+#define BN_CURRENT_CORE_ABI_VERSION 133
// 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 131
+#define BN_MINIMUM_CORE_ABI_VERSION 133
#ifdef __GNUC__
#ifdef BINARYNINJACORE_LIBRARY
@@ -205,6 +205,8 @@ extern "C"
typedef struct BNDatabase BNDatabase;
typedef struct BNFileMetadata BNFileMetadata;
typedef struct BNTransform BNTransform;
+ typedef struct BNTransformContext BNTransformContext;
+ typedef struct BNTransformSession BNTransformSession;
typedef struct BNArchitecture BNArchitecture;
typedef struct BNFunction BNFunction;
typedef struct BNBasicBlock BNBasicBlock;
@@ -348,6 +350,19 @@ extern "C"
HashTransform = 8 // Hash function
} BNTransformType;
+ typedef enum BNTransformCapabilities
+ {
+ TransformNoCapabilities = 0,
+ TransformSupportsDetection = 1
+ } BNTransformCapabilities;
+
+ typedef enum BNTransformSessionMode
+ {
+ TransformSessionModeDisabled = 0, // Open the root file as-is (no unwrapping)
+ TransformSessionModeFull = 1, // Discover all paths (build the full context tree)
+ TransformSessionModeOnDemand = 2, // Proceed step-by-step, requesting input at each stage
+ } BNTransformSessionMode;
+
typedef enum BNBranchType
{
UnconditionalBranch = 0,
@@ -1753,10 +1768,9 @@ extern "C"
void* context;
BNTransformParameterInfo* (*getParameters)(void* ctxt, size_t* count);
void (*freeParameters)(BNTransformParameterInfo* params, size_t count);
- bool (*decode)(
- void* ctxt, BNDataBuffer* input, BNDataBuffer* output, BNTransformParameter* params, size_t paramCount);
- bool (*encode)(
- void* ctxt, BNDataBuffer* input, BNDataBuffer* output, BNTransformParameter* params, size_t paramCount);
+ bool (*decode)(void* ctxt, BNDataBuffer* input, BNDataBuffer* output, BNTransformParameter* params, size_t paramCount);
+ bool (*encode)(void* ctxt, BNDataBuffer* input, BNDataBuffer* output, BNTransformParameter* params, size_t paramCount);
+ bool (*canDecode)(void* ctxt, BNBinaryView* input);
} BNCustomTransform;
typedef struct BNInstructionInfo
@@ -3802,6 +3816,7 @@ extern "C"
BINARYNINJACOREAPI void BNSetLicense(const char* licenseData);
BINARYNINJACOREAPI bool BNIsDatabase(const char* filename);
+ BINARYNINJACOREAPI bool BNIsDatabaseFromData(const void* data, size_t len);
BINARYNINJACOREAPI bool BNAuthenticateEnterpriseServerWithCredentials(
const char* username, const char* password, bool remember);
@@ -4233,6 +4248,9 @@ extern "C"
BINARYNINJACOREAPI char* BNGetFilename(BNFileMetadata* file);
BINARYNINJACOREAPI void BNSetFilename(BNFileMetadata* file, const char* name);
+ BINARYNINJACOREAPI char* BNGetVirtualPath(BNFileMetadata* file);
+ BINARYNINJACOREAPI void BNSetVirtualPath(BNFileMetadata* file, const char* path);
+
BINARYNINJACOREAPI BNProjectFile* BNGetProjectFile(BNFileMetadata* file);
BINARYNINJACOREAPI void BNSetProjectFile(BNFileMetadata* file, BNProjectFile* pfile);
@@ -4610,10 +4628,12 @@ extern "C"
BINARYNINJACOREAPI BNTransform* BNGetTransformByName(const char* name);
BINARYNINJACOREAPI BNTransform** BNGetTransformTypeList(size_t* count);
BINARYNINJACOREAPI void BNFreeTransformTypeList(BNTransform** xforms);
- BINARYNINJACOREAPI BNTransform* BNRegisterTransformType(
- BNTransformType type, const char* name, const char* longName, const char* group, BNCustomTransform* xform);
+ BINARYNINJACOREAPI BNTransform* BNRegisterTransformType(BNTransformType type, const char* name, const char* longName, const char* group, BNCustomTransform* xform);
+ BINARYNINJACOREAPI BNTransform* BNRegisterTransformTypeWithCapabilities(BNTransformType type, uint32_t capabilities, const char* name, const char* longName, const char* group, BNCustomTransform* xform);
BINARYNINJACOREAPI BNTransformType BNGetTransformType(BNTransform* xform);
+ BINARYNINJACOREAPI uint32_t BNGetTransformCapabilities(BNTransform* xform);
+ BINARYNINJACOREAPI bool BNTransformSupportsDetection(BNTransform* xform);
BINARYNINJACOREAPI char* BNGetTransformName(BNTransform* xform);
BINARYNINJACOREAPI char* BNGetTransformLongName(BNTransform* xform);
BINARYNINJACOREAPI char* BNGetTransformGroup(BNTransform* xform);
@@ -4623,6 +4643,51 @@ extern "C"
BNTransform* xform, BNDataBuffer* input, BNDataBuffer* output, BNTransformParameter* params, size_t paramCount);
BINARYNINJACOREAPI bool BNEncode(
BNTransform* xform, BNDataBuffer* input, BNDataBuffer* output, BNTransformParameter* params, size_t paramCount);
+ BINARYNINJACOREAPI bool BNCanDecode(BNTransform* xform, BNBinaryView* input);
+
+ // Transform Context
+ BINARYNINJACOREAPI BNTransformContext* BNNewTransformContextReference(BNTransformContext* context);
+ BINARYNINJACOREAPI void BNFreeTransformContext(BNTransformContext* context);
+ BINARYNINJACOREAPI char* BNTransformContextGetTransformName(BNTransformContext* context);
+ BINARYNINJACOREAPI char* BNTransformContextGetFileName(BNTransformContext* context);
+ BINARYNINJACOREAPI BNBinaryView* BNTransformContextGetInput(BNTransformContext* context);
+ BINARYNINJACOREAPI BNMetadata* BNTransformContextGetMetadata(BNTransformContext* context);
+ BINARYNINJACOREAPI BNTransformContext* BNTransformContextGetParent(BNTransformContext* context);
+ BINARYNINJACOREAPI size_t BNTransformContextGetChildCount(BNTransformContext* context);
+ BINARYNINJACOREAPI BNTransformContext** BNTransformContextGetChildren(BNTransformContext* context, size_t* count);
+ BINARYNINJACOREAPI void BNFreeTransformContextList(BNTransformContext** contexts, size_t count);
+ BINARYNINJACOREAPI BNTransformContext* BNTransformContextGetChild(BNTransformContext* context, const char* filename);
+ BINARYNINJACOREAPI BNTransformContext* BNTransformContextCreateChild(BNTransformContext* context, BNDataBuffer* data, const char* filename);
+ BINARYNINJACOREAPI bool BNTransformContextIsLeaf(BNTransformContext* context);
+ BINARYNINJACOREAPI bool BNTransformContextIsRoot(BNTransformContext* context);
+ BINARYNINJACOREAPI char** BNTransformContextGetAvailableFiles(BNTransformContext* context, size_t* count);
+ BINARYNINJACOREAPI void BNTransformContextSetAvailableFiles(BNTransformContext* context, const char** files, size_t count);
+ BINARYNINJACOREAPI bool BNTransformContextHasAvailableFiles(BNTransformContext* context);
+ BINARYNINJACOREAPI char** BNTransformContextGetRequestedFiles(BNTransformContext* context, size_t* count);
+ BINARYNINJACOREAPI void BNTransformContextSetRequestedFiles(BNTransformContext* context, const char** files, size_t count);
+ BINARYNINJACOREAPI bool BNTransformContextHasRequestedFiles(BNTransformContext* context);
+ BINARYNINJACOREAPI bool BNTransformContextIsDatabase(BNTransformContext* context);
+
+ // Transform Session
+ BINARYNINJACOREAPI BNTransformSession* BNCreateTransformSession(const char* filename);
+ BINARYNINJACOREAPI BNTransformSession* BNCreateTransformSessionWithMode(const char* filename, BNTransformSessionMode mode);
+ BINARYNINJACOREAPI BNTransformSession* BNCreateTransformSessionFromBinaryView(BNBinaryView* initialView);
+ BINARYNINJACOREAPI BNTransformSession* BNCreateTransformSessionFromBinaryViewWithMode(BNBinaryView* initialView, BNTransformSessionMode mode);
+ BINARYNINJACOREAPI BNTransformSession* BNNewTransformSessionReference(BNTransformSession* session);
+ BINARYNINJACOREAPI void BNFreeTransformSession(BNTransformSession* session);
+ BINARYNINJACOREAPI BNBinaryView* BNTransformSessionGetCurrentView(BNTransformSession* session);
+ BINARYNINJACOREAPI BNTransformContext* BNTransformSessionGetRootContext(BNTransformSession* session);
+ BINARYNINJACOREAPI BNTransformContext* BNTransformSessionGetCurrentContext(BNTransformSession* session);
+ BINARYNINJACOREAPI bool BNTransformSessionProcess(BNTransformSession* session);
+ BINARYNINJACOREAPI bool BNTransformSessionHasAnyStages(BNTransformSession* session);
+ BINARYNINJACOREAPI bool BNTransformSessionHasSinglePath(BNTransformSession* session);
+ BINARYNINJACOREAPI BNTransformContext** BNTransformSessionGetSelectedContexts(BNTransformSession* session, size_t* count);
+ BINARYNINJACOREAPI void BNTransformSessionSetSelectedContexts(BNTransformSession* session, BNTransformContext** contexts, size_t count);
+ BINARYNINJACOREAPI bool BNTransformSessionRequiresUserInput(BNTransformSession* session);
+ BINARYNINJACOREAPI bool BNTransformSessionHasMultipleFileChoices(BNTransformSession* session);
+ BINARYNINJACOREAPI char** BNTransformSessionGetAvailableFileChoices(BNTransformSession* session, size_t* count);
+ BINARYNINJACOREAPI bool BNTransformSessionSelectFiles(BNTransformSession* session, const char** files, size_t count);
+ BINARYNINJACOREAPI bool BNTransformSessionProcessWithUserInput(BNTransformSession* session);
// Architectures
BINARYNINJACOREAPI BNArchitecture* BNGetArchitectureByName(const char* name);
diff --git a/filemetadata.cpp b/filemetadata.cpp
index e45354c7..0ed8da22 100644
--- a/filemetadata.cpp
+++ b/filemetadata.cpp
@@ -129,6 +129,21 @@ void FileMetadata::SetFilename(const string& name)
}
+string FileMetadata::GetVirtualPath() const
+{
+ char* str = BNGetVirtualPath(m_object);
+ string result = str;
+ BNFreeString(str);
+ return result;
+}
+
+
+void FileMetadata::SetVirtualPath(const string& path)
+{
+ BNSetVirtualPath(m_object, path.c_str());
+}
+
+
bool FileMetadata::IsModified() const
{
return BNIsFileModified(m_object);
diff --git a/python/filemetadata.py b/python/filemetadata.py
index fd1fc328..1aae3139 100644
--- a/python/filemetadata.py
+++ b/python/filemetadata.py
@@ -209,6 +209,15 @@ class FileMetadata:
core.BNSetFilename(self.handle, str(value))
@property
+ def virtual_path(self) -> str:
+ """The virtual path of the file including container and internal path (e.g., 'archive.zip:folder/file.bin') (read/write)"""
+ return core.BNGetVirtualPath(self.handle)
+
+ @virtual_path.setter
+ def virtual_path(self, value: str) -> None:
+ core.BNSetVirtualPath(self.handle, str(value))
+
+ @property
def modified(self) -> bool:
"""Boolean result of whether the file is modified (Inverse of 'saved' property) (read/write)"""
return core.BNIsFileModified(self.handle)
diff --git a/python/transform.py b/python/transform.py
index 54641f91..3ef5c4b6 100644
--- a/python/transform.py
+++ b/python/transform.py
@@ -26,6 +26,7 @@ import abc
import binaryninja
from .log import log_error_for_exception
from . import databuffer
+from . import binaryview
from . import _binaryninjacore as core
from .enums import TransformType
@@ -99,6 +100,8 @@ class Transform(metaclass=_TransformMetaClass):
b'173d3b2c2c373923720f242d39'
"""
transform_type = None
+ capabilities = 0
+ supports_detection = False
name = None
long_name = None
group = None
@@ -113,6 +116,7 @@ class Transform(metaclass=_TransformMetaClass):
self._cb.freeParameters = self._cb.freeParameters.__class__(self._free_parameters)
self._cb.decode = self._cb.decode.__class__(self._decode)
self._cb.encode = self._cb.encode.__class__(self._encode)
+ self._cb.canDecode = self._cb.canDecode.__class__(self._can_decode)
self._pending_param_lists = {}
self.type = self.__class__.transform_type
if not isinstance(self.type, str):
@@ -125,6 +129,8 @@ class Transform(metaclass=_TransformMetaClass):
else:
self.handle = handle
self.type = TransformType(core.BNGetTransformType(self.handle))
+ self.capabilities = core.BNGetTransformCapabilities(self.handle)
+ self.supports_detection = core.BNTransformSupportsDetection(self.handle)
self.name = core.BNGetTransformName(self.handle)
self.long_name = core.BNGetTransformLongName(self.handle)
self.group = core.BNGetTransformGroup(self.handle)
@@ -165,7 +171,7 @@ class Transform(metaclass=_TransformMetaClass):
cls.group = ""
xform = cls(None)
cls._registered_cb = xform._cb
- xform.handle = core.BNRegisterTransformType(cls.transform_type, cls.name, cls.long_name, cls.group, xform._cb)
+ xform.handle = core.BNRegisterTransformTypeWithCapabilities(cls.transform_type, cls.capabilities, cls.name, cls.long_name, cls.group, xform._cb)
def _get_parameters(self, ctxt, count):
try:
@@ -226,6 +232,14 @@ class Transform(metaclass=_TransformMetaClass):
log_error_for_exception("Unhandled Python exception in Transform._encode")
return False
+ def _can_decode(self, ctxt, input):
+ try:
+ input_obj = binaryview.BinaryView(handle=core.BNNewViewReference(input))
+ return self.can_decode(input_obj)
+ except:
+ log_error_for_exception("Unhandled Python exception in Transform._can_decode")
+ return False
+
@abc.abstractmethod
def perform_decode(self, data, params):
if self.type == TransformType.InvertingTransform:
@@ -267,3 +281,18 @@ class Transform(metaclass=_TransformMetaClass):
if not core.BNEncode(self.handle, input_buf.handle, output_buf.handle, param_buf, len(keys)):
return None
return bytes(output_buf)
+
+ def can_decode(self, input):
+ """
+ ``can_decode`` checks if this transform can decode the given input.
+
+ :param input: can be a :py:class:`bytes`, :py:class:`bytearray`, :py:class:`DataBuffer`, or :py:class:`BinaryView`
+ :return: :py:class:`bool` indicating whether the transform can decode the input
+ :rtype: bool
+ """
+ if isinstance(input, bytes) or isinstance(input, bytearray) or isinstance(input, databuffer.DataBuffer):
+ with binaryview.BinaryView.new(input) as view:
+ return core.BNCanDecode(self.handle, view.handle)
+ elif isinstance(input, binaryview.BinaryView):
+ return core.BNCanDecode(self.handle, input.handle)
+ return False
diff --git a/transform.cpp b/transform.cpp
index 86f2caf9..468cc0f2 100644
--- a/transform.cpp
+++ b/transform.cpp
@@ -31,7 +31,14 @@ Transform::Transform(BNTransform* xform)
Transform::Transform(BNTransformType type, const string& name, const string& longName, const string& group) :
- m_typeForRegister(type), m_nameForRegister(name), m_longNameForRegister(longName), m_groupForRegister(group)
+ m_typeForRegister(type), m_capabilitiesForRegister(TransformNoCapabilities), m_nameForRegister(name), m_longNameForRegister(longName), m_groupForRegister(group)
+{
+ m_object = nullptr;
+}
+
+
+Transform::Transform(BNTransformType type, BNTransformCapabilities capabilities, const string& name, const string& longName, const string& group) :
+ m_typeForRegister(type), m_capabilitiesForRegister(capabilities), m_nameForRegister(name), m_longNameForRegister(longName), m_groupForRegister(group)
{
m_object = nullptr;
}
@@ -100,6 +107,13 @@ bool Transform::EncodeCallback(
}
+bool Transform::CanDecodeCallback(void* ctxt, BNBinaryView* input)
+{
+ CallbackRef<Transform> xform(ctxt);
+ return xform->CanDecode(new BinaryView(input));
+}
+
+
vector<TransformParameter> Transform::EncryptionKeyParameters(size_t fixedKeyLength)
{
vector<TransformParameter> params;
@@ -136,9 +150,10 @@ void Transform::Register(Transform* xform)
callbacks.freeParameters = FreeParametersCallback;
callbacks.decode = DecodeCallback;
callbacks.encode = EncodeCallback;
+ callbacks.canDecode = CanDecodeCallback;
xform->AddRefForRegistration();
- xform->m_object = BNRegisterTransformType(xform->m_typeForRegister, xform->m_nameForRegister.c_str(),
- xform->m_longNameForRegister.c_str(), xform->m_groupForRegister.c_str(), &callbacks);
+ xform->m_object = BNRegisterTransformTypeWithCapabilities(xform->m_typeForRegister, xform->m_capabilitiesForRegister,
+ xform->m_nameForRegister.c_str(), xform->m_longNameForRegister.c_str(), xform->m_groupForRegister.c_str(), &callbacks);
}
@@ -172,6 +187,18 @@ BNTransformType Transform::GetType() const
}
+BNTransformCapabilities Transform::GetCapabilities() const
+{
+ return static_cast<BNTransformCapabilities>(BNGetTransformCapabilities(m_object));
+}
+
+
+bool Transform::SupportsDetection() const
+{
+ return BNTransformSupportsDetection(m_object);
+}
+
+
string Transform::GetName() const
{
char* name = BNGetTransformName(m_object);
@@ -219,6 +246,12 @@ bool Transform::Encode(const DataBuffer&, DataBuffer&, const map<string, DataBuf
}
+bool Transform::CanDecode(Ref<BinaryView> input) const
+{
+ return false;
+}
+
+
CoreTransform::CoreTransform(BNTransform* xform) : Transform(xform) {}
@@ -275,3 +308,10 @@ bool CoreTransform::Encode(const DataBuffer& input, DataBuffer& output, const ma
delete[] list;
return result;
}
+
+
+bool CoreTransform::CanDecode(Ref<BinaryView> input) const
+{
+ return BNCanDecode(m_object, input->GetObject());
+}
+
diff --git a/transformcontext.cpp b/transformcontext.cpp
new file mode 100644
index 00000000..186b24b9
--- /dev/null
+++ b/transformcontext.cpp
@@ -0,0 +1,186 @@
+#include "binaryninjaapi.h"
+
+using namespace BinaryNinja;
+using namespace std;
+
+
+TransformContext::TransformContext(BNTransformContext* context)
+{
+ m_object = context;
+}
+
+
+TransformContext::~TransformContext()
+{
+}
+
+
+string TransformContext::GetTransformName() const
+{
+ char* name = BNTransformContextGetTransformName(m_object);
+ string result = name;
+ BNFreeString(name);
+ return result;
+}
+
+
+string TransformContext::GetFileName() const
+{
+ char* name = BNTransformContextGetFileName(m_object);
+ string result = name;
+ BNFreeString(name);
+ return result;
+}
+
+
+Ref<BinaryView> TransformContext::GetInput() const
+{
+ return new BinaryView(BNTransformContextGetInput(m_object));
+}
+
+
+Ref<Metadata> TransformContext::GetMetadata() const
+{
+ return new Metadata(BNTransformContextGetMetadata(m_object));
+}
+
+
+Ref<TransformContext> TransformContext::GetParent() const
+{
+ BNTransformContext* parent = BNTransformContextGetParent(m_object);
+ if (!parent)
+ return nullptr;
+ return new TransformContext(BNNewTransformContextReference(parent));
+}
+
+
+size_t TransformContext::GetChildCount() const
+{
+ return BNTransformContextGetChildCount(m_object);
+}
+
+
+vector<Ref<TransformContext>> TransformContext::GetChildren() const
+{
+ size_t count;
+ BNTransformContext** contexts = BNTransformContextGetChildren(m_object, &count);
+
+ vector<Ref<TransformContext>> result;
+ result.reserve(count);
+
+ for (size_t i = 0; i < count; i++)
+ result.push_back(new TransformContext(BNNewTransformContextReference(contexts[i])));
+
+ BNFreeTransformContextList(contexts, count);
+ return result;
+}
+
+
+Ref<TransformContext> TransformContext::GetChild(const string& filename) const
+{
+ BNTransformContext* child = BNTransformContextGetChild(m_object, filename.c_str());
+ if (!child)
+ return nullptr;
+ return new TransformContext(BNNewTransformContextReference(child));
+}
+
+
+Ref<TransformContext> TransformContext::CreateChild(const DataBuffer& data, const string& filename)
+{
+ BNTransformContext* child = BNTransformContextCreateChild(m_object, data.GetBufferObject(), filename.c_str());
+ if (!child)
+ return nullptr;
+ return new TransformContext(BNNewTransformContextReference(child));
+}
+
+
+bool TransformContext::IsLeaf() const
+{
+ return BNTransformContextIsLeaf(m_object);
+}
+
+
+bool TransformContext::IsRoot() const
+{
+ return BNTransformContextIsRoot(m_object);
+}
+
+
+vector<string> TransformContext::GetAvailableFiles() const
+{
+ size_t count;
+ char** files = BNTransformContextGetAvailableFiles(m_object, &count);
+
+ vector<string> result;
+ result.reserve(count);
+
+ for (size_t i = 0; i < count; i++)
+ {
+ result.push_back(files[i]);
+ }
+
+ BNFreeStringList(files, count);
+ return result;
+}
+
+
+void TransformContext::SetAvailableFiles(const vector<string>& files)
+{
+ const char** cFiles = new const char*[files.size()];
+ for (size_t i = 0; i < files.size(); i++)
+ {
+ cFiles[i] = files[i].c_str();
+ }
+
+ BNTransformContextSetAvailableFiles(m_object, cFiles, files.size());
+ delete[] cFiles;
+}
+
+
+bool TransformContext::HasAvailableFiles() const
+{
+ return BNTransformContextHasAvailableFiles(m_object);
+}
+
+
+vector<string> TransformContext::GetRequestedFiles() const
+{
+ size_t count;
+ char** files = BNTransformContextGetRequestedFiles(m_object, &count);
+
+ vector<string> result;
+ result.reserve(count);
+
+ for (size_t i = 0; i < count; i++)
+ {
+ result.push_back(files[i]);
+ }
+
+ BNFreeStringList(files, count);
+ return result;
+}
+
+
+void TransformContext::SetRequestedFiles(const vector<string>& files)
+{
+ const char** cFiles = new const char*[files.size()];
+ for (size_t i = 0; i < files.size(); i++)
+ {
+ cFiles[i] = files[i].c_str();
+ }
+
+ BNTransformContextSetRequestedFiles(m_object, cFiles, files.size());
+ delete[] cFiles;
+}
+
+
+bool TransformContext::HasRequestedFiles() const
+{
+ return BNTransformContextHasRequestedFiles(m_object);
+}
+
+
+bool TransformContext::IsDatabase() const
+{
+ return BNTransformContextIsDatabase(m_object);
+}
diff --git a/transformsession.cpp b/transformsession.cpp
new file mode 100644
index 00000000..e839efb8
--- /dev/null
+++ b/transformsession.cpp
@@ -0,0 +1,150 @@
+#include "binaryninjaapi.h"
+
+using namespace BinaryNinja;
+using namespace std;
+
+
+TransformSession::TransformSession(const string& filename)
+{
+ m_object = BNCreateTransformSession(filename.c_str());
+}
+
+
+TransformSession::TransformSession(const string& filename, BNTransformSessionMode mode)
+{
+ m_object = BNCreateTransformSessionWithMode(filename.c_str(), mode);
+}
+
+
+TransformSession::TransformSession(Ref<BinaryView> initialView)
+{
+ m_object = BNCreateTransformSessionFromBinaryView(initialView->GetObject());
+}
+
+
+TransformSession::TransformSession(Ref<BinaryView> initialView, BNTransformSessionMode mode)
+{
+ m_object = BNCreateTransformSessionFromBinaryViewWithMode(initialView->GetObject(), mode);
+}
+
+
+TransformSession::TransformSession(BNTransformSession* session)
+{
+ m_object = session;
+}
+
+
+TransformSession::~TransformSession()
+{
+}
+
+
+Ref<BinaryView> TransformSession::GetCurrentView() const
+{
+ return new BinaryView(BNTransformSessionGetCurrentView(m_object));
+}
+
+
+Ref<TransformContext> TransformSession::GetRootContext() const
+{
+ return new TransformContext(BNTransformSessionGetRootContext(m_object));
+}
+
+
+Ref<TransformContext> TransformSession::GetCurrentContext() const
+{
+ return new TransformContext(BNTransformSessionGetCurrentContext(m_object));
+}
+
+
+bool TransformSession::Process()
+{
+ return BNTransformSessionProcess(m_object);
+}
+
+
+bool TransformSession::HasAnyStages() const
+{
+ return BNTransformSessionHasAnyStages(m_object);
+}
+
+
+bool TransformSession::HasSinglePath() const
+{
+ return BNTransformSessionHasSinglePath(m_object);
+}
+
+
+vector<Ref<TransformContext>> TransformSession::GetSelectedContexts() const
+{
+ size_t count;
+ BNTransformContext** contexts = BNTransformSessionGetSelectedContexts(m_object, &count);
+ vector<Ref<TransformContext>> result;
+ result.reserve(count);
+ for (size_t i = 0; i < count; i++)
+ result.push_back(new TransformContext(BNNewTransformContextReference(contexts[i])));
+
+ BNFreeTransformContextList(contexts, count);
+ return result;
+}
+
+
+void TransformSession::SetSelectedContexts(const vector<Ref<TransformContext>>& contexts)
+{
+ BNTransformContext** cContexts = new BNTransformContext*[contexts.size()];
+ for (size_t i = 0; i < contexts.size(); i++)
+ cContexts[i] = contexts[i]->GetObject();
+
+ BNTransformSessionSetSelectedContexts(m_object, cContexts, contexts.size());
+ delete[] cContexts;
+}
+
+
+bool TransformSession::RequiresUserInput() const
+{
+ return BNTransformSessionRequiresUserInput(m_object);
+}
+
+
+bool TransformSession::HasMultipleFileChoices() const
+{
+ return BNTransformSessionHasMultipleFileChoices(m_object);
+}
+
+
+vector<string> TransformSession::GetAvailableFileChoices() const
+{
+ size_t count;
+ char** files = BNTransformSessionGetAvailableFileChoices(m_object, &count);
+
+ vector<string> result;
+ result.reserve(count);
+
+ for (size_t i = 0; i < count; i++)
+ {
+ result.push_back(files[i]);
+ }
+
+ BNFreeStringList(files, count);
+ return result;
+}
+
+
+bool TransformSession::SelectFiles(const vector<string>& selectedFiles)
+{
+ const char** cFiles = new const char*[selectedFiles.size()];
+ for (size_t i = 0; i < selectedFiles.size(); i++)
+ {
+ cFiles[i] = selectedFiles[i].c_str();
+ }
+
+ bool result = BNTransformSessionSelectFiles(m_object, cFiles, selectedFiles.size());
+ delete[] cFiles;
+ return result;
+}
+
+
+bool TransformSession::ProcessWithUserInput()
+{
+ return BNTransformSessionProcessWithUserInput(m_object);
+}
diff --git a/ui/containerbrowser.h b/ui/containerbrowser.h
new file mode 100644
index 00000000..8dddad0a
--- /dev/null
+++ b/ui/containerbrowser.h
@@ -0,0 +1,106 @@
+#pragma once
+
+#include <QDialog>
+#include <QStringList>
+#include <QLineEdit>
+#include <QTreeView>
+#include <QPlainTextEdit>
+#include <QLabel>
+#include <QDialogButtonBox>
+#include <QModelIndex>
+
+#include "binaryninjaapi.h"
+#include "uitypes.h"
+
+#include <vector>
+
+
+class ContainerTreeModel : public QAbstractItemModel
+{
+ Q_OBJECT
+
+public:
+ enum Columns { ColName, ColType, ColSize, ColPath, ColCount };
+
+ explicit ContainerTreeModel(TransformSessionRef session, QObject* parent = nullptr);
+
+ // QAbstractItemModel interface
+ int columnCount(const QModelIndex& parent = {}) const override;
+ QModelIndex index(int row, int column, const QModelIndex& parent = {}) const override;
+ QModelIndex parent(const QModelIndex& child) const override;
+ int rowCount(const QModelIndex& parent = {}) const override;
+ QVariant data(const QModelIndex& index, int role) const override;
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
+ Qt::ItemFlags flags(const QModelIndex& index) const override;
+
+ // Public helpers for the dialog
+ bool isLeaf(const QModelIndex& index) const;
+ QStringList pathFor(const QModelIndex& index) const;
+ void selectNode(const QModelIndex& index);
+ void rebuild();
+
+private:
+ struct Node
+ {
+ QString displayName; // GetFileName() (or synthesized for root)
+ QString type; // GetTransformName() or "Leaf"/"Root"
+ QString breadcrumb; // human-readable path "a ▸ b ▸ c"
+ QStringList pathSegments; // list of filenames from root to this node
+ quint64 size = 0; // not exposed (kept for future metadata)
+ bool isLeaf = false;
+ bool selectable = true; // we allow selection only on leaves
+ TransformContextRef ctx;
+ Node* parent = nullptr;
+ std::vector<std::unique_ptr<Node>> children;
+ };
+
+ const Node* nodeFromIndex(const QModelIndex& index) const;
+ static QString joinBreadcrumb(const QStringList& segments);
+ void buildChildren(Node* parentNode, const TransformContextRef& ctx, const QStringList& parentSegments);
+
+ TransformSessionRef m_session;
+ std::unique_ptr<Node> m_root;
+};
+
+
+class AllColumnsFilterProxyModel : public QSortFilterProxyModel
+{
+ Q_OBJECT
+
+public:
+ explicit AllColumnsFilterProxyModel(QObject* parent = nullptr);
+
+protected:
+ bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override;
+};
+
+
+class BINARYNINJAUIAPI ContainerBrowser : public QDialog
+{
+ Q_OBJECT
+
+ TransformSessionRef m_session;
+
+ ContainerTreeModel* m_model;
+
+ QLineEdit* m_filter = nullptr;
+ QTreeView* m_tree = nullptr;
+ QPlainTextEdit* m_preview = nullptr;
+ QLabel* m_status = nullptr;
+ QDialogButtonBox* m_buttons = nullptr;
+ AllColumnsFilterProxyModel* m_proxy = nullptr;
+
+ QStringList m_selectedPaths;
+
+ void connectSignals();
+ void loadRoot();
+ void updatePreviewForIndex(const QModelIndex& proxyIndex);
+ static QString toHexDump(const QByteArray& data, int bytesPerLine = 16);
+
+public:
+ ContainerBrowser(TransformSessionRef session, QWidget* parent = nullptr);
+
+ QStringList selectedPaths() const { return m_selectedPaths; }
+
+ static BinaryViewRef openContainerFile(const QString& path);
+};
diff --git a/ui/options.h b/ui/options.h
index 3206fc7e..764cb85c 100644
--- a/ui/options.h
+++ b/ui/options.h
@@ -53,7 +53,7 @@ class BINARYNINJAUIAPI OptionsDialog : public QDialog
public:
OptionsDialog(QWidget* parent, const QString& name);
virtual ~OptionsDialog();
- bool loadViews();
+ bool loadViews(BinaryViewRef existingView = nullptr);
Q_SIGNALS:
void openFile(FileContext* file);
diff --git a/ui/uitypes.h b/ui/uitypes.h
index 476c0d79..89fa43e6 100644
--- a/ui/uitypes.h
+++ b/ui/uitypes.h
@@ -108,6 +108,8 @@ typedef BinaryNinja::Ref<BinaryNinja::Tag> TagRef;
typedef BinaryNinja::Ref<BinaryNinja::TagType> TagTypeRef;
typedef BinaryNinja::Ref<BinaryNinja::TemporaryFile> TemporaryFileRef;
typedef BinaryNinja::Ref<BinaryNinja::Transform> TransformRef;
+typedef BinaryNinja::Ref<BinaryNinja::TransformContext> TransformContextRef;
+typedef BinaryNinja::Ref<BinaryNinja::TransformSession> TransformSessionRef;
typedef BinaryNinja::Ref<BinaryNinja::Type> TypeRef;
typedef BinaryNinja::Ref<BinaryNinja::TypeArchive> TypeArchiveRef;
typedef BinaryNinja::Ref<BinaryNinja::TypeLibrary> TypeLibraryRef;
diff --git a/view/kernelcache/core/KernelCacheView.cpp b/view/kernelcache/core/KernelCacheView.cpp
index 03e8bd68..3535eec9 100644
--- a/view/kernelcache/core/KernelCacheView.cpp
+++ b/view/kernelcache/core/KernelCacheView.cpp
@@ -26,87 +26,15 @@ void KernelCacheViewType::Register()
Ref<BinaryView> KernelCacheViewType::Create(BinaryView* data)
{
- uint32_t magic;
- data->Read(&magic, data->GetStart(), 4);
- if (magic != MH_CIGAM_64 && magic != MH_MAGIC_64) // FIXME 32 bit
+ try
{
- uint32_t im4pMagic;
- data->Read(&im4pMagic, data->GetStart() + 0x8, 4);
- if (im4pMagic == 0x50344d49) // P4MI
- {
- auto img4 = Transform::GetByName("IMG4-Unencrypted");
-
- DataBuffer img4Payload;
- img4->Decode(data->ReadBuffer(data->GetStart(), data->GetLength()), img4Payload);
-
- DataBuffer machOPayload;
- uint32_t magic = ((uint32_t*)img4Payload.GetData())[0];
- if (magic == FAT_MAGIC_64 || magic == MH_MAGIC_64 || magic == MH_MAGIC
- || magic == MH_CIGAM_64 || magic == MH_CIGAM )
- {
- machOPayload = img4Payload;
- }
- else if (strncmp((char*)img4Payload.GetData(), "bvx2", 4) == 0)
- {
- auto lzfse = Transform::GetByName("LZFSE");
- if (lzfse)
- lzfse->Decode(img4Payload, machOPayload);
- }
- else
- {
-#ifdef COMPRESSION_DEBUG
- LogError("Unknown compression type in IMG4 Payload, writing img4 payload to RAW view for debug purposes.");
- LogError("KernelCache parsing will now fail to proceed.");
- data->WriteBuffer(0, img4Payload);
- return new KernelCacheView(KC_VIEW_NAME, data, false);
-#else
- LogError("Unknown compression type in IMG4 Payload, unable to proceed.");
- LogError("You can manually extract the kernelcache using `kerneldec`,`ipsw`, or other tools.");
- return nullptr;
-#endif
- }
-
- if (machOPayload.GetLength() == 0)
- {
-#ifdef COMPRESSION_DEBUG
- LogError("Failed to perform extraction on IMG4 Payload, writing img4 payload to RAW view for debug purposes.");
- LogError("KernelCache parsing will now fail to proceed.");
- data->WriteBuffer(0, img4Payload);
- return new KernelCacheView(KC_VIEW_NAME, data, false);
-#else
- return nullptr;
-#endif
- }
-
- uint32_t machoMagic = ((uint32_t*)machOPayload.GetData())[0];
- if (machoMagic == FAT_MAGIC_64)
- {
- DataBuffer output = machOPayload.GetSlice(0x1c, machOPayload.GetLength()-0x1c);
- data->WriteBuffer(0, output);
- }
- else if (machoMagic == MH_MAGIC_64 || machoMagic == MH_MAGIC || machoMagic == MH_CIGAM_64 || machoMagic == MH_CIGAM)
- {
- data->WriteBuffer(0, machOPayload);
- }
- else
- {
-#ifdef COMPRESSION_DEBUG
- LogError("Unknown Mach-O magic in IMG4 Payload, writing img4 payload to RAW view for debug purposes.");
- LogError("KernelCache parsing will now fail to proceed.");
- data->WriteBuffer(0, machOPayload);
- return new KernelCacheView(KC_VIEW_NAME, data, false);
-#else
- return nullptr;
-#endif
- }
-
- return new KernelCacheView(KC_VIEW_NAME, data, false);
- }
-
+ return new KernelCacheView(KC_VIEW_NAME, data, false);
+ }
+ catch (std::exception& e)
+ {
+ LogErrorForException(e, "%s<BinaryViewType> failed to create view! '%s'", GetName().c_str(), e.what());
return nullptr;
}
-
- return new KernelCacheView(KC_VIEW_NAME, data, false);
}
Ref<Settings> KernelCacheViewType::GetLoadSettingsForData(BinaryView* data)
@@ -159,41 +87,15 @@ Ref<Settings> KernelCacheViewType::GetLoadSettingsForData(BinaryView* data)
Ref<BinaryView> KernelCacheViewType::Parse(BinaryView* data)
{
- uint32_t magic;
- data->Read(&magic, data->GetStart(), 4);
- if (magic != MH_CIGAM_64 && magic != MH_MAGIC_64) // FIXME 32 bit
+ try
{
- uint32_t im4pMagic;
- data->Read(&im4pMagic, data->GetStart() + 0x8, 4);
- if (im4pMagic == 0x50344d49) // P4MI
- {
- auto img4 = Transform::GetByName("IMG4-Unencrypted");
- auto lzfse = Transform::GetByName("LZFSE");
-
- DataBuffer img4Payload;
- img4->Decode(data->ReadBuffer(data->GetStart(), data->GetLength()), img4Payload);
- DataBuffer machOPayload;
- lzfse->Decode(img4Payload, machOPayload);
-
- uint32_t magic = ((uint32_t*)machOPayload.GetData())[0];
- auto id = data->BeginUndoActions();
- if (magic == FAT_MAGIC_64)
- {
- DataBuffer output = machOPayload.GetSlice(0x1c, machOPayload.GetLength()-0x1c);
- data->WriteBuffer(0, output);
- }
- else
- {
- data->WriteBuffer(0, machOPayload);
- }
- data->ForgetUndoActions(id);
- return new KernelCacheView(KC_VIEW_NAME, data, true);
- }
-
+ return new KernelCacheView(KC_VIEW_NAME, data, true);
+ }
+ catch (std::exception& e)
+ {
+ LogErrorForException(e, "%s<BinaryViewType> failed to create view! '%s'", GetName().c_str(), e.what());
return nullptr;
}
-
- return new KernelCacheView(KC_VIEW_NAME, data, true);
}
bool KernelCacheViewType::IsTypeValidForData(BinaryView* data)
@@ -204,36 +106,15 @@ bool KernelCacheViewType::IsTypeValidForData(BinaryView* data)
uint32_t magic;
data->Read(&magic, data->GetStart(), 4);
- if (magic != MH_CIGAM_64 && magic != MH_MAGIC_64) // FIXME 32 bit
- {
- uint32_t im4pMagic;
- data->Read(&im4pMagic, data->GetStart() + 0x8, 4);
- if (im4pMagic == 0x50344d49) // P4MI
- {
- auto img4 = Transform::GetByName("IMG4-Unencrypted");
- auto lzfse = Transform::GetByName("LZFSE");
-
- DataBuffer img4Payload;
- img4->Decode(data->ReadBuffer(data->GetStart(), data->GetLength()), img4Payload);
- DataBuffer machOPayload;
- lzfse->Decode(img4Payload, machOPayload);
-
- uint32_t magic = ((uint32_t*)machOPayload.GetData())[0];
- if (magic == FAT_MAGIC_64 || magic == MH_CIGAM_64 || magic == MH_MAGIC_64)
- return true;
-
- return false;
- }
-
+ // TODO determine if the "krnl" string of the IMG4 container is required for non-database files
+ // If so, inspect the metadata from the TransformSession that produced this BinaryView
+ if (magic != MH_CIGAM_64 && magic != MH_MAGIC_64)
return false;
- }
uint32_t fileType;
data->Read(&fileType, data->GetStart() + 0xc, 4);
if (fileType != MH_FILESET)
- {
return false;
- }
return true;
}
diff --git a/view/kernelcache/core/transformers/KernelCacheTransforms.cpp b/view/kernelcache/core/transformers/KernelCacheTransforms.cpp
index 5cecabad..c2da5cdb 100644
--- a/view/kernelcache/core/transformers/KernelCacheTransforms.cpp
+++ b/view/kernelcache/core/transformers/KernelCacheTransforms.cpp
@@ -15,12 +15,11 @@ class IMG4PayloadTransform : public Transform
{
public:
- IMG4PayloadTransform():
- Transform(DecodeTransform, "IMG4-Unencrypted", "IMG4-Unencrypted", "IMG4")
+ IMG4PayloadTransform(): Transform(DecodeTransform, TransformSupportsDetection, "IMG4", "IMG4", "Container")
{
}
- virtual bool Decode(const DataBuffer& input, DataBuffer& output, const std::map<std::string, DataBuffer>& params)
+ virtual bool Decode(const DataBuffer& input, DataBuffer& output, const std::map<std::string, DataBuffer>& params) override
{
DERItem* item = new DERItem;
item->data = (DERByte *)input.GetData();
@@ -29,46 +28,215 @@ public:
Img4Payload *payload = new Img4Payload;
DERImg4DecodePayload(item, payload);
+ if (!payload->payload.data || !payload->payload.length)
+ return false;
+
output = DataBuffer(payload->payload.data, payload->payload.length);
return true;
}
+
+ static void der_put_len(std::vector<uint8_t>& v, size_t len) {
+ if (len < 0x80) { v.push_back(static_cast<uint8_t>(len)); return; }
+ uint8_t tmp[9]; size_t n = 0;
+ while (len) { tmp[n++] = static_cast<uint8_t>(len & 0xFF); len >>= 8; }
+ v.push_back(static_cast<uint8_t>(0x80 | n));
+ for (size_t i = 0; i < n; ++i) v.push_back(tmp[n - 1 - i]);
+ }
+
+ static void der_put_ia5(std::vector<uint8_t>& v, const void* s, size_t len) {
+ v.push_back(0x16); // IA5String
+ der_put_len(v, len);
+ const uint8_t* p = static_cast<const uint8_t*>(s);
+ v.insert(v.end(), p, p + len);
+ }
+
+ // TODO fix/support round-tripping of optional fields (type, desc)
+ virtual bool Encode(const DataBuffer& input, DataBuffer& output, const std::map<std::string, DataBuffer>& params) override
+ {
+ // type (exactly 4 chars)
+ const char* type = "krnl";
+ if (auto it = params.find("type"); it != params.end()) {
+ if (it->second.GetLength() != 4) return false;
+ type = reinterpret_cast<const char*>(it->second.GetData());
+ }
+ // optional desc (IA5String)
+ const char* desc = nullptr; size_t descLen = 0;
+ if (auto it = params.find("desc"); it != params.end() && it->second.GetLength() > 0) {
+ desc = reinterpret_cast<const char*>(it->second.GetData());
+ descLen = it->second.GetLength();
+ }
+
+ // Build SEQUENCE content
+ std::vector<uint8_t> body;
+ der_put_ia5(body, "IM4P", 4); // magic
+ der_put_ia5(body, type, 4); // type
+ if (desc && descLen) der_put_ia5(body, desc, descLen); // optional
+
+ // payload as [1] EXPLICIT OCTET STRING
+ // std::vector<uint8_t> os;
+ // os.push_back(0x04); // OCTET STRING
+ // der_put_len(os, input.GetLength());
+ // os.insert(os.end(),
+ // static_cast<const uint8_t*>(input.GetData()),
+ // static_cast<const uint8_t*>(input.GetData()) + input.GetLength());
+
+ // body.push_back(0xA1); // [1] EXPLICIT
+ // der_put_len(body, os.size());
+ // body.insert(body.end(), os.begin(), os.end());
+
+ // --- payload as *bare* OCTET STRING (what DERImg4DecodePayload expects) ---
+ body.push_back(0x04); // OCTET STRING
+ der_put_len(body, input.GetLength());
+ body.insert(body.end(),
+ static_cast<const uint8_t*>(input.GetData()),
+ static_cast<const uint8_t*>(input.GetData()) + input.GetLength());
+
+
+ // Wrap in SEQUENCE
+ std::vector<uint8_t> out;
+ out.push_back(0x30); // SEQUENCE
+ der_put_len(out, body.size());
+ out.insert(out.end(), body.begin(), body.end());
+
+ output = DataBuffer(out.data(), out.size()); // copies
+ return true;
+ }
+
+ virtual bool CanDecode(Ref<BinaryView> input) const override
+ {
+ uint8_t header[64];
+ size_t bytesRead = input->Read(header, 0, sizeof(header));
+ if (bytesRead < sizeof(header))
+ return false;
+
+ const uint8_t* data = header;
+ size_t headerLength = bytesRead;
+ size_t inputLength = input->GetLength();
+
+ auto parseDerLen = [](const uint8_t* ptr, size_t available) -> std::pair<size_t, size_t> {
+ if (!available)
+ return {0, 0};
+
+ uint8_t firstByte = ptr[0];
+ if (firstByte < 0x80) // Short form
+ return {firstByte, 1};
+ if (firstByte == 0x80) // Invalid indefinite length
+ return {0, 0};
+
+ size_t lengthBytes = firstByte & 0x7F;
+ if (lengthBytes == 0 || lengthBytes > sizeof(size_t) || lengthBytes >= available || ptr[1] == 0x00)
+ return {0, 0};
+
+ size_t result = 0;
+ for (size_t i = 0; i < lengthBytes; ++i)
+ result = (result << 8) | ptr[1 + i];
+ return {result, 1 + lengthBytes};
+ };
+
+ if (headerLength < 8) // Minimum: SEQUENCE tag(1) + len(1) + IA5String tag(1) + len(1) + "IM4P"(4)
+ return false;
+
+ if (data[0] != 0x30) // Check for DER sequence start
+ return false;
+
+ auto [seqLen, seqLenHdr] = parseDerLen(data + 1, headerLength - 1);
+ if (!seqLen || !seqLenHdr || ((seqLen + 1 + seqLenHdr) > inputLength))
+ return false;
+
+ size_t offset = 1 + seqLenHdr;
+ size_t seqEnd = offset + seqLen;
+
+ if (seqLen > (inputLength - offset))
+ return false;
+
+ // parse up to the first 5 elements to find the magic "IM4P"
+ for (int i = 0; i < 5 && offset < seqEnd; ++i)
+ {
+ if (seqEnd - offset < 2)
+ return false;
+ uint8_t tag = data[offset++];
+ auto [elementLen, elementLenHdr] = parseDerLen(data + offset, seqEnd - offset);
+ if (!elementLen || !elementLenHdr || (elementLen > (seqEnd - offset - elementLenHdr)))
+ return false;
+ offset += elementLenHdr;
+ if ((tag == 0x16) && (elementLen == 4) && memcmp(data + offset, "IM4P", 4) == 0)
+ return true;
+ }
+
+ return false;
+ }
};
class LZFSETransform : public Transform
{
public:
- LZFSETransform(): Transform(BinaryCodecTransform, "LZFSE", "LZFSE", "Compress")
+ LZFSETransform(): Transform(BinaryCodecTransform, TransformSupportsDetection, "LZFSE", "LZFSE", "Compress")
{
}
- virtual bool Decode(const DataBuffer& input, DataBuffer& output, const std::map<std::string, DataBuffer>& params)
+ virtual bool Decode(const DataBuffer& input, DataBuffer& output, const std::map<std::string, DataBuffer>& params) override
{
size_t outputBufferSize = input.GetLength() * 6;
- std::unique_ptr<uint8_t[]> lzfseOutputBuffer(new uint8_t[outputBufferSize]);
std::unique_ptr<uint8_t[]> scratchBuffer(new uint8_t[lzfse_decode_scratch_size()]);
- size_t outSize = lzfse_decode_buffer(lzfseOutputBuffer.get(), outputBufferSize,
- (uint8_t *)input.GetData(), input.GetLength(),
- scratchBuffer.get());
- if (!outSize)
- return false;
- output = DataBuffer(lzfseOutputBuffer.get(), outSize);
- return true;
+ while (true)
+ {
+ output.SetSize(outputBufferSize);
+ size_t outSize = lzfse_decode_buffer((uint8_t *)output.GetData(), outputBufferSize, (uint8_t *)input.GetData(), input.GetLength(), scratchBuffer.get());
+ if (!outSize)
+ return false;
+ if ((outSize > 0) && (outSize < outputBufferSize))
+ {
+ output.SetSize(outSize);
+ return true;
+ }
+ if (output.GetLength() > (size_t(1) << 33)) // 8GB max
+ return false;
+ outputBufferSize *= 2;
+ }
+
+ return false;
}
- virtual bool Encode(const DataBuffer& input, DataBuffer& output, const std::map<std::string, DataBuffer>&)
+ virtual bool Encode(const DataBuffer& input, DataBuffer& output, const std::map<std::string, DataBuffer>&) override
{
size_t outputBufferSize = input.GetLength() + (input.GetLength() / 16) + 64;
- std::unique_ptr<uint8_t[]> lzfseOutputBuffer(new uint8_t[outputBufferSize]);
std::unique_ptr<uint8_t[]> scratchBuffer(new uint8_t[lzfse_encode_scratch_size()]);
- size_t outSize = lzfse_encode_buffer(lzfseOutputBuffer.get(), outputBufferSize,
- (uint8_t *)input.GetData(), input.GetLength(),
- scratchBuffer.get());
- if (!outSize)
+ for (int attempts = 0; attempts < 10; attempts++)
+ {
+ output.SetSize(outputBufferSize);
+ size_t outSize = lzfse_encode_buffer((uint8_t *)output.GetData(), outputBufferSize, (uint8_t *)input.GetData(), input.GetLength(), scratchBuffer.get());
+ if (outSize > 0)
+ {
+ output.SetSize(outSize);
+ return true;
+ }
+ outputBufferSize *= 2;
+ }
+
+ return false;
+ }
+
+ virtual bool CanDecode(Ref<BinaryView> input) const override
+ {
+ uint8_t header[4];
+ if (input->Read(header, 0, 4) < 4)
return false;
- output = DataBuffer(lzfseOutputBuffer.get(), outSize);
- return true;
+
+ if (header[0] != 0x62 || header[1] != 0x76 || header[2] != 0x78) // Check for "bvx" prefix (common to all LZFSE blocks)
+ return false;
+
+ switch (header[3])
+ {
+ case '-': // raw
+ case '1': // compressed v1
+ case '2': // compressed v2
+ case 'n': // LZVN
+ return true;
+ default:
+ return false;
+ }
}
};