0% found this document useful (0 votes)
111 views6 pages

Building Fast and Reliable Reverse Engineering Tools With Frida and Rust

Uploaded by

worood.n.89
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
111 views6 pages

Building Fast and Reliable Reverse Engineering Tools With Frida and Rust

Uploaded by

worood.n.89
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 6

Building fast and reliable reverse engineering tools

2022 IEEE 18th International Conference on Intelligent Computer Communication and Processing (ICCP) | 978-1-6654-6437-6/22/$31.00 ©2022 IEEE | DOI: 10.1109/ICCP56966.2022.10053941

with Frida and Rust


István-Attila CSÁSZÁR Radu Razvan SLAVESCU
Computer Science Department Computer Science Department
Technical University of Cluj-Napoca Technical University of Cluj-Napoca
Cluj-Napoca, Romania Cluj-Napoca, Romania
[email protected] [email protected]

Abstract—Reverse engineering binary applications is a key of this method is that it allows the instrumentation of compiled
process for black-box security auditing and malware analysis. binaries at runtime (i.e. without recompiling them). The instru-
Frida is a reverse engineering framework based on dynamic mentation usually consists of inserting additional instructions
binary instrumentation that allows the user to create agents,
which are injected in the analyzed process, and can communicate into the binary at runtime to allow tracking various aspects of
with the user’s program. Frida is written in C and Vala and its behavior, such as how many times was a function called
offers high level bindings in Python and JavaScript. Dynamic and with what parameters. These instrumentation frameworks
languages allow fast development iteration, a key requirement also allow the modification of existing logic, for example
when trying to discover the inner workings of an application by changing one of the parameters or the return value of a
or protocol. The main disadvantages of such languages include
performance limitations and their error-prone nature due to lack function or removing a function call entirely. Furthermore,
of type checking. In this paper we address these limitations by these frameworks allow the tracing of the binary’s execution
building bindings in Rust, which aims to offer high performance at instruction level, which allows the creation of tools such
and numerous correctness guarantees while still maintaining as the well-known memcheck [8] for detecting errors related
reasonable development iteration speed. We show examples of to memory management and cachegrind [1] for performance
performance improvements and present a real use case to validate
the usability of the library. profiling via cache simulation, both built on top of Valgrind.
Index Terms—reverse engineering, dynamic binary analysis, Frida is also suitable for lightweight dynamic analysis of
dynamic binary instrumentation, high performance processes by allowing operations such as enumerating loaded
modules, retrieving the addresses of exported functions by
I. I NTRODUCTION name and hooking (inserting inspection code just before the
Reverse engineering is a key method for analyzing and function is called and just before it returns) or replacing
understanding the inner workings of compiled programs (bina- functions.
ries), most often without access to their source code. The main Furthermore, Frida is significantly different from the other
goals can be divided in two categories: a) black-box testing frameworks (PIN and Valgrind) in the following ways:
and security assessment of binaries, and b) behavioral analysis
1) it’s implemented in Vala with platform specific code in
of malware.
C, C++ and assembly, but the main API (Application
In [1], Nethercote identifies two dimensions of the program
Programming Interface) (i.e. the main way through which
analysis domain:
programmers interact with the framework) is exposed as
• how the analysis is performed: a) statically, without
Python and JavaScript bindings.
running the program, and b) dynamically, by running the 2) it has first class support for instrumenting applications
program on mobile devices, such as iOS and Android. Both PIN
• what is used as input to the analysis: a) the source code,
and Valgrind were designed for the main use case of
and b) the binary itself (without source code) analyzing “desktop” applications (i.e. applications com-
In a recent series of reports Tenaglia and Adams also give piled for Windows, Linux and MacOS operating systems,
an overview of the various reverse engineering methods and traditionally running on a processor with X86 or X64
associated tools in [2]. architecture).
Our paper focuses on dynamic analysis of binaries by using 3) it allows the instrumentation code running in remote
the Frida reverse engineering framework [3]. Frida enables processes to connect and communicate with a tool (also
dynamic analysis through DBI (Dynamic Binary Instrumenta- using Frida) over the internet.
tion), a technique used by the popular frameworks such as PIN
[4], Valgrind [5, 6] and DynamoRIO [7]. The major advantage Since this paper also focuses on the use case of analyzing
“desktop” applications, the main feature of interest is the
advantages and disadvantages that come with exposing the
978-1-6654-6437-6/22/$31.00 ©2022 IEEE main API of Frida through scripting languages. In short, their

289

Authorized licensed use limited to: Universiti Malaysia Perlis. Downloaded on December 19,2024 at 17:22:44 UTC from IEEE Xplore. Restrictions apply.
main advantage is allowing the developers to experiment and
iterate faster on their code thanks to their interactive nature
(i.e. they can be run directly, without the need for compilation),
less strict nature (i.e. they don’t require type annotations and
perform many automatic conversions) and dynamic evaluation
(i.e. an already running script can simply use the eval
function to load and run additional code).

Contributions
• We provide an overview of the Frida framework
• We analyze the performance overhead of using Frida with
Python and JavaScript versus using Frida with Rust
• We show how Rust can be used to prevent common
programming mistakes that existing language bindings
(C, C++, Python and JavaScript) allow Fig. 1: Architecture for the most common use case of Frida
• We present examples that analyze the advantages and
disadvantages of the Python and JavaScript bindings
versus the Rust bindings for common reverse engineering • injecting a shared library containing the instrumentation
and testing use cases code into a process
• a communication layer between the tool using
Outline
frida-core and the injected library built with
The rest of this paper is organized as follows. Section II frida-gum
positions our work in the field, while Section III gives a brief
The frida-gum2 library is used to build the instrumenta-
overview of Frida and Rust. Section IV presents the design an
tion tools that will be injected into the process of interest. The
implementation of the proposed solution in detail. Section V
main features include:
shows the results of the performance and usability evaluations.
Section VI concludes and describes the intended future work. • listing loaded modules and their exported functions
• inline function hooking and replacing, provided by the
II. R ELATED WORK Interceptor API
Due to its native support for mobile devices, the Frida • code tracing, provided by the Stalker API3
framework has seen many uses, most being in the security It is very important to note that components described
assessment of communication protocols used by IoT (Internet above are actually not the main “entrypoint” of the Frida
of Things) devices. Examples include: framework. Most of the documentation and examples use the
• reverse engineering the Android Nearby Connections Python and JavaScript bindings because they are much easier
protocol, presented by Antonioli et al. in [9] to use, compared to C and C++. As such, most developers will
• creating a reverse engineering framework for analyzing interact with Frida using frida-python (Python bindings
protocols used by Linux by IoT system, presented by for frida-core) or frida-node (JavaScript (Node.js)
Liu et al. in [10] bindings for frida-core) and GumJS (JavaScript bindings
• extending Frida to be able to interoperate with applica- for frida-gum).
tions written in the Swift language, presented by Kraus Furthermore, some of the functionality offered by GumJS
and Haupert in [11] (the JavaScript bindings) is not available when using the C
implementation (frida-gum), the most important one being
III. BACKGROUND the communication layer provided by frida-core.
A. Frida 2) Architecture of tools using Frida: Figure 1 shows how
the components described in the previous subsection interact
1) Project organization: The Frida project is split into
in the main use case of Frida (injecting instrumentation code
two main components, frida-core and frida-gum, both
that uses the JavaScript API into a process).
written in Vala, with platform specific code (e.g. the code that
Assuming that that the Frida Python package is installed,
interacts directly with the operating system) written in C and
all the developer has to do is:
assembly.
frida-core1 , as the name suggests, is the central com- 1) write the instrumentation code in JavaScript
ponent that is used as a base to build tools that use Frida or 2) write a Python script that finds or spawns a process of
libraries that expose Frida’s API in a given language (language interest and injects the code written in the previous step
bindings). It also provides features that aim to make the main 1 https://github.com/frida/frida-core
steps of dynamic binary analysis faster and easier: 2 https://github.com/frida/frida-gum

• listing all running processes 3 https://frida.re/docs/stalker/

290

Authorized licensed use limited to: Universiti Malaysia Perlis. Downloaded on December 19,2024 at 17:22:44 UTC from IEEE Xplore. Restrictions apply.
B. Rust

Rust is a modern, multiparadigm programming language


with an emphasis on performance, correctness and developer
productivity. It combines the advantages of multiple languages,
such as: a) the low level control and high performance lan-
guages of C and C++; b) the strong type system of functional
languages like Haskell and OCaml, and c) easy dependency
management of JavaScript (Node.js).
1) Memory safety: To prevent common memory manage-
ment errors, such as use after free or accessing invalid memory, Fig. 2: Organization of the frida-rust project
Rust can validate the lifetime (the scope in which it is valid)
of a reference at compile time. For example, the ’static
lifetime can be used to guarantee that a reference is valid for
the whole duration of the program (i.e. it’s either allocated B. Implementation
statically by the compiler, or allocated on the heap and only
freed at the end of the program or leaked).
2) Thread safety: Concurrency issues can be very subtle, We started from the existing Frida Rust bindings4 . Two
hard to diagnose and to reproduce, thus being able to detect libraries hold only generated code for interfacing with the C
them at compile time is a significant benefit. To achieve this, API of frida-core and frida-gum, and the other two libraries
Rust uses its trait system (similar to Haskell’s type classes) expose an idiomatic Rust API by using the two interfacing
to introduce marker traits for thread safety. The Send trait libraries as shown in Figure 2.
indicates that a type can be sent across thread boundaries and The existing (incomplete) API was updated to encode
the Sync trait indicates that a type can be accessed from memory safety and thread safety requirements where they
multiple threads at the same time. were missing and more of the API surface was exposed to
Rust. Additionally an interprocess communication library was
also integrated to achieve parity with the GumJS API.
IV. D ESIGN AND I MPLEMENTATION
1) Guaranteed library initialization: Some features of
A. Design frida-gum require the global state of the library to be initialized
before they are called (otherwise undefined behavior will
Though the target audience for reverse engineering tools
occur). To enforce this requirement, the following pattern can
(security researchers) should have a good knowledge of C/C++
be used: when the runtime is initialized, a token (i.e. an in-
and low level programming issues such as memory manage-
stance of an opaque type) will be returned by the initialization
ment and concurrency problems, it’s still hard for humans to
function, and all types that require the library to be initialized
identify and properly uphold the invariants required to write
will require the token in order to be instantiated. Essentially,
correct code.
the token serves a proof that the library is initialized and it is
Rust offers a rich type system that can be used to encode safe to create instances of and use the given types.
many invariants, such that the compiler can check if they are
upheld and emit errors in case of violations, thus reducing the An example in shown in Listing 1, where the new function
mental burden on developers. requires an instance of Gum (the token proving that the
1) Memory safety: When hooking functions or instrument- runtime is initialized) in order to create an instance of
ing code, most often some extra state is kept between calls to ModuleMap.
the same instrumentation callback, for example, to count the
number of files opened or even keep a shadow stack for call impl ModuleMap {
and return instructions. This state needs to be valid as long as pub fn new(_gum: &Gum) -> Option<Self> {
let p = unsafe {
the instrumentation code is active, which in most cases is the gum_sys::gum_module_map_new()
duration of the whole program, so in order to prevent passing };
variables that are not valid for this duration, the ’static if p.is_null() {
None
lifetime can be used. } else {
2) Thread safety: If the instrumented program is multi- Some(Self::from_raw(p))
}
threaded, hooked functions and instrumentation callbacks can }
be called from multiple threads, thus the callback must update }
the state in a thread safe way. This requirement can be
Listing 1: Requiring the runtime to be initialzied (Gum)
represented in the source code using the Sync trait bound,
when instatiating ModuleMap
preventing many concurrency bugs.

291

Authorized licensed use limited to: Universiti Malaysia Perlis. Downloaded on December 19,2024 at 17:22:44 UTC from IEEE Xplore. Restrictions apply.
Overhead of an instrumented run of ripgrep
20.58±0.35s
Uninstrumented
20.0 Rust
Python and JavaScript
17.5
15.0
Execution time (s)

12.5
10.0
8.56±0.14s
7.71±0.10s 8.08±0.09s
7.5
5.0
3.29±0.14s
2.5 1.95±0.01s 1.96±0.01s 1.97±0.02s
0.41±0.00s 0.42±0.00s 0.42±0.00s 0.45±0.01s
0.0
ripgrep (85 files) tokio (580 files) clippy (1385 files) rust (21421 files)

Fig. 3: Performance

V. E VALUATION to the fact that both JavaScript and Python are dynamically
A. Performance typed scripting languages, while Rust is a compiled language
with strong, static typing, and low level control (like C and
To evaluate the change in performance impact caused C++). On the other hand, these features of Rust can also
by using the Rust bindings, the ripgrep program (recursive help speed up development by offering a better experience
searcher similar to grep) was instrumented to monitor all with IDE tooling (i.e. in general, autocomplete works better
calls to CreateFileW and CreateThread on Windows with strongly typed languages) and reducing the need for
when performing a search in repositories of various sizes. The debugging because many mistakes (typos, accessing non-
uninstrumented binary along with the tools that instrument it existent fields or methods) can be prevented at compile time.
(Python and JavaScript implementation and Rust implemen- For example, when processing messages sent by JavaScript
tation) were run 10 times, with 1 warmup run for each tool (Listing 2), each dictionary access or string comparison in
before each series of measurements. Note that the measured the message handling code Listing 3 has the potential to be
time include the time it takes for the tool to spawn the target mistyped.
process and wait for it to terminate.
The results presented in Figure 3 show that for up to send({tid, addr, id: cpu.rax})
1385 files the uninstrumented program takes about 420ms
to complete, with the Rust instrumentation it takes about Listing 2: Sending a message to the CLI tool from GumJS
1.97 seconds (4.5 fold slowdown) and with the Python and
JavaScript instrumentation it takes about 8 seconds (20 fold def on_message(self, message, data):
ty = message[’type’]
slowdown). For more than 20.000 files, the instrumentation if ty == ’send’:
code starts becoming a bottleneck, because even though the payload = message[’payload’]
runtime for the uninstrumented version doesn’t significantly print(payload)
elif ty == ’error’:
increase, the overhead of the instrumentation increases from a print("error", message)
4.5 fold slowdown to 7.3 fold slowdown and from a 20 fold else:
slowdown to a 45.3 fold slowdown for the Rust and Python raise Exception("Unknown message")
and JavaScript versions, respectively. Listing 3: Message handling code in frida-python
B. Usability
As one might expect, the implementation of Frida tools C. Real use case: Direct syscall detection
using JavaScript and Python will be more terse than the
functionally equivalent implementation in Rust. This is due To assess how well the library can be used to build tools
for a real use case, a tool that instruments programs to detect
4 https://github.com/frida/frida-rust system calls was implemented using the Python and JavaScript

292

Authorized licensed use limited to: Universiti Malaysia Perlis. Downloaded on December 19,2024 at 17:22:44 UTC from IEEE Xplore. Restrictions apply.
API of Frida, the Rust API of Frida and using the C++ API INS_InsertCall(
ins,
of PIN. IPOINT_BEFORE,
(AFUNPTR)printip,
On Windows, a system call is considered direct when the IARG_INST_PTR,
syscall instruction is outside the expected modules (ntdll.dll or IARG_REG_VALUE, REG_EAX,
win32u.dll). Thus, to detect direct system calls, an instrumen- IARG_THREAD_ID,
IARG_PTR, static_name,
tation callback can be inserted before each syscall instruction, IARG_END
and in the callback, we can inspect the processor state to obtain );
the address of the instruction (stored in RIP) and the syscall
number (stored in RAX). If the instruction is outside of the Listing 4: Inserting an instrumentation callback before an
expected modules, the syscall address and number are recorded instruction in PIN
in an output file.
VI. C ONCLUSIONS AND FUTURE WORK
To validate the correctness of the tool, we instrumented
Dynamic binary instrumentation is key tool in reverse
Dumpert5 , a tool for dumping the memory of LSASS (Local
engineering binaries without access to source code. There
Security Authority Server Service) on Windows using direct
exist multiple established frameworks such as Valgrind, PIN
system calls for detection evasion.
and DynamoRIO for creating tools for dynamic binary in-
The tools built with the Rust API of Frida and the C++ strumentation, but the require complex setup and require good
API of PIN were copied into a virtual machine and used knowledge of C/C++. The Frida framework breaks this pattern,
to instrument Dumpert. Both tools successfully detected all by introducing scriptable, zero-setup binary instrumentation,
direct system calls performed by the tool. Both tools were at the cost of the overhead incurred by scripting languages.
implemented in a way to produce output in the same format This project aims to serve as a middle ground, by creating
and the output was parsed by a script to confirm that the results Rust bindings for Frida, that offer low overhead and numerous
match. guarantees for the correctness of the instrumentation code.
For this use case, the main advantage of the Rust tool is that As future work we foresee three directions:
the tool can be easily packaged and deployed as one executable 1) Use the Rust API to add support for a second scripting
(by embedding the instrumentation dll in the command line language, with a different set of tradeoffs than JavaScript.
tool), while the PIN tool is much harder to deploy and use on For example, the Mun language6 is statically typed and
a virtual machine, since the runtime is not open source and ahead-of-time compiled, but has first class support for
there is no possibility to link the runtime and tool into a single hot reloading.
binary. 2) Add support for sockets as a transport layer for our
communication channel.
1) Usability: The code required to implement the direct 3) Extend the API to encapsulate more of the common oper-
system call detection tool in Rust, after being formatted with ations (e.g. parsing utf-16 string parameters on Windows)
the default settings of Rust’s recommended code formatter in a safe and convenient way.
(rustfmt) requires about twice as many lines of code as the
equivalent Python and JavaScript implementation (220 lines ACKNOWLEDGEMENTS
of Rust vs 101 lines of Python for the command line tool and The work for this paper has been supported in part by the
215 lines of Rust vs 64 lines of JavaScript for injected code). Computer Science Department of the Technical University of
Cluj-Napoca, Romania.
The PIN implementation with C++ for the direct syscall
detection tool is about 90 lines of code. This is due to R EFERENCES
the fact that PIN’s API is centered around implementing [1] N. Nethercote, “Dynamic binary analysis and instrumen-
instrumentation tools, with a fairly rich, platform independent tation,” University of Cambridge, Computer Laboratory,
API. The downsides of this approach is that associated Tech. Rep., 2004.
tools (i.e. a tool that finds the target process and injects the [2] S. Tenaglia and P. Adams, “Edge of the art in vulnera-
instrumentation code) have to be implemented separately bility research,” Two Six Labs, Tech. Rep., 2020.
and integrating other libraries can also be significantly more [3] O. A. V. Ravnås, “Frida: Dynamic instrumentation
difficult. PIN’s API also includes many variadic functions, toolkit for developers, reverse-engineers, and security
like the one shown in Listing 4, for which the correct usage researchers,” https://frida.re/.
can’t be checked by the C++ compiler, and as such require [4] C.-K. Luk, R. Cohn, R. Muth, H. Patil, A. Klauser,
special attention from the developer. G. Lowney, S. Wallace, V. J. Reddi, and K. Hazelwood,
“Pin: building customized program analysis tools with
dynamic instrumentation,” Acm sigplan notices, vol. 40,
no. 6, pp. 190–200, 2005.
5 https://github.com/outflanknl/Dumpert 6 https://mun-lang.org/

293

Authorized licensed use limited to: Universiti Malaysia Perlis. Downloaded on December 19,2024 at 17:22:44 UTC from IEEE Xplore. Restrictions apply.
[5] N. Nethercote and J. Seward, “Valgrind: A program
supervision framework,” Electronic notes in theoretical
computer science, vol. 89, no. 2, pp. 44–66, 2003.
[6] ——, “Valgrind: a framework for heavyweight dynamic
binary instrumentation,” ACM Sigplan notices, vol. 42,
no. 6, pp. 89–100, 2007.
[7] D. Bruening, T. Garnett, and S. Amarasinghe, “An infras-
tructure for adaptive dynamic optimization,” in Interna-
tional Symposium on Code Generation and Optimization,
2003. CGO 2003. IEEE, 2003, pp. 265–275.
[8] J. Seward and N. Nethercote, “Using valgrind to
detect undefined value errors with Bit-Precision,” in
2005 USENIX Annual Technical Conference (USENIX
ATC 05). Anaheim, CA: USENIX Association,
Apr. 2005. [Online]. Available: https://www.usenix.org/
conference/2005-usenix-annual-technical-conference/
using-valgrind-detect-undefined-value-errors-bit
[9] D. Antonioli, N. O. Tippenhauer, and K. Rasmussen,
“Nearby threats: Reversing, analyzing, and attacking
google’s’ nearby connections’ on android,” 2019.
[10] K. Liu, M. Yang, Z. Ling, H. Yan, Y. Zhang, X. Fu, and
W. Zhao, “On manually reverse engineering communica-
tion protocols of linux-based iot systems,” IEEE Internet
of Things Journal, vol. 8, no. 8, pp. 6815–6827, 2020.
[11] M. Kraus and V. Haupert, “The swift language from
a reverse engineering perspective,” in Proceedings of
the 2nd Reversing and Offensive-oriented Trends Sym-
posium, 2018, pp. 1–12.

294

Authorized licensed use limited to: Universiti Malaysia Perlis. Downloaded on December 19,2024 at 17:22:44 UTC from IEEE Xplore. Restrictions apply.

You might also like