README.md
Rendering markdown...
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct cheese_kallsyms_lookup {
const void* kernel_data;
size_t kernel_length;
// https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/kernel/kallsyms_internal.h;l=7;drc=64e166099b69bfc09f667253358a15160b86ea43
const int* kallsyms_offsets;
uint64_t kallsyms_relative_base;
unsigned int kallsyms_num_syms;
const uint8_t* kallsyms_names;
const char* kallsyms_token_table;
const uint16_t* kallsyms_token_index;
char** decompressed_names;
uint64_t text_base;
};
uint64_t cheese_kallsyms_lookup(struct cheese_kallsyms_lookup* kallsyms_lookup,
const char* name);
static void* align_pointer_to_8(void* inptr) {
return (void*)((((uintptr_t)inptr) + 7ull) & ~7ull);
}
static size_t decompress_string(uint8_t* p, const char* kallsyms_token_table,
const uint16_t* kallsyms_token_index,
char* output) {
uint8_t count = *p;
size_t output_length = 0;
char* s = output;
for (int i = 0; i < count; i++) {
const char* token = kallsyms_token_table + kallsyms_token_index[p[i + 1]];
size_t token_length = strlen(token);
output_length += token_length;
if (s) {
strcpy(s, token);
s += token_length;
}
}
if (s) {
*s = 0;
}
return output_length;
}
static void* memmem_last(const void* big, size_t big_len, const void* little,
size_t little_len) {
for (const void* p = big + big_len - little_len; p >= big; p--) {
if (!memcmp(p, little, little_len)) {
return (void*)p;
}
}
return NULL;
}
int cheese_create_kallsyms_lookup(
struct cheese_kallsyms_lookup* kallsyms_lookup, void* kernel_data,
size_t kernel_length) {
// https://github.com/marin-m/vmlinux-to-elf/tree/master?tab=readme-ov-file#how-does-it-work-really
// https://github.com/facebookincubator/oculus-linux-kernel/blob/oculus-quest3-kernel-master/scripts/kallsyms.c#L408
// find the token table first
static const char token_table1[] = {
'A', 0, 'B', 0, 'C', 0, 'D', 0, 'E', 0, 'F', 0, 'G', 0, 'H', 0, 'I', 0,
'J', 0, 'K', 0, 'L', 0, 'M', 0, 'N', 0, 'O', 0, 'P', 0, 'Q', 0, 'R', 0,
'S', 0, 'T', 0, 'U', 0, 'V', 0, 'W', 0, 'X', 0, 'Y', 0, 'Z', 0};
void* kallsyms_token_table_letters_ptr =
memmem(kernel_data, kernel_length, token_table1, sizeof(token_table1));
if (!kallsyms_token_table_letters_ptr) {
fprintf(stderr, "can't find kallsyms_token_table: no letters\n");
return 1;
}
void* kallsyms_token_table_ptr = kallsyms_token_table_letters_ptr;
for (int i = 0; i <= 0x41; i++) {
char zero = 0;
kallsyms_token_table_ptr =
memmem_last(kernel_data, (kallsyms_token_table_ptr - kernel_data),
&zero, sizeof(zero));
if (!kallsyms_token_table_ptr) {
fprintf(stderr,
"can't find kallsyms_token_table: can't move backwards\n");
return 1;
}
}
kallsyms_token_table_ptr += 1;
void* kallsyms_token_index_ptr;
{
void* p = kallsyms_token_table_ptr;
for (int i = 0; i < 256; i++) {
p += strlen(p) + 1;
}
kallsyms_token_index_ptr = align_pointer_to_8(p);
}
void* kallsyms_markers_ptr = kallsyms_token_table_ptr - sizeof(uint32_t);
if (!*((uint32_t*)kallsyms_markers_ptr)) {
// alignment padding; skip
kallsyms_markers_ptr -= sizeof(uint32_t);
}
while (*((uint32_t*)kallsyms_markers_ptr)) {
kallsyms_markers_ptr -= sizeof(uint32_t);
}
void* kallsyms_names_end_ptr = kallsyms_markers_ptr - 1;
while (!*(char*)kallsyms_names_end_ptr) {
// alignment padding; skip
kallsyms_names_end_ptr -= 1;
}
// not going to try to do the full backwards parse here... just look for the
// 00000000 padding after num_syms
uint32_t zeroint = 0;
void* kallsyms_names_ptr =
memmem_last(kernel_data, kallsyms_names_end_ptr - kernel_data, &zeroint,
sizeof(zeroint)) +
sizeof(zeroint);
void* kallsyms_num_syms_ptr = kallsyms_names_ptr - sizeof(uint64_t);
unsigned int kallsyms_num_syms = *(unsigned int*)kallsyms_num_syms_ptr;
void* kallsyms_relative_base_ptr = kallsyms_num_syms_ptr - sizeof(uint64_t);
void* kallsyms_offsets_ptr = kallsyms_relative_base_ptr -
(((kallsyms_num_syms * sizeof(int)) + 7) & ~7);
// fprintf(stderr, "kallsyms_offsets %lx kallsyms_names %lx kallsyms_markers
// %lx kallsyms_token_table %lx kallsyms_relative_base %lx\n",
// kallsyms_offsets_ptr - kernel_data, kallsyms_names_ptr - kernel_data,
// kallsyms_markers_ptr - kernel_data, kallsyms_token_table_ptr - kernel_data,
// *(uint64_t*)kallsyms_relative_base_ptr);
kallsyms_lookup->kernel_data = kernel_data;
kallsyms_lookup->kernel_length = kernel_length;
kallsyms_lookup->kallsyms_offsets = kallsyms_offsets_ptr;
kallsyms_lookup->kallsyms_relative_base =
*(uint64_t*)kallsyms_relative_base_ptr;
kallsyms_lookup->kallsyms_num_syms = kallsyms_num_syms;
kallsyms_lookup->kallsyms_names = kallsyms_names_ptr;
kallsyms_lookup->kallsyms_token_table = kallsyms_token_table_ptr;
kallsyms_lookup->kallsyms_token_index = kallsyms_token_index_ptr;
kallsyms_lookup->decompressed_names =
malloc(kallsyms_num_syms * sizeof(char*));
{
uint8_t* p = kallsyms_names_ptr;
for (int i = 0; i < kallsyms_num_syms; i++) {
uint8_t entry_token_count = *p;
size_t length = decompress_string(p, kallsyms_token_table_ptr,
kallsyms_token_index_ptr, NULL);
char* s = malloc(length + 1);
decompress_string(p, kallsyms_token_table_ptr, kallsyms_token_index_ptr,
s);
kallsyms_lookup->decompressed_names[i] = s;
p += entry_token_count + 1;
}
}
uint64_t efi_header_end_addr =
cheese_kallsyms_lookup(kallsyms_lookup, "efi_header_end");
if (!efi_header_end_addr) {
fprintf(stderr, "can't find efi_header_end\n");
return 1;
}
uint64_t text_base = efi_header_end_addr - 0x10000;
kallsyms_lookup->text_base = text_base;
return 0;
}
uint64_t cheese_kallsyms_lookup(struct cheese_kallsyms_lookup* kallsyms_lookup,
const char* name) {
for (int i = 0; i < kallsyms_lookup->kallsyms_num_syms; i++) {
if (strcmp(kallsyms_lookup->decompressed_names[i] + 1, name) == 0) {
return kallsyms_lookup->kallsyms_relative_base +
kallsyms_lookup->kallsyms_offsets[i];
}
}
return 0;
}
// dumped from 51154110092200520's kernel
// dd if=kernel bs=1 skip=42016888 count=72 of=init_cred_start_bytes.bin
unsigned char init_cred_start_bytes_bin[] = {
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00};
uint64_t cheese_lookup_init_cred(
struct cheese_kallsyms_lookup* kallsyms_lookup) {
void* p =
memmem_last(kallsyms_lookup->kernel_data, kallsyms_lookup->kernel_length,
init_cred_start_bytes_bin, sizeof(init_cred_start_bytes_bin));
if (!p) {
return 0;
}
return kallsyms_lookup->text_base + (p - kallsyms_lookup->kernel_data);
}
uint64_t cheese_decode_adrp(uint32_t instr, uint64_t pc) {
uint32_t immhi = (instr >> 5) & ((1 << 19) - 1); // 19 bits
uint32_t immlo = (instr >> 29) & 0b11; // 2 bits
int64_t extended = ((int32_t)(immhi << 2 | immlo)) << 11 >> 11;
// fprintf(stderr, "%ld\n", extended);
int64_t off = extended << 12;
return (pc & ~((1 << 12) - 1)) + off;
}
uint64_t cheese_lookup_selinux_state(
struct cheese_kallsyms_lookup* kallsyms_lookup) {
/*
https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/third_party/kernel/v5.10/security/selinux/selinuxfs.c;l=459;drc=066314b0b76f61d4d7679f806f19c5c6bcf27441
ffffffc008799944 t sel_read_policy
(lldb) x/32i 0x799944
0x799944: paciasp
0x799948: str x30, [x18], #0x8
0x79994c: stp x29, x30, [sp, #-0x30]!
0x799950: stp x22, x21, [sp, #0x10]
0x799954: stp x20, x19, [sp, #0x20]
0x799958: mov x29, sp
0x79995c: mrs x8, SP_EL0
0x799960: ldr x8, [x8, #0x778]
0x799964: adrp x9, 5635
0x799968: ldrsw x9, [x9, #0xc18]
0x79996c: ldr x22, [x0, #0xd8]
0x799970: ldr x8, [x8, #0x78]
0x799974: adrp x0, 8415
0x799978: mov x19, x3
0x79997c: mov x20, x2
0x799980: add x8, x8, x9
0x799984: ldr w8, [x8, #0x4]
0x799988: mov x21, x1
0x79998c: add x0, x0, #0x990
0x799990: mov w2, #0x2 ; =2
0x799994: mov w3, #0x1 ; =1
0x799998: mov w4, #0x800 ; =2048
0x79999c: mov w1, w8
0x7999a0: mov x5, xzr
0x7999a4: bl 0xd41bc
*/
uint64_t sel_read_policy_addr =
cheese_kallsyms_lookup(kallsyms_lookup, "sel_read_policy");
if (!sel_read_policy_addr) {
return 0;
}
uint64_t text_base = kallsyms_lookup->text_base;
uint64_t sel_read_policy_off = sel_read_policy_addr - text_base;
const uint32_t* instrs = kallsyms_lookup->kernel_data + sel_read_policy_off;
uint64_t found_addr = 0;
for (int i = 0; i < 0x100; i++) {
uint32_t instr = instrs[i];
#define BL_MASK (0b111111 << 26)
#define BL_INST (0b100101 << 26)
#define ADRP_X0_MASK ((0b10011111 << 24) | (0b11111))
#define ADRP_X0_INST (0b10010000 << 24)
#define ADD_X0_MASK ((0b1111111111 << 22) | (0b1111111111))
#define ADD_X0_INST (0b1001000100 << 22)
// fprintf(stderr, "%lx %x\n", sel_read_policy_off + i*4, instr);
if ((instr & BL_MASK) == BL_INST) { // bl
return found_addr;
} else if ((instr & ADRP_X0_MASK) == ADRP_X0_INST) {
found_addr = cheese_decode_adrp(
instr, sel_read_policy_addr + i * sizeof(uint32_t));
// fprintf(stderr, "%lx\n", found_addr);
} else if ((instr & ADD_X0_MASK) == ADD_X0_INST) {
uint32_t imm = (instr >> 10) & ((1 << 12) - 1);
// fprintf(stderr, "add %x\n", imm);
found_addr += imm;
}
}
return 0;
}
#ifndef KALLSYMS_LOOKUP_INCLUDE
#define PATH "/Volumes/orangehd/docs/oculus/q3/q3_51154110092200520/kernel"
// #define PATH "/Volumes/orangehd/docs/oculus/q3/q3_50473320162100510/kernel"
int main() {
FILE* f = fopen(PATH, "r");
fseek(f, 0, SEEK_END);
off_t file_length = ftell(f);
fseek(f, 0, SEEK_SET);
void* kernel_data = malloc(file_length);
fread(kernel_data, 1, file_length, f);
fclose(f);
struct cheese_kallsyms_lookup kallsyms_lookup;
if (cheese_create_kallsyms_lookup(&kallsyms_lookup, kernel_data,
file_length)) {
return 1;
}
uint64_t addr = cheese_kallsyms_lookup(&kallsyms_lookup, "selinux_state");
printf("%llx\n", addr);
uint64_t init_cred_addr = cheese_lookup_init_cred(&kallsyms_lookup);
printf("%llx\n", init_cred_addr);
uint64_t selinux_state = cheese_lookup_selinux_state(&kallsyms_lookup);
printf("%llx\n", selinux_state);
}
#endif