manager: add KPM module with control codes and compact symbol handling

This commit is contained in:
ShirkNeko
2025-03-29 21:04:54 +08:00
parent 6e24d427d2
commit 8972327faa
9 changed files with 1104 additions and 0 deletions

2
kernel/kpm/Makefile Normal file
View File

@@ -0,0 +1,2 @@
obj-y += kpm.o
obj-y += compact.o

100
kernel/kpm/compact.c Normal file
View File

@@ -0,0 +1,100 @@
#include <linux/export.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/kernfs.h>
#include <linux/file.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/uaccess.h>
#include <linux/elf.h>
#include <linux/kallsyms.h>
#include <linux/version.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/rcupdate.h>
#include <asm/elf.h> /* 包含 ARM64 重定位类型定义 */
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <asm/cacheflush.h>
#include <linux/module.h>
#include <linux/vmalloc.h>
#include <linux/set_memory.h>
#include <linux/version.h>
#include <linux/export.h>
#include <linux/slab.h>
#include "kpm.h"
#include "compact.h"
unsigned long sukisu_compact_find_symbol(const char* name);
// ======================================================================
const char* kver = "0.10";
struct CompactAddressSymbol {
const char* symbol_name;
void* addr;
};
struct CompactAliasSymbol {
const char* symbol_name;
const char* compact_symbol_name;
};
struct CompactAddressSymbol address_symbol [] = {
{ "kallsyms_lookup_name", &kallsyms_lookup_name },
{ "compact_find_symbol", &sukisu_compact_find_symbol },
{ "compact_copy_to_user", &copy_to_user },
{ "compact_strncpy_from_user", &strncpy_from_user },
{ "kver", &kver },
{ "is_run_in_sukisu_ultra", (void*)1 }
};
struct CompactAliasSymbol alias_symbol[] = {
{"kf__strncat", "strncat"},
{"kf__strlen", "strlen" },
{"kf__strcpy", "strcpy"},
{"compact_copy_to_user", "__arch_copy_to_user"}
};
unsigned long sukisu_compact_find_symbol(const char* name) {
int i;
unsigned long addr;
// 先自己在地址表部分查出来
for(i = 0; i < (sizeof(address_symbol) / sizeof(struct CompactAddressSymbol)); i++) {
struct CompactAddressSymbol* symbol = &address_symbol[i];
if(strcmp(name, symbol->symbol_name) == 0) {
return (unsigned long) symbol->addr;
}
}
/* 如果符号名以 "kf__" 开头,尝试解析去掉前缀的部分 */
if (strncmp(name, "kf__", 4) == 0) {
const char *real_name = name + 4; // 去掉 "kf__"
addr = (unsigned long)kallsyms_lookup_name(real_name);
if (addr) {
return addr;
}
}
// 通过内核来查
addr = kallsyms_lookup_name(name);
if(addr) {
return addr;
}
// 查不到就查查兼容的符号
for(i = 0; i < (sizeof(alias_symbol) / sizeof(struct CompactAliasSymbol)); i++) {
struct CompactAliasSymbol* symbol = &alias_symbol[i];
if(strcmp(name, symbol->symbol_name) == 0) {
addr = kallsyms_lookup_name(symbol->compact_symbol_name);
if(addr)
return addr;
}
}
return 0;
}

6
kernel/kpm/compact.h Normal file
View File

@@ -0,0 +1,6 @@
#ifndef ___SUKISU_KPM_COMPACT_H
#define ___SUKISU_KPM_COMPACT_H
unsigned long sukisu_compact_find_symbol(const char* name);
#endif

933
kernel/kpm/kpm.c Normal file
View File

@@ -0,0 +1,933 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2025 Liankong (xhsw.new@outlook.com). All Rights Reserved.
* 本代码由GPL-2授权
*
* 适配KernelSU的KPM 内核模块加载器兼容实现
*
* 集成了 ELF 解析、内存布局、符号处理、重定位(支持 ARM64 重定位类型)
* 并参照KernelPatch的标准KPM格式实现加载和控制
*/
#include <linux/export.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/kernfs.h>
#include <linux/file.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/uaccess.h>
#include <linux/elf.h>
#include <linux/kallsyms.h>
#include <linux/version.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/rcupdate.h>
#include <asm/elf.h> /* 包含 ARM64 重定位类型定义 */
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <asm/cacheflush.h>
#include <linux/module.h>
#include <linux/vmalloc.h>
#include <linux/set_memory.h>
#include <linux/version.h>
#include <linux/export.h>
#include <linux/slab.h>
#include "kpm.h"
#include "compact.h"
static inline void local_flush_icache_all(void)
{
asm volatile("ic iallu");
asm volatile("dsb nsh" : : : "memory");
asm volatile("isb" : : : "memory");
}
static inline void flush_icache_all(void)
{
asm volatile("dsb ish" : : : "memory");
asm volatile("ic ialluis");
asm volatile("dsb ish" : : : "memory");
asm volatile("isb" : : : "memory");
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,0,0) && defined(CONFIG_MODULES)
#include <linux/moduleloader.h> // 需要启用 CONFIG_MODULES
#endif
/**
* kpm_malloc_exec - 分配可执行内存
* @size: 需要分配的内存大小(字节)
*
* 返回值: 成功返回内存指针,失败返回 NULL
*/
void *kpm_malloc_exec(size_t size)
{
void *addr = NULL;
unsigned long nr_pages;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,0,0)
/* 内核 5.0+ 方案 */
#if defined(CONFIG_MODULES)
// 使用 module_alloc + set_memory_x
addr = module_alloc(size);
if (!addr)
return NULL;
nr_pages = DIV_ROUND_UP(size, PAGE_SIZE);
if (set_memory_x((unsigned long)addr, nr_pages)) {
vfree(addr); // 注意:某些内核版本用 module_memfree
return NULL;
}
#else
// 如果未启用模块支持,回退到 vmalloc + 手动设置权限(可能有安全风险)
addr = __vmalloc(size, GFP_KERNEL, PAGE_KERNEL_EXEC);
if (addr) {
nr_pages = DIV_ROUND_UP(size, PAGE_SIZE);
if (set_memory_x((unsigned long)addr, nr_pages)) {
vfree(addr);
addr = NULL;
}
}
#endif
#else
/* 内核 <5.0 方案 */
#if defined(vmalloc_exec)
// 旧版直接使用 vmalloc_exec
addr = vmalloc_exec(size);
#else
// 兼容某些旧版本变种
addr = __vmalloc(size, GFP_KERNEL, PAGE_KERNEL_EXEC);
#endif
#endif
return addr;
}
/**
* kpm_free_exec - 释放可执行内存
* @addr: alloc_exec_memory 返回的指针
* @size: 分配时的大小
*/
void kpm_free_exec(void *addr, size_t size)
{
unsigned long nr_pages;
if (!addr)
return;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,0,0)
nr_pages = DIV_ROUND_UP(size, PAGE_SIZE);
set_memory_nx((unsigned long)addr, nr_pages);
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,0,0) && defined(CONFIG_MODULES)
module_memfree(addr); // 5.0+ 且启用模块支持
#else
vfree(addr); // 旧版或未启用模块
#endif
}
/* KPM 模块头部结构体,存放于 ELF 的 .kpm 段中 */
struct kpm_header {
u32 magic; /* 魔数,要求为 'KPM' -> 0x4B504D */
u32 version;/* 版本号,目前要求为 1 */
int (*entry)(const char *args, const char *event, void *__user reserved);
void (*exit)(void *__user reserved);
};
#define KPM_MAGIC 0x4B504D
#define KPM_VERSION 1
/* 加载信息结构体,避免与内核已有 load_info 冲突 */
struct kpm_load_info {
const void *hdr; /* ELF 数据 */
Elf64_Ehdr *ehdr; /* ELF 头 */
Elf64_Shdr *sechdrs; /* 段表 */
const char *secstrings; /* 段名字符串表 */
size_t len; /* 文件长度 */
struct {
const char *base;
const char *name;
const char *version;
const char *license;
const char *author;
const char *description;
size_t size;
} info;
struct {
int info;
int sym;
int str;
} index;
char *strtab; /* 符号表对应的字符串表 */
unsigned long symoffs, stroffs;
};
/* 模块数据结构,改名为 kpm_module 避免冲突 */
struct kpm_module {
struct list_head list;
char *args;
char *ctl_args;
void *start; /* 分配的连续内存区域 */
unsigned int size; /* 总大小 */
unsigned int text_size;
unsigned int ro_size;
int (*init)(const char *args, const char *event, void *__user reserved);
void (*exit)(void *__user reserved);
int (*ctl0)(const char *ctl_args, char *__user out_msg, int outlen);
int (*ctl1)(void *a1, void *a2, void *a3);
struct {
const char *base;
const char *name;
const char *version;
const char *license;
const char *author;
const char *description;
} info;
};
/* 全局模块列表,改名为 kpm_module_list */
static LIST_HEAD(kpm_module_list);
static DEFINE_SPINLOCK(kpm_module_lock);
/*-----------------------------------------------------------
* ELF 头和段表解析(针对 ARM64 检查)
*----------------------------------------------------------*/
#define kpm_elf_check_arch(x) ((x)->e_machine == EM_AARCH64)
static int kpm_elf_header_check(struct kpm_load_info *info)
{
if (info->len < sizeof(Elf64_Ehdr))
return -EINVAL;
info->ehdr = (Elf64_Ehdr *)info->hdr;
if (memcmp(info->ehdr->e_ident, ELFMAG, SELFMAG) != 0)
return -EINVAL;
if (info->ehdr->e_shoff + info->ehdr->e_shnum * sizeof(Elf64_Shdr) > info->len)
return -EINVAL;
info->sechdrs = (Elf64_Shdr *)((const char *)info->hdr + info->ehdr->e_shoff);
if (info->ehdr->e_shstrndx >= info->ehdr->e_shnum)
return -EINVAL;
info->secstrings = (const char *)info->hdr + info->sechdrs[info->ehdr->e_shstrndx].sh_offset;
return 0;
}
/* 在 ELF 文件中查找指定段 */
Elf64_Shdr *find_sec(struct kpm_load_info *info, const char *sec_name)
{
Elf64_Ehdr *ehdr = info->ehdr;
Elf64_Shdr *shdr = (Elf64_Shdr *)((char *)ehdr + ehdr->e_shoff);
const char *shstrtab = (char *)ehdr + shdr[ehdr->e_shstrndx].sh_offset;
int i;
for (i = 0; i < ehdr->e_shnum; i++) {
if (strcmp(shstrtab + shdr[i].sh_name, sec_name) == 0)
return &shdr[i];
}
return NULL;
}
int find_sec_num(struct kpm_load_info *info, const char *sec_name) {
Elf64_Ehdr *ehdr = info->ehdr;
Elf64_Shdr *shdr = (Elf64_Shdr *)((char *)ehdr + ehdr->e_shoff);
const char *shstrtab = (char *)ehdr + shdr[ehdr->e_shstrndx].sh_offset;
int i;
for (i = 0; i < ehdr->e_shnum; i++) {
if (strcmp(shstrtab + shdr[i].sh_name, sec_name) == 0)
return i;
}
return -1;
}
/*-----------------------------------------------------------
* 模块 modinfo 提取
*----------------------------------------------------------*/
static char *kpm_next_string(char *string, unsigned long *secsize)
{
while (string[0]) {
string++;
if ((*secsize)-- <= 1)
return NULL;
}
while (!string[0]) {
string++;
if ((*secsize)-- <= 1)
return NULL;
}
return string;
}
static char *kpm_get_next_modinfo(const struct kpm_load_info *info, const char *tag, char *prev)
{
char *p;
unsigned int taglen = strlen(tag);
Elf_Shdr *infosec = &info->sechdrs[info->index.info];
unsigned long size = infosec->sh_size;
char *modinfo = (char *)info->hdr + infosec->sh_offset;
if (prev) {
size -= prev - modinfo;
modinfo = kpm_next_string(prev, &size);
}
for (p = modinfo; p; p = kpm_next_string(p, &size)) {
if (strncmp(p, tag, taglen) == 0 && p[taglen] == '=')
return p + taglen + 1;
}
return NULL;
}
static char *kpm_get_modinfo(const struct kpm_load_info *info, const char *tag)
{
return kpm_get_next_modinfo(info, tag, NULL);
}
/*-----------------------------------------------------------
* 内存布局与段复制
*----------------------------------------------------------*/
static long kpm_get_offset(struct kpm_module *mod, unsigned int *size, Elf64_Shdr *sechdr)
{
long ret = ALIGN(*size, sechdr->sh_addralign ? sechdr->sh_addralign : 1);
*size = ret + sechdr->sh_size;
return ret;
}
static void kpm_layout_sections(struct kpm_module *mod, struct kpm_load_info *info)
{
int i;
for (i = 0; i < info->ehdr->e_shnum; i++)
info->sechdrs[i].sh_entsize = ~0UL;
for (i = 0; i < info->ehdr->e_shnum; i++) {
Elf64_Shdr *s = &info->sechdrs[i];
if (!(s->sh_flags & SHF_ALLOC))
continue;
s->sh_entsize = kpm_get_offset(mod, &mod->size, s);
}
mod->size = ALIGN(mod->size, 8);
}
/*-----------------------------------------------------------
* 符号处理与重定位(针对 ARM64
*----------------------------------------------------------*/
static bool kpm_is_core_symbol(const Elf64_Sym *src, const Elf64_Shdr *sechdrs, unsigned int shnum)
{
const Elf64_Shdr *sec;
if (src->st_shndx == SHN_UNDEF || src->st_shndx >= shnum || !src->st_name)
return false;
sec = &sechdrs[src->st_shndx];
if (!(sec->sh_flags & SHF_ALLOC))
return false;
return true;
}
static int kpm_simplify_symbols(struct kpm_module *mod, const struct kpm_load_info *info)
{
Elf64_Shdr *symsec = &info->sechdrs[info->index.sym];
Elf64_Sym *sym = (Elf64_Sym *)symsec->sh_addr;
unsigned long secbase;
int ret = 0;
unsigned int i;
unsigned long addr = 0;
for (i = 1; i < symsec->sh_size / sizeof(Elf64_Sym); i++) {
const char *name = info->strtab + sym[i].st_name;
switch (sym[i].st_shndx) {
case SHN_COMMON:
if (!strncmp(name, "__gnu_lto", 9)) {
printk(KERN_ERR "ARM64 KPM Loader: compile with -fno-common\n");
ret = -ENOEXEC;
}
break;
case SHN_ABS:
break;
case SHN_UNDEF:
// addr = kallsyms_lookup_name(name);
addr = sukisu_compact_find_symbol(name);
if (!addr) {
printk(KERN_ERR "ARM64 KPM Loader: unknown symbol: %s\n", name);
ret = -ENOENT;
break;
}
sym[i].st_value = addr;
break;
default:
secbase = info->sechdrs[sym[i].st_shndx].sh_addr;
sym[i].st_value += secbase;
break;
}
}
return ret;
}
/* ARM64 重定位处理:支持 R_AARCH64_RELATIVE 与 R_AARCH64_ABS64 */
static int kpm_apply_relocate_arm64(Elf64_Shdr *sechdrs, const char *strtab, int sym_idx, int rel_idx, struct kpm_module *mod)
{
Elf64_Shdr *relsec = &sechdrs[rel_idx];
int num = relsec->sh_size / sizeof(Elf64_Rel);
Elf64_Rela *rel = (Elf64_Rela *)((char *)mod->start + relsec->sh_entsize);
int i;
for (i = 0; i < num; i++) {
unsigned long type = ELF64_R_TYPE(rel[i].r_info);
unsigned long *addr = (unsigned long *)(mod->start + rel[i].r_offset);
switch (type) {
case R_AARCH64_RELATIVE:
*addr = (unsigned long)mod->start + rel[i].r_addend;
break;
default:
printk(KERN_ERR "ARM64 KPM Loader: Unsupported REL relocation type %lu\n", type);
return -EINVAL;
}
}
return 0;
}
static int kpm_apply_relocate_add_arm64(Elf64_Shdr *sechdrs, const char *strtab, int sym_idx, int rela_idx, struct kpm_module *mod)
{
Elf64_Shdr *relasec = &sechdrs[rela_idx];
int num = relasec->sh_size / sizeof(Elf64_Rela);
Elf64_Rela *rela = (Elf64_Rela *)((char *)mod->start + relasec->sh_entsize);
int i;
for (i = 0; i < num; i++) {
unsigned long type = ELF64_R_TYPE(rela[i].r_info);
unsigned long sym_index = ELF64_R_SYM(rela[i].r_info);
unsigned long *addr = (unsigned long *)(mod->start + rela[i].r_offset);
switch (type) {
case R_AARCH64_RELATIVE:
*addr = (unsigned long)mod->start + rela[i].r_addend;
break;
case R_AARCH64_ABS64:
if (sym_index) {
Elf64_Sym *sym = (Elf64_Sym *)((char *)mod->start + sechdrs[sym_idx].sh_offset) + sym_index;
*addr = sym->st_value + rela[i].r_addend;
} else {
printk(KERN_ERR "ARM64 KPM Loader: R_AARCH64_ABS64 with zero symbol\n");
return -EINVAL;
}
break;
default:
printk(KERN_ERR "ARM64 KPM Loader: Unsupported RELA relocation type %lu\n", type);
return -EINVAL;
}
}
return 0;
}
static int kpm_apply_relocations(struct kpm_module *mod, const struct kpm_load_info *info)
{
int rc = 0;
int i;
for (i = 1; i < info->ehdr->e_shnum; i++) {
unsigned int target = info->sechdrs[i].sh_info;
if (target >= info->ehdr->e_shnum)
continue;
if (!(info->sechdrs[target].sh_flags & SHF_ALLOC))
continue;
if (info->sechdrs[i].sh_type == SHT_REL)
rc = kpm_apply_relocate_arm64(info->sechdrs, info->strtab, info->index.sym, i, mod);
else if (info->sechdrs[i].sh_type == SHT_RELA)
rc = kpm_apply_relocate_add_arm64(info->sechdrs, info->strtab, info->index.sym, i, mod);
if (rc < 0)
break;
}
return rc;
}
/*-----------------------------------------------------------
* 符号表与字符串表布局
*----------------------------------------------------------*/
static void kpm_layout_symtab(struct kpm_module *mod, struct kpm_load_info *info)
{
Elf64_Shdr *symsect = &info->sechdrs[info->index.sym];
Elf64_Shdr *strsect = &info->sechdrs[info->index.str];
const Elf64_Sym *src;
unsigned int i, nsrc, ndst;
unsigned int strtab_size = 1;
symsect->sh_flags |= SHF_ALLOC;
symsect->sh_entsize = kpm_get_offset(mod, &mod->size, symsect);
src = (Elf64_Sym *)((char *)info->hdr + symsect->sh_offset);
nsrc = symsect->sh_size / sizeof(Elf64_Sym);
for (ndst = i = 0; i < nsrc; i++) {
if (i == 0 || kpm_is_core_symbol(src + i, info->sechdrs, info->ehdr->e_shnum)) {
strtab_size += strlen(info->strtab + src[i].st_name) + 1;
ndst++;
}
}
info->symoffs = ALIGN(mod->size, symsect->sh_addralign ? symsect->sh_addralign : 1);
info->stroffs = mod->size = info->symoffs + ndst * sizeof(Elf64_Sym);
mod->size += strtab_size;
strsect->sh_flags |= SHF_ALLOC;
strsect->sh_entsize = kpm_get_offset(mod, &mod->size, strsect);
}
/*-----------------------------------------------------------
* 重写段表头:修正各段的 sh_addr 为在连续内存中的地址
*----------------------------------------------------------*/
static int kpm_rewrite_section_headers(struct kpm_load_info *info)
{
int i;
info->sechdrs[0].sh_addr = 0;
for (i = 1; i < info->ehdr->e_shnum; i++) {
Elf64_Shdr *shdr = &info->sechdrs[i];
if (shdr->sh_type != SHT_NOBITS && info->len < shdr->sh_offset + shdr->sh_size)
return -ENOEXEC;
shdr->sh_addr = (size_t)info->hdr + shdr->sh_offset;
}
return 0;
}
/*-----------------------------------------------------------
* 将各段复制到连续内存区域中
*----------------------------------------------------------*/
static int kpm_move_module(struct kpm_module *mod, struct kpm_load_info *info)
{
int i;
printk(KERN_INFO "ARM64 KPM Loader: Allocating module memory: size=0x%x\n", mod->size);
mod->start = kpm_malloc_exec(mod->size);
if (!mod->start)
return -ENOMEM;
memset(mod->start, 0, mod->size);
printk(KERN_INFO "ARM64 KPM Loader: Final section addresses:\n");
for (i = 1; i < info->ehdr->e_shnum; i++) {
void *dest;
Elf64_Shdr *shdr = &info->sechdrs[i];
if (!(shdr->sh_flags & SHF_ALLOC))
continue;
dest = mod->start + shdr->sh_entsize;
if (shdr->sh_type != SHT_NOBITS)
memcpy(dest, (void *)shdr->sh_addr, shdr->sh_size);
shdr->sh_addr = (unsigned long)dest;
if (!mod->init && !strcmp(".kpm.init", info->secstrings + shdr->sh_name))
mod->init = (int (*)(const char *, const char *, void *__user))dest;
if (!strcmp(".kpm.exit", info->secstrings + shdr->sh_name))
mod->exit = (void (*)(void *__user))dest;
if (!strcmp(".kpm.ctl0", info->secstrings + shdr->sh_name))
mod->ctl0 = (int (*)(const char *, char *__user, int))dest;
if (!strcmp(".kpm.ctl1", info->secstrings + shdr->sh_name))
mod->ctl1 = (int (*)(void *, void *, void *))dest;
if (!mod->info.base && !strcmp(".kpm.info", info->secstrings + shdr->sh_name))
mod->info.base = (const char *)dest;
}
mod->info.name = info->info.name - info->info.base + mod->info.base;
mod->info.version = info->info.version - info->info.base + mod->info.base;
if (info->info.license)
mod->info.license = info->info.license - info->info.base + mod->info.base;
if (info->info.author)
mod->info.author = info->info.author - info->info.base + mod->info.base;
if (info->info.description)
mod->info.description = info->info.description - info->info.base + mod->info.base;
return 0;
}
/*-----------------------------------------------------------
* 初始化 kpm_load_info解析 ELF 头、modinfo、符号表
*----------------------------------------------------------*/
static int kpm_setup_load_info(struct kpm_load_info *info)
{
int rc = 0;
int i;
const char *name;
const char *version;
info->sechdrs = (Elf64_Shdr *)((const char *)info->hdr + info->ehdr->e_shoff);
info->secstrings = (const char *)info->hdr + info->sechdrs[info->ehdr->e_shstrndx].sh_offset;
rc = kpm_rewrite_section_headers(info);
if (rc) {
printk(KERN_ERR "ARM64 KPM Loader: rewrite section headers error\n");
return rc;
}
if (find_sec_num(info, ".kpm.init") == -1 || find_sec_num(info, ".kpm.exit") == -1) {
printk(KERN_ERR "ARM64 KPM Loader: Missing .kpm.init or .kpm.exit section\n");
return -ENOEXEC;
}
info->index.info = find_sec_num(info, ".kpm.info");
if (!info->index.info) {
printk(KERN_ERR "ARM64 KPM Loader: Missing .kpm.info section\n");
return -ENOEXEC;
}
info->info.base = (const char *)info->hdr + info->sechdrs[info->index.info].sh_offset;
info->info.size = info->sechdrs[info->index.info].sh_entsize;
name = kpm_get_modinfo(info, "name");
if (!name) {
printk(KERN_ERR "ARM64 KPM Loader: Module name not found\n");
return -ENOEXEC;
}
info->info.name = name;
printk(KERN_INFO "ARM64 KPM Loader: Module name: %s\n", name);
version = kpm_get_modinfo(info, "version");
if (!version) {
printk(KERN_ERR "ARM64 KPM Loader: Module version not found\n");
return -ENOEXEC;
}
info->info.version = version;
printk(KERN_INFO "ARM64 KPM Loader: Module version: %s\n", version);
info->info.license = kpm_get_modinfo(info, "license");
printk(KERN_INFO "ARM64 KPM Loader: Module license: %s\n", info->info.license ? info->info.license : "N/A");
info->info.author = kpm_get_modinfo(info, "author");
printk(KERN_INFO "ARM64 KPM Loader: Module author: %s\n", info->info.author ? info->info.author : "N/A");
info->info.description = kpm_get_modinfo(info, "description");
printk(KERN_INFO "ARM64 KPM Loader: Module description: %s\n", info->info.description ? info->info.description : "N/A");
for (i = 1; i < info->ehdr->e_shnum; i++) {
if (info->sechdrs[i].sh_type == SHT_SYMTAB) {
info->index.sym = i;
info->index.str = info->sechdrs[i].sh_link;
info->strtab = (char *)info->hdr + info->sechdrs[info->index.str].sh_offset;
break;
}
}
if (info->index.sym == 0) {
printk(KERN_ERR "ARM64 KPM Loader: Module has no symbols\n");
return -ENOEXEC;
}
return 0;
}
// ============================================================================================
/*-----------------------------------------------------------
* KPM 模块加载主流程
*----------------------------------------------------------*/
/* 注意:接口名称改为 kpm_load_module避免与内核原有 load_module 冲突 */
long kpm_load_module(const void *data, int len, const char *args, const char *event, void *__user reserved)
{
struct kpm_load_info load_info = { .hdr = data, .len = len };
long rc = 0;
struct kpm_module *mod;
rc = kpm_elf_header_check(&load_info);
if (rc)
goto out;
rc = kpm_setup_load_info(&load_info);
if (rc)
goto out;
if (find_sec_num(&load_info, ".kpm.init") == -1 || find_sec_num(&load_info, ".kpm.exit") == -1) {
printk(KERN_ERR "ARM64 KPM Loader: Required sections missing\n");
rc = -ENOEXEC;
goto out;
}
if (find_module(load_info.info.name)) {
printk(KERN_ERR "ARM64 KPM Loader: Module %s already loaded\n", load_info.info.name);
rc = -EEXIST;
goto out;
}
mod = vmalloc(sizeof(struct kpm_module));
if (!mod)
return -ENOMEM;
memset(mod, 0, sizeof(struct kpm_module));
if (args) {
mod->args = (typeof(mod->args)) vmalloc(strlen(args) + 1);
if (!mod->args) {
rc = -ENOMEM;
goto free_mod;
}
strcpy(mod->args, args);
}
kpm_layout_sections(mod, &load_info);
kpm_layout_symtab(mod, &load_info);
rc = kpm_move_module(mod, &load_info);
if (rc)
goto free_mod;
rc = kpm_simplify_symbols(mod, &load_info);
if (rc)
goto free_mod;
rc = kpm_apply_relocations(mod, &load_info);
if (rc)
goto free_mod;
flush_icache_all();
rc = mod->init(mod->args, event, reserved);
if (!rc) {
printk(KERN_INFO "ARM64 KPM Loader: Module [%s] loaded successfully with args [%s]\n", mod->info.name, args ? args : "");
spin_lock(&kpm_module_lock);
list_add_tail(&mod->list, &kpm_module_list);
spin_unlock(&kpm_module_lock);
goto out;
} else {
printk(KERN_ERR "ARM64 KPM Loader: Module [%s] init failed with error %ld\n", mod->info.name, rc);
mod->exit(reserved);
}
free_mod:
if (mod->args)
vfree(mod->args);
kpm_free_exec(mod->start, mod->size);
vfree(mod);
out:
return rc;
}
/* 卸载模块接口,改名为 sukisu_kpm_unload_module */
long sukisu_kpm_unload_module(const char *name, void *__user reserved)
{
long rc = 0;
struct kpm_module *mod = NULL;
if (!name)
return -EINVAL;
spin_lock(&kpm_module_lock);
list_for_each_entry(mod, &kpm_module_list, list) {
if (!strcmp(name, mod->info.name))
break;
}
if (!mod) {
rc = -ENOENT;
spin_unlock(&kpm_module_lock);
return rc;
}
list_del(&mod->list);
spin_unlock(&kpm_module_lock);
// rc = mod->exit(reserved);
mod->exit(reserved);
if (mod->args)
vfree(mod->args);
if (mod->ctl_args)
vfree(mod->ctl_args);
kpm_free_exec(mod->start, mod->size);
vfree(mod);
printk(KERN_INFO "ARM64 KPM Loader: Module %s unloaded, rc = %ld\n", name, rc);
return rc;
}
/*-----------------------------------------------------------
* 导出接口:从文件路径加载 KPM 模块(改名为 sukisu_kpm_load_module_path
*----------------------------------------------------------*/
long sukisu_kpm_load_module_path(const char *path, const char *args, void *__user reserved)
{
long rc = 0;
struct file *filp;
loff_t len;
void *data;
loff_t pos = 0;
printk(KERN_INFO "ARM64 KPM Loader: Loading module from file: %s\n", path);
if (!path)
return -EINVAL;
filp = filp_open(path, O_RDONLY, 0);
if (IS_ERR(filp)) {
printk(KERN_ERR "ARM64 KPM Loader: Failed to open file %s\n", path);
return PTR_ERR(filp);
}
len = vfs_llseek(filp, 0, SEEK_END);
printk(KERN_INFO "ARM64 KPM Loader: Module file size: %llx\n", len);
vfs_llseek(filp, 0, SEEK_SET);
data = vmalloc(len);
if (!data) {
filp_close(filp, NULL);
return -ENOMEM;
}
memset(data, 0, len);
kernel_read(filp, data, len, &pos);
filp_close(filp, 0);
if (pos != len) {
printk(KERN_ERR "ARM64 KPM Loader: Read file error\n");
rc = -EIO;
goto free_data;
}
rc = kpm_load_module(data, len, args, "load-file", reserved);
free_data:
vfree(data);
return rc;
}
/*-----------------------------------------------------------
* 模块管理查询接口
*----------------------------------------------------------*/
struct kpm_module *sukisu_kpm_find_module(const char *name)
{
struct kpm_module *pos;
spin_lock(&kpm_module_lock);
list_for_each_entry(pos, &kpm_module_list, list) {
if (!strcmp(name, pos->info.name)) {
spin_unlock(&kpm_module_lock);
return pos;
}
}
spin_unlock(&kpm_module_lock);
return NULL;
}
// 获取已经加载的KPM数量
int sukisu_kpm_num(void) {
struct kpm_module *pos;
int num = 0;
spin_lock(&kpm_module_lock);
list_for_each_entry(pos, &kpm_module_list, list) {
num++;
}
spin_unlock(&kpm_module_lock);
return num;
}
// 获取指定名称的KPM信息
int sukisu_kpm_info(const char* name, char __user* out) {
char buffer[512] = { 0 };
struct kpm_module *kpm = sukisu_kpm_find_module(name);
int osize;
if(kpm == NULL) {
return -1;
}
memset((void*)&buffer, 0, sizeof(buffer));
osize = snprintf(buffer, 511,
"Name: %s\n"
"Version: %s\n"
"Author: %s\n"
"License: %s\n"
"Description: %s",
kpm->info.name, kpm->info.version, kpm->info.author, kpm->info.license, kpm->info.description);
osize = copy_to_user((void __user*) out, (const void*)buffer, osize + 1);
return osize;
}
int sukisu_kpm_list(char __user *out,unsigned int bufferSize)
{
struct kpm_module *pos;
int outSize = 0;
int len;
char buffer[128]; // 临时缓冲区,避免直接操作用户空间
spin_lock(&kpm_module_lock);
list_for_each_entry(pos, &kpm_module_list, list) {
/* 格式化输出,每行一个模块名称 */
len = snprintf(buffer, sizeof(buffer), "%s\n", pos->info.name);
/* 检查剩余空间是否足够 */
if (outSize + len > bufferSize) {
spin_unlock(&kpm_module_lock);
return -ENOSPC; // 空间不足
}
/* 复制到用户空间 */
if (copy_to_user(out + outSize, buffer, len)) {
spin_unlock(&kpm_module_lock);
return -EFAULT; // 复制失败
}
outSize += len;
}
spin_unlock(&kpm_module_lock);
return outSize;
}
// 打印所有KPM信息
/* 直接写入进程 stdoutfd = 1 */
void sukisu_kpm_print_list(void)
{
struct kpm_module *kpm;
struct file *stdout_file;
loff_t pos = 0;
char *buffer;
int len;
/* 打开当前进程的 stdout */
stdout_file = filp_open("/proc/self/fd/1", O_WRONLY, 0);
if (IS_ERR(stdout_file)) {
pr_err("sukisu_kpm_print_list: Failed to open stdout.\n");
return;
}
/* 分配内核缓冲区 */
buffer = kmalloc(256, GFP_KERNEL);
if (!buffer) {
pr_err("sukisu_kpm_print_list: Failed to allocate buffer.\n");
filp_close(stdout_file, NULL);
return;
}
spin_lock(&kpm_module_lock);
list_for_each_entry(kpm, &kpm_module_list, list) {
/* 格式化模块信息 */
len = snprintf(buffer, 256,
"Name: %s\n"
"Version: %s\n"
"Author: %s\n"
"License: %s\n"
"Description: %s\n\n",
kpm->info.name, kpm->info.version, kpm->info.author,
kpm->info.license, kpm->info.description);
/* 通过 kernel_write() 直接写入 stdout */
kernel_write(stdout_file, buffer, len, &pos);
}
spin_unlock(&kpm_module_lock);
/* 释放资源 */
kfree(buffer);
filp_close(stdout_file, NULL);
}
EXPORT_SYMBOL(sukisu_kpm_load_module_path);
EXPORT_SYMBOL(sukisu_kpm_unload_module);
EXPORT_SYMBOL(sukisu_kpm_find_module);
// ============================================================================================
int sukisu_handle_kpm(unsigned long arg3, unsigned long arg4, unsigned long arg5)
{
if(arg3 == SUKISU_KPM_LOAD) {
char kernel_load_path[256] = { 0 };
char kernel_args_buffer[256] = { 0 };
if(arg4 == 0) {
return -1;
}
strncpy_from_user((char*)&kernel_load_path, (const char __user *)arg4, 255);
if(arg5 != 0) {
strncpy_from_user((char*)&kernel_args_buffer, (const char __user *)arg4, 255);
}
return sukisu_kpm_load_module_path((const char*)&kernel_load_path, (const char*) &kernel_args_buffer, NULL);
} else if(arg3 == SUKISU_KPM_UNLOAD) {
char kernel_name_buffer[256] = { 0 };
if(arg4 == 0) {
return -1;
}
strncpy_from_user((char*)&kernel_name_buffer, (const char __user *)arg4, 255);
return sukisu_kpm_unload_module((const char*) &kernel_name_buffer, NULL);
} else if(arg3 == SUKISU_KPM_NUM) {
return sukisu_kpm_num();
} else if(arg3 == SUKISU_KPM_INFO) {
char kernel_name_buffer[256] = { 0 };
if(arg4 == 0 || arg5 == 0) {
return -1;
}
strncpy_from_user((char*)&kernel_name_buffer, (const char __user *)arg4, 255);
return sukisu_kpm_info((const char*) &kernel_name_buffer, (char __user*) arg5);
} else if(arg3 == SUKISU_KPM_LIST) {
return sukisu_kpm_list((char __user*) arg4, (unsigned int) arg5);
} else if(arg3 == SUKISU_KPM_PRINT) {
sukisu_kpm_print_list();
}
return 0;
}
int sukisu_is_kpm_control_code(unsigned long arg2) {
return (arg2 >= CMD_KPM_CONTROL && arg2 <= CMD_KPM_CONTROL_MAX) ? 1 : 0;
}
EXPORT_SYMBOL(sukisu_handle_kpm);

44
kernel/kpm/kpm.h Normal file
View File

@@ -0,0 +1,44 @@
#ifndef ___SUKISU_KPM_H
#define ___SUKISU_KPM_H
int sukisu_handle_kpm(unsigned long arg3, unsigned long arg4, unsigned long arg5);
int sukisu_is_kpm_control_code(unsigned long arg2);
// KPM控制代码
#define CMD_KPM_CONTROL 28
#define CMD_KPM_CONTROL_MAX 34
// 控制代码
// prctl(xxx, xxx, 1, "PATH", "ARGS")
// success return 0, error return -N
#define SUKISU_KPM_LOAD 28
// prctl(xxx, xxx, 2, "NAME")
// success return 0, error return -N
#define SUKISU_KPM_UNLOAD 29
// num = prctl(xxx, xxx, 3)
// error return -N
// success return +num or 0
#define SUKISU_KPM_NUM 30
// prctl(xxx, xxx, 4, Buffer, BufferSize)
// success return +out, error return -N
#define SUKISU_KPM_LIST 31
// prctl(xxx, xxx, 5, "NAME", Buffer[256])
// success return +out, error return -N
#define SUKISU_KPM_INFO 32
// prctl(xxx, xxx, 6, "NAME", "ARGS")
// success return KPM's result value
// error return -N
#define SUKISU_KPM_CONTROL 33
// prctl(xxx, xxx, 7)
// success will printf to stdout and return 0
// error will return -1
#define SUKISU_KPM_PRINT 34
#endif

View File

View File