summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGlenn Smith <glenn@vector35.com>2022-09-06 19:14:03 -0400
committerAlexander Taylor <alex@vector35.com>2022-10-12 01:40:27 -0400
commitc398bd1ea423f1f9a4fa9e6e3fcadc9143a99f99 (patch)
tree6cd62115acaa6e18d9d136be28cfbd31435a01e2
parentf68aa8ba0145f17a9ad76f59b1885c998f5fffa5 (diff)
[Enterprise] C++ api and example
-rw-r--r--enterprise.cpp254
-rw-r--r--enterprise.h224
-rw-r--r--examples/CMakeLists.txt3
-rw-r--r--examples/enterprise_test/CMakeLists.txt33
-rw-r--r--examples/enterprise_test/src/enterprise_test.cpp101
5 files changed, 615 insertions, 0 deletions
diff --git a/enterprise.cpp b/enterprise.cpp
new file mode 100644
index 00000000..446c4607
--- /dev/null
+++ b/enterprise.cpp
@@ -0,0 +1,254 @@
+// Copyright (c) 2015-2022 Vector 35 Inc
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "enterprise.h"
+
+using namespace BinaryNinja::EnterpriseServer;
+
+bool BinaryNinja::EnterpriseServer::AuthenticateWithCredentials(const std::string& username, const std::string& password, bool remember)
+{
+ return BNAuthenticateEnterpriseServerWithCredentials(username.c_str(), password.c_str(), remember);
+}
+
+
+bool BinaryNinja::EnterpriseServer::AuthenticateWithMethod(const std::string& method, bool remember)
+{
+ return BNAuthenticateEnterpriseServerWithMethod(method.c_str(), remember);
+}
+
+
+std::vector<std::pair<std::string, std::string>> BinaryNinja::EnterpriseServer::GetAuthenticationMethods()
+{
+ char** methods;
+ char** names;
+ size_t count = BNGetEnterpriseServerAuthenticationMethods(&methods, &names);
+
+ std::vector<std::pair<std::string, std::string>> results;
+ for (size_t i = 0; i < count; i++)
+ {
+ results.push_back({methods[i], names[i]});
+ }
+
+ BNFreeStringList(methods, count);
+ BNFreeStringList(names, count);
+
+ return results;
+}
+
+
+bool BinaryNinja::EnterpriseServer::Deauthenticate()
+{
+ return BNDeauthenticateEnterpriseServer();
+}
+
+
+void BinaryNinja::EnterpriseServer::CancelAuthentication()
+{
+ return BNCancelEnterpriseServerAuthentication();
+}
+
+
+bool BinaryNinja::EnterpriseServer::Connect()
+{
+ return BNConnectEnterpriseServer();
+}
+
+
+bool BinaryNinja::EnterpriseServer::AcquireLicense(uint64_t timeout)
+{
+ return BNAcquireEnterpriseServerLicense(timeout);
+}
+
+
+bool BinaryNinja::EnterpriseServer::ReleaseLicense()
+{
+ return BNReleaseEnterpriseServerLicense();
+}
+
+
+bool BinaryNinja::EnterpriseServer::IsConnected()
+{
+ return BNIsEnterpriseServerConnected();
+}
+
+
+bool BinaryNinja::EnterpriseServer::IsAuthenticated()
+{
+ return BNIsEnterpriseServerAuthenticated();
+}
+
+
+std::string BinaryNinja::EnterpriseServer::GetUsername()
+{
+ char* value = BNGetEnterpriseServerUsername();
+ std::string result = value;
+ BNFreeString(value);
+ return result;
+}
+
+
+std::string BinaryNinja::EnterpriseServer::GetToken()
+{
+ char* value = BNGetEnterpriseServerToken();
+ std::string result = value;
+ BNFreeString(value);
+ return result;
+}
+
+
+std::string BinaryNinja::EnterpriseServer::GetServerName()
+{
+ char* value = BNGetEnterpriseServerName();
+ std::string result = value;
+ BNFreeString(value);
+ return result;
+}
+
+
+std::string BinaryNinja::EnterpriseServer::GetServerId()
+{
+ char* value = BNGetEnterpriseServerId();
+ std::string result = value;
+ BNFreeString(value);
+ return result;
+}
+
+
+uint64_t BinaryNinja::EnterpriseServer::GetServerVersion()
+{
+ return BNGetEnterpriseServerVersion();
+}
+
+
+std::string BinaryNinja::EnterpriseServer::GetServerBuildId()
+{
+ char* value = BNGetEnterpriseServerBuildId();
+ std::string result = value;
+ BNFreeString(value);
+ return result;
+}
+
+
+uint64_t BinaryNinja::EnterpriseServer::GetLicenseExpirationTime()
+{
+ return BNGetEnterpriseServerLicenseExpirationTime();
+}
+
+
+uint64_t BinaryNinja::EnterpriseServer::GetLicenseDuration()
+{
+ return BNGetEnterpriseServerLicenseDuration();
+}
+
+
+uint64_t BinaryNinja::EnterpriseServer::GetReservationTimeLimit()
+{
+ return BNGetEnterpriseServerReservationTimeLimit();
+}
+
+
+bool BinaryNinja::EnterpriseServer::IsLicenseStillActivated()
+{
+ return BNIsEnterpriseServerLicenseStillActivated();
+}
+
+
+std::string BinaryNinja::EnterpriseServer::GetLastError()
+{
+ return BNGetEnterpriseServerLastError();
+ char* str = BNGetEnterpriseServerLastError();
+ std::string value = str;
+ BNFreeString(str);
+ return value;
+}
+
+
+void BinaryNinja::EnterpriseServer::RegisterNotification(BNEnterpriseServerCallbacks* notify)
+{
+ BNRegisterEnterpriseServerNotification(notify);
+}
+
+
+void BinaryNinja::EnterpriseServer::UnregisterNotification(BNEnterpriseServerCallbacks* notify)
+{
+ BNUnregisterEnterpriseServerNotification(notify);
+}
+
+
+BinaryNinja::EnterpriseServer::LicenseCheckout::LicenseCheckout(int64_t duration)
+{
+ // This is a port of python's binaryninja.enterprise.LicenseCheckout
+
+ // UI builds have their own license manager
+ if (BNIsUIEnabled())
+ return;
+
+ if (!IsConnected())
+ {
+ Connect();
+ }
+ if (!IsAuthenticated())
+ {
+ // Try keychain
+ bool gotAuth = AuthenticateWithMethod("Keychain", false);
+ if (!gotAuth)
+ {
+ char* username = getenv("BN_ENTERPRISE_USERNAME");
+ char* password = getenv("BN_ENTERPRISE_PASSWORD");
+ if (username && password)
+ {
+ gotAuth = AuthenticateWithCredentials(username, password, true);
+ }
+ }
+ if (!gotAuth)
+ {
+ throw EnterpriseException(
+ "Could not checkout a license: Not authenticated. Try one of the following: \n"
+ " - Log in and check out a license for an extended time\n"
+ " - Set BN_ENTERPRISE_USERNAME and BN_ENTERPRISE_PASSWORD environment variables\n"
+ " - Use BinaryNinja::Enterprise::AuthenticateWithCredentials or AuthenticateWithMethod in your code"
+ );
+ }
+ }
+
+ // Keychain auth can activate a license if we have one in the keychain
+ if (!IsLicenseStillActivated())
+ {
+ if (!EnterpriseServer::AcquireLicense(duration))
+ {
+ throw EnterpriseException("Could not checkout a license: " + GetLastError());
+ }
+ m_acquiredLicense = true;
+ }
+}
+
+
+BinaryNinja::EnterpriseServer::LicenseCheckout::~LicenseCheckout()
+{
+ // UI builds have their own license manager
+ if (BNIsUIEnabled())
+ return;
+
+ // Don't release if we got one from keychain
+ if (m_acquiredLicense)
+ {
+ EnterpriseServer::ReleaseLicense();
+ }
+}
diff --git a/enterprise.h b/enterprise.h
new file mode 100644
index 00000000..4d6b1fdd
--- /dev/null
+++ b/enterprise.h
@@ -0,0 +1,224 @@
+// Copyright (c) 2015-2022 Vector 35 Inc
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#pragma once
+
+#include <string>
+#include <vector>
+#include <stdexcept>
+#include "binaryninjacore.h"
+
+namespace BinaryNinja
+{
+ /*!
+ API for interacting with an Enterprise Server.
+ These methods will only do anything on Enterprise editions of Binary Ninja.
+ */
+ namespace EnterpriseServer
+ {
+ /*!
+ Custom exception class for all Enterprise functions that can throw exceptions
+ */
+ struct EnterpriseException: std::runtime_error {
+ EnterpriseException(const std::string& what): std::runtime_error(what) {}
+ };
+
+ /*!
+ Authenticate to the server with username and password
+ \param username Username to authenticate with
+ \param password Password to authenticate with
+ \param remember Remember token in keychain
+ \return True if successful
+ */
+ bool AuthenticateWithCredentials(const std::string& username, const std::string& password, bool remember);
+
+ /*!
+ Authenticate with an external provider
+ \param method Provider method
+ \param remember Remember token in keychain
+ \return True if successful
+ */
+ bool AuthenticateWithMethod(const std::string& method, bool remember);
+
+ /*!
+ Get a list of accepted methods for authentication
+ \return List of (method, name) pairs
+ */
+ std::vector<std::pair<std::string, std::string>> GetAuthenticationMethods();
+
+ /*!
+ Forget saved credentials
+ \return True if successful
+ */
+ bool Deauthenticate();
+
+ /*!
+ Cancel a currently running authentication task
+ */
+ void CancelAuthentication();
+
+ /*!
+ Perform initial connect to the server, pulling signing key and time limit
+ \return True if successful
+ */
+ bool Connect();
+
+ /*!
+ Acquire or refresh a floating license
+ \param timeout Time (in minutes)
+ \return True if successful
+ */
+ bool AcquireLicense(uint64_t timeout);
+
+ /*!
+ Release the current hold on a license
+ \return True if successful
+ */
+ bool ReleaseLicense();
+
+ /*!
+ Check if the server is connected
+ \return True if connected
+ */
+ bool IsConnected();
+
+ /*!
+ Check if the user has authenticated with the server
+ \return True if authenticated
+ */
+ bool IsAuthenticated();
+
+ /*!
+ Get currently connected username
+ \return Username of currently connected user
+ */
+ std::string GetUsername();
+
+ /*!
+ Get token for current login session
+ \return Token for currently connected user
+ */
+ std::string GetToken();
+
+ /*!
+ Get the display name of the currently connected server
+ \return Display name of the currently connected server
+ */
+ std::string GetServerName();
+
+ /*!
+ Get the internal id of the currently connected server
+ \return Id of the currently connected server
+ */
+ std::string GetServerId();
+
+ /*!
+ Get the version number of the currently connected server
+ \return Version of the currently connected server
+ */
+ uint64_t GetServerVersion();
+
+ /*!
+ Get the build id string of the currently connected server
+ \return Build id of the currently connected server
+ */
+ std::string GetServerBuildId();
+
+ /*!
+ Get the expiry time for the current license
+ \return Expiry time, in seconds from the epoch
+ */
+ uint64_t GetLicenseExpirationTime();
+
+ /*!
+ Get the total length of the current license
+ \return Total time, in seconds
+ */
+ uint64_t GetLicenseDuration();
+
+ /*!
+ Get the maximum time limit for reservations
+ \return Maximum reservation time, in seconds
+ */
+ uint64_t GetReservationTimeLimit();
+
+ /*!
+ Check if the user's license is still activated
+ \return True if still activated
+ */
+ bool IsLicenseStillActivated();
+
+ /*!
+ Get the last recorded error
+ \return Error text
+ */
+ std::string GetLastError();
+
+ /*!
+ Register an object to receive callbacks when enterprise server events happen
+ \param notify Object to receive callbacks
+ */
+ void RegisterNotification(BNEnterpriseServerCallbacks* notify);
+
+ /*!
+ Un-register a previously registered notification handler object
+ \param notify Object to un-register
+ */
+ void UnregisterNotification(BNEnterpriseServerCallbacks* notify);
+
+ /*!
+ RAII object for holding an Enterprise license in a scope. Automatically
+ releases the license when destroyed.
+
+ \b Example:
+ \code{.cpp}
+
+ using namespace BinaryNinja;
+ assert(EnterpriseServer::Connect());
+ assert(EnterpriseServer::AuthenticateWithCredentials("username", "password", true));
+ {
+ EnterpriseServer::LicenseCheckout _{};
+ Ref<BinaryView> bv = OpenView("/bin/ls", true, {}, options);
+ printf("%llx\n", bv->GetStart());
+ // License is released at end of scope
+ }
+
+ \endcode
+ */
+ class LicenseCheckout
+ {
+ bool m_acquiredLicense;
+ public:
+ /*!
+ RAII constructor that checks out a license. License will be refreshed
+ automatically in a background thread while checked out, in intervals of `duration`
+ In the event of program crash, the license will expire `duration` seconds after
+ the most recent background refresh, so you may want a smaller value like 60 if
+ you expect your program to crash / be killed often.
+ See class docs for example usage.
+ \param duration Duration for refreshes and also length of each license checkout.
+ \throws EnterpriseException If license checkout fails
+ */
+ explicit LicenseCheckout(int64_t duration = 900);
+
+ ~LicenseCheckout();
+ };
+ }
+}
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 96e13e00..8a831b46 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -11,4 +11,7 @@ if(NOT DEMO)
add_subdirectory(workflows/inliner)
add_subdirectory(workflows/tailcall)
endif()
+if(ENTERPRISE)
+ add_subdirectory(enterprise_test)
+endif()
add_subdirectory(x86_extension)
diff --git a/examples/enterprise_test/CMakeLists.txt b/examples/enterprise_test/CMakeLists.txt
new file mode 100644
index 00000000..9833817c
--- /dev/null
+++ b/examples/enterprise_test/CMakeLists.txt
@@ -0,0 +1,33 @@
+cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
+
+project(enterprise_test CXX C)
+
+add_executable(${PROJECT_NAME}
+ src/enterprise_test.cpp)
+
+if(NOT BN_API_BUILD_EXAMPLES AND NOT BN_INTERNAL_BUILD)
+ # Out-of-tree build
+ find_path(
+ BN_API_PATH
+ NAMES binaryninjaapi.h
+ HINTS ../.. binaryninjaapi $ENV{BN_API_PATH}
+ REQUIRED
+ )
+ add_subdirectory(${BN_API_PATH} api)
+endif()
+
+target_link_libraries(${PROJECT_NAME}
+ binaryninjaapi)
+
+if (NOT WIN32)
+ target_link_libraries(${PROJECT_NAME}
+ dl)
+endif()
+
+set_target_properties(${PROJECT_NAME} PROPERTIES
+ CXX_STANDARD 17
+ CXX_VISIBILITY_PRESET hidden
+ CXX_STANDARD_REQUIRED ON
+ VISIBILITY_INLINES_HIDDEN ON
+ POSITION_INDEPENDENT_CODE ON
+ RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/out/bin)
diff --git a/examples/enterprise_test/src/enterprise_test.cpp b/examples/enterprise_test/src/enterprise_test.cpp
new file mode 100644
index 00000000..a0bff140
--- /dev/null
+++ b/examples/enterprise_test/src/enterprise_test.cpp
@@ -0,0 +1,101 @@
+// This is just bin-info but with an enterprise license checkout
+
+#include <sys/stat.h>
+
+#include <iostream>
+#include <cstdlib>
+
+#include "binaryninjacore.h"
+#include "binaryninjaapi.h"
+#include "enterprise.h"
+
+using namespace BinaryNinja;
+using namespace std;
+
+bool is_file(char* fname)
+{
+ struct stat buf;
+ if (stat(fname, &buf) == 0 && (buf.st_mode & S_IFREG) == S_IFREG)
+ return true;
+
+ return false;
+}
+
+int main(int argc, char* argv[])
+{
+ if (argc != 2)
+ {
+ cerr << "USAGE: " << argv[0] << " <file_name>" << endl;
+ exit(-1);
+ }
+
+ char* fname = argv[1];
+ if (!is_file(fname))
+ {
+ cerr << "Error: " << fname << " is not a regular file" << endl;
+ exit(-1);
+ }
+
+ EnterpriseServer::LicenseCheckout _{};
+
+ /* In order to initiate the bundled plugins properly, the location
+ * of where bundled plugins directory is must be set. */
+ SetBundledPluginDirectory(GetBundledPluginDirectory());
+ InitPlugins();
+
+ Ref<BinaryData> bd = new BinaryData(new FileMetadata(), argv[1]);
+ Ref<BinaryView> bv;
+ for (auto type : BinaryViewType::GetViewTypes())
+ {
+ if (type->IsTypeValidForData(bd) && type->GetName() != "Raw")
+ {
+ bv = type->Create(bd);
+ break;
+ }
+ }
+
+ if (!bv || bv->GetTypeName() == "Raw")
+ {
+ fprintf(stderr, "Input file does not appear to be an exectuable\n");
+ return -1;
+ }
+
+ bv->UpdateAnalysisAndWait();
+
+ cout << "Target: " << fname << endl << endl;
+ cout << "TYPE: " << bv->GetTypeName() << endl;
+ cout << "START: 0x" << hex << bv->GetStart() << endl;
+ cout << "ENTRY: 0x" << hex << bv->GetEntryPoint() << endl;
+ cout << "PLATFORM: " << bv->GetDefaultPlatform()->GetName() << endl;
+ cout << endl;
+
+ cout << "---------- 10 Functions ----------" << endl;
+ int x = 0;
+ for (auto func : bv->GetAnalysisFunctionList())
+ {
+ cout << hex << func->GetStart() << " " << func->GetSymbol()->GetFullName() << endl;
+ if (++x >= 10)
+ break;
+ }
+ cout << endl;
+
+ cout << "---------- 10 Strings ----------" << endl;
+ x = 0;
+ for (auto str_ref : bv->GetStrings())
+ {
+ char* str = (char*)malloc(str_ref.length + 1);
+ bv->Read(str, str_ref.start, str_ref.length);
+ str[str_ref.length] = 0;
+
+ cout << hex << str_ref.start << " (" << dec << str_ref.length << ") " << str << endl;
+ free(str);
+
+ if (++x >= 10)
+ break;
+ }
+
+ // Shutting down is required to allow for clean exit of the core
+ BNShutdown();
+
+ return 0;
+}