Skip to content

Commit 2fbd3f4

Browse files
committed
Add an example for using constant renderers to render unusual floating point constant encodings directly in the decompilation
1 parent 14cd98a commit 2fbd3f4

File tree

5 files changed

+197
-0
lines changed

5 files changed

+197
-0
lines changed

examples/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Personal+ edition (free edition doesn't get plugins due to no API access)
22
add_subdirectory(background_task)
3+
add_subdirectory(bid64_constant)
34
add_subdirectory(breakpoint)
45
add_subdirectory(encoded_strings)
56
add_subdirectory(x86_extension)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
cmake_minimum_required(VERSION 3.15 FATAL_ERROR)
2+
3+
project(bid64_constant CXX C)
4+
5+
add_library(${PROJECT_NAME} SHARED
6+
src/bid64_constant.cpp)
7+
8+
if(NOT BN_API_BUILD_EXAMPLES AND NOT BN_INTERNAL_BUILD)
9+
# Out-of-tree build
10+
find_path(
11+
BN_API_PATH
12+
NAMES binaryninjaapi.h
13+
HINTS ../.. binaryninjaapi $ENV{BN_API_PATH}
14+
REQUIRED
15+
)
16+
add_subdirectory(${BN_API_PATH} api)
17+
endif()
18+
19+
target_link_libraries(${PROJECT_NAME}
20+
binaryninjaapi)
21+
22+
set_target_properties(${PROJECT_NAME} PROPERTIES
23+
CXX_STANDARD 20
24+
CXX_VISIBILITY_PRESET hidden
25+
CXX_STANDARD_REQUIRED ON
26+
VISIBILITY_INLINES_HIDDEN ON
27+
POSITION_INDEPENDENT_CODE ON
28+
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/out/bin)
29+
30+
bn_install_plugin(${PROJECT_NAME})
366 KB
Binary file not shown.
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
// This plugin renders 64-bit binary integer decimal floating point constants directly in the
2+
// decompilation. See the sample binary at `examples/bid64_constant/sample_binary` for an
3+
// example of a binary that uses this unusual format.
4+
5+
#define _CRT_SECURE_NO_WARNINGS
6+
#include <cinttypes>
7+
#include <cstdio>
8+
#include <cstring>
9+
#include <map>
10+
#include <functional>
11+
#include <vector>
12+
#include "binaryninjaapi.h"
13+
14+
using namespace BinaryNinja;
15+
using namespace std;
16+
17+
18+
static string Bid64ToString(bool sign, uint64_t magnitude, int exponent)
19+
{
20+
if (magnitude == 0)
21+
exponent = 0;
22+
23+
string digits = to_string(magnitude);
24+
int intPartDigits = digits.length() + exponent;
25+
26+
string fracDigits;
27+
if (intPartDigits < 0)
28+
fracDigits = digits;
29+
else if (intPartDigits <= digits.length())
30+
fracDigits = digits.substr(intPartDigits);
31+
32+
int trailingZeros = 0;
33+
for (size_t i = 0; i < fracDigits.length(); i++)
34+
{
35+
if (fracDigits[(fracDigits.length() - 1) - i] != '0')
36+
break;
37+
trailingZeros++;
38+
}
39+
40+
int nonzeroFracDigits = fracDigits.length() - trailingZeros;
41+
fracDigits = fracDigits.substr(0, nonzeroFracDigits);
42+
43+
string result;
44+
if (intPartDigits > 0)
45+
{
46+
for (size_t i = 0; i < intPartDigits; i++)
47+
{
48+
if (i >= digits.length())
49+
result += "0";
50+
else
51+
result += string(1, digits[i]);
52+
}
53+
}
54+
else
55+
{
56+
result = "0";
57+
}
58+
59+
if (intPartDigits < 0 && fracDigits.length() > 0)
60+
{
61+
result += ".";
62+
for (size_t i = 0; i < -intPartDigits; i++)
63+
result += "0";
64+
result += fracDigits;
65+
}
66+
else if (fracDigits.length() > 0)
67+
{
68+
result += ".";
69+
result += fracDigits;
70+
}
71+
72+
return result;
73+
}
74+
75+
76+
class Bid64ConstantRenderer : public ConstantRenderer
77+
{
78+
public:
79+
Bid64ConstantRenderer() : ConstantRenderer("bid64_constant")
80+
{
81+
}
82+
83+
bool RenderConstant(const HighLevelILInstruction&, Type* type, int64_t val, HighLevelILTokenEmitter& tokens,
84+
DisassemblySettings* settings, BNOperatorPrecedence) override
85+
{
86+
// Typedef name doesn't survive propagation, but check for 8 byte integers with the
87+
// alternate name "long long unsigned int", which is what BID_UINT64 is a typedef for.
88+
if (!type || type->GetClass() != IntegerTypeClass)
89+
return false;
90+
if (type->GetWidth() != 8)
91+
return false;
92+
if (type->GetAlternateName() != "long long unsigned int")
93+
return false;
94+
95+
// Get sign bit and raw exponent
96+
bool sign = (val & (1LL << 63)) != 0;
97+
int rawExponent = (int)((val >> 53) & 0x3ff);
98+
if (rawExponent >= 0x300)
99+
{
100+
// Don't try and render NaN or infinity
101+
return false;
102+
}
103+
104+
// Get magnitude and actual exponent
105+
constexpr uint64_t BIAS = 398;
106+
int exponent = rawExponent - BIAS;
107+
uint64_t magnitude = val & ((1LL << 53) - 1);
108+
109+
tokens.Append(FloatingPointToken, Bid64ToString(sign, magnitude, exponent) + "_bid");
110+
return true;
111+
}
112+
};
113+
114+
115+
extern "C"
116+
{
117+
BN_DECLARE_CORE_ABI_VERSION
118+
119+
BINARYNINJAPLUGIN void CorePluginDependencies()
120+
{
121+
}
122+
123+
BINARYNINJAPLUGIN bool CorePluginInit()
124+
{
125+
ConstantRenderer::Register(new Bid64ConstantRenderer());
126+
return true;
127+
}
128+
}

python/examples/bid64_constant.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# This plugin renders 64-bit binary integer decimal floating point constants directly in the
2+
# decompilation. See the sample binary at `examples/bid64_constant/sample_binary` for an
3+
# example of a binary that uses this unusual format.
4+
5+
from binaryninja import (ConstantRenderer, InstructionTextToken, InstructionTextTokenType, IntegerType)
6+
from decimal import Decimal
7+
8+
9+
class Bid64ConstantRenderer(ConstantRenderer):
10+
renderer_name = "bid64_constant"
11+
12+
def render_constant(self, instr, type, val, tokens, settings, precedence):
13+
# Typedef name doesn't survive propagation, but check for 8 byte integers with the
14+
# alternate name "long long unsigned int", which is what BID_UINT64 is a typedef for.
15+
if not isinstance(type, IntegerType):
16+
return False
17+
if type.width != 8:
18+
return False
19+
if type.altname != 'long long unsigned int':
20+
return False
21+
22+
sign = (val & (1 << 63)) != 0
23+
raw_exponent = (val >> 53) & 0x3ff
24+
if raw_exponent >= 0x300:
25+
# Don't try and render NaN or infinity
26+
return False
27+
28+
bias = 398
29+
exponent = raw_exponent - bias
30+
31+
value = Decimal(val & ((1 << 53) - 1)) * Decimal(10.0) ** Decimal(exponent)
32+
if sign:
33+
value = -value
34+
tokens.append(InstructionTextToken(InstructionTextTokenType.FloatingPointToken, str(value) + "_bid"))
35+
return True
36+
37+
38+
Bid64ConstantRenderer().register()

0 commit comments

Comments
 (0)