Black Hat USA 2025 | Breaking Control Flow Integrity by Abusing Modern C++

"Coroutine Frame-Oriented Programming: Breaking Control Flow Integrity by Abusing Modern C++" by Marcos Bajo: OverviewThe presentation introduces a novel exploitation technique called Coroutine Frame-Oriented Programming (CFOP). It demonstrates how attackers can leverage C++20 coroutines to completely bypass modern Control Flow Integrity (CFI) defenses (such as Intel CET and Microsoft CFG) that are designed to prevent code-reuse attacks like ROP (Return-Oriented Programming). Key Concepts & Background Control Flow Integrity (CFI): A defense mechanism that prevents attackers from redirecting a program's execution flow by enforcing valid transition paths for indirect jumps and calls.…

Black Hat USA 2025 | Clue-Driven Reverse Engineering by LLM in Real-World Malware Analysis

Here is a comprehensive summary of the Black Hat USA 2025 presentation "Pay Attention to the Clue: Clue-driven Reverse Engineering by LLM in Real-world Malware Analysis" by Tien-Chih Lin and Wei-Chieh Chao from CyCraft Technology. Summary The presentation explores how to effectively use Large Language Models (LLMs) for malware reverse engineering while overcoming their biggest flaw: hallucinations. The speakers introduce Celebi, an automated, context-aware system that uses the internal mechanics of LLMs (attention heads and token probabilities) to verify if the AI is telling the truth, ultimately resulting in faster, more accurate…

Executable Exports Symbols

There are actually several critical scenarios where an executable must export symbols. The confusion usually lies in the direction of the linking. You are right that Executable A rarely links dynamically to Executable B to call functions inside B. However, the reverse happens frequently: Dynamic Libraries (Plugins) loaded by Executable A often need to call functions inside Executable A. Here are the specific reasons why an executable needs to keep exported symbols: 1. The "Host-Plugin" Architecture (Most Common) This is the primary reason. If your executable supports plugins…

LDR vs. LDUR in AArch64

In AArch64 (ARMv8-A), the main difference between LDR and LDUR is how they handle the immediate offset from the base address. LDR (Load Register): Uses a scaled positive immediate offset. It is the standard instruction for loading data from validly aligned structures and arrays. LDUR (Load Register Unscaled): Uses an unscaled signed immediate offset. It is used for accessing data at negative offsets or unaligned addresses that LDR cannot reach. Here is a detailed breakdown of the differences: 1. Offset Scaling LDR (Scaled): The immediate value you provide…

Memory Layout(global data, code, stack, heap, etc) with TLS

On AArch64 (ARM64), the memory layout for Thread Local Storage (TLS) follows TLS Variant 1. This is distinct from x86_64 (which uses Variant 2). The key difference is the location of the TLS data relative to the thread pointer. 1. The High-Level View (Process Memory) For a standard Linux process on AArch64, the memory is laid out as follows (from Low Address to High Address): +----------------------+ <-- High Address (e.g., 0x0000ffff...) | Stack | (Main Thread Stack, grows DOWN) +----------------------+ | ... | | Memory Mapping | <--…

AFL Coverage Instrumentation Callback

0000000000000bc0 <bbCallback>: bc0: 90000102 adrp x2, 20000 <_exit@GLIBC_2.17> bc4: f9404c43 ldr x3, [x2, #152] bc8: b4000263 cbz x3, c14 <bbCallback+0x54> bcc: d53bd042 mrs x2, tpidr_el0 bd0: a9bf7bfd stp x29, x30, [sp, #-16]! bd4: 12003c01 and w1, w0, #0xffff bd8: 910003fd mov x29, sp bdc: 90000100 adrp x0, 20000 <_exit@GLIBC_2.17> be0: f9403404 ldr x4, [x0, #104] be4: 9101a000 add x0, x0, #0x68 be8: d63f0080 blr x4 bec: 78606844 ldrh w4, [x2, x0] bf0: 53017c25 lsr w5, w1, #1 bf4: 78206845 strh w5, [x2, x0] bf8: 4a040021 eor w1, w1,…

Executable Startup And Initialization

CRT (C Runtime) "glue code" refers to a set of pre-compiled object files (typically crt1.o, crti.o, crtn.o, crtbegin.o, and crtend.o) that are automatically linked with your program. They "glue" the operating system's process loader to your main() function by handling low-level setup (stack, environment) and high-level initialization (global constructors). Execution Order Summary: _start (Entry Point) __libc_start_main (Standard C Library setup) __libc_csu_init / _init (Generic initialization hooks) .init_array (Global constructors/C++ initializers) main() (Your code) Detailed Explanation 1. What is CRT Glue Code? The "glue" consists of startup files provided…

Why the load of main by _start uses got entry, not adrp+add pair?

The _start function uses a Global Offset Table (GOT) entry to load the address of main primarily because _start is defined in a pre-compiled object file (typically Scrt1.o) that was built with Position-Independent Code (PIC) enabled. Here is the detailed explanation of why this happens and why adrp + add isn't used by default: 1. _start is Pre-Compiled Generic Code The _start function is not compiled at the same time as your application's main.c. It is part of the C Runtime (CRT) startup files (specifically Scrt1.o for Position…

AArch64 Pre/Post Indexing

In AArch64 (ARMv8-A 64-bit architecture), Pre-indexing and Post-indexing are memory addressing modes used with Load (LDR) and Store (STR) instructions. Their primary purpose is to perform Writeback: they automatically update the base register (the pointer) with a new address as part of the instruction execution. This is extremely efficient for iterating through arrays or managing stacks because it eliminates the need for a separate ADD or SUB instruction to move the pointer. Here is the breakdown of how they work. 1. Pre-Indexed Addressing Syntax: [base, #offset]!Key Symbol: The…

Check whether an executable is pure C or CPP

Distinguishing between a pure C and a C++ executable can be achieved by examining the symbols and library dependencies of the binary file. C++ compilers employ a technique called "name mangling" to support function overloading and namespaces, which is absent in C. Furthermore, C++ programs have a distinct set of standard library dependencies. Inspecting Symbol Tables for Name Mangling A primary indicator of C++ code is the presence of "mangled" names in the executable's symbol table. C++ compilers alter function and variable names to encode information about their…