命令字:!ndx.modsyms

用法描述

用于显示内核模块相关的符号信息,包括常规模块、BPF、Kprobe、Ftrace 等模块的符号,相当于cat /proc/kallsyms中的模块符号部分
命令行参数支持:
-a <地址> 按地址查找模块符号
-n <名称> 按符号名称查找
-h / ? 显示帮助信息

开发过程

1.找到关键全局变量

符号类型 关键全局变量 作用
常规模块符号 lk!modules 内核所有模块的链表头,存储所有已加载模块的元信息(如名称、符号表地址)
BPF 符号 lk!bpf_kallsyms BPF 符号的链表头,存储所有 BPF 程序相关的符号
Kprobe 符号 lk!kprobe_insn_slots Kprobe 指令页面的管理结构,内部包含 pages 链表,存储 Kprobe 相关符号
Ftrace 符号 lk!ftrace_mod_maps Ftrace 跟踪模块的链表头,存储所有被 Ftrace 跟踪的模块及函数信息

查找过程

内核中遍历打印符号的时候,如果当前遍历的索引大于核心符号数量,就会进入模块符号的遍历。

根据指定的位置 pos 更新迭代器状态,并尝试从不同符号源中获取符号

以常规符号的获取为例:
通过调用module_get_kallsym,从结构体module中提取其符号表中的符号信息

具体则调用list_for_each_entry_rcu,传入全局链表表头modules的地址,遍历整个双向循环链表,获取每个moudule结构体。
再进一步获取符号信息

2.相关数据结构

以常规模块为例:

在遍历链表获取每个module结构体时,再获取module对应结构体kallsyms
这是它的定义

// 模块符号表结构,用于存储内核模块的符号信息
struct mod_kallsyms {
    Elf_Sym *symtab;        // 指向符号表数组的指针,每个元素是一个Elf_Sym结构,包含符号的地址
    unsigned int num_symtab;// 符号表中符号的总数
    char *strtab;           // 指向字符串表的指针,存储符号的名称字符串
    char *typetab;          //整个字符串集合的起始地址
};

结构体 字段名 说明 类型/来源
module name 模块名称 char[]
module mod_kallsyms 指向模块符号表结构体 struct mod_kallsyms*
mod_kallsyms symtab 符号表数组指针,每个元素为符号结构 Elf_Sym*
mod_kallsyms num_symtab 符号表中符号的总数 unsigned int
mod_kallsyms strtab 字符串表基地址,存储所有符号名称字符串,通过符号表中的偏移字段定位具体符号名 char*
mod_kallsyms typetab 类型表指针,存储所有符号类型信息 char*
Elf_Sym st_value 符号的地址 unsigned long
Elf_Sym st_name 符号名称在字符串表中的偏移 unsigned int

字段获取说明:

  • 模块名 : mod->name
  • 符号地址 : kallsyms_symbol_value(sym) ,即 sym->st_value
  • 符号类型 : kallsyms->typetab[symnum]
  • 符号名称 : kallsyms_symbol_name(kallsyms, symnum) ,即 kallsyms->strtab + kallsyms->symtab[symnum].st_name

3.读取变量和相关偏移,输出信息

#include "ndx.h"
#include <windows.h>
#include <minwindef.h>
#include <WDBGEXTS.H>
#include <cstdio>
#include <string.h>
#include <wdbgexts.h>
#include <time.h>
#include <string>
#include "kallsyms_mod.h"

// ========== 全局变量定义 ==========
ModSymbol mod_symbol_manager;


/**
 * 通配符匹配函数
 * 支持前缀匹配(abc*)、后缀匹配(*xyz)、包含匹配(*def*)
 */
static bool wildcard_match(const std::string& text, const std::string& pattern) {
    if (pattern.empty()) {
        return text.empty();
    }

    // 检查是否包含通配符
    if (pattern.find('*') == std::string::npos) {
        // 没有通配符,精确匹配
        return text == pattern;
    }

    // 处理特殊情况:只有一个*
    if (pattern == "*") {
        return true;  // 匹配任何字符串
    }

    // 【重要】前后缀匹配:*abc* - 必须优先检查,避免被单独的前缀/后缀匹配拦截
    if (pattern.front() == '*' && pattern.back() == '*') {
        if (pattern.length() <= 2) {
            return true;  // ** 或 * 匹配任何字符串
        }
        std::string substring = pattern.substr(1, pattern.length() - 2);
        return text.find(substring) != std::string::npos;
    }

    // 后缀匹配:*abc
    if (pattern.front() == '*') {
        std::string suffix = pattern.substr(1);
        if (suffix.empty()) {
            return true;  // * 匹配任何字符串
        }
        if (text.length() < suffix.length()) {
            return false;
        }
        return text.substr(text.length() - suffix.length()) == suffix;
    }

    // 前缀匹配:abc*
    if (pattern.back() == '*') {
        std::string prefix = pattern.substr(0, pattern.length() - 1);
        if (prefix.empty()) {
            return true;  // * 匹配任何字符串
        }
        return text.substr(0, prefix.length()) == prefix;
    }

    // 精确匹配
    return text == pattern;
}

// ModSymbol构造函数
ModSymbol::ModSymbol() {
    module_offsets = { 0, 0, 0, 0, false };
}

/**
 * 初始化模块结构体偏移量
 * 使用GetFieldOffset动态获取字段偏移
 */
bool ModSymbol::init_module_offsets() {
    if (module_offsets.initialized) {
        return true;
    }

    dprintf("Initializing module structure offsets...\n");

    // 获取module结构体各字段的偏移量
    if (GetFieldOffset("module", "state", &module_offsets.state_offset) != 0) {
        dprintf("ERROR: Failed to get module.state offset\n");
        return false;
    }

    if (GetFieldOffset("module", "list", &module_offsets.list_offset) != 0) {
        dprintf("ERROR: Failed to get module.list offset\n");
        return false;
    }

    if (GetFieldOffset("module", "name", &module_offsets.name_offset) != 0) {
        dprintf("ERROR: Failed to get module.name offset\n");
        return false;
    }

    if (GetFieldOffset("module", "kallsyms", &module_offsets.kallsyms_offset) != 0) {
        dprintf("ERROR: Failed to get module.kallsyms offset\n");
        return false;
    }

    module_offsets.initialized = true;


    return true;
}


/**
 * 通用的模块遍历函数
 * 对每个模块调用回调函数进行处理
 */
typedef bool (*ModuleCallback)(ULONG64 module_addr, const char* mod_name,
    const struct mod_kallsyms& kallsyms, void* context);

// 通用链表节点处理回调函数类型
typedef bool (*ListNodeProcessor)(ULONG64 node_addr, void* context);

/**
 * 通用链表遍历函数
 * @param list_head_addr 链表头地址
 * @param list_head 链表头结构
 * @param list_offset 链表节点在结构体中的偏移量
 * @param processor 节点处理函数
 * @param context 上下文参数
 * @return 遍历是否成功
 */
bool ModSymbol::traverse_generic_list(ULONG64 list_head_addr, const struct list_head& list_head,
    ULONG list_offset, ListNodeProcessor processor, void* context) {
    ULONG read;

    // 遍历链表
    ULONG64 current_list_node = (ULONG64)list_head.next;

    // 检查是否为空链表或无效指针
    if (current_list_node == list_head_addr || current_list_node == NULL) {
        /*dprintf("Empty list or invalid pointer\n");*/
        return true; // 空链表不是错误
    }

    while (current_list_node != NULL && current_list_node != list_head_addr) {
        // 计算结构体地址
        ULONG64 struct_addr = current_list_node - list_offset;

        // 调用处理函数处理当前节点
        if (!processor(struct_addr, context)) {
            return false;
        }

        // 移动到下一个节点
        struct list_head next_list;
        if (!ExtReadMemory(current_list_node, &next_list, sizeof(next_list), &read) ||
            read != sizeof(next_list)) {
            dprintf("WARNING: Failed to read next list entry at 0x%llx, stopping traversal\n", current_list_node);
            break;
        }

        ULONG64 next_addr = (ULONG64)next_list.next;

        // 检查指针有效性
        if (next_addr == 0 || next_addr == current_list_node) {
            dprintf("Reached end of list (next=0x%llx)\n", next_addr);
            break;
        }

        current_list_node = next_addr;
    }

    return true;
}

// 模块处理上下文结构
struct ModuleProcessContext {
    ModuleCallback callback;
    void* user_context;
    ModSymbol* mod_symbol;
};

/**
 * 模块节点处理函数(静态函数)
 * @param module_addr 模块结构体地址
 * @param context 处理上下文
 * @return 处理是否成功
 */
static bool process_module_node(ULONG64 module_addr, void* context) {
    ULONG read;
    ModuleProcessContext* mod_ctx = static_cast<ModuleProcessContext*>(context);

    // 读取模块状态
    int module_state;
    if (!ExtReadMemory(module_addr + mod_ctx->mod_symbol->module_offsets.state_offset, &module_state, sizeof(module_state), &read) ||
        read != sizeof(module_state)) {
        dprintf("WARNING: Failed to read module state at 0x%llx, skipping\n", module_addr);
        return true; 
    }

    // 跳过未初始化完成的模块
    if (module_state == MODULE_STATE_UNFORMED) {
        return true; 
    }

    // 读取模块名称
    char mod_name[MODULE_NAME_LEN];
    if (!ExtReadMemory(module_addr + mod_ctx->mod_symbol->module_offsets.name_offset, mod_name, sizeof(mod_name), &read)) {
        dprintf("WARNING: Failed to read module name at 0x%llx, skipping\n", module_addr);
        return true; 
    }
    mod_name[MODULE_NAME_LEN - 1] = '\0';

    // 读取模块的kallsyms指针
    ULONG64 kallsyms_ptr;
    if (!ExtReadMemory(module_addr + mod_ctx->mod_symbol->module_offsets.kallsyms_offset, &kallsyms_ptr, sizeof(kallsyms_ptr), &read) ||
        read != sizeof(kallsyms_ptr) || kallsyms_ptr == 0) {
        return true; 
    }

    // 读取mod_kallsyms结构
    struct mod_kallsyms kallsyms;
    if (!ExtReadMemory(kallsyms_ptr, &kallsyms, sizeof(kallsyms), &read) ||
        read != sizeof(kallsyms)) {
        return true; 
    }

    // 调用用户回调函数处理当前模块
    return mod_ctx->callback(module_addr, mod_name, kallsyms, mod_ctx->user_context);
}

/**
 * 遍历模块链表的核心函数
 * @param modules_addr 模块链表头地址
 * @param modules_list 模块链表头结构
 * @param callback 回调函数
 * @param context 回调函数上下文
 * @return 遍历是否成功
 */
bool ModSymbol::traverse_module_list(ULONG64 modules_addr, const struct list_head& modules_list,
    ModuleCallback callback, void* context) {
    // 创建模块处理上下文
    ModuleProcessContext mod_ctx = { callback, context, this };

    // 使用通用链表遍历函数
    return traverse_generic_list(modules_addr, modules_list, module_offsets.list_offset,
        process_module_node, &mod_ctx);
}

bool ModSymbol::for_each_module(ModuleCallback callback, void* context) {
    ULONG read;

    // 初始化模块结构体偏移量
    if (!init_module_offsets()) {
        dprintf("ERROR: Failed to initialize module offsets\n");
        return false;
    }

    // 获取modules全局变量地址
    ULONG64 modules_addr;
    if (!GetExpressionEx("lk!modules", &modules_addr, NULL)) {
        dprintf("ERROR: Failed to get modules address\n");
        return false;
    }

    // 读取模块链表头
    struct list_head modules_list;
    if (!ExtReadMemory(modules_addr, &modules_list, sizeof(modules_list), &read) ||
        read != sizeof(modules_list)) {
        dprintf("ERROR: Failed to read modules list head\n");
        return false;
    }

    // 调用链表遍历函数
    return traverse_module_list(modules_addr, modules_list, callback, context);
}

/**
 * 加载符号表到缓存 - 封装所有批量读取逻辑
 */
bool ModSymbol::load_symbol_tables(const struct mod_kallsyms& kallsyms, SymbolTableCache& cache) {
    ULONG read;

    // 1. 清理之前的缓存
    cache.cleanup();

    if (kallsyms.num_symtab == 0) {
        return false;
    }

    // 2. 批量读取符号表
    size_t symtab_size = kallsyms.num_symtab * sizeof(Elf64_Sym);
    cache.symbols = (Elf64_Sym*)malloc(symtab_size);
    if (!cache.symbols) {
        return false;
    }

    if (!ExtReadMemory(kallsyms.symtab, cache.symbols, symtab_size, &read) || read != symtab_size) {
        cache.cleanup();
        return false;
    }

    // 3. 批量读取类型表
    cache.types = (char*)malloc(kallsyms.num_symtab);
    if (!cache.types) {
        cache.cleanup();
        return false;
    }

    if (!ExtReadMemory(kallsyms.typetab, cache.types, kallsyms.num_symtab, &read) || read != kallsyms.num_symtab) {
        cache.cleanup();
        return false;
    }

    // 4. 计算字符串表大小 - 最大偏移量
    ULONG max_offset = 0;
    //最大偏移量来确定字符串表的大小
    max_offset = cache.symbols[kallsyms.num_symtab - 1].st_name;


    // 5. 批量读取字符串表(使用分块读取)
    if (max_offset > 0) {
        size_t estimated_strtab_size = max_offset + 32;

        cache.strtab_cache = (char*)malloc(estimated_strtab_size);
        if (!cache.strtab_cache) {
            dprintf("ERROR: Failed to allocate %zu bytes for string table cache\n", estimated_strtab_size);
        }
        else {
            // 使用封装的分块读取函数
            if (read_memory_chunked(kallsyms.strtab, cache.strtab_cache, estimated_strtab_size)) {
                cache.actual_strtab_size = estimated_strtab_size;
                cache.is_valid = true;
                return true;
            }
            else {
                dprintf("ERROR: String table chunked read failed\n");
                free(cache.strtab_cache);
                cache.strtab_cache = NULL;
            }
        }
    }

    // 字符串表读取失败,但符号表和类型表成功
    cache.is_valid = true;
    return true;
}

/**
 * 分块读取内存数据 - 处理大块内存读取限制
 * @param address 起始地址
 * @param buffer 目标缓冲区
 * @param total_size 总大小
 * @param chunk_size 单次读取块大小
 * @return 是否成功读取所有数据
 */
bool ModSymbol::read_memory_chunked(ULONG64 address, void* buffer, size_t total_size, size_t chunk_size) {
    size_t total_read = 0;
    char* buf_ptr = (char*)buffer;
    BOOL read_success = TRUE;

    // 分块读取整个数据
    while (total_read < total_size && read_success) {
        size_t remaining = total_size - total_read;
        size_t read_this_time = (remaining < chunk_size) ? remaining : chunk_size;
        ULONG read = 0;

        // 读取当前块(从偏移total_read处开始)
        read_success = ExtReadMemory(
            address + total_read,      // 当前读取地址
            buf_ptr + total_read,      // 缓存中的写入位置
            read_this_time,
            &read
        );

        if (read_success && read > 0) {
            total_read += read;
        }
        else {
            dprintf("Chunked read failed: address=0x%llx, offset=%zu, requested=%zu, actual=%lu\n",
                address, total_read, read_this_time, read);
            break;
        }
    }

    if (total_read != total_size) {
        dprintf("Chunked read incomplete: total_read=%zu, required=%zu\n", total_read, total_size);
    }

    return (total_read == total_size);
}

/**
 * 从缓存获取符号名称 - 封装符号名称读取逻辑
 * 模拟内核kallsyms_symbol_name()函数
 */
const char* ModSymbol::get_symbol_name_from_cache(const struct mod_kallsyms& kallsyms,
    const Elf64_Sym& sym,
    const SymbolTableCache& cache,
    char* fallback_buffer,
    size_t buffer_size) {
    ULONG read;

    // 简单的有效性检查
    if (sym.st_name >= 0x100000) {
        strncpy_s(fallback_buffer, buffer_size, "<invalid>", _TRUNCATE);
        return fallback_buffer;
    }

    // 优先从缓存获取符号名称(模拟内核kallsyms_symbol_name)
    if (cache.strtab_cache && sym.st_name < cache.actual_strtab_size) {
        const char* cached_name = cache.strtab_cache + sym.st_name;  // kallsyms->strtab + symtab[symnum].st_name

        // 确保字符串在缓存边界内
        size_t remaining_size = cache.actual_strtab_size - sym.st_name;
        size_t name_len = strnlen(cached_name, remaining_size);

        if (name_len < remaining_size) {
            return cached_name;  // 直接返回缓存中的字符串指针
        }
    }

    // 缓存中未找到或偏移量超出范围,回退到单独读取
    if (ExtReadMemory(kallsyms.strtab + sym.st_name, fallback_buffer, buffer_size, &read)) {
        dprintf("Failed to get symbol name from cache! sym_addr=0x%lx, name_offset=%lu\n",
            (unsigned long)sym.st_value,
            (unsigned long)sym.st_name);
        fallback_buffer[buffer_size - 1] = '\0';
        return fallback_buffer;
    }

    // 完全失败的情况
    strncpy_s(fallback_buffer, buffer_size, "<unknown>", _TRUNCATE);
    return fallback_buffer;
}

// 统计上下文结构
struct ModuleStats {
    int module_count;
    unsigned int total_symbols;
};

// 回调函数:输出模块信息和所有符号
static bool print_module_info(ULONG64 module_addr, const char* mod_name,
    const struct mod_kallsyms& kallsyms, void* context) {
    ModuleStats* stats = (ModuleStats*)context;
    ULONG read;

    stats->module_count++;
    stats->total_symbols += kallsyms.num_symtab;

    // 输出所有符号信息
    if (kallsyms.num_symtab > 0) {

        // 使用封装函数加载符号表到缓存
        SymbolTableCache cache;
        if (!mod_symbol_manager.load_symbol_tables(kallsyms, cache)) {
            dprintf("ERROR: Failed to load symbol tables\n");
            return true;
        }

        // 遍历所有符号
        for (unsigned int i = 0; i < kallsyms.num_symtab; i++) {
            const Elf64_Sym& sym = cache.symbols[i];
            char sym_type = cache.types[i];

            // 使用封装函数获取符号名称
            char sym_name_buffer[KSYM_NAME_LEN];
            const char* sym_name = mod_symbol_manager.get_symbol_name_from_cache(
                kallsyms, sym, cache, sym_name_buffer, sizeof(sym_name_buffer));

            // 跳过第一个空符号(ELF标准中索引0是未定义符号)
            if (i == 0 && sym.st_value == 0 && strlen(sym_name) == 0) {
                continue;
            }


            // 使用内核标准格式输出: %p %c %s\t[%s]
            dprintf("%p %c %s\t[%s]\n", (void*)sym.st_value, sym_type, sym_name, mod_name);
        }

        // 缓存会在cache析构时自动释放内存
    }
    else {
        dprintf("No symbols found in module %s\n", mod_name);
    }

    return true; // 继续遍历
}



// 查找上下文结构
struct SymbolSearchContext {
    const char* target_name;
    bool found;
    int count;  // 找到的符号数量
};


// 回调函数:按名称查找符号
static bool search_symbol_by_name(ULONG64 module_addr, const char* mod_name,
    const struct mod_kallsyms& kallsyms, void* context) {
    SymbolSearchContext* ctx = (SymbolSearchContext*)context;
    ULONG read;

    // 使用封装函数加载符号表到缓存
    SymbolTableCache cache;
    if (!mod_symbol_manager.load_symbol_tables(kallsyms, cache)) {
        return true; // 继续遍历下一个模块
    }

    // 遍历当前模块的所有符号
    for (unsigned int i = 0; i < kallsyms.num_symtab; i++) {
        const Elf64_Sym& sym = cache.symbols[i];

        // 使用封装函数获取符号名称
        char sym_name_buffer[KSYM_NAME_LEN];
        const char* sym_name = mod_symbol_manager.get_symbol_name_from_cache(
            kallsyms, sym, cache, sym_name_buffer, sizeof(sym_name_buffer));

        if (strlen(sym_name) == 0) {
            continue; // 跳过空名称符号
        }

        // 使用通配符匹配检查符号名称
        if (wildcard_match(std::string(sym_name), std::string(ctx->target_name))) {
            char sym_type = cache.types[i];

            // 使用内核格式输出: %p %c %s\t[%s]
            dprintf("%p %c %s\t[%s]\n", (void*)sym.st_value, sym_type, sym_name, mod_name);
            ctx->found = true;
            ctx->count++;  // 增加符号计数
        }
    }

    // 缓存会在cache析构时自动释放内存

    return true; // 继续遍历所有模块
}


// 地址查找上下文结构
struct AddressSearchContext {
    ULONG64 target_addr;           // 需要搜索的目标地址
    ULONG64 closest_addr;          // 找到的最接近目标地址的地址
    ULONG64 closest_distance;      // 最接近地址与目标地址的距离
    char closest_type;             // 最接近地址的类型(如符号类型)
    char closest_name[KSYM_NAME_LEN];  // 最接近地址的名称
    char closest_module[MODULE_NAME_LEN];  // 最接近地址所在的模块名
    bool found;                    // 是否找到目标地址的标识
    int symbols_searched;          // 搜索的符号总数
};

// 回调函数:按地址查找符号
static bool search_symbol_by_address(ULONG64 module_addr, const char* mod_name,
    const struct mod_kallsyms& kallsyms, void* context) {
    AddressSearchContext* ctx = (AddressSearchContext*)context;
    ULONG read;

    // 使用封装函数加载符号表到缓存
    SymbolTableCache cache;
    if (!mod_symbol_manager.load_symbol_tables(kallsyms, cache)) {
        return true;
    }

    // 遍历当前模块的所有符号
    for (unsigned int i = 0; i < kallsyms.num_symtab; i++) {
        const Elf64_Sym& sym = cache.symbols[i];
        char sym_type = cache.types[i];
        ctx->symbols_searched++;  // 增加搜索计数

        // 查找最接近但不超过目标地址的符号
        if (sym.st_value <= ctx->target_addr) {
            ULONG64 distance = ctx->target_addr - sym.st_value;
            if (distance < ctx->closest_distance) {
                ctx->closest_distance = distance;
                ctx->closest_addr = sym.st_value;

                // 使用已批量读取的类型
                ctx->closest_type = sym_type;

                // 使用封装函数获取符号名称
                char sym_name_buffer[KSYM_NAME_LEN];
                const char* sym_name = mod_symbol_manager.get_symbol_name_from_cache(
                    kallsyms, sym, cache, sym_name_buffer, sizeof(sym_name_buffer));

                strncpy_s(ctx->closest_name, sizeof(ctx->closest_name), sym_name, _TRUNCATE);

                // 复制模块名称
                strncpy_s(ctx->closest_module, sizeof(ctx->closest_module), mod_name, _TRUNCATE);
                ctx->found = true;
            }
        }
    }

    // 缓存会在cache析构时自动释放内存

    return true; // 继续遍历所有模块
}



/**
 * 遍历bpf符号链表
 */
bool ModSymbol::for_each_bpf_symbol(ListNodeProcessor callback, void* context) {
    //获取链表头节点地址
    ULONG64 bpf_kallsyms_addr;
    if (!GetExpressionEx("lk!bpf_kallsyms", &bpf_kallsyms_addr, NULL)) {
        dprintf("Failed to get bpf_kallsyms address\n");
        return 0;
    }

    if (bpf_kallsyms_addr == 0) {
        return false;
    }

    struct list_head bpf_list;     //链表头节点
    ULONG read;
    if (!ExtReadMemory(bpf_kallsyms_addr, &bpf_list, sizeof(bpf_list), &read) || read != sizeof(bpf_list)) {
        dprintf("Failed to read bpf_kallsyms list head\n");
        return false;
    }

    // 计算bpf_ksym结构中lnode字段的偏移量
    ULONG lnode_offset;
    if (GetFieldOffset("bpf_ksym", "lnode", &lnode_offset) != 0) {
        dprintf("ERROR: Failed to get bpf_ksym.lnode offset\n");
        return false;
    }

    return traverse_generic_list(bpf_kallsyms_addr, bpf_list, lnode_offset, callback, context);
}


/**
 * bpf符号显示回调函数
 */
static bool print_bpf_symbol_info(ULONG64 bpf_ksym_addr, void* context) {
    ModuleStats* stats = (ModuleStats*)context;
    struct bpf_ksym ksym;
    ULONG read;

    if (!ExtReadMemory(bpf_ksym_addr, &ksym, sizeof(ksym), &read) || read != sizeof(ksym)) {
        return true; // 继续遍历
    }

    // 确保符号名称以null结尾
    ksym.name[KSYM_NAME_LEN - 1] = '\0';

    // 显示bpf符号信息
    dprintf("%p %c %s\t[bpf]\n", (void*)ksym.start, BPF_SYM_ELF_TYPE, ksym.name);

    stats->total_symbols++;
    return true; // 继续遍历
}


/**
 * bpf符号按名称搜索回调函数
 */
static bool search_bpf_symbol_by_name(ULONG64 bpf_ksym_addr, void* context) {
    SymbolSearchContext* ctx = (SymbolSearchContext*)context;
    struct bpf_ksym ksym;
    ULONG read;

    if (!ExtReadMemory(bpf_ksym_addr, &ksym, sizeof(ksym), &read) || read != sizeof(ksym)) {
        return true; // 继续遍历
    }

    // 确保符号名称以null结尾
    ksym.name[KSYM_NAME_LEN - 1] = '\0';

    // 检查符号名称是否匹配(支持通配符)
    if (wildcard_match(ksym.name, ctx->target_name)) {
        dprintf("%p %c %s\t[bpf]\n", (void*)ksym.start, BPF_SYM_ELF_TYPE, ksym.name);
        ctx->found = true;
        ctx->count++;
    }

    return true; // 继续遍历
}

/**
 * bpf符号按地址搜索回调函数
 */
static bool search_bpf_symbol_by_address(ULONG64 bpf_ksym_addr, void* context) {
    AddressSearchContext* ctx = (AddressSearchContext*)context;
    struct bpf_ksym ksym;
    ULONG read;

    if (!ExtReadMemory(bpf_ksym_addr, &ksym, sizeof(ksym), &read) || read != sizeof(ksym)) {
        return true; // 继续遍历
    }

    ctx->symbols_searched++;

    // 查找最接近但不超过目标地址的符号
    if (ksym.start <= ctx->target_addr) {
        ULONG64 distance = ctx->target_addr - ksym.start;
        if (distance < ctx->closest_distance) {
            ctx->closest_distance = distance;
            ctx->closest_addr = ksym.start;
            ctx->closest_type = BPF_SYM_ELF_TYPE;

            // 确保符号名称以null结尾
            ksym.name[KSYM_NAME_LEN - 1] = '\0';
            strncpy_s(ctx->closest_name, sizeof(ctx->closest_name), ksym.name, _TRUNCATE);
            strncpy_s(ctx->closest_module, sizeof(ctx->closest_module), "bpf", _TRUNCATE);
            ctx->found = true;
        }
    }

    return true; // 继续遍历
}


/**
 * kprobe符号显示回调函数
 */
static bool print_kprobe_symbol_info(ULONG64 kprobe_insn_page_addr, void* context) {
    ModuleStats* stats = (ModuleStats*)context;
    struct kprobe_insn_page page;
    ULONG read;

    if (!ExtReadMemory(kprobe_insn_page_addr, &page, sizeof(page), &read) || read != sizeof(page)) {
        return true; // 继续遍历
    }

    // 通过偏移量读取 kprobe_insn_cache 中的符号名
    char symbol_name[KSYM_NAME_LEN] = "kprobe_insn_page";
    if (page.cache != 0) {
        ULONG64 sym_addr;
        // 获取 kprobe_insn_cache.sym 字段偏移量
        ULONG sym_offset;
        if (GetFieldOffset("kprobe_insn_cache", "sym", &sym_offset) != 0) {
            dprintf("ERROR: Failed to get kprobe_insn_cache.sym offset\n");
            return true; // 继续遍历
        }
        if (ExtReadMemory(page.cache + sym_offset, &sym_addr, sizeof(sym_addr), &read) && read == sizeof(sym_addr) && sym_addr != 0) {
            // 读取符号名字符串
            if (!ExtReadMemory(sym_addr, symbol_name, sizeof(symbol_name) - 1, &read)) {
                strcpy_s(symbol_name, sizeof(symbol_name), "kprobe_insn_page");
            }
            else {
                symbol_name[read] = '\0'; // 确保字符串以null结尾
            }
        }
    }

    // 显示kprobe指令页面信息
    dprintf("%p %c %s\t[__builtin__kprobes]\n",
        (void*)page.insns, KPROBE_SYM_ELF_TYPE, symbol_name);

    stats->total_symbols++;
    return true; // 继续遍历
}

/**
 * kprobe符号按名称搜索回调函数
 */
static bool search_kprobe_symbol_by_name(ULONG64 kprobe_insn_page_addr, void* context) {
    SymbolSearchContext* ctx = (SymbolSearchContext*)context;
    struct kprobe_insn_page page;
    ULONG read;

    if (!ExtReadMemory(kprobe_insn_page_addr, &page, sizeof(page), &read) || read != sizeof(page)) {
        return true; // 继续遍历
    }

    // 通过偏移量读取 kprobe_insn_cache 中的符号名
    char symbol_name[KSYM_NAME_LEN] = "";
    if (page.cache != 0) {
        ULONG64 sym_addr;
        // 获取 kprobe_insn_cache.sym 字段偏移量
        ULONG sym_offset;
        if (GetFieldOffset("kprobe_insn_cache", "sym", &sym_offset) == 0) {
            if (ExtReadMemory(page.cache + sym_offset, &sym_addr, sizeof(sym_addr), &read) && read == sizeof(sym_addr) && sym_addr != 0) {
                // 读取符号名字符串
                if (ExtReadMemory(sym_addr, symbol_name, sizeof(symbol_name) - 1, &read)) {
                    symbol_name[read] = '\0'; // 确保字符串以null结尾
                }
            }
        }
    }

    // 检查符号名称是否匹配(支持通配符)
    if (wildcard_match(symbol_name, ctx->target_name)) {
        dprintf("%p %c %s\t[__builtin__kprobes]\n", (void*)page.insns, KPROBE_SYM_ELF_TYPE, symbol_name);
        ctx->found = true;
        ctx->count++;
    }

    return true; // 继续遍历
}

/**
 * kprobe符号按地址搜索回调函数
 */
static bool search_kprobe_symbol_by_address(ULONG64 kprobe_insn_page_addr, void* context) {
    AddressSearchContext* ctx = (AddressSearchContext*)context;
    struct kprobe_insn_page page;
    ULONG read;

    if (!ExtReadMemory(kprobe_insn_page_addr, &page, sizeof(page), &read) || read != sizeof(page)) {
        return true; // 继续遍历
    }

    ctx->symbols_searched++;

    // 查找最接近但不超过目标地址的符号
    if (page.insns <= ctx->target_addr) {
        ULONG64 distance = ctx->target_addr - page.insns;
        if (distance < ctx->closest_distance) {
            ctx->closest_distance = distance;
            ctx->closest_addr = page.insns;
            ctx->closest_type = KPROBE_SYM_ELF_TYPE;

            // 通过偏移量读取 kprobe_insn_cache 中的符号名
            char symbol_name[KSYM_NAME_LEN] = "";
            if (page.cache != 0) {
                ULONG64 sym_addr;
                // 获取 kprobe_insn_cache.sym 字段偏移量
                ULONG sym_offset;
                if (GetFieldOffset("kprobe_insn_cache", "sym", &sym_offset) == 0) {
                    if (ExtReadMemory(page.cache + sym_offset, &sym_addr, sizeof(sym_addr), &read) && read == sizeof(sym_addr) && sym_addr != 0) {
                        // 读取符号名字符串
                        if (ExtReadMemory(sym_addr, symbol_name, sizeof(symbol_name) - 1, &read)) {
                            symbol_name[read] = '\0'; // 确保字符串以null结尾
                        }
                    }
                }
            }

            strncpy_s(ctx->closest_name, sizeof(ctx->closest_name), symbol_name, _TRUNCATE);
            strncpy_s(ctx->closest_module, sizeof(ctx->closest_module), "__builtin__kprobes", _TRUNCATE);
            ctx->found = true;
        }
    }

    return true; // 继续遍历
}


/**
 * 遍历kprobe符号链表
 */

bool ModSymbol::for_each_kprobe_symbol(ListNodeProcessor callback, void* context)
{
    // 获取 kprobe_insn_slots 结构体地址
    ULONG64 kprobe_insn_slots_addr;
    if (!GetExpressionEx("lk!kprobe_insn_slots", &kprobe_insn_slots_addr, NULL)) {
        dprintf("Failed to get kprobe_insn_slots address\n");
        return false;
    }

    if (kprobe_insn_slots_addr == 0) {
        return false;
    }

    // 计算 pages 字段的偏移量
    ULONG pages_offset;
    if (GetFieldOffset("kprobe_insn_cache", "pages", &pages_offset) != 0) {
        dprintf("ERROR: Failed to get kprobe_insn_slots.pages offset\n");
        return false;
    }

    // 获取 pages 链表头节点地址
    ULONG64 pages_list_addr = kprobe_insn_slots_addr + pages_offset;

    struct list_head pages_list;     // 链表头节点
    ULONG read;
    if (!ExtReadMemory(pages_list_addr, &pages_list, sizeof(pages_list), &read) || read != sizeof(pages_list)) {
        dprintf("Failed to read kprobe_insn_slots pages list head\n");
        return false;
    }

    // 由于 struct kprobe_insn_page 中 list_head list 是第一个字段,所以偏移为 0
    ULONG list_offset = 0;

    return traverse_generic_list(pages_list_addr, pages_list, list_offset, callback, context);
}


/**
 * 显示ftrace符号信息(包含模块信息)
 */
static bool print_ftrace_symbol_info(ULONG64 ftrace_mod_func_addr, const char* module_name, void* context) {
    ModuleStats* stats = (ModuleStats*)context;
    struct ftrace_mod_func mod_func;
    ULONG read;

    // 读取ftrace_mod_func结构
    if (!ExtReadMemory(ftrace_mod_func_addr, &mod_func, sizeof(mod_func), &read) || read != sizeof(mod_func)) {
        return true; // 继续遍历
    }

    // 读取函数名称
    char func_name[KSYM_NAME_LEN];
    if (!ExtReadMemory((ULONG64)mod_func.name, func_name, sizeof(func_name), &read)) {
        strcpy_s(func_name, sizeof(func_name), "<unknown>");
    }
    func_name[KSYM_NAME_LEN - 1] = '\0';

    // 显示ftrace符号信息,使用传入的模块名称
    dprintf("%p %c %s\t[%s]\n", (void*)mod_func.ip, FTRACE_SYM_ELF_TYPE, func_name, module_name);

    stats->total_symbols++;
    return true; // 继续遍历
}


/**
 * 遍历ftrace符号链表
 */
bool ModSymbol::for_each_ftrace_symbol(FtraceSymbolProcessor callback, void* context) {
    // 获取ftrace_mod_maps链表头节点地址
    ULONG64 ftrace_mod_maps_addr;
    if (!GetExpressionEx("lk!ftrace_mod_maps", &ftrace_mod_maps_addr, NULL)) {
        dprintf("Failed to get ftrace_mod_maps address\n");
        return 0;
    }


    struct list_head ftrace_list;
    ULONG read;
    if (!ExtReadMemory(ftrace_mod_maps_addr, &ftrace_list, sizeof(ftrace_list), &read) || read != sizeof(ftrace_list)) {
        dprintf("Failed to read ftrace_mod_maps list head\n");
        return false;
    }

    // 计算ftrace_mod_map结构中list字段的偏移量
    ULONG list_offset;
    if (GetFieldOffset("ftrace_mod_map", "list", &list_offset) != 0) {
        dprintf("ERROR: Failed to get ftrace_mod_map.list offset\n");
        return false;
    }

    // 遍历ftrace_mod_maps链表
    ULONG64 current = (ULONG64)ftrace_list.next;
    if (current == ftrace_mod_maps_addr || current == 0) {
        //dprintf("Empty ftrace_mod_maps list or invalid pointer\n");
        return true; // 空链表不是错误
    }

    while (current != ftrace_mod_maps_addr) {
        // 计算ftrace_mod_map结构的起始地址
        ULONG64 ftrace_mod_map_addr = current - list_offset;

        // 读取ftrace_mod_map结构
        struct ftrace_mod_map mod_map;
        if (!ExtReadMemory(ftrace_mod_map_addr, &mod_map, sizeof(mod_map), &read) || read != sizeof(mod_map)) {
            dprintf("Failed to read ftrace_mod_map at 0x%llx\n", ftrace_mod_map_addr);
            break;
        }

        // 如果该模块没有被跟踪的函数,直接跳过
        if (mod_map.num_funcs == 0) {
            current = (ULONG64)mod_map.list.next;
            continue;
        }

        // 读取模块名称
        char mod_name[MODULE_NAME_LEN];
        if (mod_map.mod != 0) {
            // 从module结构中读取name字段
            ULONG64 mod_name_addr = (ULONG64)mod_map.mod + module_offsets.name_offset;
            if (!ExtReadMemory(mod_name_addr, mod_name, sizeof(mod_name), &read)) {
                strcpy_s(mod_name, sizeof(mod_name), "<unknown>");
            }
            mod_name[MODULE_NAME_LEN - 1] = '\0';
        }
        else {
            strcpy_s(mod_name, sizeof(mod_name), "<null>");
        }

        // 获取funcs链表的偏移量
        ULONG funcs_list_offset;
        if (GetFieldOffset("ftrace_mod_func", "list", &funcs_list_offset) != 0) {
            dprintf("ERROR: Failed to get ftrace_mod_func.list offset\n");
            break;
        }

        // 遍历当前mod_map的funcs链表
        ULONG funcs_head_addr;
        if (GetFieldOffset("ftrace_mod_map", "funcs", &funcs_head_addr) != 0) {
            dprintf("ERROR: Failed to get ftrace_mod_func.list offset\n");
            break;
        }

        struct list_head funcs_list;
        if (!ExtReadMemory(funcs_head_addr, &funcs_list, sizeof(funcs_list), &read) || read != sizeof(funcs_list)) {
            dprintf("Failed to read funcs list head at 0x%llx\n", funcs_head_addr);
            current = (ULONG64)mod_map.list.next;
            continue;
        }

        ULONG64 func_current = (ULONG64)funcs_list.next;
        while (func_current != funcs_head_addr) {
            // 计算ftrace_mod_func结构的起始地址
            ULONG64 ftrace_mod_func_addr = func_current - funcs_list_offset;

            // 调用回调函数处理每个函数,传递模块名称
            if (!callback(ftrace_mod_func_addr, mod_name, context)) {
                return true; 
            }

            // 读取下一个函数节点
            struct ftrace_mod_func mod_func;
            if (!ExtReadMemory(ftrace_mod_func_addr, &mod_func, sizeof(mod_func), &read) || read != sizeof(mod_func)) {
                break;
            }
            func_current = (ULONG64)mod_func.list.next;
        }

        current = (ULONG64)mod_map.list.next;
    }

    return true;
}

/**
 * ftrace符号按名称搜索回调函数
 */
static bool search_ftrace_symbol_by_name(ULONG64 ftrace_mod_func_addr, const char* module_name, void* context) {
    SymbolSearchContext* ctx = (SymbolSearchContext*)context;
    struct ftrace_mod_func mod_func;
    ULONG read;

    if (!ExtReadMemory(ftrace_mod_func_addr, &mod_func, sizeof(mod_func), &read) || read != sizeof(mod_func)) {
        return true; // 继续遍历
    }

    // 读取函数名称
    char func_name[KSYM_NAME_LEN];
    if (!ExtReadMemory((ULONG64)mod_func.name, func_name, sizeof(func_name), &read)) {
        return true; // 继续遍历
    }
    func_name[KSYM_NAME_LEN - 1] = '\0';

    // 检查是否匹配目标名称
    if (wildcard_match(func_name, ctx->target_name)) {
        // 使用传入的模块名称
        dprintf("%p %c %s\t[%s]\n", (void*)mod_func.ip, FTRACE_SYM_ELF_TYPE, func_name, module_name);
        ctx->found = true;
        ctx->count++;
    }

    return true; // 继续遍历
}


/**
 * ftrace符号按地址搜索回调函数
 */
static bool search_ftrace_symbol_by_address(ULONG64 ftrace_mod_func_addr, const char* module_name, void* context) {
    AddressSearchContext* ctx = (AddressSearchContext*)context;
    struct ftrace_mod_func mod_func;
    ULONG read;

    if (!ExtReadMemory(ftrace_mod_func_addr, &mod_func, sizeof(mod_func), &read) || read != sizeof(mod_func)) {
        return true; // 继续遍历
    }

    ctx->symbols_searched++;

    // 检查地址是否在函数范围内
    if (ctx->target_addr >= mod_func.ip && ctx->target_addr < (mod_func.ip + mod_func.size)) {
        // 精确匹配
        ctx->closest_addr = mod_func.ip;
        ctx->closest_distance = ctx->target_addr - mod_func.ip;
        ctx->closest_type = FTRACE_SYM_ELF_TYPE;
        ctx->found = true;

        // 读取函数名称
        if (ExtReadMemory((ULONG64)mod_func.name, ctx->closest_name, sizeof(ctx->closest_name), &read)) {
            ctx->closest_name[KSYM_NAME_LEN - 1] = '\0';
        }
        else {
            strcpy_s(ctx->closest_name, sizeof(ctx->closest_name), "<unknown>");
        }
        // 使用传入的模块名称
        snprintf(ctx->closest_module, sizeof(ctx->closest_module), "[%s]", module_name);
        return false; // 找到精确匹配,停止搜索
    }

    // 检查是否是更接近的符号
    ULONG64 distance = (ctx->target_addr > mod_func.ip) ?
        (ctx->target_addr - mod_func.ip) :
        (mod_func.ip - ctx->target_addr);

    if (distance < ctx->closest_distance) {
        ctx->closest_addr = mod_func.ip;
        ctx->closest_distance = distance;
        ctx->closest_type = FTRACE_SYM_ELF_TYPE;

        // 读取函数名称
        if (ExtReadMemory((ULONG64)mod_func.name, ctx->closest_name, sizeof(ctx->closest_name), &read)) {
            ctx->closest_name[KSYM_NAME_LEN - 1] = '\0';
        }
        else {
            strcpy_s(ctx->closest_name, sizeof(ctx->closest_name), "<unknown>");
        }
        // 使用传入的模块名称
        snprintf(ctx->closest_module, sizeof(ctx->closest_module), "[%s]", module_name);
    }

    return true; // 继续遍历
}



/**
 * 按名称查询模块符号
 * 通过遍历模块链表来查找符号
 */
bool ModSymbol::query_symbol_by_name(const char* symbol_name) {
    dprintf("Querying symbol name: %s\n", symbol_name);

    SymbolSearchContext ctx = { symbol_name, false, 0 };

    // 搜索模块符号
    for_each_module(search_symbol_by_name, &ctx);

    // 搜索bpf符号
    for_each_bpf_symbol(search_bpf_symbol_by_name, &ctx);

    // 搜索kprobe符号
    for_each_kprobe_symbol(search_kprobe_symbol_by_name, &ctx);

    // 搜索ftrace符号
    for_each_ftrace_symbol(search_ftrace_symbol_by_name, &ctx);

    if (!ctx.found) {
        dprintf("Symbol '%s' not found\n", symbol_name);
    }
    else {
        dprintf("\nFound %d symbol(s) matching '%s'\n", ctx.count, symbol_name);
    }

    return ctx.found;
}

/**
 * 按地址查询模块符号
 * 通过遍历模块链表来查找最接近的符号
 */
bool ModSymbol::query_symbol_by_address(ULONG64 target_addr) {
    dprintf("Querying address: 0x%llx\n", target_addr);


    AddressSearchContext ctx = {
        target_addr,
        0,
        0xFFFFFFFFFFFFFFFFULL,
        '?',
        "",
        "",
        false,
        0  // 初始化搜索计数
    };

    // 搜索模块符号
    for_each_module(search_symbol_by_address, &ctx);

    // 搜索bpf符号
    for_each_bpf_symbol(search_bpf_symbol_by_address, &ctx);

    // 搜索kprobe符号
    for_each_kprobe_symbol(search_kprobe_symbol_by_address, &ctx);

    // 搜索ftrace符号
    for_each_ftrace_symbol(search_ftrace_symbol_by_address, &ctx);

    if (ctx.found) {
        // 使用内核格式输出: %px %c %s\t[%s]
        dprintf("%p %c %s\t[%s]", (void*)ctx.closest_addr, ctx.closest_type, ctx.closest_name, ctx.closest_module);
        if (ctx.closest_distance > 0) {
            dprintf("+0x%llx", ctx.closest_distance);
        }
        dprintf("\n");
    }
    else {
        dprintf("No symbol found for address 0x%llx\n", target_addr);
    }

    return ctx.found;
}

/**
 * 显示所有模块符号
 */
void ModSymbol::show_all_symbols() {
    ModuleStats stats = { 0, 0 };  // 初始化统计结构

    // 遍历模块符号
    bool module_success = for_each_module(print_module_info, &stats);

    // 遍历bpf符号
    bool bpf_success = for_each_bpf_symbol(print_bpf_symbol_info, &stats);

    //遍历kprobe符号
    bool kprobe_success = for_each_kprobe_symbol(print_kprobe_symbol_info, &stats);

    //遍历ftrace符号
    bool ftrace_success = for_each_ftrace_symbol(print_ftrace_symbol_info, &stats);




    /*  if (module_success || bpf_success || ftrace_success || kprobe_success) {
          dprintf("\nTotal modules processed: %d\n", stats.module_count);
          dprintf("Total symbols found: %u\n", stats.total_symbols);
      }
      else {
          dprintf("ERROR: Module traversal all failed\n");
      }*/
}

/**
 * 显示使用帮助
 */
void ModSymbol::show_usage() {
    dprintf("Usage:\n");
    dprintf("  !ndx.modsyms                    - Show all symbols and usage\n");
    dprintf("  !ndx.modsyms -n <symbol_name>   - Query symbol by name\n");
    dprintf("  !ndx.modsyms -a <address>       - Query symbol by address\n");
    dprintf("  !ndx.modsyms -h, -help, ?         - Show this help\n");
}

// ========== 解析命令和查询函数 ==========

/**
 * 解析并执行命令行参数
 */
void ModSymbol::parse_and_execute_command(const char* args) {
    if (!args || !*args) {
        // 无参数时显示所有符号,和帮助信息
        show_all_symbols();
        show_usage();
        return;
    }

    // 创建参数副本以避免修改原始字符串
    char args_copy[256];
    strncpy_s(args_copy, sizeof(args_copy), args, _TRUNCATE);

    char* token = strtok(args_copy, " ");
    if (!token) {
        show_usage();
        return;
    }

    // 解析命令选项
    if (_stricmp(token, "-n") == 0) {
        // 按名称查询
        char* symbol_name = strtok(nullptr, " ");
        if (symbol_name) {
            query_symbol_by_name(symbol_name);
        }
        else {
            dprintf("ERROR: Missing symbol name\n");
            show_usage();
        }
    }
    else if (_stricmp(token, "-a") == 0) {
        // 按地址查询
        char* addr_str = strtok(nullptr, " ");
        if (addr_str) {
            ULONG64 target_addr = 0;
            if (GetExpressionEx(addr_str, &target_addr, NULL)) {
                query_symbol_by_address(target_addr);
            }
            else {
                dprintf("ERROR: Invalid address format '%s'\n", addr_str);
            }
        }
        else {
            dprintf("ERROR: Missing address\n");
            show_usage();
        }
    }
    else if (_stricmp(token, "-h") == 0 || _stricmp(token, "--help") == 0 || _stricmp(token, "?") == 0) {
        // 显示帮助
        show_usage();
    }
    else if (_stricmp(token, "-l") == 0 || _stricmp(token, "--list") == 0) {
        // 显示所有符号
        show_all_symbols();
    }
    else {
        dprintf("ERROR: Unknown option '%s'\n", token);
        show_usage();
    }
}

// ========== 调试扩展命令实现 ==========

/**
 * modsyms命令实现
 * 按名称或地址查询模块符号
 * 支持多种命令选项
 */
DECLARE_API(modsyms) {

    clock_t start_time = clock();

    //命令解析函数
    mod_symbol_manager.parse_and_execute_command(args);

    // 记录结束时间并计算差值
    clock_t end_time = clock();
    double execution_time = (double)(end_time - start_time) / CLOCKS_PER_SEC;

    // 打印执行时间,保留2位小数
    dprintf("Command execution time: %.2f seconds\n", execution_time);
}

4.实例测试

!ndx.modsyms

sudo cat /proc/kallsyms

作者:郭建程  创建时间:2025-09-15 10:48
最后编辑:郭建程  更新时间:2025-09-22 20:17