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