Skip to content

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:

/A/B/C

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:

  1. 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.
  2. 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:

<relationship>::<object_type>

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 of contains 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
  • 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
  • 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:

<function_name>(<arg1>, <arg2>, ...)

Functions can be used in three contexts: 1. As query starting points - functions that return object sets:

pattern("48 89 5c 24")/^function
address(0x401000)/contained-in::*

  1. In predicates - functions that return values for filtering:

    /function[count(calls::*) > 10]
    

  2. As arguments to other functions - composing complex queries:

    /function[count(xref(@address)) > 5]
    

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:

[<expression>]

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:

@<attribute_name>

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:

function("main")/variable[@name == "arg_k"]
/function[@is_thunk]

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:

A | B

An example of a union query that returns functions that either call malloc or free:

/function[calls::function[@name == 'malloc']] | /function[calls::function[@name == 'free']]

Note: Inside predicates, use or for Boolean logic rather than | for set union. For example:

/function[calls::function[@name == 'malloc'] or calls::function[@name == 'free']]
While both operators would produce the same filtering result within a predicate, 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:

function("caller")/calls::*[@name == 'target']
Note: The callees function is often more convenient:
callees("caller")[@name == 'target']

In contrast, called-by always returns function objects; it can start from either a function or a symbol.

symbol('target')/called-by::function
Note: The callers function is often more convenient:
callers('target')

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.

/function[@name == 'target']/address-taken-by::*

Disassembly

BNQL queries return structured objects, not raw text. To view the disassembly of a function, your query should select the function object itself.

function("sha256_update")

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:

/function/il::hlil/instruction
/function/il::mlil/instruction
/function/il::llil/instruction

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:

/function[@name == 'create_query_term_plan']/il::hlil/instruction[reads::variable[@name == 'arg1']]

Most analysis is best done at the function level. However, you can analyze instructions directly. For example, to find MLIL instructions that call malloc:

/function/il::mlil/instruction[calls::*[@name == '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:

callers('malloc')/il::mlil/instruction[calls::*[@name == 'malloc']]

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.

    address(0x401000)/contained-in::*
    

  • pattern(<hex_pattern>): Returns address objects for matches of a byte pattern in the binary.

    pattern("48 89 5c 24")/^function
    

  • 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.

    function("myFunction")/calls::*
    function(0x401000)/uses::string
    

  • type(<name>): A convenience function that returns a type object by name.

    type("MyStructType")/referenced-by::*
    

  • datavar(<name_or_address>): A convenience function that returns a data variable object by name or address.

    datavar("g_configTable")/used-by::function
    datavar(0x404000)/used-by::function
    

  • symbol(<name_or_address>): A convenience function that returns a symbol object by name or address.

    symbol("main")/^section
    

Call Graph Functions

  • callers(<function>): Returns functions that call the specified function.

    callers("recvfrom")
    callers(0x18000000)
    

  • callees(<function>): Returns functions that are called by the specified function.

    callees("command_handler")
    

  • reachable-from(<function>, <depth>=none): Returns functions reachable from the given function within the specified depth of the call graph.

    reachable-from(function("main"), 2)[uses::datavar[@name == 'g_configTable']]
    

  • reaching-to(<function>, <depth>=none): Returns functions that can reach the given function within the specified depth of the call graph.

    reaching-to(function("free"))[not calls::symbol[@name == 'malloc']]
    

Complexity and Analysis Functions

  • entropy(<object>): Returns the entropy of the object's code or data.
    /datavar[entropy(.) > 7.5]
    /function[entropy(.) > 6.0]
    
  • count(<path_expression>): Returns the count of objects in the set.

    /function[count(calls::*) > 10]
    /datavar[count(read-by::*) > 5]
    

  • first(<path_expression>): Returns the first object in the set.

Get the first call made by the 'init' function:

first(function('init')/calls::*)

Cross-Reference Functions

  • xref(<address>): Returns instruction and data variable cross-references to the specified address.
    xref(0x401000)/contained-in::function