Skip to content

Commit 76215b8

Browse files
committed
Move workflow_objc to API repo, remove CFString & relptr renderers
1 parent 17b2cb5 commit 76215b8

File tree

16 files changed

+892
-0
lines changed

16 files changed

+892
-0
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
BasedOnStyle: WebKit
3+
...
4+

plugins/workflow_objc/.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
build/
2+
3+
cmake-build-*/
4+
.idea/
5+
6+
.cache/
7+
compile_commands.json
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright (c) 2022-2023 Jon Palmisciano. All rights reserved.
3+
*
4+
* Use of this source code is governed by the BSD 3-Clause license; the full
5+
* terms of the license can be found in the LICENSE.txt file.
6+
*/
7+
8+
#pragma once
9+
10+
#include <binaryninjaapi.h>
11+
12+
using AnalysisContextRef = BinaryNinja::Ref<BinaryNinja::AnalysisContext>;
13+
using BinaryViewRef = BinaryNinja::Ref<BinaryNinja::BinaryView>;
14+
using LLILFunctionRef = BinaryNinja::Ref<BinaryNinja::LowLevelILFunction>;
15+
using SymbolRef = BinaryNinja::Ref<BinaryNinja::Symbol>;
16+
using TypeRef = BinaryNinja::Ref<BinaryNinja::Type>;
17+
18+
using BinaryViewPtr = BinaryNinja::BinaryView*;
19+
using TypePtr = BinaryNinja::Type*;
20+
21+
using BinaryViewID = std::size_t;
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
2+
3+
project(workflow_objc)
4+
5+
6+
if((NOT BN_API_PATH) AND (NOT BN_INTERNAL_BUILD))
7+
set(BN_API_PATH $ENV{BN_API_PATH})
8+
if(NOT BN_API_PATH)
9+
message(FATAL_ERROR "Provide path to Binary Ninja API source in BN_API_PATH")
10+
endif()
11+
endif()
12+
if(NOT BN_INTERNAL_BUILD)
13+
set(HEADLESS ON CACHE BOOL "")
14+
add_subdirectory(${BN_API_PATH} ${PROJECT_BINARY_DIR}/api)
15+
endif()
16+
17+
# Binary Ninja plugin ----------------------------------------------------------
18+
19+
set(PLUGIN_SOURCE
20+
GlobalState.h
21+
GlobalState.cpp
22+
MessageHandler.cpp
23+
MessageHandler.h
24+
Plugin.cpp
25+
Workflow.h
26+
Workflow.cpp)
27+
28+
add_library(workflow_objc SHARED ${PLUGIN_SOURCE})
29+
target_link_libraries(workflow_objc binaryninjaapi)
30+
target_compile_features(workflow_objc PRIVATE cxx_std_17 c_std_99)
31+
32+
# Library targets linking against the Binary Ninja API need to be compiled with
33+
# position-independent code on Linux.
34+
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
35+
target_compile_options(workflow_objc PRIVATE "-fPIC")
36+
endif()
37+
38+
# Configure plugin output directory for internal builds, otherwise configure
39+
# plugin installation for public builds.
40+
if(BN_INTERNAL_BUILD)
41+
set_target_properties(workflow_objc PROPERTIES
42+
LIBRARY_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR}
43+
RUNTIME_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR})
44+
else()
45+
bn_install_plugin(workflow_objc)
46+
endif()
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Contribution Guidelines
2+
3+
Contributions in the form of issues and pull requests are welcome! See the
4+
sections below if you are contributing code.
5+
6+
## Conventions
7+
8+
Refer to the [WebKit Style Guide](https://webkit.org/code-style-guidelines/)
9+
when in doubt.
10+
11+
## Formatting
12+
13+
Let `clang-format` take care of it. The built-in WebKit style is used.
14+
15+
```sh
16+
clang-format -i --style=WebKit <file>
17+
```
18+
19+
- Split long lines when it improves readability. 80 columns is the preferred
20+
maximum line length, but use some judgement and don't split lines just because a
21+
semicolon exceeds the length limit, etc.
22+
23+
## Testing
24+
25+
If you are making changes to the core analysis library, run the test suite and
26+
ensure that there are no unexpected changes to analysis behavior.

plugins/workflow_objc/Constants.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/*
2+
* Copyright (c) 2022-2023 Jon Palmisciano. All rights reserved.
3+
*
4+
* Use of this source code is governed by the BSD 3-Clause license; the full
5+
* terms of the license can be found in the LICENSE.txt file.
6+
*/
7+
8+
#pragma once
9+
10+
constexpr auto PluginLoggerName = "Plugin.Objective-C";
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* Copyright (c) 2022-2023 Jon Palmisciano. All rights reserved.
3+
*
4+
* Use of this source code is governed by the BSD 3-Clause license; the full
5+
* terms of the license can be found in the LICENSE.txt file.
6+
*/
7+
8+
#include "GlobalState.h"
9+
10+
#include <set>
11+
#include <shared_mutex>
12+
#include <unordered_map>
13+
14+
static std::unordered_map<BinaryViewID, MessageHandler*> g_messageHandlers;
15+
static std::shared_mutex g_messageHandlerLock;
16+
static std::unordered_map<BinaryViewID, SharedAnalysisInfo> g_viewInfos;
17+
static std::shared_mutex g_viewInfoLock;
18+
static std::set<BinaryViewID> g_ignoredViews;
19+
20+
21+
MessageHandler* GlobalState::messageHandler(BinaryViewRef bv)
22+
{
23+
std::unique_lock<std::shared_mutex> lock(g_messageHandlerLock);
24+
auto messageHandler = g_messageHandlers.find(id(bv));
25+
if (messageHandler != g_messageHandlers.end())
26+
return messageHandler->second;
27+
auto newMessageHandler = new MessageHandler(bv);
28+
g_messageHandlers[id(bv)] = newMessageHandler;
29+
return newMessageHandler;
30+
}
31+
32+
BinaryViewID GlobalState::id(BinaryViewRef bv)
33+
{
34+
return bv->GetFile()->GetSessionId();
35+
}
36+
37+
void GlobalState::addIgnoredView(BinaryViewRef bv)
38+
{
39+
g_ignoredViews.insert(id(std::move(bv)));
40+
}
41+
42+
bool GlobalState::viewIsIgnored(BinaryViewRef bv)
43+
{
44+
return g_ignoredViews.count(id(std::move(bv))) > 0;
45+
}
46+
47+
SharedAnalysisInfo GlobalState::analysisInfo(BinaryViewRef data)
48+
{
49+
{
50+
std::shared_lock<std::shared_mutex> lock(g_viewInfoLock);
51+
if (auto it = g_viewInfos.find(id(data)); it != g_viewInfos.end())
52+
if (data->GetStart() == it->second->imageBase)
53+
return it->second;
54+
}
55+
56+
std::unique_lock<std::shared_mutex> lock(g_viewInfoLock);
57+
SharedAnalysisInfo info = std::make_shared<AnalysisInfo>();
58+
info->imageBase = data->GetStart();
59+
60+
if (auto objcStubs = data->GetSectionByName("__objc_stubs"))
61+
{
62+
info->objcStubsStartEnd = {objcStubs->GetStart(), objcStubs->GetEnd()};
63+
info->hasObjcStubs = true;
64+
}
65+
66+
auto meta = data->QueryMetadata("Objective-C");
67+
if (!meta)
68+
{
69+
g_viewInfos[id(data)] = info;
70+
return info;
71+
}
72+
73+
auto metaKVS = meta->GetKeyValueStore();
74+
if (metaKVS["version"]->GetUnsignedInteger() != 1)
75+
{
76+
BinaryNinja::LogError("workflow_objc: Invalid metadata version received!");
77+
g_viewInfos[id(data)] = info;
78+
return info;
79+
}
80+
for (const auto& selAndImps : metaKVS["selRefImplementations"]->GetArray())
81+
info->selRefToImp[selAndImps->GetArray()[0]->GetUnsignedInteger()] = selAndImps->GetArray()[1]->GetUnsignedIntegerList();
82+
for (const auto& selAndImps : metaKVS["selImplementations"]->GetArray())
83+
info->selToImp[selAndImps->GetArray()[0]->GetUnsignedInteger()] = selAndImps->GetArray()[1]->GetUnsignedIntegerList();
84+
85+
g_viewInfos[id(data)] = info;
86+
return info;
87+
}
88+
89+
bool GlobalState::hasAnalysisInfo(BinaryViewRef data)
90+
{
91+
return data->QueryMetadata("Objective-C") != nullptr;
92+
}
93+
94+
bool GlobalState::hasFlag(BinaryViewRef bv, const std::string& flag)
95+
{
96+
return bv->QueryMetadata(flag);
97+
}
98+
99+
void GlobalState::setFlag(BinaryViewRef bv, const std::string& flag)
100+
{
101+
bv->StoreMetadata(flag, new BinaryNinja::Metadata("YES"));
102+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright (c) 2022-2023 Jon Palmisciano. All rights reserved.
3+
*
4+
* Use of this source code is governed by the BSD 3-Clause license; the full
5+
* terms of the license can be found in the LICENSE.txt file.
6+
*/
7+
8+
#pragma once
9+
10+
#include <condition_variable>
11+
#include "BinaryNinja.h"
12+
13+
#include "MessageHandler.h"
14+
15+
/**
16+
* Namespace to hold metadata flag key constants.
17+
*/
18+
namespace Flag {
19+
20+
constexpr auto DidRunWorkflow = "objectiveNinja.didRunWorkflow";
21+
constexpr auto DidRunStructureAnalysis = "objectiveNinja.didRunStructureAnalysis";
22+
23+
}
24+
25+
struct AnalysisInfo {
26+
std::uint64_t imageBase;
27+
bool hasObjcStubs = false;
28+
std::pair<uint64_t, uint64_t> objcStubsStartEnd;
29+
std::unordered_map<uint64_t, std::vector<uint64_t>> selRefToImp;
30+
std::unordered_map<uint64_t, std::vector<uint64_t>> selToImp;
31+
};
32+
33+
typedef std::shared_ptr<AnalysisInfo> SharedAnalysisInfo;
34+
35+
/**
36+
* Global state/storage interface.
37+
*/
38+
class GlobalState {
39+
/**
40+
* Get the ID for a view.
41+
*/
42+
static BinaryViewID id(BinaryViewRef);
43+
44+
public:
45+
/**
46+
* Get the analysis info for a view.
47+
*/
48+
static SharedAnalysisInfo analysisInfo(BinaryViewRef);
49+
50+
/**
51+
* Get ObjC Message Handler for a view
52+
*/
53+
static MessageHandler* messageHandler(BinaryViewRef);
54+
55+
/**
56+
* Check if analysis info exists for a view.
57+
*/
58+
static bool hasAnalysisInfo(BinaryViewRef);
59+
60+
/**
61+
* Add a view to the list of ignored views.
62+
*/
63+
static void addIgnoredView(BinaryViewRef);
64+
65+
/**
66+
* Check if a view is ignored.
67+
*/
68+
static bool viewIsIgnored(BinaryViewRef);
69+
70+
/**
71+
* Check if the a metadata flag is present for a view.
72+
*/
73+
static bool hasFlag(BinaryViewRef, const std::string&);
74+
75+
/**
76+
* Set a metadata flag for a view.
77+
*/
78+
static void setFlag(BinaryViewRef, const std::string&);
79+
};

plugins/workflow_objc/LICENSE.txt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
Copyright (c) 2022-2023 Jon Palmisciano
2+
3+
Redistribution and use in source and binary forms, with or without modification,
4+
are permitted provided that the following conditions are met:
5+
6+
1. Redistributions of source code must retain the above copyright notice, this
7+
list of conditions and the following disclaimer.
8+
9+
2. Redistributions in binary form must reproduce the above copyright notice,
10+
this list of conditions and the following disclaimer in the documentation
11+
and/or other materials provided with the distribution.
12+
13+
3. Neither the name of the copyright holder nor the names of its contributors
14+
may be used to endorse or promote products derived from this software without
15+
specific prior written permission.
16+
17+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
21+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
24+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#include "MessageHandler.h"
2+
3+
using namespace BinaryNinja;
4+
5+
MessageHandler::MessageHandler(Ref<BinaryView> data)
6+
{
7+
m_msgSendFunctions = findMsgSendFunctions(data);
8+
}
9+
10+
std::set<uint64_t> MessageHandler::findMsgSendFunctions(BinaryNinja::Ref<BinaryNinja::BinaryView> data)
11+
{
12+
std::set<uint64_t> results;
13+
14+
const auto authStubsSection = data->GetSectionByName("__auth_stubs");
15+
const auto stubsSection = data->GetSectionByName("__stubs");
16+
const auto authGotSection = data->GetSectionByName("__auth_got");
17+
const auto gotSection = data->GetSectionByName("__got");
18+
const auto laSymbolPtrSection = data->GetSectionByName("__la_symbol_ptr");
19+
20+
// Shorthand to check if a symbol lies in a given section.
21+
auto sectionContains = [](Ref<Section> section, Ref<Symbol> symbol) {
22+
const auto start = section->GetStart();
23+
const auto length = section->GetLength();
24+
const auto address = symbol->GetAddress();
25+
26+
return (uint64_t)(address - start) <= length;
27+
};
28+
29+
// There can be multiple `_objc_msgSend` symbols in the same binary; there
30+
// may even be lots. Some of them are valid, others aren't. In order of
31+
// preference, `_objc_msgSend` symbols in the following sections are
32+
// preferred:
33+
//
34+
// 1. __auth_stubs
35+
// 2. __stubs
36+
// 3. __auth_got
37+
// 4. __got
38+
// ?. __la_symbol_ptr
39+
//
40+
// There is often an `_objc_msgSend` symbol that is a stub function, found
41+
// in the `__stubs` section, which will come with an imported symbol of the
42+
// same name in the `__got` section. Not all `__objc_msgSend` calls will be
43+
// routed through the stub function, making it important to make note of
44+
// both symbols' addresses. Furthermore, on ARM64, the `__auth{stubs,got}`
45+
// sections are preferred over their unauthenticated counterparts.
46+
const auto candidates = data->GetSymbolsByName("_objc_msgSend");
47+
for (const auto& c : candidates) {
48+
if ((authStubsSection && sectionContains(authStubsSection, c))
49+
|| (stubsSection && sectionContains(stubsSection, c))
50+
|| (authGotSection && sectionContains(authGotSection, c))
51+
|| (gotSection && sectionContains(gotSection, c))
52+
|| (laSymbolPtrSection && sectionContains(laSymbolPtrSection, c))) {
53+
results.insert(c->GetAddress());
54+
}
55+
}
56+
57+
return results;
58+
}
59+
60+
bool MessageHandler::isMessageSend(uint64_t functionAddress)
61+
{
62+
return m_msgSendFunctions.count(functionAddress);
63+
}

0 commit comments

Comments
 (0)