Skip to main content

Function names

In some binaries, it is possible that the name of a library function used in the source code result in a different symbol name being added to the binary. This depends on many factors such as aliasing and weak symbols, but it’s important to consider it when writing rules because you might find a binary that uses memcmp, while another sample of the same software component, even matching the same version, uses bcmp, which is an alias for memcmp in glibc. Also, decompilers might add a prefix or suffix to the symbol name when applying their own logic to identify functions. For example, an imp. prefix means the function is external to the binary and it is therefore imported. To illustrate such situation, suppose you have three different samples of the same software component, where:
  1. Sample A uses memcmp.
  2. Sample B uses bcmp.
  3. Sample C uses bcmp, but the decompiler shows imp.bcmp instead (a prefix was added).
Assume you’re using scope:project. You could, of course, try this:
The above code works, but it’s a bit hard to read. Here’s a smarter approach:
This would match memcmp or bcmp, with or without the imp. prefix.

Inlined functions

Functions are often inlined by the compiler, meaning the function call is replaced by its code. For example, consider the following code:
Without any special compiler options or optimization, the decompiled worker function should be similar to this:
If a rule contains code like the following, it will work (assuming the binary has symbols or signatures applied):
However, the developer or the compiler might decide to inline one or more function calls. For example, small_work’s code might replace its call within worker. If small_work is the function of interest, the above rule would break, as small_work would no longer be available:
Function inlining, when done correctly, does not change the program’s semantics. It is an optimization technique commonly used by compilers to eliminate function call overhead and potentially reduce stack frame usage. You can easily detect whether a function has been inlined by using rules with logic like the following:
One might feel tempted to use project:functions({matching = "small_work|worker"}), but this would introduce a bug in the rule, since the matching order is not guaranteed. This method could return the worker function even when small_work has not been inlined, which would be a bug in the rule logic.

Arrays indexing convention

In Lua, arrays are a particular use of tables. By convention, they are 1-based.
However, any type (except nil) can be used as keys, effectively creating associative arrays:

Table emptiness

A reference to an empty table evaluates to true:
To check whether a table is empty, use the next() function:
The length operator (#) is only reliable for proper sequences (1-based arrays without holes). See: