This organization contains the implementation of GlueZilla. In GlueZilla, we protect a program against unauthorized deployment and reverse engineering by associating every software instance to a single hardware instance using the rowhammer effect. You can find more details in our paper.
GlueZilla consists of multiple components:
- gluezilla-templater in a separate repo: the templater tests the DRAM module for rowhammer-susceptible cells using different rowhammer patterns.
- gluezilla-compiler in the
compilerdirectory: the compiler compiles the protected program and matches the preselected junction bits with a rowhammer-susceptible cell discoved by the templater. Because the bit flips distribution in a DRAM module is rather sparse, the final binary has a custom binary layout to make sure every junction bit lands in a rowhammer-susceptible cell. - gluezilla-loader in the
loaderdirectory: the loader component loads the protected binary in physical memory according to the loader map produced by the gluezilla-compiler. This component is built using the reusable code in therowhammer_toolsdirectory. This directory also contains some small supporting tools for rowhammer-related experiments. - Some overarching scripts, tests, and documentation:
- The
scriptsdirectory contains several helper scripts to generate input files, convert between formats, etc. At the top of each script is a comment about its usage. - The
docsdirectory contains examples and explanations for most configuration and input files. - The
test_codedirectory contains the setup for our evaluation oncoreutilsandSPEC.
- The
The remaining of this README applies to this repo only. See the gluezilla-templater repo for more info about the templater component.
- "victim XXXX" refers to the XXXX containing the rowhammer-susceptible location in physical memory, e.g. a victim frame contains a victim byte, which contains a victim bit that we can flip using rowhammer.
- "target XXXX" refers to the XXXX containing the bit we want to get flipped in the binary, e.g. a target binary contains a target page, which contains a target byte, which contains a target bit. In our paper, we refer to this as the "junction bit".
- "page" is a page in virtual memory.
- "page frame" is a page frame in physical memory.
- The bit flip direction is usually indicated by '+' for a 0->1 flip and '-' for a 1->0 flip.
- Bit and byte indices start at 0 with the least significant bit/byte.
- Tested on Fedora 40
- Build dependencies:
cmake, ninja, clang, binutils-dev, llvm- A Rust toolchain is required for the
loadercomponent (Stablechannel is sufficient)
- Run-time dependencies:
decode-dimms, dmidecode, msr-tools - Python libraries for the scripts (python3):
pyelftools, pysqlite3, numpy, capstone, tomlkit==0.11.6, pathlib
- Build the compiler by running
./build.shinside thecompilerdirectory. - Build the supporting rowhammer tools by running
cargo build --releaseinside therowhammer_toolsdirectory. - Build the loader by running
cargo build --releaseinside theloaderdirectory. This creates thelibloader.solibrary intarget/releaseagainst which the protected program is linked.
Note: to deduce noise factors, you should run these tools pinned (e.g., using taskset -c <core_id>) to an isolated core (e.g., using isolcpus=<core_id> in /etc/default/grub (remake grub config!)), disable SMT (e.g., in BIOS), and set a fixed core frequency (e.g., using /sys pseudo-file interface).
This document visualizes the workflow (credits to Mathéo Vergnolle).
-
Template the memory and create a
victim_addresses.txtfile.-
Using the gluezilla-templater:
- Run the templater to create an SQLite database with the discoved vulnerable locations.
- Create/modify the filter query
query.sqland run./db2victim_addresses.py(from inside thescriptsdirectory).
-
Using the small
templatertool inrowhammer_tools:- Simply copy the
target/release/templaterbinary to your rowhammer machine - Create a
templater_config.tomlfile and adram_info.tomlordram_config.tomlfile in the same directory (see the examples indocs) sudo ./templater <id>- Use
json2victim_addresses.pyto filter the template by the number of times a flip occurred, and to create thevictim_addresses.txtfile
- Simply copy the
-
-
Compile the source code using the gluezilla-compiler (
compiler/compiler_build/bin/clang) with the following compiler options:-flto (put in CFLAGS and LDFLAGS) -fuse-ld=</abs/path/to/compiler_build/bin/ld.lld> -L</abs/path/to/libloader.so_dir> -lloader -Wl,-T,</abs/path/to/compiler/linker_script.ld>,-rpath=<path/to/runtime/libloader.so_dir>,-mllvm,-VA=<path/to/victim_addresses.txt>,-mllvm,-TO=<path/to/target_offsets.toml>,-mllvm,-dbl_output=<path/to/output_dir/for/compiler_info>,-mllvm,-dbl_mode=offsetsThe
offsetsmode (-dbl_mode=offsets) compiles the provided source code without modifying the layout. It does however have some DBL effects (e.g. it separates the code into special sections, changes some parts of the compilation process,...).Notes:
VA= Victim Addresses ;TO= Target Offsets- The
VA,TO,dbl_outputoptions are ignored in theoffsetsmode. You can drop them for now. - There is also a
baselinemode to compile with no DBL effects, used to create a benchmark baseline. For a clean comparison with thedblmode version, you should still compile the baseline with LTO and use lld with the custom linker script though. When no-dbl_modeoption is provided, it defaults tobaseline. - You can add
,-mllvm,-debug-only=dbl_traceto the linker flags to print debug info at compile time. - You can add the
,-mllvm,-dbl_id=<ID>option to add an ID to the output files created by the compiler. This ID is also passed on by thegenerate_attack_config.pyscript.
-
Disassemble the resulting binary with
objdump -drwC binary_name > binary_name.dumpto select the target bits (paper: junction bits) you want to flip. Those targets should be put per section in a
target_offsets.tomlfile (see the example). This is the only file you have to create by hand. For now, only bitflips in the.dbl_textsection are supported! -
Recompile the program but change the
-dbl_modeoption todbland make sure theVA,TOanddbl_outputare set correctly this time. In this step, the compiler will emit the final binary with the required custom layout along with loader info in thecompiler_output.txtfile in the directory given bydbl_output(make sure this directory exists).Note: other compiler options you added on your own (opt level, ...), should be the same for the run in
offsetsmode anddblmode! -
Run
generate_attack_config.pyto generate anattack_config.tomlfile (paper: the loader map) for the loader component. -
Copy the created binary,
libloader.soandattack_config.tomlto your associated rowhammer machine for which the RowHammer template database was created.libloader.soshould be in the assignedrpath, andattack_config.tomlshould be in the same directory as the created binary. -
Run the target binary and enjoy your bit flips.
Control the log level of the
loadercomponent at run time with the RUST_LOG environment variable. Default isinfo.sudo RUST_LOG=trace ./binary
We measure the run-time overhead introduced by the custom binary layout when using a large number of target bits.
We do not need to flip these bits at run time to measure the layout overhead.
We, therefore, use generate_random_target_offsets.py in the scripts directory to generate random target bits for a binary compiled in offsets mode.
By not flipping the random target bits, we keep the binary functional.
See the SPEC documentation and the provided SPEC config file for build and run info.
Create a DBL directory in the SPEC root directory containing the target_offsets_<benchmark_name_EXEBASE>.toml files, the victim_addresses.txt file and libloader.so.
To run the benchmark programs on a machine without flipping bits, see the Debugging section below to create versions without the loader component.
Notes:
- We do not support C++ benchmarks with exceptions.
- Gluezilla is only tested on the following benchmarks: 500.perlbench_r, 502.gcc_r, 505.mcf_r, 525.x264_r, 531.deepsjeng, 557.xz_r, 508.namd_r, 519.lbm_r, 538.imagick_r, 544.nab_r.
- The x264 benchmark has 2 extra binaries: imagevalidate_525 and ldecod_r. These are used for setup and post-validation and are not part of the actual performance benchmark. So it doesn't matter whether they run with a DBL config or not.
Used for functionality evaluation.
This directory contains patches for the cp and ls programs.
Each patch transforms the source code into its unintentional form.
This form's binary representation differs only in a small number of bits from the intended form.
At startup time, these bits are flipped and the intended behavior is reconstructed on the associated machine.
See the README.txt for build instructions.
- Do not compile with debug info! The debug info could/will be wrong and it will confuse you!
- I advise you to not use a debugger on the fully rowhammered programs.
If you want to debug the emitted program, create a version that does not rely on the
loadercomponent to load the binary:- Run
toggle_segment_type.pyto change the segment type toPT_LOADinstead ofPT_NULL(or directly change it inlinker_script.ld) - Set the environment variable
RH_TESTto an arbitrary value. This deactivates theloadercomponent.
- Run