diff options
| author | Brian Potchik <brian@vector35.com> | 2021-08-13 18:48:51 -0400 |
|---|---|---|
| committer | Brian Potchik <brian@vector35.com> | 2021-08-13 18:48:51 -0400 |
| commit | 801b4138784a45643a7201b7e54105ea5eb3b6c4 (patch) | |
| tree | 02916c8e665e7dbc16958b55abe08ff48372abf2 /examples/workflows | |
| parent | ac70b362bb4f15b1d1eeced7a44a222f663e695f (diff) | |
Workflows early preview.
Diffstat (limited to 'examples/workflows')
| -rw-r--r-- | examples/workflows/inliner/CMakeLists.txt | 39 | ||||
| -rw-r--r-- | examples/workflows/inliner/inliner.cpp | 159 | ||||
| -rw-r--r-- | examples/workflows/objectivec/CMakeLists.txt | 39 | ||||
| -rw-r--r-- | examples/workflows/objectivec/objectivec.cpp | 156 | ||||
| -rw-r--r-- | examples/workflows/tailcall/CMakeLists.txt | 39 | ||||
| -rw-r--r-- | examples/workflows/tailcall/tailcall.cpp | 129 |
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; + } +} |
