Searching Binaries with BNQL¶
Sidekick provides a powerful way to search for items in binaries using the Binary Ninja Query Language (BNQL). BNQL is designed to retrieve Binary Ninja API objects that represent the functions, variables, sections, strings, symbols, and various other entities of a binary (including Indexes). Queries make use of relationships between these entities to efficiently find the right objects.
BNQL Syntax¶
A BNQL query consists of a series of steps (a "path"), each selecting objects based on their relationships and attributes, and filtering objects using predicates. Every query step transforms one set of objects into another set.
For example, the following query starts at the root object and progresses through three steps:
The /
character separates steps in a query. The resulting set of objects at step C is the final output of the query.
A leading /
character indicates that the query starts from the root object, which is the binary
object representing the binary being analyzed.
Selecting Objects¶
There are two ways to select objects in BNQL:
- Traversal Steps: traverse relationships from the source objects (from the previous step) to target objects. Relationships are semantic connections based on how objects relate in the binary's memory layout, control flow, data flow, or type system.
- Function Calls: call functions that return sets of objects based on arguments, addresses, or patterns.
Each step of a query can be a traversal step or a function call.
Traversal Step Syntax¶
A traversal step selects target objects by traversing relationships from the source object(s). The syntax for a traversal step is:
The <object_type>
specifies the type of target objects to select. A wildcard (*
) character matches any object type.
The <relationship>
specifies the relationship to traverse. The contains/contained-in relationships have a shorthand syntax:
contains
is the default relationship and can be omitted. For example,/function
is equivalent to/contains::function
.contained-in
is the inverse ofcontains
and has the shorthand syntax/^
. For example,/datavar/^segment
is equivalent to/datavar/contained-in::segment/
.
The complete list of available relationships are defined in the Relationship Reference section. The complete list of objects types and their corresponding Binary Ninja API classes is:
function
: Functions defined in the binary (Function)datavar
: Variables stored in the data sections (DataVariable)variable
: Local variables and parameters, when querying within a function (Variable)string
: String literals which may or may not be referenced by code or data (StringReference)symbol
: Named entities (Symbol or CoreSymbol)block
: Basic blocks (BasicBlock)instruction
: IL instructions (LowLevelILInstruction, MediumLevelILInstruction, HighLevelILInstruction)type
: Data types and structures (Type and its subclasses)segment
: Memory segments defined by the loader format (Segment)section
: Named sections defined by the loader format (Section)external-library
: External library references (ExternalLibrary)external-location
: Symbol that refers to an external location (ExternalLocation)component
: Logical grouping of functions and data variables (Component)hlil
,mlil
,llil
: High, medium, and low-level IL functions (HighLevelILFunction, MediumLevelILFunction, LowLevelILFunction)comment
: User comments (There is not corresponding Binary Ninja API object)index
: Analysis indexes are user-defined collections of objects with per-object metadata (Analysis Indexes are a Sidekick feature)
Understanding Relationship Categories¶
BNQL relationships fall into distinct conceptual categories. Understanding these categories is crucial for writing effective queries:
Hierarchical Relationships (contains
, contained-in
):
- These describe parent-child relationships in the binary's object hierarchy
- The meaning of containment depends on the object type:
- Memory-based objects: Containment is based on virtual address location, for example:
- A function is
contained-in
a section/segment where its code resides - A section
contains
functions whose addresses fall within its range
- A function is
- Logical objects: Containment represents ownership or definition, for example:
- The binary
contains
types that are defined or used within it - An index
contains
index-entries that were added to that collection
- The binary
- Note:
contains
is the default relationship and can be omitted in queries
Control Flow Relationships (calls
, called-by
):
- These describe the runtime control flow between callable entities
- Based on actual call instructions in the code
- Connect functions to other functions or external symbols
Data Flow Relationships (reads
, read‑by
, writes
, written-by
, uses
, used-by
):
- These track how data moves through the program
- Connect code (functions/instructions) to data (variables/strings)
- Based on actual memory access patterns in the code
Representation Relationships (il
, il-of
):
- These connect different representations of the same code
- A function has multiple IL representations (HLIL, MLIL, LLIL)
- Each IL form contains its own instructions and blocks
Type System Relationships (instance-of
, instance
, inherits
, inherited-by
):
- These describe the type hierarchy and type assignments
- Connect variables/functions to their types
- Model inheritance and type relationships
Reference/Pointer Relationships (address-taken-by
, takes-address-of
, has-symbol
, symbol-of
):
- These track where addresses are stored or used
- Model indirect references and function pointers
- Connect symbols to their underlying objects
Function Call Syntax¶
A function call step selects objects from addresses, patterns, and other complex criteria. The syntax for a function call step is:
Functions can be used in three contexts: 1. As query starting points - functions that return object sets:
-
In predicates - functions that return values for filtering:
-
As arguments to other functions - composing complex queries:
The complete list of built-in functions available in BNQL is defined in the Built-In BNQL Functions section.
Filtering with Predicates¶
After each step, an optional predicate can be used to filter the selected objects using a logical expression. The syntax for a predicate is:
The <expression>
is evaluated relative to each selected object. The expression can use logical operators, relational operators, and functions to filter the objects based on their attributes or relationships. Parentheses are used to group expressions and to control the order of evaluation.
The logical operators are:
and
or
not
The relational operators are:
<
: Less than>
: Greater than<=
: Less than or equal to>=
: Greater than or equal to==
: Equal to!=
: Not equal to~=
: Matches (regular expression matching)in
: Tests if a value is in a list of values
A predicate expression can itself contain a path that selects and filters related objects. For a given object, the main predicate evaluates to true if its nested path expression returns one or more objects. This allows you to filter objects based on the characteristics of the other objects they are related to.
Attributes of objects are commonly used in predicates. The syntax for accessing an attribute is:
The <attribute_name>
is the name of the attribute defined by the Binary Ninja API for the corresponding object type. For example, @name
retrieves the "name" attribute of an object.
Examples of using attributes for filtering:
Set Operations and Queries¶
A query may use the union
or |
operator to build a set of objects from path queries.
The following type of query returns objects via either path A or path B:
An example of a union query that returns functions that either call malloc
or free
:
Note: Inside predicates, use or
for Boolean logic rather than |
for set union. For example:
or
better expresses the Boolean nature of predicate expressions. Reserve |
for combining query paths when building result sets.
Practical Examples¶
Callers and Callees¶
In Binary Ninja, the target of a call instruction can be a function
(subroutine with code the binary) or a symbol
(external library function). Moreover, Binary Ninja sometimes creates thunks to imported library functions. So, sometimes an external library function is a symbol
and sometimes it is a thunk function
. To query all call targets, you MUST use the *
wildcard -- which matches any callable object type:
callees
function is often more convenient:
In contrast, called-by
always returns function objects; it can start from either a function or a symbol.
callers
function is often more convenient:
Find indirect calls using the address-taken-by
relationship. This reveals where the function's address is loaded or stored, which can help you find indirect call sites.
Disassembly¶
BNQL queries return structured objects, not raw text. To view the disassembly of a function, your query should select the function object itself.
The tool that executes the BNQL query is then responsible for formatting the returned function object into a human-readable view. You can configure the tool to display the object's disassembly, its C-like decompilation, or other available representations. This separation of concerns allows you to use a single query to retrieve an object and then view it in multiple ways depending on your analysis needs.
IL Instructions¶
Instructions are contained within the IL function forms, which are accessed through the il
relationship from a function
:
Query the IL representation that best matches your needs:
- HLIL: High-level operations and control flow
- MLIL: Medium-level operations with explicit registers
- LLIL: Low-level operations closest to assembly
The instruction objects support relationships like reads, writes, and calls to query how they interact with variables and functions. For example, to find all instructions that read a specific variable in HLIL:
Most analysis is best done at the function level. However, you can analyze instructions directly. For example, to find MLIL instructions that call malloc
:
A more efficient way to find MLIL instructions that call malloc
is to first filter for functions that call malloc
and then look at their MLIL instructions:
Data and Local Variables¶
In Binary Ninja, local variables include function parameters, register variables, and stack variables. Data variables are values stored in the data sections and may be used anywhere in the binary. Functions can read and write both local and global variables.
The datavar
object type represents global variables. The variable
object type represents local variables.
Relationship Reference¶
Here is the complete list of the available relationships for each object type:
Relationships from binary
¶
Relationship | To Object | Meaning | Example |
---|---|---|---|
contains | function | Functions defined within the binary | /function[@name=='initializeLogger'] |
contains | datavar | Global variables defined or stored in the binary's memory space | /datavar[@name=='g_configTable'] |
contains | string | String literals stored in the binary | /string[@value ~= r'password|secret|key'] |
contains | symbol | Symbols provided or imported by the binary | /symbol[@name=='pthread_create'] |
contains | segment | Memory segments defined in the binary | /segment[@name=='.text'] |
contains | section | File-format sections present in the binary | /section[@name=='.bss'] |
contains | component | Logical components or namespaces defined in the binary | /component[@name=='USART_Driver'] |
contains | type | User-defined or auto-detected types included in the binary | /type[@name=='PluginDescriptor'] |
contains | index | Analysis indices grouping functions, data, or other objects | /index |
contains | comment | User-defined comments attached to addresses or objects | /comment[@text ~= r'TODO|FIXME'] |
Relationships from function
¶
Relationship | To Object | Meaning | Example |
---|---|---|---|
calls | function | Functions directly called by this function | /function[@name=='parseRequest']/calls::function[@name=='readHeader'] |
calls | symbol | External symbols called by this function | /function[@name=='sslHandshake']/calls::symbol[@name=='BIO_write'] |
called-by | function | Functions that call this function | /function[@name=='cleanup']/called-by::function[@name=='shutdownAll'] |
contains | variable | Local variables declared or allocated in this function | /function[@name=='computeHash']/variable[@name=='tempBuf'] |
contains | block | Basic blocks that make up this function's control flow | /function[@name=='decryptBlock']/block |
contained-in | section | Sections where the function is located | /function[@name=='renderScene']/^section |
contained-in | segment | Segments where the function is located | /function[@name=='renderScene']/^segment |
contained-in | component | Components holding the function | /function[@name=='renderScene']/^component |
reads | variable | Local variables read by this function | /function[@name=='loadConfig']/reads::variable[@name=='filename'] |
reads | datavar | Global variables read by this function | /function[@name=='initDriver']/reads::datavar[@name=='g_deviceStatus'] |
writes | variable | Local variables written to by this function | /function[@name=='updateScore']/writes::variable[@name=='score'] |
writes | datavar | Global variables written to by this function | /function[@name=='commitSettings']/writes::datavar[@name=='g_userConfig'] |
uses | variable | Local variables read or written by this function | /function[@name=='processEvents']/uses::variable[@name=='eventQueue'] |
uses | datavar | Global variables read or written by this function | /function[@name=='handleConnection']/uses::datavar[@name=='g_connectionPool'] |
uses | string | String literals referenced by this function | /function[@name=='reportError']/uses::string[@value=='ERR_MSG_CONN_FAILED'] |
address-taken-by | instruction | Instructions that take this function's address | /function[@name=='logMessage']/address-taken-by::instruction[@address==0x400523] |
address-taken-by | function | Functions that take this function's address | /function[@name=='onTimer']/address-taken-by::function[@name=='registerCallback'] |
address-taken-by | datavar | Global variables that store this function's address | /function[@name=='interruptHandler']/address-taken-by::datavar[@name=='g_irqVector'] |
takes-address-of | function | Functions whose addresses are taken by this function | /function[@name=='setupCallback']/takes-address-of::function[@name=='callbackA'] |
takes-address-of | variable | Local variables whose addresses are taken by this function | /function[@name=='fillBuffer']/takes-address-of::variable[@name=='buf'] |
takes-address-of | datavar | Global variables whose addresses are taken by this function | /function[@name=='loader']/takes-address-of::datavar[@name=='g_dataPool'] |
instance-of | type | Type signature or prototype of this function | /function[@name=='compareStrings']/instance-of::type[@name=='int(*)(char*,char*)'] |
has-symbol | symbol | Symbol associated with this function | /function[@name=='main']/has-symbol::symbol[@name=='entry_main'] |
il | hlil | High-level IL form of this function | /function[@name=='computeOffsets']/il::hlil |
il | mlil | Medium-level IL form of this function | /function[@name=='parseHeaders']/il::mlil |
il | llil | Low-level IL form of this function | /function[@name=='AES_decrypt']/il::llil |
Relationships from block
¶
Relationship | To Object | Meaning | Example |
---|---|---|---|
next | block | Blocks that follow this block in control flow | /block[@address==0x1000]/next::block[@address==0x1020] |
prev | block | Blocks that precede this block in control flow | /block[@address==0x1020]/prev::block[@address==0x1000] |
contains | instruction | Instructions contained within this block | /block[@address==0x5000]/instruction[@operation == 'HLIL_CALL'] |
contained-in | function | Function that contains this block | /block[@address==0x5000]/contained-in::function[@name=='processFile'] |
Relationships from type
¶
Relationship | To Object | Meaning | Example |
---|---|---|---|
referenced-by | instruction | Instructions that involve this type | /type[@name=='list_t']/referenced-by::instruction |
instance | datavar | Global variables that are instances of this type | /type[@name=='HttpHeader']/instance::datavar[@name=='g_defaultHeader'] |
instance | function | Functions that are instances of this function type | /type[@name=='int(*)(void)']/instance::function[@name=='taskRunner'] |
instance | variable | Local variables that are instances of this type | /type[@name=='ConfigStruct']/instance::variable[@name=='config'] |
inherits | type | Base types that this type inherits from | /type[@name=='DerivedClass']/inherits::type[@name=='BaseClass'] |
inherited-by | type | Types that inherit from this type | /type[@name=='BaseClass']/inherited-by::type[@name=='DerivedClass'] |
Relationships from datavar
¶
Relationship | To Object | Meaning | Example |
---|---|---|---|
instance-of | type | Type assigned to this global variable | /datavar[instance-of::type[@width > 1024]] |
has-symbol | symbol | Symbol associated with this global variable | /datavar[@name=='g_ioBuffer']/has-symbol::symbol[@name=='IO_BUFFER'] |
read-by | function | Functions that read this global variable | /datavar[@name=='g_bufferSize']/read-by::function[@name=='initBuffers'] |
write-by | function | Functions that write to this global variable | /datavar[@name=='g_errorCount']/write-by::function[@name=='logError'] |
used-by | function | Functions that read or write this global variable | /datavar[@name=='g_lockFlag']/used-by::function[@name=='acquireLock'] |
address-taken-by | datavar | Global variables that store this variable's address | /datavar[@name=='g_primaryBuffer']/address-taken-by::datavar[@name=='g_bufferTable'] |
address-taken-by | function | Functions that take this variable's address | /datavar[@name=='g_handler']/address-taken-by::function[@name=='configureHandler'] |
takes-address-of | datavar | Global variables whose addresses are stored in this variable | /datavar[@name=='g_secondaryBuffer']/takes-address-of::datavar[@name=='g_primaryBuffer'] |
takes-address-of | function | Functions whose addresses are stored in this variable | /datavar[@name=='g_irqVector']/takes-address-of::function[@name=='timerIRQ'] |
contained-in | section | Sections where the variable is located | /datavar[@name=='table']/^section |
contained-in | segment | Segments where the variable is located | /datavar[@name=='count']/^segment |
contained-in | component | Components holding the variable | /datavar[@name~='yy.*']/^component |
Relationships from string
¶
Relationship | To Object | Meaning | Example |
---|---|---|---|
used-by | function | Functions that reference this string literal | /string[@value=='STR_WELCOME_MESSAGE']/used-by::function[@name=='displayWelcomeScreen'] |
Relationships from symbol
¶
Relationship | To Object | Meaning | Example |
---|---|---|---|
symbol-of | function | Function that this symbol represents | /symbol[@name=='init_symbols']/symbol-of::function[@name=='SymbolInit'] |
symbol-of | datavar | Global variable that this symbol represents | /symbol[@name=='APP_VERSION']/symbol-of::datavar[@name=='g_versionString'] |
called-by | function | Functions that call this symbol | /symbol[@name=='fwrite']/called-by::function[@name=='writeLog'] |
contained-in | section | Section where this symbol is defined | /symbol[@name=='main']/contained-in::section |
contained-in | segment | Segment where this symbol is defined | /symbol[@name=='main']/contained-in::segment |
Relationships from variable
¶
Relationship | To Object | Meaning | Example |
---|---|---|---|
instance-of | type | Type assigned to this local variable | /function/variable[@name=='cmdBuffer']/instance-of::type[@name=='char*'] |
contained-in | function | Function that declares this local variable | /function/variable[@name=='loopCounter']/^function |
address-taken-by | instruction | Instructions that take this variable's address | /function/variable[@name=='tempValue']/address-taken-by::instruction[@address==0x401234] |
read-by | function | Functions that read this local variable | /function/variable[@name=='tempValue']/read-by::function[@name=='computeAverage'] |
write-by | function | Functions that write to this local variable | /function/variable[@name=='counter']/write-by::function[@name=='incrementCounter'] |
used-by | function | Functions that read or write this local variable | /function/variable[@name=='status']/used-by::function[@name=='pollHardware'] |
Relationships from component
¶
Relationship | To Object | Meaning | Example |
---|---|---|---|
contains | component | Components nested within this component | /component[@name=='AudioEngine']/component[@name=='EffectsProcessor'] |
contains | function | Functions contained within this component | /component[@name=='USART_Driver']/function[@name=='USART_Send'] |
contains | datavar | Global variables contained within this component | /component[@name=='ConfigManager']/datavar[@name=='g_logLevel'] |
contained-in | component | Parent component that contains this component | /component[@name=='EffectsProcessor']/contained-in::component[@name=='AudioEngine'] |
Relationships from segment
¶
Relationship | To Object | Meaning | Example |
---|---|---|---|
contains | function | Functions located in this segment | /segment[@name=='.text']/function[@name=='drawPixel'] |
contains | datavar | Global variables located in this segment | /segment[@name=='.data']/datavar[@name=='g_colorPalette'] |
contains | string | String literals located in this segment | /segment[@name=='.rodata']/string[@value=='BANNER_TEXT'] |
contains | section | File-format sections covered by this segment | /segment[@name=='.data']/section[@name=='.data'] |
Relationships from section
¶
Relationship | To Object | Meaning | Example |
---|---|---|---|
contains | function | Functions located in this section | /section[@name=='.text']/function[@name=='updateFrame'] |
contains | datavar | Global variables located in this section | /section[@name=='.data']/datavar[@name=='g_fontTable'] |
contains | string | String literals located in this section | /section[@name=='.rodata']/string[@value=='ERR_MSG_TIMEOUT'] |
Relationships from llil
or mlil
or hlil
¶
Relationship | To Object | Meaning | Example |
---|---|---|---|
contains | instruction | IL instructions that make up this IL function | callers("analyzePacket")/il::hlil/instruction |
contains | block | Basic blocks that make up this IL function | callers("log_event")[count(il::mlil/block) > 10] |
il-of | function | Source function that this IL represents | xref(0x405510)/^hlil/il-of::function |
Relationships from instruction
¶
Relationship | To Object | Meaning | Example |
---|---|---|---|
reads | variable | Local variables read by this instruction | function(0x400123)/il::mlil/instruction[reads::variable[@name=='loopIndex']] |
reads | datavar | Global variables read by this instruction | function('sub_1000ea9')/il::hlil/instruction[reads::datavar[@name=='g_flags']] |
writes | variable | Local variables written to by this instruction | function(0x401000)/il::mlil/instruction[writes::variable[@name=='accumulator']] |
writes | datavar | Global variables written to by this instruction | function('analyzePacket')/il::hlil/instruction[writes::datavar[@name=='g_globalCount']] |
uses | variable | Local variables read or written by this instruction | function(0x402010)/il::mlil/instruction[uses::variable[@name=='tempBuffer']] |
uses | datavar | Global variables read or written by this instruction | function(0x4020A0)/il::llil/instruction[uses::datavar[@name=='g_someFlag']] |
uses | string | String literals referenced by this instruction | function(0x403000)/il::llil/instruction[uses::string[@value=='LOG_INFO_FORMAT']] |
takes-address-of | variable | Local variables whose addresses are taken by this instruction | function(0x404010)/il::mlil/instruction[takes-address-of::variable[@name=='outputBuffer']] |
calls | function | Functions directly called by this instruction | function(0x404050)/il::mlil/instruction[calls::function[@name=='sub_400200']] |
calls | symbol | External symbols called by this instruction | function(0x404080)/il::mlil/instruction[calls::symbol[@name=='puts']] |
contained-in | llil, mlil, hlil | IL function that contains this instruction | xref(0x405000)/contained-in::hlil |
Built-In BNQL Functions¶
The following functions are available in BNQL:
Core Object Selection Functions¶
-
address(<address>)
: Returns address object from a numeric address. -
pattern(<hex_pattern>)
: Returns address objects for matches of a byte pattern in the binary. -
function(<name_or_address>)
: A convenience function that returns a function object by name or address. The address may be any address contained by the function. -
type(<name>)
: A convenience function that returns a type object by name. -
datavar(<name_or_address>)
: A convenience function that returns a data variable object by name or address. -
symbol(<name_or_address>)
: A convenience function that returns a symbol object by name or address.
Call Graph Functions¶
-
callers(<function>)
: Returns functions that call the specified function. -
callees(<function>)
: Returns functions that are called by the specified function. -
reachable-from(<function>, <depth>=none)
: Returns functions reachable from the given function within the specified depth of the call graph. -
reaching-to(<function>, <depth>=none)
: Returns functions that can reach the given function within the specified depth of the call graph.
Complexity and Analysis Functions¶
entropy(<object>)
: Returns the entropy of the object's code or data.
Set-Related Functions¶
-
count(<path_expression>)
: Returns the count of objects in the set. -
first(<path_expression>)
: Returns the first object in the set.
Get the first call made by the 'init' function:
Cross-Reference Functions¶
xref(<address>)
: Returns instruction and data variable cross-references to the specified address.