命令字:!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
最后编辑:郭建程 更新时间:2025-09-22 20:17