Dynamic offset generation is essential for tools like ecapture that need to interact with internal OpenSSL structures, whose layouts can change between versions. This process ensures that the tool remains compatible with a wide range of OpenSSL releases, including future updates, by programmatically determining the memory offsets of relevant struct fields.
Workflow Overview#
The ecapture project automates offset generation using a combination of Bash scripts and C utilities. The workflow involves cloning the OpenSSL source code, checking out specific version tags, building OpenSSL, compiling a C program that calculates offsets, and generating version-specific header files. These headers are then used by the eBPF kernel bytecode to correctly interpret OpenSSL internals at runtime.
Utilities and Scripts#
For each major OpenSSL version series, ecapture provides a dedicated Bash script (e.g., utils/openssl_offset_3.0.sh for OpenSSL 3.0.x, utils/openssl_offset_3.5.sh for 3.5.x). These scripts orchestrate the following steps:
- Repository Preparation: If the OpenSSL source is not already present, the script clones the official OpenSSL repository.
- Version Iteration: The script iterates over a map of supported patch versions, checking out each relevant git tag.
- Build Process: For each version, the script configures and builds OpenSSL to generate the necessary headers.
- Offset Calculation: The script copies a C offset generator (e.g.,
openssl_3_0_offset.coropenssl_3_5_0_offset.c) into the OpenSSL source directory and compiles it. - Header Generation: The compiled offset utility is executed, and its output—C preprocessor
#definestatements for struct offsets—is redirected into a version-specific header file (e.g.,kern/openssl_3_0_0_kern.c). The script wraps this output with include guards and additional includes as needed. - Cleanup: Temporary files and build artifacts are removed before proceeding to the next version.
Example snippet from a Bash script:
for ver in ${!sslVerMap[@]}; do
tag="openssl-3.0.${ver}"
git checkout ${tag}
./config
make clean
make include/openssl/opensslconf.h
clang -I include/ -I . offset.c -o offset
./offset >>${header_file}
make clean
done
The C offset generator uses the offsetof macro to compute offsets for fields in structs such as ssl_st, ssl_session_st, ssl_cipher_st, bio_st, bio_method_st, and (for newer versions) quic_conn_st. It prints out #define statements in uppercase, suitable for inclusion in kernel code:
#define SSL_STRUCT_OFFSETS \
X(ssl_st, version) \
X(ssl_st, session) \
X(ssl_session_st, master_key) \
X(bio_st, num) \
X(bio_method_st, type)
...
#define X(struct_name, field_name) format(#struct_name, #field_name, offsetof(struct struct_name, field_name));
SSL_STRUCT_OFFSETS
#undef X
Integration and Runtime Selection#
The generated offset headers are compiled into eBPF kernel object files, each tailored for a specific OpenSSL version or version group. At runtime, ecapture detects the OpenSSL version by reading the .rodata section of the target shared object and matching the version string against a map of supported versions. It then loads the corresponding eBPF bytecode containing the correct offsets for that version. If an exact match is not found, ecapture attempts to select the closest supported version as a fallback, ensuring continued operation even with minor version mismatches source.
Benefits of Dynamic Offset Generation#
Dynamic offset generation provides several key advantages:
- Future-proofing: By automating offset extraction, ecapture can quickly adapt to new OpenSSL releases. When a new version is released, rerunning the offset generator scripts produces updated headers, minimizing manual intervention.
- Multi-version Support: The approach allows ecapture to support a wide range of OpenSSL versions simultaneously, each with its own kernel object file.
- Reduced Maintenance: Developers do not need to hand-calculate or hard-code offsets for each new OpenSSL release, reducing the risk of errors and simplifying updates.
- Resilience to Structural Changes: When OpenSSL or BoringSSL internal structures change (e.g., fields are removed or made opaque), only the offset generator C program needs to be updated to reflect the new structure definitions. After updating, offsets can be regenerated and the eBPF bytecode recompiled discussion.
Limitations and Manual Steps#
While the process is largely automated, some manual intervention is required when OpenSSL or BoringSSL makes significant structural changes. In such cases, developers must update the offset generator C program to match the new structure layouts before regenerating offsets and recompiling the eBPF bytecode. This ensures that all referenced struct members exist and are accessible in the current source code discussion.
Practical Advice#
To adapt the workflow for a new OpenSSL or BoringSSL version:
- Update the offset generator C program to match the new structure definitions.
- Run the relevant Bash script to regenerate the offset headers.
- Recompile the eBPF kernel bytecode with the new headers.
- Test against the target OpenSSL/BoringSSL version to ensure correct operation.
For more details, refer to the scripts and utilities in the utils directory of the ecapture repository and review the version-specific logic in the Go runtime source.