summaryrefslogtreecommitdiff
path: root/examples/workflows
diff options
context:
space:
mode:
authorBrian Potchik <brian@vector35.com>2021-08-13 18:48:51 -0400
committerBrian Potchik <brian@vector35.com>2021-08-13 18:48:51 -0400
commit801b4138784a45643a7201b7e54105ea5eb3b6c4 (patch)
tree02916c8e665e7dbc16958b55abe08ff48372abf2 /examples/workflows
parentac70b362bb4f15b1d1eeced7a44a222f663e695f (diff)
Workflows early preview.
Diffstat (limited to 'examples/workflows')
-rw-r--r--examples/workflows/inliner/CMakeLists.txt39
-rw-r--r--examples/workflows/inliner/inliner.cpp159
-rw-r--r--examples/workflows/objectivec/CMakeLists.txt39
-rw-r--r--examples/workflows/objectivec/objectivec.cpp156
-rw-r--r--examples/workflows/tailcall/CMakeLists.txt39
-rw-r--r--examples/workflows/tailcall/tailcall.cpp129
6 files changed, 561 insertions, 0 deletions
diff --git a/examples/workflows/inliner/CMakeLists.txt b/examples/workflows/inliner/CMakeLists.txt
new file mode 100644
index 00000000..59611afb
--- /dev/null
+++ b/examples/workflows/inliner/CMakeLists.txt
@@ -0,0 +1,39 @@
+cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
+
+project(workflow_inliner)
+
+if((NOT BN_API_PATH) AND (NOT BN_INTERNAL_BUILD))
+ set(BN_API_PATH $ENV{BN_API_PATH})
+ if(NOT BN_API_PATH)
+ message(FATAL_ERROR "Provide path to Binary Ninja API source in BN_API_PATH")
+ endif()
+endif()
+if(NOT BN_INTERNAL_BUILD)
+ add_subdirectory(${BN_API_PATH} ${PROJECT_BINARY_DIR}/api)
+endif()
+
+file(GLOB SOURCES
+ *.cpp
+ *.c
+ *.h)
+
+add_library(workflow_inliner SHARED ${SOURCES})
+
+target_link_libraries(workflow_inliner binaryninjaapi)
+
+set_target_properties(workflow_inliner PROPERTIES
+ CXX_STANDARD 17
+ CXX_VISIBILITY_PRESET hidden
+ CXX_STANDARD_REQUIRED ON
+ C_STANDARD 99
+ C_STANDARD_REQUIRED ON
+ C_VISIBILITY_PRESET hidden
+ VISIBILITY_INLINES_HIDDEN ON
+ POSITION_INDEPENDENT_CODE ON)
+
+if(BN_INTERNAL_BUILD)
+ plugin_rpath(workflow_inliner)
+ set_target_properties(workflow_inliner PROPERTIES
+ LIBRARY_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR}
+ RUNTIME_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR})
+endif()
diff --git a/examples/workflows/inliner/inliner.cpp b/examples/workflows/inliner/inliner.cpp
new file mode 100644
index 00000000..01eacbd2
--- /dev/null
+++ b/examples/workflows/inliner/inliner.cpp
@@ -0,0 +1,159 @@
+#define _CRT_SECURE_NO_WARNINGS
+#define NOMINMAX
+
+#include <cinttypes>
+#include <cstdint>
+#include <cstdio>
+#include <string>
+#include <tuple>
+#include <unordered_map>
+
+#include "binaryninjaapi.h"
+#include "lowlevelilinstruction.h"
+#include "mediumlevelilinstruction.h"
+
+
+using namespace BinaryNinja;
+using namespace std;
+
+#if defined(_MSC_VER)
+#define snprintf _snprintf
+#endif
+
+
+extern "C"
+{
+ BN_DECLARE_CORE_ABI_VERSION
+
+ unordered_map<uint64_t, set<uint64_t>> g_callSiteInlines;
+
+ void FunctionInliner(Ref<AnalysisContext> analysisContext)
+ {
+ Ref<Function> function = analysisContext->GetFunction();
+ Ref<BinaryView> data = function->GetView();
+
+ set<uint64_t> callSiteInlines;
+ if (const auto& itr = g_callSiteInlines.find(function->GetStart()); itr != g_callSiteInlines.end())
+ callSiteInlines = itr->second;
+
+ bool updated = false;
+ uint8_t opcode[BN_MAX_INSTRUCTION_LENGTH];
+ InstructionInfo iInfo;
+ Ref<LowLevelILFunction> llilFunc = analysisContext->GetLowLevelILFunction();
+ for (const auto inlineAddr : callSiteInlines)
+ {
+ // if (m_owner->IsAborted())
+ // return;
+
+ for (auto& i : llilFunc->GetBasicBlocks())
+ {
+ Ref<Architecture> arch = i->GetArchitecture();
+ for (size_t instrIndex = i->GetStart(); instrIndex < i->GetEnd(); instrIndex++)
+ {
+ LowLevelILInstruction instr = llilFunc->GetInstruction(instrIndex);
+
+ if (instr.address != inlineAddr)
+ continue;
+
+ if (instr.operation != LLIL_CALL)
+ {
+ LogWarn("Failed to inline function at: 0x%" PRIx64 ". Mapping to LLIL_CALL Failed!", instr.address);
+ continue;
+ }
+
+ uint64_t platformAddr;
+ LowLevelILInstruction destExpr = instr.GetDestExpr<LLIL_CALL>();
+ RegisterValue target = destExpr.GetValue();
+ if (target.IsConstant())
+ platformAddr = target.value;
+ else
+ {
+ LogWarn("Failed to inline function at: 0x%" PRIx64 ". Destination not Constant!", instr.address);
+ continue;
+ }
+
+ size_t opLen = data->Read(opcode, instr.address, arch->GetMaxInstructionLength());
+ if (!opLen || !arch->GetInstructionInfo(opcode, instr.address, opLen, iInfo))
+ continue;
+ Ref<Platform> platform = iInfo.archTransitionByTargetAddr ? function->GetPlatform()->GetAssociatedPlatformByAddress(platformAddr) : function->GetPlatform();
+ if (platform)
+ {
+ Ref<Function> targetFunc = data->GetAnalysisFunction(platform, platformAddr);
+ auto targetLlil = targetFunc->GetLowLevelIL();
+ LowLevelILLabel inlineStartLabel;
+ llilFunc->MarkLabel(inlineStartLabel);
+ instr.Replace(llilFunc->Goto(inlineStartLabel));
+
+ llilFunc->PrepareToCopyFunction(targetLlil);
+ for (auto& ti : targetLlil->GetBasicBlocks())
+ {
+ llilFunc->PrepareToCopyBlock(ti);
+ for (size_t tinstrIndex = ti->GetStart(); tinstrIndex < ti->GetEnd(); tinstrIndex++)
+ {
+ LowLevelILInstruction tinstr = targetLlil->GetInstruction(tinstrIndex);
+ if (tinstr.operation == LLIL_RET)
+ {
+ LowLevelILLabel label;
+ label.operand = instrIndex + 1;
+ llilFunc->AddInstruction(llilFunc->Pop(instr.size));
+ llilFunc->AddInstruction(llilFunc->Goto(label));
+ }
+ else
+ llilFunc->AddInstruction(tinstr.CopyTo(llilFunc));
+ }
+ }
+ llilFunc->Finalize();
+ }
+
+ updated = true;
+ break;
+ }
+ }
+ }
+
+ if (!updated)
+ return;
+
+ // Updates found, regenerate SSA form
+ llilFunc->GenerateSSAForm();
+ }
+
+
+ BINARYNINJAPLUGIN bool CorePluginInit()
+ {
+ auto inlinerIsValid = [](BinaryView* view, Function* func) {
+ if (auto workflow = func->GetWorkflow(); workflow)
+ return workflow->Contains("extension.functionInliner");
+ return false;
+ };
+
+ // PluginCommand::RegisterForFunction(
+ // "Optimizer\\Inline All Calls to Current Function",
+ // "Inline all calls to the current function.",
+ // [](BinaryView* view, Function* func) {
+ // LogError("TODO Inline Current Function: %" PRIx64, func->GetStart());
+ // }, inlinerIsValid);
+
+ PluginCommand::RegisterForFunction(
+ "Optimizer\\Inline Function at Current Call Site",
+ "Inline function call at current call site.",
+ [](BinaryView* view, Function* func) {
+ // TODO func->Inform("inlinedCallSites")
+ // TODO resolve multiple embedded inlines
+ g_callSiteInlines[func->GetStart()].insert(view->GetCurrentOffset());
+ func->Reanalyze();
+ }, inlinerIsValid);
+
+ Ref<Workflow> inlinerWorkflow = Workflow::Instance()->Clone("InlinerWorkflow");
+ inlinerWorkflow->RegisterActivity(new Activity("extension.functionInliner", &FunctionInliner));
+ inlinerWorkflow->Insert("core.function.translateTailCalls", "extension.functionInliner");
+ Workflow::RegisterWorkflow(inlinerWorkflow,
+ R"#({
+ "title" : "Function Inliner (Example)",
+ "description" : "This analysis stands in as an example to demonstrate Binary Ninja's extensible analysis APIs. ***Note** this feature is under active development and subject to change without notice.",
+ "capabilities" : []
+ })#");
+
+ return true;
+ }
+}
diff --git a/examples/workflows/objectivec/CMakeLists.txt b/examples/workflows/objectivec/CMakeLists.txt
new file mode 100644
index 00000000..e807235c
--- /dev/null
+++ b/examples/workflows/objectivec/CMakeLists.txt
@@ -0,0 +1,39 @@
+cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
+
+project(workflow_objectivec)
+
+if((NOT BN_API_PATH) AND (NOT BN_INTERNAL_BUILD))
+ set(BN_API_PATH $ENV{BN_API_PATH})
+ if(NOT BN_API_PATH)
+ message(FATAL_ERROR "Provide path to Binary Ninja API source in BN_API_PATH")
+ endif()
+endif()
+if(NOT BN_INTERNAL_BUILD)
+ add_subdirectory(${BN_API_PATH} ${PROJECT_BINARY_DIR}/api)
+endif()
+
+file(GLOB SOURCES
+ *.cpp
+ *.c
+ *.h)
+
+add_library(workflow_objectivec SHARED ${SOURCES})
+
+target_link_libraries(workflow_objectivec binaryninjaapi)
+
+set_target_properties(workflow_objectivec PROPERTIES
+ CXX_STANDARD 17
+ CXX_VISIBILITY_PRESET hidden
+ CXX_STANDARD_REQUIRED ON
+ C_STANDARD 99
+ C_STANDARD_REQUIRED ON
+ C_VISIBILITY_PRESET hidden
+ VISIBILITY_INLINES_HIDDEN ON
+ POSITION_INDEPENDENT_CODE ON)
+
+if(BN_INTERNAL_BUILD)
+ plugin_rpath(workflow_objectivec)
+ set_target_properties(workflow_objectivec PROPERTIES
+ LIBRARY_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR}
+ RUNTIME_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR})
+endif()
diff --git a/examples/workflows/objectivec/objectivec.cpp b/examples/workflows/objectivec/objectivec.cpp
new file mode 100644
index 00000000..e967c5e3
--- /dev/null
+++ b/examples/workflows/objectivec/objectivec.cpp
@@ -0,0 +1,156 @@
+#define _CRT_SECURE_NO_WARNINGS
+#define NOMINMAX
+
+#include <cinttypes>
+#include <cstdint>
+#include <cstdio>
+#include <string>
+#include <tuple>
+#include <unordered_map>
+
+#include "binaryninjaapi.h"
+#include "lowlevelilinstruction.h"
+#include "mediumlevelilinstruction.h"
+
+
+using namespace BinaryNinja;
+using namespace std;
+
+#if defined(_MSC_VER)
+#define snprintf _snprintf
+#endif
+
+
+extern "C"
+{
+ BN_DECLARE_CORE_ABI_VERSION
+
+ // TODO
+ // * __objc_const missing xrefs to implementations
+ void ObjectiveCAnalysis(Ref<AnalysisContext> analysisContext)
+ {
+ Ref<Function> function = analysisContext->GetFunction();
+ Ref<BinaryView> data = function->GetView();
+
+ static unordered_map<uint64_t, tuple<string, uint64_t>> classData;
+ static bool sInit = false;
+ if (!sInit) // TODO move to module-based workflow activity with run-once semantics
+ {
+ auto constSection = data->GetSectionByName("__objc_const");
+ if (!constSection)
+ return;
+
+ BinaryReader reader(data);
+ reader.SetEndianness(data->GetDefaultEndianness()); // TODO fix GetDefaultEndianness for non-elf formats
+ reader.Seek(constSection->GetStart());
+
+ reader.Read32();
+ reader.Read32();
+ reader.Read32();
+ reader.Read32();
+ reader.Read64();
+ uint64_t namePtr = reader.Read64();
+ reader.Read64();
+ reader.Read64();
+ reader.Read64();
+ reader.Read64();
+ reader.Read64();
+ uint32_t methodListFlags = reader.Read32();
+ uint32_t methodListCount = reader.Read32();
+ for (uint32_t i = 0; i < methodListCount; i++) // section end/symbol validation
+ {
+ uint64_t selector = reader.Read64();
+ uint64_t typePtr = reader.Read64();
+ uint64_t impPtr = reader.Read64();
+ //string methodName = reader.ReadCString(selector);
+ string typeEncoding = "";//reader.ReadCString(typePtr);
+ classData.insert_or_assign(selector, std::forward_as_tuple(typeEncoding, impPtr));
+ }
+
+ reader.Seek(namePtr);
+ string className = reader.ReadCString();
+
+ auto cfStringSection = data->GetSectionByName("__cfstring");
+ if (!cfStringSection)
+ return;
+
+ sInit = true;
+ }
+
+ bool updated = false;
+ uint8_t opcode[BN_MAX_INSTRUCTION_LENGTH];
+ InstructionInfo iInfo;
+
+ // // if (m_owner->IsAborted())
+ // // return;
+
+ // TODO fix this....
+ auto sym = data->GetSymbolByRawName("_objc_msgSend");
+ if (!sym)
+ return;
+ uint64_t msgSendAddr = sym->GetAddress();
+
+ Ref<LowLevelILFunction> llilFunc = analysisContext->GetLowLevelILFunction();
+ auto ssa = llilFunc->GetSSAForm();
+ if (!ssa)
+ return;
+ for (auto& i : ssa->GetBasicBlocks())
+ {
+ Ref<Architecture> arch = i->GetArchitecture();
+ for (size_t instrIndex = i->GetStart(); instrIndex < i->GetEnd(); instrIndex++)
+ {
+ LowLevelILInstruction instr = ssa->GetInstruction(instrIndex);
+
+ // TODO process subexpressions
+ if (instr.operation != LLIL_CALL_SSA)
+ continue;
+
+ LowLevelILInstruction destExpr = instr.GetDestExpr<LLIL_CALL_SSA>();
+ if (msgSendAddr == (uint64_t)destExpr.GetValue().value)
+ {
+ auto params = instr.GetParameterExprs<LLIL_CALL_SSA>();
+ if ((params.size() >= 2) && (params[0].operation == LLIL_REG_SSA) && (params[1].operation == LLIL_REG_SSA))
+ {
+ auto selfSSAReg = params[0].GetSourceSSARegister<LLIL_REG_SSA>();
+ auto selSSAReg = params[1].GetSourceSSARegister<LLIL_REG_SSA>();
+ if (auto itr = classData.find(ssa->GetSSARegisterValue(selSSAReg).value); itr != classData.end())
+ {
+ size_t llilIndex = ssa->GetNonSSAInstructionIndex(instrIndex);
+ LowLevelILInstruction llilInstr = llilFunc->GetInstruction(llilIndex);
+ auto destExpr = llilInstr.GetDestExpr<LLIL_CALL>();
+ const auto& [typeEncoding, impPtr] = itr->second;
+ destExpr.Replace(llilFunc->ConstPointer(destExpr.size, impPtr, destExpr));
+ llilInstr.Replace(llilFunc->Call(destExpr.exprIndex, llilInstr));
+ analysisContext->Inform("directRefs", "insert", impPtr, i->GetArchitecture(), instr.address);
+ updated = true;
+ }
+ }
+ // else
+ // LogError("Unhandled _objc_msgSend: 0x%" PRIx64, instr.address);
+ }
+ }
+ }
+
+ if (!updated)
+ return;
+
+ // Updates found, regenerate SSA form
+ llilFunc->GenerateSSAForm();
+ }
+
+
+ BINARYNINJAPLUGIN bool CorePluginInit()
+ {
+ Ref<Workflow> objectiveCWorkflow = Workflow::Instance()->Clone("ObjectiveCWorkflow");
+ objectiveCWorkflow->RegisterActivity(new Activity("extension.objectiveC", &ObjectiveCAnalysis));
+ objectiveCWorkflow->Insert("core.function.translateTailCalls", "extension.objectiveC");
+ Workflow::RegisterWorkflow(objectiveCWorkflow,
+ R"#({
+ "title" : "Objective C Meta-Analysis (Example)",
+ "description" : "This analysis stands in as an example to demonstrate Binary Ninja's extensible analysis APIs. ***Note** this feature is under active development and subject to change without notice.",
+ "capabilities" : []
+ })#");
+
+ return true;
+ }
+}
diff --git a/examples/workflows/tailcall/CMakeLists.txt b/examples/workflows/tailcall/CMakeLists.txt
new file mode 100644
index 00000000..d1d547a4
--- /dev/null
+++ b/examples/workflows/tailcall/CMakeLists.txt
@@ -0,0 +1,39 @@
+cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
+
+project(workflow_tailcall)
+
+if((NOT BN_API_PATH) AND (NOT BN_INTERNAL_BUILD))
+ set(BN_API_PATH $ENV{BN_API_PATH})
+ if(NOT BN_API_PATH)
+ message(FATAL_ERROR "Provide path to Binary Ninja API source in BN_API_PATH")
+ endif()
+endif()
+if(NOT BN_INTERNAL_BUILD)
+ add_subdirectory(${BN_API_PATH} ${PROJECT_BINARY_DIR}/api)
+endif()
+
+file(GLOB SOURCES
+ *.cpp
+ *.c
+ *.h)
+
+add_library(workflow_tailcall SHARED ${SOURCES})
+
+target_link_libraries(workflow_tailcall binaryninjaapi)
+
+set_target_properties(workflow_tailcall PROPERTIES
+ CXX_STANDARD 17
+ CXX_VISIBILITY_PRESET hidden
+ CXX_STANDARD_REQUIRED ON
+ C_STANDARD 99
+ C_STANDARD_REQUIRED ON
+ C_VISIBILITY_PRESET hidden
+ VISIBILITY_INLINES_HIDDEN ON
+ POSITION_INDEPENDENT_CODE ON)
+
+if(BN_INTERNAL_BUILD)
+ plugin_rpath(workflow_tailcall)
+ set_target_properties(workflow_tailcall PROPERTIES
+ LIBRARY_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR}
+ RUNTIME_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR})
+endif()
diff --git a/examples/workflows/tailcall/tailcall.cpp b/examples/workflows/tailcall/tailcall.cpp
new file mode 100644
index 00000000..9305a51d
--- /dev/null
+++ b/examples/workflows/tailcall/tailcall.cpp
@@ -0,0 +1,129 @@
+#define _CRT_SECURE_NO_WARNINGS
+#define NOMINMAX
+
+#include <cinttypes>
+#include <cstdint>
+#include <cstdio>
+#include <string>
+#include <tuple>
+#include <unordered_map>
+
+#include "binaryninjaapi.h"
+#include "lowlevelilinstruction.h"
+#include "mediumlevelilinstruction.h"
+
+
+using namespace BinaryNinja;
+using namespace std;
+
+#if defined(_MSC_VER)
+#define snprintf _snprintf
+#endif
+
+
+extern "C"
+{
+ BN_DECLARE_CORE_ABI_VERSION
+
+ void TailCallTranslation(Ref<AnalysisContext> analysisContext)
+ {
+ Ref<Function> function = analysisContext->GetFunction();
+ Ref<BinaryView> data = function->GetView();
+
+ bool updated = false;
+ uint8_t opcode[BN_MAX_INSTRUCTION_LENGTH];
+ InstructionInfo iInfo;
+
+ // Look for jumps to other functions
+ Ref<LowLevelILFunction> llilFunc = analysisContext->GetLowLevelILFunction();
+ for (auto& i : llilFunc->GetBasicBlocks())
+ {
+ // if (m_owner->IsAborted())
+ // return;
+
+ Ref<Architecture> arch = i->GetArchitecture();
+ size_t instrIndex = i->GetEnd() - 1;
+ LowLevelILInstruction instr = llilFunc->GetInstruction(instrIndex);
+ if (instr.operation != LLIL_JUMP)
+ continue;
+
+ uint64_t platformAddr;
+ LowLevelILInstruction destExpr = instr.GetDestExpr<LLIL_JUMP>();
+ RegisterValue target = destExpr.GetValue();
+ if (target.IsConstant())
+ platformAddr = target.value;
+ else if (target.state == ImportedAddressValue) // Call to imported function, look up type from import symbol
+ platformAddr = target.value;
+ else if (target.state == ExternalPointerValue && target.offset == 0)
+ platformAddr = target.value;
+ else
+ continue;
+
+ size_t opLen = data->Read(opcode, instr.address, arch->GetMaxInstructionLength());
+ if (!opLen || !arch->GetInstructionInfo(opcode, instr.address, opLen, iInfo))
+ continue;
+ Ref<Platform> platform = iInfo.archTransitionByTargetAddr ? function->GetPlatform()->GetAssociatedPlatformByAddress(platformAddr) : function->GetPlatform();
+ if (platform)
+ {
+ bool canReturn = true;
+ Ref<Function> targetFunc = nullptr;
+ if (target.state == ImportedAddressValue)
+ {
+ DataVariable var;
+ if (data->GetDataVariableAtAddress(target.value, var))
+ {
+ if (var.type && (var.type->GetClass() == PointerTypeClass) && (var.type->GetChildType()->GetClass() == FunctionTypeClass))
+ canReturn = var.type->GetChildType()->CanReturn().GetValue();
+ }
+ }
+ else if (target.state == ExternalPointerValue && target.offset == 0)
+ {
+ targetFunc = data->GetAnalysisFunction(platform, platformAddr);
+ if (targetFunc)
+ canReturn = targetFunc->CanReturn();
+ }
+ else
+ {
+ targetFunc = data->GetAnalysisFunction(platform, platformAddr);
+ if (targetFunc)
+ canReturn = targetFunc->CanReturn();
+ else
+ continue;
+ }
+
+ updated = true;
+ instr.Replace(llilFunc->TailCall(destExpr.exprIndex, instr));
+ analysisContext->Inform("directRefs", "insert", platformAddr, i->GetArchitecture(), instr.address);
+
+ if (!canReturn)
+ {
+ analysisContext->Inform("directNoReturnCalls", "insert", i->GetArchitecture(), instr.address);
+ i->GetSourceBlock()->SetCanExit(false);
+ i->SetCanExit(false);
+ }
+ }
+ }
+
+ if (!updated)
+ return;
+
+ // Updates found, regenerate SSA form
+ llilFunc->GenerateSSAForm();
+ }
+
+
+ BINARYNINJAPLUGIN bool CorePluginInit()
+ {
+ Ref<Workflow> customTailCallWorkflow = Workflow::Instance()->Clone("CustomTailCallWorkflow");
+ customTailCallWorkflow->RegisterActivity(new Activity("extension.translateTailCalls", &TailCallTranslation));
+ customTailCallWorkflow->Replace("core.function.translateTailCalls", "extension.translateTailCalls");
+ Workflow::RegisterWorkflow(customTailCallWorkflow,
+ R"#({
+ "title" : "Tail Call Translation (Example)",
+ "description" : "This analysis stands in as an example to demonstrate Binary Ninja's extensible analysis APIs. ***Note** this feature is under active development and subject to change without notice.",
+ "capabilities" : []
+ })#");
+
+ return true;
+ }
+}