Skip to content

Commit 9f7a427

Browse files
committed
Add API to recognize strings from constant structure initializers
1 parent eef36c8 commit 9f7a427

7 files changed

Lines changed: 159 additions & 2 deletions

File tree

binaryninjaapi.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23303,6 +23303,22 @@ namespace BinaryNinja {
2330323303
virtual std::optional<DerivedString> RecognizeConstantData(
2330423304
const HighLevelILInstruction& instr);
2330523305

23306+
/*! Can be overridden to recognize strings for a structure initializer expression
23307+
(HLIL_STRUCT_INIT). These are produced when the optimizer folds a run of structure field
23308+
assignments into a single initializer. This is only called when all fields of the
23309+
structure are assigned constants. The \c values map provides the constant value
23310+
assigned to each field, keyed by the field's byte offset within the structure.
23311+
23312+
If a string is found, return a \c DerivedString with the string information.
23313+
23314+
\param instr High level structure initializer expression
23315+
\param type Structure type of the initializer
23316+
\param values Map from field offset to the constant value assigned to that field
23317+
\return Optional \c DerivedString for any string that is found
23318+
*/
23319+
virtual std::optional<DerivedString> RecognizeStructInit(
23320+
const HighLevelILInstruction& instr, Type* type, const std::map<uint64_t, int64_t>& values);
23321+
2330623322
/*! Registers the string recognizer.
2330723323

2330823324
\param recognizer The string recognizer to register.
@@ -23324,6 +23340,8 @@ namespace BinaryNinja {
2332423340
void* ctxt, BNHighLevelILFunction* hlil, size_t expr, BNType* type, int64_t val, BNDerivedString* result);
2332523341
static bool RecognizeConstantDataCallback(
2332623342
void* ctxt, BNHighLevelILFunction* hlil, size_t expr, BNDerivedString* result);
23343+
static bool RecognizeStructInitCallback(void* ctxt, BNHighLevelILFunction* hlil, size_t expr, BNType* type,
23344+
const uint64_t* fieldOffsets, const int64_t* fieldValues, size_t fieldCount, BNDerivedString* result);
2332723345
};
2332823346

2332923347
class CoreStringRecognizer : public StringRecognizer
@@ -23341,6 +23359,8 @@ namespace BinaryNinja {
2334123359
const HighLevelILInstruction& instr, Type* type, int64_t val) override;
2334223360
std::optional<DerivedString> RecognizeConstantData(
2334323361
const HighLevelILInstruction& instr) override;
23362+
std::optional<DerivedString> RecognizeStructInit(
23363+
const HighLevelILInstruction& instr, Type* type, const std::map<uint64_t, int64_t>& values) override;
2334423364
};
2334523365
} // namespace BinaryNinja
2334623366

binaryninjacore.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4241,6 +4241,8 @@ extern "C"
42414241
void* ctxt, BNHighLevelILFunction* hlil, size_t expr, BNType* type, int64_t val, BNDerivedString* result);
42424242
bool (*recognizeConstantData)(
42434243
void* ctxt, BNHighLevelILFunction* hlil, size_t expr, BNDerivedString* result);
4244+
bool (*recognizeStructInit)(void* ctxt, BNHighLevelILFunction* hlil, size_t expr, BNType* type,
4245+
const uint64_t* fieldOffsets, const int64_t* fieldValues, size_t fieldCount, BNDerivedString* result);
42444246
} BNCustomStringRecognizer;
42454247

42464248
typedef struct BNCustomStringTypeInfo
@@ -9409,6 +9411,9 @@ extern "C"
94099411
size_t exprIndex, BNType* type, int64_t val, BNDerivedString* out);
94109412
BINARYNINJACOREAPI bool BNStringRecognizerRecognizeConstantData(BNStringRecognizer* recognizer,
94119413
BNHighLevelILFunction* il, size_t exprIndex, BNDerivedString* out);
9414+
BINARYNINJACOREAPI bool BNStringRecognizerRecognizeStructInit(BNStringRecognizer* recognizer,
9415+
BNHighLevelILFunction* il, size_t exprIndex, BNType* type, const uint64_t* fieldOffsets,
9416+
const int64_t* fieldValues, size_t fieldCount, BNDerivedString* out);
94129417

94139418
// PossibleValueSet operations
94149419
BINARYNINJACOREAPI void BNFreePossibleValueSet(BNPossibleValueSet* object);

lang/c/pseudoc.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3053,6 +3053,17 @@ void PseudoCFunction::GetExprTextInternal(const HighLevelILInstruction& instr, H
30533053
case HLIL_STRUCT_INIT:
30543054
[&]() {
30553055
const auto hlilFunc = GetHighLevelILFunction();
3056+
auto str = hlilFunc->GetDerivedStringReferenceForExpr(instr.exprIndex);
3057+
if (str.has_value() && str.value().customType)
3058+
{
3059+
tokens.Append(BraceToken, str.value().customType->GetStringPrefix() + string("\""));
3060+
tokens.Append(StringToken, DerivedStringReferenceTokenContext,
3061+
DataBuffer(str.value().value.c_str(), str.value().value.size()).ToEscapedString(), instr.address,
3062+
instr.exprIndex);
3063+
tokens.Append(BraceToken, string("\"") + str.value().customType->GetStringPostfix());
3064+
return;
3065+
}
3066+
30563067
auto type = instr.GetType();
30573068
if (type.GetValue())
30583069
{

lang/rust/pseudorust.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2964,6 +2964,17 @@ void PseudoRustFunction::GetExprText(const HighLevelILInstruction& instr, HighLe
29642964
case HLIL_STRUCT_INIT:
29652965
[&]() {
29662966
const auto hlilFunc = GetHighLevelILFunction();
2967+
auto str = hlilFunc->GetDerivedStringReferenceForExpr(instr.exprIndex);
2968+
if (str.has_value() && str.value().customType)
2969+
{
2970+
tokens.Append(BraceToken, str.value().customType->GetStringPrefix() + string("\""));
2971+
tokens.Append(StringToken, DerivedStringReferenceTokenContext,
2972+
DataBuffer(str.value().value.c_str(), str.value().value.size()).ToEscapedString(), instr.address,
2973+
instr.exprIndex);
2974+
tokens.Append(BraceToken, string("\"") + str.value().customType->GetStringPostfix());
2975+
return;
2976+
}
2977+
29672978
auto type = instr.GetType();
29682979
if (type.GetValue())
29692980
{

python/examples/pseudo_python.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
HighLevelILTokenEmitter, HighLevelILOperation, OperatorPrecedence, ScopeType,
2525
SymbolDisplayType, SymbolDisplayResult, SymbolType, BoolType, VoidType, PointerType,
2626
NamedTypeReferenceType, StructureType, InstructionTextTokenContext, StructureMember,
27-
BinaryView, BuiltinType)
27+
BinaryView, BuiltinType, DataBuffer)
2828
from typing import Optional
2929
import struct
3030

@@ -1166,6 +1166,19 @@ def perform_get_expr_text(
11661166
self.perform_get_expr_text(instr.low, tokens, settings)
11671167
tokens.append_close_paren()
11681168
elif instr.operation == HighLevelILOperation.HLIL_STRUCT_INIT:
1169+
# Check for a recognized string before rendering as a structure initializer
1170+
derived_string = instr.derived_string_reference
1171+
if derived_string is not None and derived_string.custom_type is not None:
1172+
prefix = derived_string.custom_type.string_prefix
1173+
postfix = derived_string.custom_type.string_postfix
1174+
tokens.append(InstructionTextToken(InstructionTextTokenType.BraceToken, f'{prefix}"'))
1175+
tokens.append(InstructionTextToken(InstructionTextTokenType.StringToken,
1176+
DataBuffer(bytes(derived_string.value)).escape(),
1177+
address=instr.address, value=instr.expr_index,
1178+
context=InstructionTextTokenContext.DerivedStringReferenceTokenContext))
1179+
tokens.append(InstructionTextToken(InstructionTextTokenType.BraceToken, f'"{postfix}'))
1180+
return
1181+
11691182
# Render the structure type if it is known, otherwise just the `struct` keyword. Use
11701183
# Python-style constructor syntax for the initializer.
11711184
struct_type = instr.expr_type

python/stringrecognizer.py

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
1919
# IN THE SOFTWARE.
2020

21-
from typing import Optional, Union, Any
21+
from typing import Optional, Union, Any, Dict
2222
from dataclasses import dataclass
2323
import ctypes
2424

@@ -198,6 +198,8 @@ def register(self):
198198
if self.recognize_constant_data.__func__ != StringRecognizer.recognize_constant_data:
199199
self._cb.recognizeConstantData = self._cb.recognizeConstantData.__class__(
200200
self._recognize_constant_data)
201+
if self.recognize_struct_init.__func__ != StringRecognizer.recognize_struct_init:
202+
self._cb.recognizeStructInit = self._cb.recognizeStructInit.__class__(self._recognize_struct_init)
201203
self.handle = core.BNRegisterStringRecognizer(self.__class__.recognizer_name, self._cb)
202204
self.__class__._registered_recognizers.append(self)
203205

@@ -279,6 +281,21 @@ def _recognize_constant_data(self, ctxt, hlil, expr, result):
279281
log_error_for_exception("Unhandled Python exception in StringRecognizer._recognize_constant_data")
280282
return False
281283

284+
def _recognize_struct_init(self, ctxt, hlil, expr, type, field_offsets, field_values, field_count, result):
285+
try:
286+
hlil = highlevelil.HighLevelILFunction(handle=core.BNNewHighLevelILFunctionReference(hlil))
287+
type = types.Type.create(handle=core.BNNewTypeReference(type))
288+
instr = hlil.get_expr(highlevelil.ExpressionIndex(expr))
289+
vals = {field_offsets[i]: field_values[i] for i in range(field_count)}
290+
ref = self.recognize_struct_init(instr, type, vals)
291+
if ref is None:
292+
return False
293+
result[0] = ref._to_core_struct(True)
294+
return True
295+
except Exception:
296+
log_error_for_exception("Unhandled Python exception in StringRecognizer._recognize_struct_init")
297+
return False
298+
282299
@property
283300
def name(self) -> str:
284301
if hasattr(self, 'handle'):
@@ -379,6 +396,25 @@ def recognize_constant_data(
379396
"""
380397
return None
381398

399+
def recognize_struct_init(
400+
self, instr: 'highlevelil.HighLevelILInstruction', type: 'types.Type', vals: Dict[int, int]
401+
) -> Optional['binaryview.DerivedString']:
402+
"""
403+
Can be overridden to recognize strings for a structure initializer expression (HLIL_STRUCT_INIT).
404+
These are produced when the optimizer folds a run of structure field assignments into a single
405+
initializer. This is only called when all fields of the structure are assigned constants.
406+
The ``vals`` dictionary maps each field's byte offset within the structure to the constant value
407+
assigned to that field. If no string is found, this method should return `None`.
408+
409+
If a string is found, return a :py:class:`~binaryninja.binaryview.DerivedString` with the string information.
410+
411+
:param instr: High level structure initializer expression
412+
:param type: Structure type of the initializer
413+
:param vals: Dictionary mapping field offset to the constant value assigned to that field
414+
:return: Optional :py:class:`~binaryninja.binaryview.DerivedString` for any string that is found.
415+
"""
416+
return None
417+
382418

383419
_recognizer_cache = {}
384420

@@ -442,3 +478,17 @@ def recognize_constant_data(
442478
if not core.BNStringRecognizerRecognizeConstantData(self.handle, instr.function.handle, instr.expr_index, string):
443479
return None
444480
return binaryview.DerivedString._from_core_struct(string, True)
481+
482+
def recognize_struct_init(
483+
self, instr: 'highlevelil.HighLevelILInstruction', type: 'types.Type', vals: Dict[int, int]
484+
) -> Optional['binaryview.DerivedString']:
485+
count = len(vals)
486+
field_offsets = (ctypes.c_ulonglong * count)()
487+
field_values = (ctypes.c_longlong * count)()
488+
for i, (offset, value) in enumerate(vals.items()):
489+
field_offsets[i] = offset
490+
field_values[i] = value
491+
string = core.BNDerivedString()
492+
if not core.BNStringRecognizerRecognizeStructInit(self.handle, instr.function.handle, instr.expr_index, type.handle, field_offsets, field_values, count, string):
493+
return None
494+
return binaryview.DerivedString._from_core_struct(string, True)

stringrecognizer.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,13 @@ std::optional<DerivedString> StringRecognizer::RecognizeConstantData(const HighL
142142
}
143143

144144

145+
std::optional<DerivedString> StringRecognizer::RecognizeStructInit(
146+
const HighLevelILInstruction&, Type*, const std::map<uint64_t, int64_t>&)
147+
{
148+
return std::nullopt;
149+
}
150+
151+
145152
void StringRecognizer::Register(StringRecognizer* recognizer)
146153
{
147154
BNCustomStringRecognizer callbacks;
@@ -152,6 +159,7 @@ void StringRecognizer::Register(StringRecognizer* recognizer)
152159
callbacks.recognizeExternPointer = RecognizeExternPointerCallback;
153160
callbacks.recognizeImport = RecognizeImportCallback;
154161
callbacks.recognizeConstantData = RecognizeConstantDataCallback;
162+
callbacks.recognizeStructInit = RecognizeStructInitCallback;
155163

156164
recognizer->AddRefForRegistration();
157165
recognizer->m_object = BNRegisterStringRecognizer(recognizer->m_nameForRegister.c_str(), &callbacks);
@@ -241,6 +249,24 @@ bool StringRecognizer::RecognizeConstantDataCallback(
241249
}
242250

243251

252+
bool StringRecognizer::RecognizeStructInitCallback(void* ctxt, BNHighLevelILFunction* hlil, size_t expr, BNType* type,
253+
const uint64_t* fieldOffsets, const int64_t* fieldValues, size_t fieldCount, BNDerivedString* result)
254+
{
255+
StringRecognizer* recognizer = (StringRecognizer*)ctxt;
256+
Ref<HighLevelILFunction> hlilObj = new HighLevelILFunction(BNNewHighLevelILFunctionReference(hlil));
257+
HighLevelILInstruction instr = hlilObj->GetExpr(expr);
258+
Ref<Type> typeObj = new Type(BNNewTypeReference(type));
259+
std::map<uint64_t, int64_t> values;
260+
for (size_t i = 0; i < fieldCount; i++)
261+
values.emplace(fieldOffsets[i], fieldValues[i]);
262+
auto str = recognizer->RecognizeStructInit(instr, typeObj, values);
263+
if (!str.has_value())
264+
return false;
265+
*result = str->ToAPIObject(true);
266+
return true;
267+
}
268+
269+
244270
Ref<StringRecognizer> StringRecognizer::GetByName(const std::string& name)
245271
{
246272
BNStringRecognizer* recognizer = BNGetStringRecognizerByName(name.c_str());
@@ -329,3 +355,24 @@ std::optional<DerivedString> CoreStringRecognizer::RecognizeConstantData(
329355
return std::nullopt;
330356
return DerivedString::FromAPIObject(&str, true);
331357
}
358+
359+
360+
std::optional<DerivedString> CoreStringRecognizer::RecognizeStructInit(
361+
const HighLevelILInstruction& instr, Type* type, const std::map<uint64_t, int64_t>& values)
362+
{
363+
std::vector<uint64_t> fieldOffsets;
364+
std::vector<int64_t> fieldValues;
365+
fieldOffsets.reserve(values.size());
366+
fieldValues.reserve(values.size());
367+
for (auto [offset, value] : values)
368+
{
369+
fieldOffsets.push_back(offset);
370+
fieldValues.push_back(value);
371+
}
372+
373+
BNDerivedString str;
374+
if (!BNStringRecognizerRecognizeStructInit(m_object, instr.function->GetObject(), instr.exprIndex,
375+
type->GetObject(), fieldOffsets.data(), fieldValues.data(), values.size(), &str))
376+
return std::nullopt;
377+
return DerivedString::FromAPIObject(&str, true);
378+
}

0 commit comments

Comments
 (0)