# Unstripping Cloud Container ELF binaries

Jonathan Brossard

► To cite this version:

Jonathan Brossard. Unstripping Cloud Container ELF binaries. IEEE International Conference on Emerging Technologies and Computing ICETC2025, IEEE/ICETC2025, Jun 2025, Brest, France. hal-05124482

HAL Id: hal-05124482

https://hal.science/hal-05124482v1

Submitted on 22 Jun 2025

HAL is a multi-disciplinary open access
archive for the deposit and dissemination of sci-
entific research documents, whether they are pub-
lished or not. The documents may come from
teaching and research institutions in France or
abroad, or from public or private research centers.

L'archive ouverte pluridisciplinaire HAL, est
destinée au dépôt et à la diffusion de documents
scientifiques de niveau recherche, publiés ou non,
émanant des établissements d'enseignement et de
recherche français ou étrangers, des laboratoires
publics ou privés.

<figure><img src="image_1.png" /></figure>

Distributed under a Creative Commons Attribution 4.0 International License
# Unstripping Cloud Container ELF binaries

Jonathan Brossard

CEDRIC/Isid

Conservatoire National des Arts et Métiers

Paris, France

jonathan.brossard@lecnam.net

***Abstract***—Evaluating the security of containers, as part of the supply chain of Cloud services, has become compulsory with the European Directive NIS2. The binaries used within containers are typically stripped, meaning they do not possess debug symbols, making the evaluation of their security posture more complex. This article focuses on automated ways to recover some of this critical debugging information, a process named unstripping, on Linux x86_64 binaries, such as those presently overwhelmingly used within containers.

***Index Terms***—ELF unstripping, Reverse Engineering, Cloud binaries, NIS2, debugging

## I. INTRODUCTION

The recent upgrades to the European legal framework relative to the security of Cloud services mandate that Product Security Incident Response Teams (or PSIRT) offering services in Europe shall report new vulnerabilities affecting their product and services under 24 hours, to their respective national authorities. In particular, the Directive (UE) 2022/2555 [1], dubbed NIS2, renders compulsory the evaluation and reporting of security vulnerabilities under those timelines.

The public disclosure of over 40,000 vulnerabilities under the form of CVEs by the NIST in 2024 [2] (a number up from about 29,000 in 2023), leaves PSIRT teams with the daunting task of evaluating the exposure of their services to over 100 new vulnerabilities published every day. Arguably, the emergence of tools such as Software Bill of Materials (SBOMs), alleviate some of the PSIRT burden by automating the collection of potential vulnerabilities [10] affecting containers, based solely on the versions of the binaries used within such containers. However, recent studies [3] cast a significant doubt over the effectiveness and replicability of such SBOMs. This leaves PSIRT teams with the task of manually verifying potential vulnerabilities reported via SBOMs monitoring [11] through extensive debugging. In particular, a given version of a piece of software may or may not be vulnerable to a given vulnerability, and its CVSS [16] score drastically changed, depending on their exact compilation options [4] [5]. Those hardening properties are currently not reflected in major SBOM standards [9] [6] [7] [8].

Recent studies [13] indicate that the vast majority of containers [12] readily available today operate primarily under Linux and the x86_64 architecture, by as much as 97%. The ELF [15] binaries shipped within those containers are typically production-ready, and in particular have been stripped

of debugging symbols, making debugging by PSIRT teams harder.

In this article, we aim to provide a means for PSIRT teams to improve their response time when evaluating their exposure to a given vulnerability within a C/C++ Linux ELF x86_64 binary by automatically unstripping the target binary, meaning entirely or partially retrieving their debug information. We implement several avenues to perform this task in a single application, named *wunstrip*, published under a permissive open-source MIT/BSD-2 license, hoping to foster its study and adoption by the cybersecurity community. To the best of our knowledge, the latter two techniques leveraging the specifics of the x86_64 ELF standard [14] are entirely new.

## II. STATE OF THE ART

### A. Overview of an ELF executable debug symbols

The need to recover debugging information, and primarily the name and prototype of functions used within binaries is probably as old as debugging itself [24]. The ELF file format [15] features several optional structures and sections. If an executable ELF must have a segment header, its section header is optional [19]. The main symbol table of the binary is typically either not emitted by the compiler (if the -s compiler flag is used with either gcc orclang), or removed after linking by the strip command. The debugging sections, in dwarf format [17], can be entirely removed or compiled separately [18], leaving the production-ready executable without debug information.

Current versions of the GNU C Compiler [21] and LLVM's `clang` [20] toolchains further hide non-exported functions, by applying the `-fvisibility=hidden` flag by default [35], meaning that their symbols and addresses are not present in the final binary's symbol tables. Unless the `-rdynamic` compiler flag is used (which is not the default), in which case, the function symbols and their addresses are kept in the dynamic symbol table of the final binary (in the `.dynstr` and `.dynsym` sections respectively).

As such, a typical executable ELF binary as shipped by major distributions, such as Redhat or Debian, in a container image, does not retain its main symbol table, and possesses no debug symbols, effectively hiding the addresses of functions within the executable. However, debug symbols may be downloaded separately [41].
### B. Function prologues identification via control flow analysis

Identifying function prologues is a common static analysis task, typically performed using control flow analysis. In all generality, identifying functions in an arbitrary binary is undecidable, since simply distinguishing code from data in an arbitrary sequence of bytes is undecidable [22].

A decompiler based on *linear sweep* [24] disassembly was published by Cifuentes in her PhD thesis [23]. The algorithm starts with a sequential disassembly of the `.text` section of the executable. Individual instructions are then grouped into basic blocks by observing branching instructions. A clustering algorithm is then applied over the list of basic blocks to recover contiguous code lattices. Functions prologues and epilogues may finally be recovered using compiler heuristics [24].

This algorithm, while working reasonably well under RISC architectures [30] where instruction lengths are constant, instructions aligned, and for non-malicious C code generated by actual compilers, can be fooled by producing lattices that exhibit a polysemic behavior under a CISC architecture [31]. In this case, often seen in malware [36], a branching condition may be inserted to divert the control flow at run time to return in the middle of a valid instruction as identified by a *linear sweep* algorithm, hereby producing a portion of code having two distinct meanings at assembly level, an unusual condition not expected nor handled well by disassemblers [37].

Instead of performing a *linear sweep* disassembly, more robust disassemblers such as the IDA Pro [26], Ghidra [25], Radare2 [28], ANGR [29] or BAP [32] use an algorithm named *recursive descent* [24], where a sequential disassembly starts at the entry point of an application, ends with each basic block, but where the destination addresses of branching conditions are recorded and used subsequently as starting addresses of valid code to disassemble. *Recursive descent* based algorithms therefore typically offer a more accurate disassembly than *linear sweep* based disassemblers, especially when code and data are interleaved, but heavily rely on the destination addresses of branching conditions being readily computable statically, which remains undecidable in general [38]. For instance, function pointers may hold values computed at runtime, oblivious to disassemblers.

While those techniques may identify the start of functions within a binary, they do not provide their names.

### C. Unstripping: Naming functions via binary code fingerprinting

To label recovered functions with actual function names, Zalewski published *dress*. This tool attempts to match the function prologues of an arbitrary, statically compiled binary with the functions found within a statically compiled Glibc shared library, by computing MD5 checksums from the start of each function, after zeroing the addresses corresponding to relocations, over an arbitrary length of 24 bytes, using padding should the function be shorter than this value. This technique requires prior identification of the exact version of the Glibc statically linked with the binary and suffers from a high number of false positives, as the number of functions in the

static library increases. If it has been originally implemented only on GNU/Linux x86 32-bit architectures with the intent to fingerprint solely the Glibc library, this algorithm has been extended to support other static libraries and architectures [40].

The Fast Library Identification and Recognition Technology (FLIRT) algorithm [39] used within the commercial IDA disassembler [26] features a knowledge base of function fingerprints stemming from static libraries, that may be used to identify function prologues within an executable. Like the algorithm by Zalewski, it fingerprints the start of functions (the first 32 bytes), zeroing out addresses corresponding to relocations [39] and using padding should the function be shorter. In addition, calls to external function symbols are listed, ordered, and stored as a CRC16 checksum to help reduce false positives. Because short functions with few external references may still have similar signatures, they lead to higher rates of false positives. Conversely, small variations in compilation flags or library versions will hinder detection [34].

## III. PRACTICAL CLOUD BINARY UNSTRIPPING

### A. Leveraging Segment headers to re-create section headers

As detailed previously, the section headers of ELF executables or shared libraries are optional. They may be safely removed from final executables by tools such as `sstrip` [19] since section headers are not in fact used when running the executable or shared library.

On the other hand, segment headers may not be stripped, since they are used by the kernel and dynamic linker to map the executable in memory, and perform relocations if necessary. Compared to sections, segments are much thicker grained, a single segment encompassing several sections with identical permissions. Information relative to sections may be observed via the `readelf -S` command from binutils, while `readelf -l` will display segments. Still, information from segments may be used to refactor an ELF and recreate sections headers that will help the debugging of the application.

The segments of a valid ELF executable or shared library must start with a segment describing the program header of the application [15]. If the ELF is a dynamically linked executable, a segment of type `PT_INTERP` follows. This segment coincides exactly with the section describing the interpreter, of type `SHT_INTERP`. The loadable segments of type `PT_LOAD` follow: those correspond to the sections with the allocated flag set in their `.sh_flag` field. The permissions of those `PT_LOAD` segments (Read, Write, Execute) correspond to their roles: read-only segments contain read-only data, which would contain at least the `.rodata` section of the ELF. The segment with Execute permissions would contain at least the `.text` section of the ELF. The segment with Read and Write permissions would contain writable data, such as the `.data` and `.bss` sections of the executable. The segment headers may contain references to additional segments such as `PT swt` entries corresponding to the `.note` section, and under x86_64 architectures, must contain a segment of type `PT_EH_FRAME` corresponding to the `.eh_frameASC` section that will be later detailed in depth in section III.D of this article.
Dynamically linked executables and shared library must contain a segment of type PT_DYNAMIC, that coincides exactly with the `.dynamic` section. This segment is parsed at load time by the dynamic linker and contains information allowing it to find dependencies, resolve symbols and relocations. Formally, the DYNAMIC segment is an array of structures of type Elf64_Dyn under and ELF 64-bit, that maps a `d_tag` to either a `d_val` (immediate value) or a `d_ptr` (pointer to a location within the binary) via a union. The DYNAMIC section must contain an entry of type DT_STRTAB pointing to the start of the dynamic string table (the `.dynstr` section), an entry of type DT_SYMTAB pointing to the start of the dynamic symbol table (the `.dynsym` section), as well as entries DT_SYMENT specifying the size of a symbol in the dynamic symbol table and an entry of type DT_STRSZ providing the size of the dynamic symbol table. Those four entries allow the dynamic linker to walk the entire dynamic symbol table and dynamic string table of the ELF executable. By parsing them, we may equally retrieve the offsets and sizes of the dynamic symbol table and dynamic string table to identify the location of the `.dynsym` and `.dynstr` sections of the executable. This will prove critically valuable in the section III.C of this article. The DYNAMIC segment may also contain DT_INIT pointers to the start of the `.init` section, DT_FINI pointers to the start of the `.fini` section, DT_PLTGOT pointer to the `.plt.got` section, in which case the DT_PLTRELSZ provides the size of this section. To allow the processing of relocations, the DYNAMIC segment may contain a DT_RELA entry pointing to the start of the `.rela.dyn` section, in which case its size is encoded in a DT_RELASZ entry. The DYNAMIC segment may contain GNU symbol versioning information, strictly speaking an extension to the ELF file format, in which case a DT_VERNEEDED points to the start of the `.gnu.version_r` section of type SHT_VERNEED, a DT_VERSYM entry points to the start of the `.gnu.version` section of type SHT_VERSYM. The number of entries in those tables is identical to the number of entries in the dynamic symbol table, each symbol having a corresponding version information. This is enough to recover the locations and size of the `.gnu.version_r` and `.gnu.version` sections. Finally, the DYNAMIC segment must contain an entry of type DT_HASH (or DT_GNU_HASH on GNU/Linux), which is the location of the symbol hash table of the executable, which is a bloom filter of symbol addresses used to speed up symbols lookup within the dynamic linker. By walking the entire `.hash` section, we may recover its actual size as well.

Based on the above heuristics, it becomes possible to recreate sections based on segments, with correct or aproximated permissions, start addresses and lengths. The names of the sections being lost with the stripping of the `.shstrtab`, they are made up from the section type to recreate a valid `.shstrtab`.

We provide a tool named `whashlookup` able to dump the hash table of an ELF binary in order to recover its size. We also implement the above algorithm to unstrip sections within an ELF binary by parsing its segments within `wunstrip`, which we believe is an original contribution.

## B. Base OS Executables Unstripping via Buildids and External Debug Symbols

When it comes to containers, the binaries from the base OS often leverage existing distributions like Debian, Ubuntu, Alpine Linux, or CentOS and are installed either as part of the base Linux distribution, or using the distribution's package manager.

Major distributions including Debian, Ubuntu, Redhat, Fedora, CentOS, OpenSuse, Arch Linux, or Alt Linux provide a standard way to retrieve debugging symbols for a given binary using a debuginfod service [41]. At compile time, a `.note.gnu.build-id` entry in the `.note` section is included in each ELF executable by the static linker [43], containing a unique `buildid` [42], consisting of a cryptographic hash of the core parts binary (ELF header, segment headers, section headers, segments, and sections content), originally in MD5, and nowadays in SHA1 format.

This mechanism was designed by Roland McGrath from Redhat, to allow debuggers, in particular the GNU debugger GDB to download debug symbols of applications and shared libraries, by querying the `buildid` value extracted from each binary via a debuginfod server [41]. Depending on the exact compilation pipeline, either a complete non-stripped binary, or an ELF containing no code but all the debug symbols may be obtained through this service, if the binary was stripped post-linking, or compiled with separate debugging symbols respectively. In this latter case, debug symbols are downloaded to a predefined location, hardcoded within the original binary via an additional section named `.gnu_debuglink` included by the linker at compilation time. The optional section also contains a CRC32 checksum of the original unstripped binary to ensure the integrity of the unstripping process [44].

Our implementation of `wunstrip` can recompute the `buildid` hash, to ensure binary integrity, download debugging symbols in either form from the debuginfod server, and recreate a valid unstripped ELF by merging the original ELF with its debug symbols in case of separate debug files compilation if necessary.

If the container is not based on a distribution supporting debuginfod services or contains custom-built ELF binaries, this mechanism will indeed not work, and other methods will be required to unstrip executables.

## C. Leveraging Existing Dynamic Symbols to Unstrip Dynamically-linked ELF Binaries

As detailed in the state-of-the-art section of this paper, dynamically-linked ELF executables [15] compiled with the `-rdynamic` compiler flag have their local functions addresses and symbol names copied to the dynamic string table and dynamic symbol table. Those two sections may not be stripped post-compilation since the dynamic linker uses them to resolve symbols in shared libraries at runtime. While not typical, this use case is important since security-critical binaries such as the apache2 webserver under Debian and Ubuntu for instance are compiled this way. If symbols local to the binary are present
in the `.dynstr` and `.dynsym` dynamic sections, they may be used to recreate `.strtab` and `.symtab` sections in a refactored ELF.

After this refactoring, the newly created ELF will be seen as unstripped by standard tools like the file command from GNU fileutils or the readelf and objdump commands from GNU binutils, as well as other binary code manipulation tools and debuggers. It is worth noticing that the recomputation of the `buildid` as detailed in section III.B will however be able to detect that the binary has been refactored.

While this mechanism may only be used on the few ELF dynamic executables compiled with the `-rdynamic` compiler flag, to recover symbols from the dynamic symbol symbol table and its corresponding string table, it illustrates a more general algorithm to update sections within an executable to recreate missing sections by appending them to the original ELF executable. It is worth noticing that the original and refactored ELF will have identical mappings in memory, since the newly created `.strtab` and `.symtab` are not allocated in memory.

## D. Leveraging `.eh_frame` and `.eh_frame Header Exception Handling Frame Information Sections to Recover Function Prologues

Once detailed how to generally unstrip a binary by recreating `.strtab` and `.symtab` sections in a refactored ELF, the problem of unstripping binaries may be split into two distinct problems. First, identifying the start of every function within the binary, and second, assigning them names.

If identifying function prologues is generally performed using control flow analysis, as seen in the state-of-the-art section of this paper, the peculiarities of x86_64 executables predominantly used within containers offer another avenue, which, unlike control flow analysis, isn't undecidable in general.

The System V Application Binary Interface - AMD64 Architecture Processor Supplement [14] mandates that valid x86_64 ELF executables and shared libraries must have two supplemental sections allowing a debugger to retrieve the state of stack frames at run time: the `.eh_frame` and `.eh_frame Header` sections. The need for those extra sections stems from the calling convention of x86_64 ELF binaries, where most arguments are passed as registers. Unlike x86 32-bit binaries, where arguments were passed via the stack, and returning to a previous stack frame readily understood by debuggers, x86_64 binaries require extra information for correct stack unwinding, as the state of *callee-saved* registers (rbx, rbp, r12-r15) in the calling stack frame in particular is otherwise unknown at runtime, should an exception occur. Not having access to the base pointer (rbp) register in particular, would hinder walking back stack frames entirely, making it impossible to print a backtrace at crash time.

To encode stack unwinding information, the `.eh_frame` section contains "Exception Handling Frame Information". For each call frame in the application, it stores "Call Frame Information" (CFI) in the form of "Frame Description Entries" (FDEs) that detail how to unwind the stack from any runtime

address in the current frame, and "Common Information Entries" (CIEs) that contain rules encoding register preservation. The `.eh_frame Header` section acts as an index to the `.eh_frame` section.

The format used to encode `.eh_frame` and `.eh_frame Header` sections is named "Compact Unwind Encoding". While close to the DWARF format used within `.debug_frame` sections, it has major differences: first, it is allocated in memory at runtime, and second, it must not be stripped.

An example of Frame Description Entry starting at offset 0x36c in the `.eh_frame` section, describing a frame expending from addresses 0x7ea0 to 0x7ec5 may resemble the code below. Noticeably, the value of registers rbp, rbx and rsp may be retrieved from "Canonical Frame Addresses" (CFA), which is a fixed location in the current stack frame, or incremented directly, as in the last instruction:

```
0000036c 1c 00000370 FDE cie=0 pc=7ea0..7ec5
    DW_CFAadvanceLoc: 5 to 7ea5
    DW_CFA_def_cfa_offset: 16
    DW_CFA_offset: r6 (rbp) at cfa-16
    DW_CFAadvanceLoc: 3 to 7ea8
    DW_CFA_def_cfa_register: r6 (rbp)
    DW_CFAadvanceLoc: 1 to 7ea9
    DW_CFA_offset: r3 (rbx) at cfa-24
    DW_CFAadvanceLoc: 23 to 7ec0
    DW_CFA_def_cfa: r7 (rsp) ofs 8
```

Because the granularity of the `.eh_frame` and `.eh_frame Header` mechanism is thinner than functions, it becomes possible to parse `.eh_frame` and `.eh_frame Header` sections to retrieve function information, and in particular the prologue of each function within an ELF executable or shared library, without relying on control-flow analysis.

We have implemented such a parser within *wunstrip*, allowing the retrieval of function prologues within an ELF x86_64 binary. Since addresses of each function within the binary becomes known, but not its actual original name, we may temporarily name each function, including the ones hidden by gcc or `clang's -fvisibility=hidden` compilation flag (now their defaults), with a pattern of the type `function_{offset}`, where `{offset}` is the offset of the function within the `.text` of the executable.

To the best of our knowledge, using `.eh_frame` and `.eh_frame Header` information to unstrip binaries is an original contribution of this article.

## E. Function names recovery via object files identification

A large source of misidentification in the FLIRT or *dress* algorithms stems from short signatures in smaller functions. Because they may apply to functions of just a few bytes in size, the potential for collisions in the signatures across different static libraries is important.

Instead of attempting to match individual functions, we suggest attempting to match entire object files extracted from static libraries. The rational behind it is that developers tend to create compilation units (id est: individual source files)
based on coherent relations between functions. As such, we may conjecture that if a function from an object file is included in an ELF binary during linking, chances are the other functions from this compilation unit will too. Hence fingerprinting the entire object file may make sense. We therefor devise a two step algorithm, where entire object files are first searched within target libraries or binaries to unstrip, in order to identify the best matching static libraries from a knowledge base. In a second step, we may look to match individual functions from those best-fit static libraries to unstrip the target ELF shared library or executable.

To identify object files within an ELF binary, we aim to create a pcre regular expression representing the `.text` of the file. We first parse the relocations of the object file, then use the bytes of the `.text` of the object file as a signature, using wildcards in lieu of the bytes that will be relocated during linking. This provides a simple text signature that may be searched within arbitrary ELF executables to evidence linking against that particular object file. We may create as many signatures as we find object files within a shared library.

By observing the best matching libraries (in terms of number of object files identified) among a corpus of pre-selected shared libraries (eg: all the shared libraries distributed with a particular version of the base OS or the package repositories of a container) against a target ELF binary, we may be able to determine which shared libraries were in fact linked against and ELF binary.

In a second step, the individual functions from the selected static libraries may be used to create pcre regular expressions for individual function signatures and matched against the target library to recover function names and their respective locations within the binary.

Naturally, this technique may be combined with the methodology introduced in section III.D to further reduce the number of mismatches, since parsing the `.eh_frame` and `.eh_frame_hdr` sections provides not only function addresses, but also immediately their sizes.

To facilitate individual object files, individual static libraries, or entire corpuses identification, we have implemented these techniques in a dedicated tool named `wobjgrep`. Functions identified this way may be imported into `wunstrip` to recreate a symbol table in an ELF binary.

## IV. EVALUATION

### A. Evaluation of Function Addresses Recovery via Exception Handling Frame Information Sections Parsing

To evaluate the detection rate of function addresses via the parsing of `.eh_frame` and `.eh_frame_hdr` sections, we chose to work on a Ubuntu 24.04 LTS container. For each chosen sample test file, we retrieve the executable via `wunstrip` using the debuginfod symbol service. This will serve as a baseline and source of truth. We also use `wunstrip` to process the original stripped binary and attempt to retrieve their function addresses by parsing the Exception Handling Frames. Finally, we compare the addresses found for each symbol with the

addresses obtained within the unstripped binary using the `nm` command from the binutils package.<fnref n="1" />

As sample test binaries, we arbitrarily chose the binaries `ls` from coreutils, `sshd` from the openssh-server package, `mysqld` from the mysql-server package, the `libjpeg.so` shared library from the libjpeg package, and the `nginx` webserver. Results are reproduced in the Table I.

<table><caption>TABLE I
FUNCTION ADDRESSES IDENTIFICATION RESULTS</caption><thead><tr><th>Binary</th><th>Functions†</th><th>Identified</th><th>Valid</th><th>False Positives</th><th>False Negatives</th></tr></thead><tbody><tr><td>ls</td><td>187</td><td>188</td><td>185</td><td>3 (0.016%)</td><td>1 (0.005%)</td></tr><tr><td>sshd</td><td>833</td><td>834</td><td>831</td><td>3 (0.004%)</td><td>1 (0.0012%)</td></tr><tr><td>mysqld</td><td>61747</td><td>61748</td><td>61745</td><td>3 (0.00005%)</td><td>1 (0.00002%)</td></tr><tr><td>libjpeg.so</td><td>365</td><td>366</td><td>363</td><td>3 (0.008%)</td><td>1 (0.003%)</td></tr><tr><td>nginx</td><td>1659</td><td>1660</td><td>1657</td><td>3 (0.002%)</td><td>1 (0.0006%)</td></tr></tbody></table>

With a false positive rate below 0.02% and a rate of false negatives below 0.005% against real-world binaries, the methodology of automated function addresses recovery via Exception Handling Frame information parsing via `wunstrip` seems attractive to PSIRT teams.

### B. Evaluation of object files identification

To evaluate the ability of `wobjgrep` to determine the best matching static libraries linked against ELF binaries, we pick Ubuntu 24.04 LTS as a base OS for a docker image, install several stock static libraries and their dynamic counterparts. We also install a handful of static executables written in C, and are at least linked against the static libc library. We then, for each dynamic library or static executable ELF in our study-set, run `objgrep` in a recursive fashion so it attempts to match every object file from every static library installed on the docker image against the said ELF binary. Results are reproduced in the Table II, reflecting the percentage of object files from each static library matched against a given binary (the highest matching percentages are reported in bold). The matching addresses of each function and object file are omitted for brevity.

<table><caption>TABLE II
OBJECT FILES WITHIN STATIC LIBRARIES IDENTIFICATION RESULTS</caption><thead><tr><th>Binary</th><th>libc.a</th><th>libssl.a</th><th>libcrypto.a</th><th>libjpeg.a</th><th>libz.a</th></tr></thead><tbody><tr><td>libc.so.6</td><td><strong>66%</strong></td><td>1%</td><td>0%</td><td>0%</td><td>0%</td></tr><tr><td>libssl.so.3</td><td>0%</td><td><strong>26%</strong></td><td>0%</td><td>0%</td><td>0%</td></tr><tr><td>libcrypto.so.3</td><td>2%</td><td>2%</td><td><strong>32%</strong></td><td>0%</td><td>0%</td></tr><tr><td>libjpeg.so.8</td><td>0%</td><td>0%</td><td>0%</td><td><strong>44%</strong></td><td>0%</td></tr><tr><td>libz.so.1</td><td>0%</td><td>2%</td><td>0%</td><td>0%</td><td><strong>26%</strong></td></tr><tr><td>zsh-static</td><td><strong>16%</strong></td><td>0%</td><td>0%</td><td>0%</td><td>0%</td></tr><tr><td>bash-static</td><td><strong>17%</strong></td><td>2%</td><td>0%</td><td>0%</td><td>0%</td></tr><tr><td>e2fsck static</td><td><strong>18%</strong></td><td>2%</td><td>0%</td><td>0%</td><td>0%</td></tr><tr><td>qemu-aarch64-static</td><td><strong>21%</strong></td><td>2%</td><td>0%</td><td>0%</td><td>6%</td></tr></tbody></table>

Considering the best matching static library in terms of percentage of object files identified, the algorithm correctly predicts that each shared library is linked with its static counterpart and that the statically linked C binaries are linked

<footnote n="1">¹† Functions introduced by the compiler runtime: `__do_globalDtors_aux()`, `deregister_tm_clones()`, `frameibrain()`, and `register_tm_clones()` excluded.</footnote>
against the static C library libc.a. The remaining false positives
remain below 10% and may be attributed to small object files
within static libraries. We conclude that object file identifica-
tion is a valid avenue to unstrip ELF binaries.

V. CONCLUSION AND FUTURE WORK

In this article, we’ve introduced a number of new method-
ologies to help unstrip x86_64 container binaries. While the
.eh_frame驱 is x86_64 specific, the others are architecture
independant. Their evaluation produced conclusive results to
demonstrate their usefulness. The tools wunstrip and wohjgrep
have been released under a permissive dual MIT/BSD license,
with the hope to benefit the security community.

REFERENCES

[1] European Union, "Directive (EU) 2022/2555 of the European Parliament and of the Council of 14 December 2022 on measures for a high common level of cybersecurity across the Union", Official Journal of the European Union, L 333, 27.12.2022, p. 80-152

[2] The MITRE Corporation, "Common Vulnerabilities and Exposures, CVE Metrics and Data", https://www.cve.org/about/Metrics, 2025

[3] YU, Sheng, SONG, Wei, HU, Xunchao, et al. "On the correctness of metadata-based SBOM generation: A differential analysis approach.", 2024 54th Annual IEEE/IFIP International Conference on Dependable Systems and Networks (DSN). IEEE, 2024. p. 29-36.

[4] Pirry, Conor, Hector Marco-Gisbert, and Carolyn Begg, "A review of memory errors exploitation in x86-64.", Computers 9.2 (2020), p. 48.

[5] Li, Senyang, et al., "Hardware-Based Software Control Flow Integrity: Review on the State-of-the-Art Implementation Technology.", IEEE Access 11 (2023): 133255-133280.

[6] The Open Web Application Security Project, "CycloneDX 1.6 JSON Documentation", https://cyclonedx.org/docs/1.6/json/, 2024

[7] SPDX Working Group, "SPDX - Software Package Data Exchange v3.0", https://spdx.dev/, April 2024

[8] National Telecommunications and Information Administration (NTIA), "Draft Requirements for Sharing of Vulnerability Status Information - VEX", October 2020

[9] National Telecommunications and Information Administration (NTIA), "SBOM Minimum Elements Report", July 2021

[10] Eggers, Shannon Leigh, et al., "The Benefits of a Software Bill of Materials Program at Nuclear Facilities.", Jul. 2023. https://doi.org/10.13182/NPICHMIT23-42305

[11] LU, Qinghua, ZHU, Liming, XU, Xiwei, et al., "Responsible-AI-by-design: a pattern collection for designing responsible artificial intelligence systems.", IEEE Software, 2023, vol. 40, no 3, p. 63-71.

[12] Open Containers Initiative (OCI), "Open Container Image Specification v1.1.0", February 2024

[13] Lin, C., Nadi, S., and Khazaei, H., "A large-scale data set and an empirical study of docker images hosted on docker hub.", 2020 IEEE International Conference on Software Maintenance and Evolution (ICSME) (pp. 371-381). IEEE.

[14] The Linux Foundation, "System V Application Binary Interface - AMD64 Architecture Processor Supplement, Draft Version 0.95", 2008

[15] The Tool Interface Standard (TIS) Committee, "Tool Interface Standard (TIS) Portable Formats Specification, Version 1.2", May 1995

[16] Figueroa-Lorenzo, S., Añorga, J., and Arrizabalaga S, "A survey of IloT protocols: A measure of vulnerability risk analysis based on CVSS.", ACM Computing Surveys (CSUR), 53(2), 1-53.

[17] DWARF Debugging Information Format Committee, "DWARF Debugging Information Format Version 5", February 2017

[18] Meghana, D. K., and J. Geetha Reddy, "Cloud-based approach to increase the performance of execution of binary by using the separate debug file.", 2016 2nd International Conference on Applied and Theoretical Computing and Communication Technology (iCATccT). IEEE, 2016.

[19] Brian Raiter, "sstrip : truncates ELF files to remove non-program content", https://www.muppetlabs.com/ breadbox/software/elfkickers.html

[20] Clang Development Team, "Clang: a C language family frontend for LLVM", 2025, https://clang.llvm.org/

[21] GCC Development Team, "GCC, the GNU Compiler Collection", 2025, https://gcc.gnu.org/

[22] DE NICOLAO, Pietro, POGLIANI, Marcello, POLINO, Mario, et al. ELISA: ELiciting ISA of raw binaries for fine-grained code and data separation. In : International Conference on Detection of Intrusions and Malware, and Vulnerability Assessment. Cham : Springer International Publishing, 2018. p. 351-371.

[23] CIFUENTES, Cristina et GOUGH, K. John. Decompiletion of binary programs. Software: Practice and Experience, 1995, vol. 25, no 7, p. 811-829.

[24] PANG, Chengbin, YU, Ruotong, CHEN, Yaohui, et al. Sok: All you ever wanted to know about x86/x64 binary disassembly but were afraid to ask. In: 2021 IEEE symposium on security and privacy (SP). IEEE, 2021. p. 833-851.

[25] NSA, "The Ghidra Decompiler (source code)", https://github.com/NationalSecurityAgency/ghidra build, 2025.

[26] Chris Eagle, The IDA Pro Book: The Unofficial Guide to the World's Most Popular Disassembler, No Starch Press, ISBN:978-1593272890, 2011

[27] Frank Ch. Eigler and the elfutils developers, "debuginfod- find: A Tool for Debugging Information Retrieval", https://sourceware.org/elfutils/documentation/debuginfod.html

[28] radare.org, "Radare2 github repository", https://github.com/radareorg/radare2/, 2025

[29] WANG, Fish et SHOSHITAISHVILI, Yan. "Angr-the next generation of binary analysis", In : 2017 IEEE Cybersecurity Development (SecDev). IEEE, 2017. p. 8-9.

[30] WANG, Shuai, WANG, Pei, et WU, Dinghao. "Reassemblable disassembling", In : 24th USENIX Security Symposium (USENIX Security 15). 2015. p. 627-642.

[31] BEN KHADRA, M. Ammar, STOFFEL, Dominik, et KUNZ, Wolfgang. "Speculative disassembly of binary code", In : Proceedings of the International Conference on Compilers, Architectures and Synthesis for Embedded Systems. 2016. p. 1-10.

[32] BRUMLEY, David, JAGER, Ivan, AVGERINOS, Thanassis, et al. "BAP: A binary analysis platform. In : Computer Aided Verification", 23rd International Conference, CAV 2011, Snowbird, UT, USA, July 14-20, 2011. Proceedings 23. Springer Berlin Heidelberg, 2011. p. 463-469.

[33] BAO, Tiffany, BURKET, Jonathan, WOO, Maverick, et al. "BYTEWEIGHT: Learning to recognize functions in binary code.", In : 23rd USENIX Security Symposium (USENIX Security 14). 2014. p. 845-860.

[34] QIU, Jing, SU, Xiaohong, et MA, Peijun. "Library functions identification in binary code by using graph isomorphism testings" In : 2015 ieee 22nd international conference on software analysis, evolution, and reengineering (saner). IEEE, 2015. p. 261-270.

[35] GNU Compiler Collection (GCC) Developers, "GCC Wiki: Symbol Visibility", https://gcc.gnu.org/wiki/Visibility, 2025

[36] OЛАИМАТ, Mohammad N., МААРОФ, Mohd Aizaini, et AL-RIMY, Bander Ali S. "Ransomware anti-analysis and evasion techniques: A survey and research directions", In : 2021 3rd international cyber resilience conference (CRC). IEEE, 2021. p. 1-6.

[37] XU, Fangzhou, ZHANG, Wang, QIANG, Weizhong, et al, "Multi-path exploration guided by taint and probability against evasive malware", Security and Safety, 2023, vol. 2, p. 2023023.

[38] RICE, Henry Gordon. Classes of recursively enumerable sets and their decision problems. Transactions of the American Mathematical society, 1953, vol. 74, no 2, p. 358-366.

[39] Hex-Rays Documentation Team, "IDA F.L.I.R.T. Technology In-Depth", https://docs.hex-rays.com/user-guide/signatures/flirt/ida-f.l.i.r.t.-technology-in-depth, 2025

[40] Adam Van Prooyen, "dress: Add symbols back into a stripped ELF binary ( strip )", https://github.com/sciencemanx/dress, 2016

[41] "Debuginfod - ELF utilities", https://sourceware.org/elfutils/Debuginfod.html, 2025

[42] Roland McGrath, "Build ID - Fedora Project Wiki", https://fedoraproject.org/wiki/RolandMcGrath/BuildID, May 2008

[43] Roland McGrath, Original buildid implementation in Binutils' linker, https://sourceware.org/legacy-ml/binutils/2007-07/msg00012.html, 2007

[44] MEGHANA, D. K. et REDDY, J. Geetha. "Cloud-based approach to increase the performance of execution of binary by using the separate debug file" In : 2016 2nd International Conference on Applied and Theoretical Computing and Communication Technology (iCATccT). IEEE, 2016. p. 743-746.