XNU kernel được viết bằng C. Hiểu C ở mức memory layout, không chỉ ở mức syntax, là yêu cầu bắt buộc.


Tại Sao Cần Học Sâu C

  • Kernel source code (XNU) là C — bạn sẽ đọc nó hàng ngày
  • Exploit development yêu cầu hiểu chính xác binary layout của struct, union, pointer
  • Heap exploitation phụ thuộc vào hiểu biết về memory allocator behavior
  • Type confusion exploits khai thác sự khác biệt giữa cách compiler layout các struct

Kiến Thức Cần Nắm

1. Pointers & Memory

// Pointer arithmetic — mỗi +1 nhảy sizeof(*ptr) bytes
uint64_t *p = (uint64_t *)0x1000;
p + 1;  // = 0x1008, KHÔNG phải 0x1001

// Void pointer — không có type info, cast manual
void *raw = malloc(64);
uint32_t val = *(uint32_t *)((char *)raw + 0x10);  // đọc 4 bytes tại offset 0x10

// Function pointer — nền tảng cho vtable exploitation
typedef int (*handler_fn)(void *ctx, int cmd);
handler_fn fn = (handler_fn)0xFFFFF00001234000;  // giả sử kernel address
fn(ctx, 0);  // indirect call

// Double pointer — thường gặp trong kernel APIs
kern_return_t task_for_pid(mach_port_t target, int pid, mach_port_t *task);
// task là output parameter — kernel ghi vào *task

2. Struct Layout & Padding

// Compiler thêm padding để align fields
struct example {
    uint8_t  a;      // offset 0x00, size 1
    // 7 bytes padding
    uint64_t b;      // offset 0x08, size 8
    uint32_t c;      // offset 0x10, size 4
    uint16_t d;      // offset 0x14, size 2
    // 2 bytes padding
};  // total size = 0x18 (24 bytes)

// Packed struct — không padding (dùng trong protocol/file format parsing)
struct __attribute__((packed)) header {
    uint8_t  magic;   // offset 0x00
    uint64_t size;    // offset 0x01 (unaligned!)
};  // total size = 9 bytes

Tại sao quan trọng:

  • Exploit cần biết chính xác offset của mỗi field trong kernel struct
  • Type confusion exploit thay thế object A bằng object B — cần field X của A nằm tại cùng offset với field Y của B
  • offsetof(struct, field) cho bạn offset chính xác

3. Union — Type Punning

// Union cho phép interpret cùng memory theo nhiều cách
union isa_t {
    uintptr_t bits;
    struct {
        uintptr_t nonpointer     : 1;
        uintptr_t has_assoc      : 1;
        uintptr_t has_cxx_dtor   : 1;
        uintptr_t shiftcls       : 33;
        uintptr_t magic          : 6;
        uintptr_t weakly_referenced : 1;
        // ...
    };
};

// Dùng trong exploitation để reinterpret kernel objects
union {
    uint64_t raw;
    struct {
        uint32_t lo;
        uint32_t hi;
    };
} kaddr;
kaddr.raw = 0xFFFFF00012345678;
// kaddr.lo = 0x12345678, kaddr.hi = 0xFFFFF000

4. Bitwise Operations

// Masking — lấy specific bits
uint64_t addr = ptr & 0x0000FFFFFFFFFFFF;  // strip PAC bits (upper 16)
uint64_t page = addr & ~0x3FFF;            // page-align (16KB pages trên iOS)
uint64_t offset = addr & 0x3FFF;           // offset within page

// Setting/clearing flags
uint32_t flags = pte;
flags |= PTE_VALID;           // set bit
flags &= ~PTE_READ_ONLY;     // clear bit
flags ^= PTE_ACCESSED;       // toggle bit

// Checking flags
if (pte & PTE_VALID) { ... }               // check single bit
if ((pte & MASK) == EXPECTED) { ... }       // check bit pattern

// Shifts — xây dựng addresses, extract fields
uint64_t phys = (pte & 0x0000FFFFFFFFF000ULL);  // extract physical address from PTE
uint64_t ppn = phys >> 14;                        // physical page number (16KB pages)

5. Memory Management Patterns Trong Kernel

// Zone allocation (XNU pattern)
zone_t my_zone = zone_create("my_objects", sizeof(struct my_obj), ZC_NONE);
struct my_obj *obj = zalloc(my_zone);
zfree(my_zone, obj);
// BUG: obj still points to freed memory → UAF

// Kalloc (general purpose)
void *buf = kalloc(256);  // allocated from kalloc.256 zone
kfree(buf, 256);          // phải pass đúng size!

// Reference counting pattern
void obj_retain(struct obj *o) { os_ref_retain(&o->ref_count); }
void obj_release(struct obj *o) {
    if (os_ref_release(&o->ref_count) == 0)
        obj_free(o);  // BUG nếu ai đó vẫn giữ pointer
}

// Mach port lifecycle — common exploitation target
struct ipc_port {
    struct ipc_object   ip_object;      // header
    struct ipc_mqueue   ip_messages;    // message queue
    union {
        struct ipc_kmsg *data;
        struct task *task;              // cho task ports
        struct semaphore *sema;
    } kdata;
    // ...
};

6. Volatile & Memory Barriers

// Volatile — compiler không optimize away reads/writes
volatile uint32_t *mmio_reg = (volatile uint32_t *)0x200000000;
uint32_t val = *mmio_reg;  // thực sự đọc hardware register

// Memory barriers — quan trọng cho multi-core exploitation
__asm__ volatile("dmb sy" ::: "memory");  // Data Memory Barrier
__asm__ volatile("dsb sy" ::: "memory");  // Data Synchronization Barrier
__asm__ volatile("isb" ::: "memory");     // Instruction Synchronization Barrier

XNU Kernel Source Code Patterns Cần Nhận Biết

// Kernel function error handling pattern
kern_return_t
some_kernel_function(args...)
{
    kern_return_t kr = KERN_SUCCESS;

    // ... validation ...
    if (bad_input) {
        kr = KERN_INVALID_ARGUMENT;
        goto out;
    }

    // ... do work ...

out:
    // cleanup
    return kr;
}

// IOKit C++ pattern (restricted C++)
class IOSurfaceRootUserClient : public IOUserClient {
    virtual IOReturn externalMethod(uint32_t selector,
        IOExternalMethodArguments *args,
        IOExternalMethodDispatch *dispatch,
        OSObject *target,
        void *reference) override;
};

Tài Nguyên

  • K&R — The C Programming Language (classic, tuy cũ nhưng nền tảng vững)
  • Expert C Programming: Deep C Secrets — Peter van der Linden
  • XNU Source Code — đọc trực tiếp
  • Compiler Explorer (godbolt.org) — xem C compile thành ARM64 assembly

Bài Tập

  1. Tính offset thủ công cho 5 struct khác nhau có mixed types, verify bằng offsetof()
  2. Viết type-punning code dùng union để extract fields từ 64-bit packed value
  3. Implement simple zone allocator bằng C (fixed-size allocations, freelist)
  4. Đọc XNU source: mở osfmk/kern/ipc_kobject.c và trace 1 function call path
  5. Compile C to ARM64, đọc assembly output, map mỗi C statement sang instructions tương ứng