diff options
| -rw-r--r-- | binaryninjaapi.h | 80 | ||||
| -rw-r--r-- | binaryninjacore.h | 81 | ||||
| -rw-r--r-- | filemetadata.cpp | 15 | ||||
| -rw-r--r-- | python/filemetadata.py | 9 | ||||
| -rw-r--r-- | python/transform.py | 31 | ||||
| -rw-r--r-- | transform.cpp | 46 | ||||
| -rw-r--r-- | transformcontext.cpp | 186 | ||||
| -rw-r--r-- | transformsession.cpp | 150 | ||||
| -rw-r--r-- | ui/containerbrowser.h | 106 | ||||
| -rw-r--r-- | ui/options.h | 2 | ||||
| -rw-r--r-- | ui/uitypes.h | 2 | ||||
| -rw-r--r-- | view/kernelcache/core/KernelCacheView.cpp | 149 | ||||
| -rw-r--r-- | view/kernelcache/core/transformers/KernelCacheTransforms.cpp | 210 |
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; + } } }; |
