Skip to content

Commit 314d2ca

Browse files
committed
Add an example for using constant renderers to render unusual floating point constant encodings directly in the decompilation
1 parent f080d1c commit 314d2ca

File tree

5 files changed

+217
-0
lines changed

5 files changed

+217
-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: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
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+
int displayedExponent = 0;
27+
if (intPartDigits > 10 || intPartDigits < -4)
28+
{
29+
int newExponent = 1 - (int)digits.length();
30+
displayedExponent = exponent - newExponent;
31+
intPartDigits = digits.length() + newExponent;
32+
}
33+
34+
string fracDigits;
35+
if (intPartDigits < 0)
36+
fracDigits = digits;
37+
else if (intPartDigits <= (int)digits.length())
38+
fracDigits = digits.substr(intPartDigits);
39+
40+
int trailingZeros = 0;
41+
for (size_t i = 0; i < fracDigits.length(); i++)
42+
{
43+
if (fracDigits[(fracDigits.length() - 1) - i] != '0')
44+
break;
45+
trailingZeros++;
46+
}
47+
48+
int nonzeroFracDigits = (int)fracDigits.length() - trailingZeros;
49+
fracDigits = fracDigits.substr(0, nonzeroFracDigits);
50+
51+
string result;
52+
if (sign)
53+
result = "-";
54+
if (intPartDigits > 0)
55+
{
56+
for (size_t i = 0; i < intPartDigits; i++)
57+
{
58+
if (i >= digits.length())
59+
result += "0";
60+
else
61+
result += string(1, digits[i]);
62+
}
63+
}
64+
else
65+
{
66+
result += "0";
67+
}
68+
69+
if (intPartDigits < 0 && fracDigits.length() > 0)
70+
{
71+
result += ".";
72+
for (size_t i = 0; i < -intPartDigits; i++)
73+
result += "0";
74+
result += fracDigits;
75+
}
76+
else if (fracDigits.length() > 0)
77+
{
78+
result += ".";
79+
result += fracDigits;
80+
}
81+
82+
if (displayedExponent > 0)
83+
result += "E+" + to_string(displayedExponent);
84+
else if (displayedExponent < 0)
85+
result += "E" + to_string(displayedExponent);
86+
87+
return result;
88+
}
89+
90+
91+
class Bid64ConstantRenderer : public ConstantRenderer
92+
{
93+
public:
94+
Bid64ConstantRenderer() : ConstantRenderer("bid64_constant")
95+
{
96+
}
97+
98+
bool RenderConstant(const HighLevelILInstruction&, Type* type, int64_t val, HighLevelILTokenEmitter& tokens,
99+
DisassemblySettings* settings, BNOperatorPrecedence) override
100+
{
101+
// Typedefs have the final type, so make sure it is a 64 bit integer. The registered name
102+
// should be the typedef "BID_UINT64".
103+
if (!type || type->GetClass() != IntegerTypeClass)
104+
return false;
105+
if (type->GetWidth() != 8)
106+
return false;
107+
auto name = type->GetRegisteredName();
108+
if (!name || name->GetName().GetString() != "BID_UINT64")
109+
return false;
110+
111+
// Get sign bit and raw exponent
112+
bool sign = (val & (1LL << 63)) != 0;
113+
int rawExponent = (int)((val >> 53) & 0x3ff);
114+
if (rawExponent >= 0x300)
115+
{
116+
// Don't try and render NaN or infinity
117+
return false;
118+
}
119+
120+
// Get magnitude and actual exponent
121+
constexpr uint64_t BIAS = 398;
122+
int exponent = rawExponent - BIAS;
123+
uint64_t magnitude = val & ((1LL << 53) - 1);
124+
125+
tokens.Append(FloatingPointToken, Bid64ToString(sign, magnitude, exponent) + "_bid");
126+
return true;
127+
}
128+
};
129+
130+
131+
extern "C"
132+
{
133+
BN_DECLARE_CORE_ABI_VERSION
134+
135+
BINARYNINJAPLUGIN void CorePluginDependencies()
136+
{
137+
}
138+
139+
BINARYNINJAPLUGIN bool CorePluginInit()
140+
{
141+
ConstantRenderer::Register(new Bid64ConstantRenderer());
142+
return true;
143+
}
144+
}

python/examples/bid64_constant.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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+
# Typedefs have the final type, so make sure it is a 64 bit integer. The registered name
14+
# should be the typedef "BID_UINT64".
15+
if not isinstance(type, IntegerType):
16+
return False
17+
if type.width != 8:
18+
return False
19+
if type.registered_name is None or type.registered_name.name != 'BID_UINT64':
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+
magnitude = val & ((1 << 53) - 1)
31+
32+
if magnitude == 0:
33+
exponent = 0
34+
35+
value = Decimal(magnitude) * Decimal(10.0) ** Decimal(exponent)
36+
if sign:
37+
value = -value
38+
tokens.append(InstructionTextToken(InstructionTextTokenType.FloatingPointToken, str(value) + "_bid"))
39+
return True
40+
41+
42+
Bid64ConstantRenderer().register()

0 commit comments

Comments
 (0)