Skip to content

Version Detection and Bytecode Selection

Relevant source files

The following files were used as context for generating this wiki page:

Purpose and Scope

This page documents eCapture's sophisticated version detection and bytecode selection system, which enables the tool to work across multiple SSL/TLS library versions and kernel configurations without requiring manual version specification. The system automatically identifies the target library version, selects compatible eBPF bytecode, and falls back to safe defaults when exact matches are unavailable.

For information about the eBPF compilation process that produces these bytecode variants, see Build System. For details on module initialization and lifecycle, see Module System and Lifecycle.

System Overview

eCapture compiles over 130 different eBPF bytecode variants to support:

  • OpenSSL: versions 1.0.2a through 3.5.4
  • BoringSSL: Android 12-16 variants and non-Android versions
  • GnuTLS: versions 3.6.12 through 3.8.7
  • Go TLS: built-in crypto/tls library
  • System utilities: bash, zsh, mysqld, postgres

Each SSL/TLS library version has different internal structure layouts (offsets), requiring version-specific bytecode. The system performs runtime detection to select the correct variant.

Sources: variables.mk:189-228, user/module/probe_openssl_lib.go:30-42, CHANGELOG.md:14-23

Version Detection Architecture

Sources: user/module/probe_openssl_lib.go:189-282, user/module/probe_openssl_lib.go:284-317, user/module/probe_openssl_lib.go:341-369

ELF Parsing and Version Extraction

The detectOpenssl() function implements a robust version string extraction mechanism:

Process Steps

  1. ELF File Opening: Opens the target shared library using os.OpenFile() and elf.NewFile()
  2. Architecture Validation: Ensures the binary is EM_X86_64 or EM_AARCH64
  3. Section Location: Finds the .rodata section containing read-only data (version strings)
  4. Chunk Reading: Reads in 1MB chunks with 30-byte overlap to handle edge cases
  5. Regex Matching: Applies pattern OpenSSL\s\d\.\d\.[0-9a-z]+ to extract version

Edge Case Handling

ScenarioSolution
Version string spans buffer boundary30-byte overlap (OpenSslVersionLen) ensures capture
.rodata section not foundReturns ErrProbeOpensslVerNotFound
Multiple version strings in binaryUses first match found
BoringSSL without version stringFalls back to Android detection or default

Example version strings extracted:

  • "OpenSSL 1.1.1j 16 Feb 2021""openssl 1.1.1j"
  • "OpenSSL 3.2.0 23 Nov 2023""openssl 3.2.0"

Sources: user/module/probe_openssl_lib.go:189-282, user/module/probe_openssl_lib.go:41-42

Detection Flow Diagram

Sources: user/module/probe_openssl_lib.go:206-282

Version-to-Bytecode Mapping System

The initOpensslOffset() function initializes the sslVersionBpfMap with 100+ version mappings. This map serves as the authoritative source for version-to-bytecode associations.

Mapping Table Structure

Version FamilyVersion RangeBytecode FileNotes
OpenSSL 1.0.21.0.2a - 1.0.2uopenssl_1_0_2a_kern.oAll 1.0.2 versions share offsets
OpenSSL 1.1.01.1.0a - 1.1.0lopenssl_1_1_0a_kern.oAll 1.1.0 versions share offsets
OpenSSL 1.1.1a1.1.1a onlyopenssl_1_1_1a_kern.oUnique offset group
OpenSSL 1.1.1b-c1.1.1b - 1.1.1copenssl_1_1_1b_kern.oSecond offset group
OpenSSL 1.1.1d-i1.1.1d - 1.1.1iopenssl_1_1_1d_kern.oThird offset group
OpenSSL 1.1.1j+1.1.1j - 1.1.1wopenssl_1_1_1j_kern.oMost common 1.1.1 variant
OpenSSL 3.0.x3.0.0 - 3.0.11, 3.0.13+openssl_3_0_0_kern.oDefault for 3.0 series
OpenSSL 3.0.123.0.12 onlyopenssl_3_0_12_kern.oSpecial case with unique offsets
OpenSSL 3.1.x3.1.0 - 3.1.8openssl_3_1_0_kern.oShares base with 3.0 series
OpenSSL 3.2.0-23.2.0 - 3.2.2openssl_3_2_0_kern.oEarly 3.2 series
OpenSSL 3.2.33.2.3 onlyopenssl_3_2_3_kern.oStructure changes
OpenSSL 3.2.53.2.5 onlyopenssl_3_2_4_kern.oAdditional changes
OpenSSL 3.3.0-13.3.0 - 3.3.1openssl_3_3_0_kern.oEarly 3.3 series
OpenSSL 3.3.23.3.2 onlyopenssl_3_3_2_kern.oMid-series changes
OpenSSL 3.3.3-43.3.3 - 3.3.4openssl_3_3_3_kern.oLatest 3.3 series
OpenSSL 3.4.03.4.0 onlyopenssl_3_4_0_kern.oInitial 3.4 release
OpenSSL 3.4.1+3.4.1 - 3.4.2openssl_3_4_1_kern.oUpdated 3.4 series
OpenSSL 3.5.x3.5.0 - 3.5.4openssl_3_5_0_kern.oLatest supported
BoringSSL A12-13Android 12-13boringssl_a_13_kern.oGoogle's fork
BoringSSL A14Android 14boringssl_a_14_kern.oUpdated structures
BoringSSL A15Android 15boringssl_a_15_kern.oLatest Android
BoringSSL A16Android 16boringssl_a_16_kern.oUpcoming Android
BoringSSL NANon-Androidboringssl_na_kern.oGeneric BoringSSL

Mapping Code Structure

Sources: user/module/probe_openssl_lib.go:73-187, user/module/probe_openssl_lib.go:44-62

Downgrade Version Selection Algorithm

When an exact version match is not found in sslVersionBpfMap, the downgradeOpensslVersion() function implements a progressive truncation algorithm to find the closest compatible older version.

Algorithm Steps

Sources: user/module/probe_openssl_lib.go:341-369

Version Comparison Logic

The isVersionLessOrEqual() function performs lexicographic version comparison:

  1. Extract version parts: Split by dots ("3.0.12"["3", "0", "12"])
  2. Parse numeric and suffix: "1a"num=1, suffix="a"
  3. Compare iteratively: Compare numbers first, then suffixes

Example comparisons:

  • isVersionLessOrEqual("openssl 3.0.11", "openssl 3.0.12")true
  • isVersionLessOrEqual("openssl 3.0.12", "openssl 3.0.11")false
  • isVersionLessOrEqual("openssl 1.1.1j", "openssl 1.1.1k")true

Sources: user/module/probe_openssl_lib.go:371-448

Default Fallback Behavior

When all detection and downgrade attempts fail, the system applies platform-specific defaults:

Linux Platform Defaults

Rationale:

  • libssl.so.3 indicates OpenSSL 3.x series (most likely 3.0.x)
  • Older library names default to 1.1.1j (most stable/common 1.1.1 variant)

Android Platform Defaults

Android version mapping:

  • --android_ver=13boringssl_a_13_kern.o
  • --android_ver=14boringssl_a_14_kern.o
  • --android_ver=15boringssl_a_15_kern.o
  • --android_ver=16boringssl_a_16_kern.o

Sources: user/module/probe_openssl_lib.go:284-317, user/module/probe_openssl_lib.go:361-368

CO-RE vs Non-CO-RE Bytecode Selection

eCapture maintains two bytecode variants for each version: CO-RE (Compile Once, Run Everywhere) and non-CO-RE (kernel-specific). The selection happens automatically based on kernel BTF support.

Selection Criteria

CriterionCO-RE ModeNon-CO-RE Mode
Kernel BTF SupportCONFIG_DEBUG_INFO_BTF=yNot required
Minimum Kernel Version5.2+ (x86_64), 5.5+ (aarch64)4.18+ (x86_64), 5.5+ (aarch64)
Bytecode Suffix_core.o_noncore.o
RelocationRuntime via libbpfCompile-time via kernel headers
PortabilityWorks across kernel versionsTied to specific kernel

BTF Detection Flow

Compilation Targets

The build system generates both variants simultaneously:

CO-RE compilation (x86_64 example):

clang -target bpfel -D__TARGET_ARCH_x86 -I./kern/bpf/x86 \
      -O2 -g -c -o openssl_3_0_0_kern_core.o openssl_3_0_0_kern.c

Non-CO-RE compilation (x86_64 example):

clang -D__TARGET_ARCH_x86 -I/lib/modules/$(uname -r)/build/include \
      -DNOCORE -emit-llvm -S -o openssl_3_0_0_kern.ll openssl_3_0_0_kern.c
llc -march=bpf -filetype=obj -o openssl_3_0_0_kern_noncore.o openssl_3_0_0_kern.ll

Sources: variables.mk:236-268, README.md:86-101

Bytecode Loading and Validation

After bytecode selection, the module performs validation before attachment:

Loading Process

Error Handling Strategy

Error ConditionRecovery ActionLog Message
Version not found in .rodataUse default based on library name"OpenSSL/BoringSSL version not found, used default version"
Bytecode file not foundFatal error, exit"BPF bytecode file is not matched"
Uprobe attachment failedLog warning, continue"attach uprobe failed"
Symbol not foundSkip that hook"symbol not found, ignored"

Sources: user/module/probe_openssl_lib.go:189-282, user/module/probe_openssl_lib.go:284-317

Version Support Matrix

This table summarizes the complete version support landscape:

LibraryMinimum VersionMaximum VersionTotal VariantsNotes
OpenSSL 1.0.21.0.2a1.0.2u1Legacy support
OpenSSL 1.1.01.1.0a1.1.0l1Legacy support
OpenSSL 1.1.11.1.1a1.1.1w4Most common in production
OpenSSL 3.03.0.03.0.172Default for Ubuntu 22.04+
OpenSSL 3.13.1.03.1.81Shares base with 3.0
OpenSSL 3.23.2.03.2.53Multiple structure changes
OpenSSL 3.33.3.03.3.43Latest stable
OpenSSL 3.43.4.03.4.22Recent release
OpenSSL 3.53.5.03.5.41Newest supported
BoringSSL Android12164Per-Android-version
BoringSSLNon-AndroidN/A1Generic variant

Total bytecode files: 22 SSL/TLS variants × 2 modes (CO-RE + non-CO-RE) = 44 bytecode files just for OpenSSL/BoringSSL

Sources: user/module/probe_openssl_lib.go:44-62, variables.mk:189-228, CHANGELOG.md:14-23

Configuration Options

Users can override automatic detection using CLI flags:

Command-Line Overrides

FlagPurposeExampleUse Case
--ssl_versionForce specific version--ssl_version="openssl 3.0.12"Known version, skip detection
--libsslSpecify library path--libssl=/opt/openssl/lib/libssl.soNon-standard installation
--android_verSet Android version--android_ver=14Android BoringSSL detection
--nocoreDisable CO-RE mode--nocoreForce non-CO-RE bytecode

Example Usage

Manual version specification:

bash
sudo ecapture tls --ssl_version="openssl 3.2.3" --libssl=/usr/local/lib/libssl.so.3

Android device:

bash
adb shell "ecapture tls --android_ver=14"

Force non-CO-RE mode (older kernels):

bash
sudo ecapture tls --nocore

Sources: README.md:165-169, README_CN.md:144-149

Logging and Diagnostics

The version detection system provides detailed logging at multiple stages:

Log Message Categories

StageLog LevelExample MessageInterpretation
Detection SuccessINFO"Openssl Version=openssl 3.0.12"Exact version found
Default FallbackWARN"OpenSSL/BoringSSL version not found, used default version OpenSSL Version=linux_default_3_0"Using generic default
Downgrade AppliedERROR"OpenSSL Version=openssl 3.6.0, used downgrade version openssl 3.5.4"Newer version, using closest older
Bytecode MatchINFO"BPF bytecode file is matched. bpfFileName=openssl_3_0_0_kern_core.o"Bytecode selection complete
BTF ModeINFO"BTF bytecode mode: CORE. btfMode=0"CO-RE enabled

Troubleshooting Guide

Problem: "OpenSSL/BoringSSL version not found"

Solutions:

  1. Check if .rodata section exists: readelf -S /path/to/libssl.so | grep rodata
  2. Manually specify version: --ssl_version="openssl x.x.x"
  3. For BoringSSL, use: --ssl_version="boringssl_a_13"

Problem: "BPF bytecode file is not matched"

Solutions:

  1. Verify bytecode files exist: ls user/bytecode/*.o
  2. Check build completed: make
  3. Try forcing version: --ssl_version

Problem: Capture works but data is garbled

Cause: Incorrect version/offset mismatch

Solutions:

  1. Enable debug mode: ecapture tls --debug
  2. Try adjacent versions: --ssl_version="openssl 3.0.11" vs "openssl 3.0.13"
  3. Check library version: openssl version or strings /lib/libssl.so.3 | grep "OpenSSL"

Sources: user/module/probe_openssl_lib.go:300-316, README.md:89-101

Version Detection and Bytecode Selection has loaded