Retrofitting XoM for Stripped Binaries without Embedded Data Relocation

Chenke Luo

Network and Distributed System Security (NDSS) Symposium 2025 · Day 3 · Software Security: Code and Compiler

Overview

This talk introduces PXOM, a novel approach to implement Execute-Only Memory (XOM) for stripped binaries, aiming to significantly enhance defenses against advanced memory disclosure attacks like Just-In-Time Return-Oriented Programming (JIT-ROP). Presented by Chenke Luo from Tsinghua University and Wuhan University, the research addresses a long-standing challenge in memory protection: how to enforce execute-only permissions on code pages without breaking legitimate program functionality due to embedded data. Traditional XOM implementations often struggle with the precise separation of code and data, leading to either crashes or security vulnerabilities.

Watch on YouTube · Slides

Key moments

  1. 0:08 Introduction to JIT-ROP attacks and XOM
  2. 2:40 XOM's challenge: embedded data causing program crashes
  3. 3:20 Limitations of current XOM due to disassembly challenges
  4. 4:50 PXOM's core idea: fine-grained read permission control
  5. 6:10 PXOM system architecture: disassembly, loader, and handler
  6. 7:40 Understanding unidirectional disassembly for embedded data identification
  7. 8:40 Refining disassembly by finding additional code entry points

Retrofitting XoM for Stripped Binaries without Embedded Data Relocation

Speakers: Chenke Luo, from Tsinghua University and Wuhan University

Conference: NDSS Symposium

YouTube: https://www.youtube.com/watch?v=SxG5b-Pw6BI

Overview

This talk introduces PXOM, a novel approach to implement Execute-Only Memory (XOM) for stripped binaries, aiming to significantly enhance defenses against advanced memory disclosure attacks like Just-In-Time Return-Oriented Programming (JIT-ROP). Presented by Chenke Luo from Tsinghua University and Wuhan University, the research addresses a long-standing challenge in memory protection: how to enforce execute-only permissions on code pages without breaking legitimate program functionality due to embedded data. Traditional XOM implementations often struggle with the precise separation of code and data, leading to either crashes or security vulnerabilities.

JIT-ROP attacks leverage memory disclosure vulnerabilities to dynamically discover gadgets in randomized code layouts, bypassing even sophisticated fine-grained Address Space Layout Randomization (ASLR). XOM is a crucial countermeasure, preventing attackers from reading code pages to find these gadgets. However, many binaries, especially stripped ones, contain legitimate data embedded within code sections (e.g., jump tables, string literals, constants). Simply making entire code pages unreadable would cause programs to crash when attempting to access this embedded data. PXOM proposes a radical solution that avoids the error-prone process of binary rewriting and data relocation, instead relying on a fine-grained, kernel-assisted read permission control mechanism and a robust unidirectional disassembly approach. This work is highly significant for the security community, offering a practical and high-performance defense against a critical class of modern exploitation techniques.

Background

▶ Watch: Introduction to JIT-ROP attacks and XOM (0:08)

The landscape of software exploitation has evolved significantly, moving beyond simple code injection to sophisticated techniques that repurpose existing code. Return-Oriented Programming (ROP) is a prime example, where attackers chain small snippets of legitimate code, known as "gadgets," to achieve arbitrary execution. Initially, ROP attacks relied on static analysis of a target binary to identify these gadgets offline. However, the widespread adoption of Address Space Layout Randomization (ASLR) made this approach unreliable. ASLR randomizes the base address of code and data segments, meaning that pre-computed gadget addresses become invalid across different program runs.

While ASLR provides a coarse-grained randomization, it is not impenetrable. Attackers developed techniques like "return-to-PRT point leaking" and "partial pointer override" to bypass ASLR, often by disclosing a single pointer and deducing the base address. To counter these bypasses, fine-grained code randomization was introduced, which shuffles code at the function or even basic block granularity, making the entire code layout highly unpredictable. This makes it far more difficult for attackers to guess or derive gadget locations.

However, fine-grained code randomization introduced a new frontier for attackers: Just-In-Time ROP (JIT-ROP). JIT-ROP attacks exploit memory disclosure vulnerabilities to dynamically read the randomized code layout of a running program. Once the code pages are disclosed, attackers can traverse them, search for necessary gadgets in real-time, compile these gadgets into a malicious payload using a JIT-ROP compiler, and then use a control flow hijacking vulnerability to compromise the target. In essence, JIT-ROP breaks the "secrecy promise" of fine-grained code randomization by making the randomized layout transparent to the attacker.

The natural defense against JIT-ROP is to restrict the attacker's ability to disclose code. This is where Execute-Only Memory (XOM) comes into play. XOM disables the read permission of code pages, ensuring that even if an attacker gains a memory disclosure primitive, they cannot read the executable code to find gadgets. The challenge, however, lies in the fact that code pages often contain legitimate embedded data. This data can include constants, jump tables, string literals, or other non-instruction bytes that are logically part of the code segment but are meant to be read by the program. If an entire code page is marked execute-only, any attempt by the legitimate program to read this embedded data will result in a page fault and a crash.

Existing binary-based XOM solutions attempt to solve this problem by employing disassembly and binary rewriting techniques. They use disassembly to identify all embedded data within code pages and then use binary rewriting to relocate this identified data to separate, read-only memory areas. While conceptually sound, this approach faces significant hurdles. The precise distinction between code and data is an inherent challenge of disassembly, especially for stripped binaries where symbolic information is absent. Misidentification can lead to severe problems:

  • Code misidentified as embedded data: If an instruction is mistakenly identified as data and relocated, the original code location becomes invalid, leading to a crash when the program attempts to execute the missing instruction.
  • Embedded data misidentified as code: If embedded data is mistakenly identified as code and left in the code page, that data remains unreadable when the XOM policy is applied. When the program tries to read this data, it will crash. The speaker highlights a case where four bytes of embedded data were misidentified as an instruction, causing a crash when left in an unreadable code page.

These challenges make traditional binary-based XOM implementations complex, brittle, and prone to introducing new vulnerabilities or instability. PXOM aims to overcome these limitations by introducing a novel strategy that avoids the problematic data relocation step entirely.

Key Findings

▶ Watch: Limitations of current XOM due to disassembly challenges (3:20)

The core contribution of PXOM is its ability to retrofit XOM for stripped binaries without requiring embedded data relocation. This fundamentally bypasses the inherent difficulties of precise code/data separation and binary rewriting that plague existing XOM solutions. PXOM achieves this by introducing a fine-grained read permission control mechanism within code pages, allowing legitimate data reads while disallowing all other reads, effectively protecting the code.

Key findings and contributions include:

  1. Elimination of Data Relocation: PXOM completely avoids relocating embedded data. Instead, it identifies legitimate embedded data regions and uses a kernel-level mechanism to grant read permissions specifically for these regions within otherwise execute-only code pages. This design drastically simplifies the XOM implementation and enhances its robustness.
  2. Unidirectional Disassembly: A novel unidirectional disassembly approach is proposed to identify embedded data. This method is designed to be tolerant of code misidentified as embedded data, meaning if an instruction is mistakenly flagged as data, it remains in the code page and retains its executable permission, preventing crashes. This approach prioritizes identifying all legitimate data while minimizing the exposure of actual code.
  3. Kernel-Assisted Fine-Grained Read Control: PXOM leverages a custom kernel loader and an exception handler to monitor all code page read attempts. Legitimate reads targeting identified embedded data regions are allowed by the kernel, while all other reads are treated as malicious disclosure attempts and blocked. This kernel-space enforcement protects the integrity of the embedded data list from user-space tampering.
  4. High Code Protection and Negligible Overhead: The evaluation demonstrates that PXOM achieves exceptional security with minimal performance impact.
  • For open-source programs, the average code coverage (proportion of protected actual code) is 97.07%, and the average overall coverage (proportion of protected executable memory) is 95.29%.
  • For closed-source programs, the average overall coverage is 96.94%.
  • Performance evaluation using Aaron bench and SPEC 2017 shows that PXOM introduces a negligible overhead. The geometric mean overhead for SPEC 2017 is just 0.25%, with an average of 0.36%. Even in the most demanding scenario (on one specific task), the overhead reached 3.2 times, which is still considered reasonable given the security benefits.
  1. Robustness Against Misidentification: The unidirectional disassembly, by tolerating code misidentified as data, ensures that program execution is not disrupted. The misidentified code still retains executable permissions, so the program can continue to run correctly. This design decision is crucial for practical deployment and stability.

These findings collectively demonstrate that PXOM offers a highly effective, robust, and performant solution for protecting stripped binaries against memory disclosure attacks without the pitfalls of traditional binary rewriting and data relocation methods.

Technical Deep Dive

▶ Watch: PXOM's core idea: fine-grained read permission control (4:50)

PXOM's architecture is a sophisticated combination of offline binary analysis and online kernel-level enforcement, designed to achieve execute-only memory protection without requiring embedded data relocation. The process begins with a binary file as input and culminates in a runtime system that dynamically manages read permissions.

The first crucial step is unidirectional disassembly. Unlike traditional disassembly, which aims for perfect code/data separation, unidirectional disassembly prioritizes identifying all legitimate embedded data while tolerating a certain degree of code being misidentified as data. This tolerance is key because, as the speaker explains, if code is misidentified as embedded data, it still retains its executable permission and does not cause a crash. The process works as follows:

  1. Initial State: The entire code page is initially treated as an "embedded data superset," meaning it's assumed to contain all potential embedded data and any misidentified code.
  2. Recursive Traversal from Entry Point: The system performs a recursive traversal disassembly starting from the program's primary entry point. This identifies a significant portion of the actual code. However, relying solely on this is insufficient, as it can result in approximately 49% of code being missed (i.e., remaining in the embedded data superset and potentially exposed).
  3. Discovery of Additional Entry Points: To minimize the amount of actual code that remains in the embedded data superset, PXOM identifies additional code entry points. These can include:
  • Jump table entries: Targets of indirect jumps.
  • Address-taking functions: Functions whose addresses are taken and stored, potentially called indirectly.
  • Unwind information: Data used for stack unwinding during exception handling, which can point to code.

Recursive traversal disassembly is then performed from these additional entry points. By iteratively expanding the identified code regions, the embedded data superset is progressively reduced. The remaining portions of the original code page, which are not identified as code, are then considered the definitive list of embedded data.

After the unidirectional disassembly, two key outputs are generated:

  1. A protected binary file.
  2. An identified embedded data list. This list contains the addresses and sizes of all legitimate embedded data regions within the code pages. This list is appended to the end of the ELF file, ensuring it travels with the binary.

The runtime enforcement mechanism involves a custom loader and an exception handler operating within the kernel.

  1. Custom Kernel Loader: When the protected binary is loaded, the custom loader first loads the attached embedded data list into a protected area within the kernel space. This is a critical security measure: placing the list in kernel space prevents attackers from tampering with it (e.g., adding arbitrary addresses to gain read access to code) if they compromise user-space memory.
  2. Exception Handler for Read Monitoring: The kernel's exception handler is configured to monitor all read attempts to code pages. When a read instruction targets a memory address within a code page, the exception handler intercepts it.
  3. Validation Against Embedded Data List: The handler then checks if the target address of the read operation falls within any of the regions specified in the embedded data list.
  • If the target is found in the list, it's recognized as a legitimate embedded data read, and the read operation is allowed to complete.
  • If the target is not in the list, it's considered a code disclosure attempt, and the operation is blocked, preventing the attacker from reading arbitrary code.

The speaker also briefly mentions an optimization list within the embedded data list, designed to accelerate reads for high-frequency embedded data scenarios. While details are referred to the paper, this indicates consideration for performance in common cases.

To quantify the security provided by PXOM, two metrics are introduced:

  • Code Coverage: Reflects the proportion of actual code that is protected (i.e., made execute-only). A higher percentage here indicates fewer real instructions are exposed. For open-source programs, PXOM achieves an average code coverage of 97.07%. This means only approximately 3% of the actual code might still be readable due to being misidentified as data.
  • Overall Coverage: Reflects the proportion of all executable memory (including both code and legitimate embedded data) that is protected. For open-source programs, this averages 95.29%, and for closed-source programs, it's 96.94%.

The design choice to tolerate code misidentified as data is a pragmatic one. Since the misidentified code still resides in an executable page and retains its executable permission, the program's control flow remains intact, preventing crashes. This contrasts sharply with relocation-based approaches where misidentified code would be moved, leading to catastrophic failures. The small residual exposure (around 3% of actual code) is deemed acceptable as it is highly unlikely to contain enough coherent gadgets for a successful JIT-ROP attack.

Demo / Proof of Concept

▶ Watch: Understanding unidirectional disassembly for embedded data identification (7:40)

While the talk did not feature a live, interactive demonstration of PXOM in action, the speaker presented a comprehensive evaluation section that serves as a strong proof of concept for its effectiveness and negligible performance overhead. The evaluation focused on two key aspects: the security coverage achieved and the performance impact on real-world applications.

For assessing the security provided, PXOM was evaluated against both open-source and closed-source programs. The speaker presented the code coverage and overall coverage metrics:

  • Open-Source Programs: PXOM achieved an impressive average code coverage of 97.07%. This means that over 97% of the actual executable instructions were successfully protected from being read. The average overall coverage for these programs was 95.29%, indicating that a vast majority of the executable memory was secured.
  • Closed-Source Programs: Since ground truth for code and data separation is unavailable for closed-source binaries, only the overall coverage was reported, which stood at an average of 96.94%. These high percentages across diverse binaries strongly suggest that PXOM effectively renders most of the code unreadable to attackers. The remaining 3% of potentially readable code (due to misidentification) is considered too small and fragmented to be useful for constructing ROP gadgets.

For performance evaluation, PXOM was tested using the Aaron bench benchmark suite, a collection of applications designed to measure various system performance aspects. The results were remarkably positive:

  • For most tasks within the Aaron bench, PXOM incurred negligible overhead. This indicates that the kernel-level read monitoring and list lookups are highly optimized and do not typically introduce significant delays.
  • The speaker highlighted one specific task where the overhead was more pronounced, reaching 3.2 times the baseline execution time. However, this was presented as an outlier, and even in this case, the overhead was deemed "reasonable" given the enhanced security.
  • Crucially, when evaluating the more comprehensive SPEC 2017 benchmark suite, PXOM demonstrated an average overhead of just 0.36%, with a geometric mean of only 0.25%. These figures are exceptionally low for a security mechanism that fundamentally alters memory access permissions, underscoring PXOM's practicality for real-world deployment.

To further understand why the overhead is so low despite kernel-level intervention, the speaker introduced a metric called read identity. This metric reflects the frequency of embedded data reads within programs. The analysis showed that even in applications with relatively high embedded data read frequencies, such as OpenSSL, a single embedded data read might still occur only after millions of instructions. This low frequency of legitimate embedded data reads explains why the kernel's exception handling mechanism, despite its overhead, has such a minimal impact on overall program performance. The vast majority of code execution proceeds without triggering the XOM read permission checks.

In summary, while no live demonstration was part of the talk, the extensive and positive quantitative evaluation results provide compelling proof of concept for PXOM's efficacy, stability, and performance characteristics, making a strong case for its practical applicability.

Defensive Implications

▶ Watch: Refining disassembly by finding additional code entry points (8:40)

PXOM offers significant defensive implications for organizations and developers seeking to harden their software against sophisticated memory-based attacks, particularly JIT-ROP. By addressing the long-standing challenge of embedded data in XOM, PXOM provides a robust and practical solution that can be integrated into the security posture of critical systems.

  1. Robust JIT-ROP Defense: The primary defensive implication is a strong countermeasure against JIT-ROP attacks. By making code pages execute-only and carefully allowing only legitimate embedded data reads, PXOM effectively denies attackers the ability to disclose code. This prevents them from dynamically discovering gadgets, thus neutralizing a core component of JIT-ROP exploitation. This is crucial for protecting systems that might be vulnerable to memory disclosure bugs, even when other defenses like fine-grained ASLR are in place.
  1. Enhanced Fine-Grained ASLR: PXOM complements and strengthens fine-grained ASLR. While fine-grained ASLR randomizes code layouts, JIT-ROP can bypass it through memory disclosure. PXOM directly targets this bypass mechanism, ensuring that even if an attacker manages to leak a pointer, they cannot read the surrounding randomized code to map out the entire layout. This restores the secrecy promise of fine-grained code randomization.
  1. Simplified XOM Deployment: By eliminating the need for binary rewriting and data relocation, PXOM significantly simplifies the process of implementing XOM. The complexities and brittleness associated with precise code/data separation, potential misidentifications, and the overhead of maintaining relocated data are all avoided. This makes XOM a more accessible and reliable defense, especially for stripped binaries where symbolic information is scarce.
  1. High Compatibility and Low Overhead: The negligible performance overhead (geometric mean of 0.25% for SPEC 2017) means that PXOM can be deployed in performance-sensitive environments without significantly impacting user experience or system throughput. This high compatibility makes it a viable option for a wide range of applications, from operating system components to critical server software.
  1. Protection of Stripped Binaries: The ability to retrofit XOM for stripped binaries is particularly important. Many production binaries are stripped to reduce size and obfuscate internal structure, making them harder to analyze and debug. PXOM's approach works effectively even without symbolic information, providing a crucial layer of protection for these deployments.
  1. Kernel-Level Integrity: The decision to store the embedded data list and enforce read permissions in kernel space is a critical defensive choice. It prevents user-space attackers from tampering with the list to create artificial "read windows" into code pages. This ensures the integrity and reliability of the XOM policy.
  1. Managing Residual Risk: While PXOM achieves very high code coverage (97.07%), it acknowledges that a small percentage (around 3%) of actual code might still be readable due to being misidentified as embedded data. Defenders should be aware of this residual attack surface. However, the speaker argues that this exposed code is likely fragmented and insufficient to form meaningful ROP gadgets, thus posing a minimal practical risk. Further research could explore techniques to reduce even this small percentage.

In essence, PXOM provides a practical, robust, and high-performance mechanism for enforcing XOM, effectively raising the bar for attackers attempting to exploit memory disclosure vulnerabilities and significantly bolstering the security of modern software.

Key Takeaways

  • JIT-ROP attacks bypass fine-grained ASLR by dynamically disclosing randomized code layouts.
  • Execute-Only Memory (XOM) is a critical defense against JIT-ROP by preventing code disclosure.
  • Traditional XOM implementations struggle with embedded data in code pages, requiring error-prone binary rewriting and data relocation.
  • PXOM retrofits XOM for stripped binaries by avoiding embedded data relocation entirely, addressing a long-standing challenge.
  • It uses a novel unidirectional disassembly approach to identify embedded data, tolerating code misidentified as data to prevent crashes.
  • PXOM employs a custom kernel loader and exception handler to provide fine-grained read permission control, allowing legitimate embedded data reads while blocking all other code reads.
  • The system achieves high code protection (97.07% average code coverage) with negligible performance overhead (0.25% geometric mean for SPEC 2017).
  • The kernel-level protection of the embedded data list prevents user-space attackers from tampering with XOM policies.

About the Speaker(s)

Chenke Luo is a researcher affiliated with both Tsinghua University and Wuhan University. His work, as presented in this talk, focuses on critical areas of system security, particularly in developing robust defenses against advanced memory exploitation techniques. His research demonstrates a deep understanding of binary analysis, kernel-level security mechanisms, and the practical challenges of deploying memory protection technologies in real-world systems.

Reviews

Dr. Zero (Offensive Security Researcher) — STRONG ACCEPT

Solid systems security research with a clear novel contribution: XOM enforcement on stripped binaries without data relocation, using unidirectional disassembly paired with kernel-enforced fine-grained read control. The 0.25% geometric mean overhead on SPEC 2017 is the kind of number that makes this deployable rather than academic, and the design decision to tolerate code-misidentified-as-data (rather than crash on it) shows genuine engineering maturity.

Heather Calloway (CISO) — PASS

Technically sound academic work on XOM enforcement for stripped binaries — a real problem in exploit mitigation research. Outside my lane entirely: no governance angle, no institutional accountability question, no defender decision path for operators or security leaders.

→ Top-rated talks at Network and Distributed System Security (NDSS) Symposium 2025

All talks from Network and Distributed System Security (NDSS) Symposium 2025