命令字:!ndx.sysmodule
用法描述
该命令用于分析 Linux 内核的模块信息。它通过遍历 sysfs 的 module_kset 链表,识别并分类所有内置驱动和可加载内核模块(LKM),并能显示其详细属性。
支持的命令行参数:
-v 显示所有模块的详细属性(版本、状态、大小、引用计数等)
-n <名称> 按名称查找模块
-a <地址> 按 kobject 地址查找模块
-h /–help/ ? 显示帮助信息
开发过程
1.关键全局变量和相关逻辑
关键全局变量 | 作用 |
---|---|
module_kset | 支撑 sysfs 中 /sys/module/ 目录,向用户空间暴露模块的结构化信息; |
可以通过调试器直接查看发现module_kset的确是一个kset实例
kset->kobj 是 kset 在 sysfs 中创建目录的 “载体”
通过其内部的 kobj 调用 kobject_add_internal,最终在 sysfs 中创建目录(例如 module_kset 的 kobj 对应 /sys/module 目录)。
内置驱动模块
kobj = kset_find_obj(module_kset, drv->mod_name);找到该驱动所属模块在 /sys/modules 下对应的内核对象。
module_create_drivers_dir(mk);为模块创建 drivers 目录
内核模块
mod_sysfs_init中
mod->mkobj.kobj.kset = module_kset;指定了父对象
err = kobject_init_and_add(&mod->mkobj.kobj, &module_ktype, NULL,”%s”, mod->name);初始化一个 kobject 并将其添加到内核对象系统中
下面是创建相关目录的函数调用关系:
kobject_init_and_add->kobject_add_varg-> kobject_add_internal->create_dir->sysfs_create_dir_ns
最终调用sysfc提供的接口函数sysfs_create_dir_ns,用于创建目录文件
2.相关数据结构
struct kset
struct kset {
struct list_head list; // 管理kobject的双向链表头
spinlock_t list_lock; // 保护链表的自旋锁
struct kobject kobj; // 内嵌的kobject,使kset具备kobject属性
const struct kset_uevent_ops *uevent_ops; // uevent事件操作集指针
} __randomize_layout; // 开启结构体布局随机化,增强安全性
struct module_kobject
struct module_kobject {
struct kobject kobj; /* 基础kobject,对应sysfs中模块的根目录(/sys/module/<模块名>) */
struct module *mod; /* 指向该kobject关联的内核模块,建立kobject与模块的绑定关系 */
struct kobject *drivers_dir; /* 指向模块关联的驱动子目录(/sys/module/<模块名>/drivers) */
struct module_param_attrs *mp; /* 管理模块参数在sysfs中的属性信息(如参数文件的读写控制) */
struct completion *kobj_completion; /* 用于同步kobject释放过程的完成量,确保资源安全回收 */
} __randomize_layout;
struct module
通过module_kobject 可以获取对应的module结构体
struct module {
enum module_state state;
/* Member of list of modules */
struct list_head list;
/* Unique handle for this module */
char name[MODULE_NAME_LEN];
#ifdef CONFIG_STACKTRACE_BUILD_ID
/* Module build ID */
unsigned char build_id[BUILD_ID_SIZE_MAX];
#endif
/* Sysfs stuff. */
struct module_kobject mkobj;
struct module_attribute *modinfo_attrs;
const char *version;
const char *srcversion;
struct kobject *holders_dir;
/* Exported symbols */
const struct kernel_symbol *syms;
const s32 *crcs;
unsigned int num_syms;
}
3.读取变量和相关偏移,输出信息
#include "ndx.h"
#include <windows.h>
#include <minwindef.h>
#include <WDBGEXTS.H>
#include <cstdio>
#include <vector>
#include <string>
#include <string.h>
#include <cctype>
#include <cstdlib>
#include <cerrno>
#include <fstream>
#include <iostream>
#include "sysmodule.h"
SysModule::SysModule() {
// 初始化结构体偏移量
kobject_offsets.initialized = false;
kset_offsets.initialized = false;
module_kobject_offsets.initialized = false;
module_offsets.initialized = false;
init_kobject_offsets();
init_kset_offsets();
init_module_kobject_offsets();
init_module_offsets();
}
// 初始化kobject结构体偏移量
bool SysModule::init_kobject_offsets() {
if (kobject_offsets.initialized) {
return true;
}
memset(&kobject_offsets, 0, sizeof(kobject_offsets));
if (GetFieldOffset("kobject", "name", &kobject_offsets.name_offset) != 0) {
dprintf("WARNING: Failed to get kobject.name offset\n");
return false;
}
if (GetFieldOffset("kobject", "entry", &kobject_offsets.entry_offset) != 0) {
dprintf("WARNING: Failed to get kobject.entry offset\n");
return false;
}
kobject_offsets.initialized = true;
return true;
}
// 初始化kset结构体偏移量
bool SysModule::init_kset_offsets() {
if (kset_offsets.initialized) {
return true;
}
memset(&kset_offsets, 0, sizeof(kset_offsets));
if (GetFieldOffset("kset", "list", &kset_offsets.list_offset) != 0) {
dprintf("WARNING: Failed to get kset.list offset\n");
return false;
}
kset_offsets.initialized = true;
return true;
}
// 初始化module_kobject结构体偏移量
bool SysModule::init_module_kobject_offsets() {
if (module_kobject_offsets.initialized) {
return true;
}
memset(&module_kobject_offsets, 0, sizeof(module_kobject_offsets));
if (GetFieldOffset("module_kobject", "kobj", &module_kobject_offsets.kobj_offset) != 0) {
dprintf("WARNING: Failed to get module_kobject.kobj offset\n");
return false;
}
if (GetFieldOffset("module_kobject", "mod", &module_kobject_offsets.mod_offset) != 0) {
dprintf("WARNING: Failed to get module_kobject.mod offset\n");
return false;
}
module_kobject_offsets.initialized = true;
return true;
}
// 初始化module结构体偏移量
bool SysModule::init_module_offsets() {
if (module_offsets.initialized) {
return true;
}
memset(&module_offsets, 0, sizeof(module_offsets));
if (GetFieldOffset("module", "mkobj", &module_offsets.mkobj_offset) != 0) {
dprintf("WARNING: Failed to get module.mkobj offset\n");
return false;
}
if (GetFieldOffset("module", "version", &module_offsets.version_offset) != 0) {
dprintf("WARNING: Failed to get module.version offset\n");
return false;
}
if (GetFieldOffset("module", "srcversion", &module_offsets.srcversion_offset) != 0) {
dprintf("WARNING: Failed to get module.srcversion offset\n");
return false;
}
if (GetFieldOffset("module", "state", &module_offsets.state_offset) != 0) {
dprintf("WARNING: Failed to get module.state offset\n");
return false;
}
if (GetFieldOffset("module", "core_layout", &module_offsets.core_layout_offset) != 0) {
dprintf("WARNING: Failed to get module.core_layout offset\n");
return false;
}
if (GetFieldOffset("module", "init_layout", &module_offsets.init_layout_offset) != 0) {
dprintf("WARNING: Failed to get module.init_layout offset\n");
return false;
}
if (GetFieldOffset("module", "data_layout", &module_offsets.data_layout_offset) != 0) {
}
if (GetFieldOffset("module_layout", "size", &module_offsets.layout_size_offset) != 0) {
dprintf("WARNING: Failed to get module_layout.size offset\n");
return false;
}
if (GetFieldOffset("module", "refcnt", &module_offsets.refcnt_offset) != 0) {
dprintf("WARNING: Failed to get module.refcnt offset\n");
return false;
}
module_offsets.initialized = true;
return true;
}
// 模块属性处理上下文结构
struct ModulePropertiesContext {
const KobjectOffsets* kobject_offsets;
const ModuleKobjectOffsets* module_kobject_offsets;
const ModuleOffsets* module_offsets;
const char* target_module_name; // 目标模块名称,如果为nullptr则显示所有模块
ULONG module_count; // 统计模块数量
ULONG builtin_count; // 统计内置驱动数量
bool builtin_header_printed; // 是否已打印内置驱动标题
bool lkm_header_printed; // 是否已打印LKM标题
};
// 处理模块属性的回调函数
bool process_module_properties(ULONG64 kobject_addr, void* context) {
ULONG read;
ModulePropertiesContext* mod_ctx = static_cast<ModulePropertiesContext*>(context);
// 读取kobject名称指针
char* name_ptr = NULL;
if (!ExtReadMemory(kobject_addr + mod_ctx->kobject_offsets->name_offset, &name_ptr, sizeof(name_ptr), &read) ||
read != sizeof(name_ptr)) {
dprintf("WARNING: Failed to read kobject name pointer at 0x%llx\n", kobject_addr);
return true;
}
// 读取名称字符串
char name[64] = "<unknown>";
if (name_ptr != NULL) {
if (!ExtReadMemory((ULONG64)name_ptr, name, sizeof(name) - 1, &read)) {
dprintf("WARNING: Failed to read kobject name at %p\n", (ULONG64)name_ptr);
}
else {
name[read < sizeof(name) - 1 ? read : sizeof(name) - 1] = '\0';
}
}
// 如果指定了目标模块名称,检查是否匹配
if (mod_ctx->target_module_name != nullptr && strcmp(name, mod_ctx->target_module_name) != 0) {
return true; // 不匹配,继续处理下一个
}
// 计算module_kobject的地址
// kobject_addr是kobject的地址,需要减去kobj_offset得到module_kobject的地址
ULONG64 module_kobject_addr = kobject_addr - mod_ctx->module_kobject_offsets->kobj_offset;
// 读取module指针
ULONG64 module_ptr = 0;
if (!ExtReadMemory(module_kobject_addr + mod_ctx->module_kobject_offsets->mod_offset, &module_ptr, sizeof(module_ptr), &read) ||
read != sizeof(module_ptr)) {
dprintf("WARNING: Failed to read module pointer at 0x%llx\n", module_kobject_addr);
return true;
}
if (module_ptr == 0) {
// 这是内置驱动程序
if (!mod_ctx->builtin_header_printed) {
dprintf("\nBuilt-in drivers:\n");
dprintf(" %-30s %s\n", "Name", "Kobject Address");
dprintf(" %-30s %s\n", "------------------------------", "----------------");
mod_ctx->builtin_header_printed = true;
}
// 输出内置驱动信息(只显示名称和kobject地址)
dprintf(" %-30s 0x%016llx\n", name, kobject_addr);
mod_ctx->builtin_count++;
return true;
}
// 这是可加载内核模块(LKM)
if (!mod_ctx->lkm_header_printed) {
dprintf("\nLKM:\n");
dprintf("Module Address Name Version SrcVersion State CoreSize InitSize RefCnt\n");
dprintf("---------------- -------------------- -------- ----------------------- ---------- -------- -------- ------\n");
mod_ctx->lkm_header_printed = true;
}
// 读取version指针
char* version_ptr = NULL;
if (!ExtReadMemory(module_ptr + mod_ctx->module_offsets->version_offset, &version_ptr, sizeof(version_ptr), &read) ||
read != sizeof(version_ptr)) {
dprintf("WARNING: Failed to read version pointer at 0x%llx\n", module_ptr);
return true;
}
// 读取srcversion指针
char* srcversion_ptr = NULL;
if (!ExtReadMemory(module_ptr + mod_ctx->module_offsets->srcversion_offset, &srcversion_ptr, sizeof(srcversion_ptr), &read) ||
read != sizeof(srcversion_ptr)) {
dprintf("WARNING: Failed to read srcversion pointer at 0x%llx\n", module_ptr);
return true;
}
// 读取version字符串
char version[64] = "<unknown>";
if (version_ptr != NULL) {
if (!ExtReadMemory((ULONG64)version_ptr, version, sizeof(version) - 1, &read)) {
dprintf("WARNING: Failed to read version string at %p\n", (ULONG64)version_ptr);
}
else {
version[read < sizeof(version) - 1 ? read : sizeof(version) - 1] = '\0';
}
}
// 读取srcversion字符串
char srcversion[64] = "<unknown>";
if (srcversion_ptr != NULL) {
if (!ExtReadMemory((ULONG64)srcversion_ptr, srcversion, sizeof(srcversion) - 1, &read)) {
dprintf("WARNING: Failed to read srcversion string at %p\n", (ULONG64)srcversion_ptr);
}
else {
srcversion[read < sizeof(srcversion) - 1 ? read : sizeof(srcversion) - 1] = '\0';
}
}
// 读取模块状态
int state = 0;
if (!ExtReadMemory(module_ptr + mod_ctx->module_offsets->state_offset, &state, sizeof(state), &read) ||
read != sizeof(state)) {
dprintf("WARNING: Failed to read module state at 0x%llx\n", module_ptr);
}
// 将状态值转换为可读字符串
const char* state_str = "unknown";
switch (state) {
case 0: state_str = "live"; break; // MODULE_STATE_LIVE
case 1: state_str = "coming"; break; // MODULE_STATE_COMING
case 2: state_str = "going"; break; // MODULE_STATE_GOING
case 3: state_str = "unformed"; break; // MODULE_STATE_UNFORMED
default: state_str = "unknown"; break;
}
// 读取core_layout的size字段
unsigned int core_layout_size = 0;
if (mod_ctx->module_offsets->core_layout_offset != 0) {
ULONG64 core_layout_addr = module_ptr + mod_ctx->module_offsets->core_layout_offset;
if (!ExtReadMemory(core_layout_addr + mod_ctx->module_offsets->layout_size_offset,
&core_layout_size, sizeof(core_layout_size), &read) ||
read != sizeof(core_layout_size)) {
dprintf("WARNING: Failed to read core_layout.size at 0x%llx\n",
core_layout_addr + mod_ctx->module_offsets->layout_size_offset);
}
}
// 读取init_layout的size字段
unsigned int init_layout_size = 0;
if (mod_ctx->module_offsets->init_layout_offset != 0) {
ULONG64 init_layout_addr = module_ptr + mod_ctx->module_offsets->init_layout_offset;
if (!ExtReadMemory(init_layout_addr + mod_ctx->module_offsets->layout_size_offset,
&init_layout_size, sizeof(init_layout_size), &read) ||
read != sizeof(init_layout_size)) {
dprintf("WARNING: Failed to read init_layout.size at 0x%llx\n",
init_layout_addr + mod_ctx->module_offsets->layout_size_offset);
}
}
// 读取data_layout的size字段(如果存在)
unsigned int data_layout_size = 0;
if (mod_ctx->module_offsets->data_layout_offset != 0) {
ULONG64 data_layout_addr = module_ptr + mod_ctx->module_offsets->data_layout_offset;
if (!ExtReadMemory(data_layout_addr + mod_ctx->module_offsets->layout_size_offset,
&data_layout_size, sizeof(data_layout_size), &read) ||
read != sizeof(data_layout_size)) {
dprintf("WARNING: Failed to read data_layout.size at 0x%llx\n",
data_layout_addr + mod_ctx->module_offsets->layout_size_offset);
}
}
// 读取refcnt字段
int refcnt = 0;
if (mod_ctx->module_offsets->refcnt_offset != 0) {
if (!ExtReadMemory(module_ptr + mod_ctx->module_offsets->refcnt_offset, &refcnt, sizeof(refcnt), &read) ||
read != sizeof(refcnt)) {
dprintf("WARNING: Failed to read refcnt at 0x%llx\n",
module_ptr + mod_ctx->module_offsets->refcnt_offset);
}
else {
// 根据内核源码,需要减去MODULE_REF_BASE(1)
refcnt = refcnt - 1; // MODULE_REF_BASE = 1
}
}
// 输出模块基本信息和layout信息
dprintf("%016llx %-23s %-10s %-24s %-10s %-8u %-8u%-6d",
module_ptr, name, version, srcversion, state_str, core_layout_size, init_layout_size, refcnt);
// 输出data_layout信息(如果存在)
if (mod_ctx->module_offsets->data_layout_offset != 0) {
dprintf(" DataSize: %u", data_layout_size);
}
dprintf("\n");
mod_ctx->module_count++;
return true;
}
// kobject处理上下文结构
struct KobjectContext {
const KobjectOffsets* offsets;
const ModuleKobjectOffsets* module_kobject_offsets; // 模块kobject偏移量
ULONG kobject_count; // 统计kobject数量
ULONG builtin_count; // 统计内置驱动数量
bool builtin_header_printed; // 是否已打印内置驱动标题
bool lkm_header_printed; // 是否已打印LKM标题
};
// kobject节点处理回调函数
static bool process_kobject_node(ULONG64 kobject_addr, void* context) {
ULONG read;
KobjectContext* kobj_ctx = static_cast<KobjectContext*>(context);
// 读取kobject名称指针
char* name_ptr = NULL;
if (!ExtReadMemory(kobject_addr + kobj_ctx->offsets->name_offset, &name_ptr, sizeof(name_ptr), &read) ||
read != sizeof(name_ptr)) {
dprintf("WARNING: Failed to read kobject name pointer at 0x%llx\n", kobject_addr);
return true;
}
// 读取名称字符串
char name[64] = "<unknown>";
if (name_ptr != NULL) {
if (!ExtReadMemory((ULONG64)name_ptr, name, sizeof(name) - 1, &read)) {
dprintf("WARNING: Failed to read kobject name at %p\n", (ULONG64)name_ptr);
}
else {
name[read < sizeof(name) - 1 ? read : sizeof(name) - 1] = '\0';
}
}
// 检查是否有对应的module(如果提供了module_kobject_offsets)
if (kobj_ctx->module_kobject_offsets != nullptr) {
// 计算module_kobject的地址
ULONG64 module_kobject_addr = kobject_addr - kobj_ctx->module_kobject_offsets->kobj_offset;
// 读取module指针
ULONG64 module_ptr = 0;
if (ExtReadMemory(module_kobject_addr + kobj_ctx->module_kobject_offsets->mod_offset, &module_ptr, sizeof(module_ptr), &read) &&
read == sizeof(module_ptr)) {
if (module_ptr == 0) {
// 这是内置驱动程序
if (!kobj_ctx->builtin_header_printed) {
dprintf("\nBuilt-in drivers:\n");
// 输出标题
dprintf(" %-30s %s\n", "Name", "Kobject Address");
dprintf(" %-30s %s\n", "------------------------------", "----------------");
kobj_ctx->builtin_header_printed = true;
}
dprintf(" %-30s %016llx\n", name, kobject_addr);
kobj_ctx->builtin_count++;
return true;
}
else {
// 这是可加载内核模块(LKM)
if (!kobj_ctx->lkm_header_printed) {
dprintf("\nLKM:\n");
// 输出标题
dprintf(" %-30s %-17s %s\n", "Name", "Kobject Address", "Module Address");
dprintf(" %-30s %-14s %s\n", "------------------------------", "----------------", "----------------");
kobj_ctx->lkm_header_printed = true;
}
dprintf(" %-30s %016llx %016llx\n", name, kobject_addr, module_ptr);
kobj_ctx->kobject_count++;
return true;
}
}
}
// 如果无法确定类型,按原来的方式显示
dprintf("%-30s %016llx\n", name, kobject_addr);
kobj_ctx->kobject_count++;
return true;
}
// 链表遍历函数
bool SysModule::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) {
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;
}
// 显示所有kobject
void list_all_kobjects(ULONG64 next_node_addr, struct list_head next_node, const KobjectOffsets& kobject_offsets, SysModule& sysmodule) {
// 创建kobject处理上下文,包含module_kobject_offsets以支持分类
KobjectContext kobj_ctx = {
&kobject_offsets,
&sysmodule.module_kobject_offsets, // 添加module_kobject_offsets
0, // kobject_count
0, // builtin_count
false, // builtin_header_printed
false // lkm_header_printed
};
// 使用通用链表遍历函数,传入next_node作为参数
sysmodule.traverse_generic_list(next_node_addr, next_node, kobject_offsets.entry_offset, process_kobject_node, &kobj_ctx);
// 输出分类统计
dprintf("\nSummary:\n");
dprintf(" Built-in drivers: %d\n", kobj_ctx.builtin_count);
dprintf(" LKM modules: %d\n", kobj_ctx.kobject_count);
dprintf(" Total: %d\n", kobj_ctx.builtin_count + kobj_ctx.kobject_count);
dprintf("\nNote: The address shown is the kobject_addr used for address-based queries.\n");
return;
}
struct KobjectNameSearchContext {
const KobjectOffsets* offsets;
const char* target_name;
bool found;
bool show_properties; // 是否显示属性
const KobjectOffsets* kobject_offsets;
const ModuleKobjectOffsets* module_kobject_offsets; // 模块kobject偏移量
const ModuleOffsets* module_offsets; // 模块偏移量
ULONG module_count; // 统计模块数量
};
// 按名称查找kobject的回调函数
static bool search_kobject_by_name(ULONG64 kobject_addr, void* context) {
ULONG read;
KobjectNameSearchContext* search_ctx = static_cast<KobjectNameSearchContext*>(context);
// 读取kobject名称指针
char* name_ptr = NULL;
if (!ExtReadMemory(kobject_addr + search_ctx->offsets->name_offset, &name_ptr, sizeof(name_ptr), &read) ||
read != sizeof(name_ptr)) {
dprintf("WARNING: Failed to read kobject name pointer at 0x%llx\n", kobject_addr);
return true;
}
// 读取名称字符串
char name[64] = "<unknown>";
if (name_ptr != NULL) {
if (!ExtReadMemory((ULONG64)name_ptr, name, sizeof(name) - 1, &read)) {
dprintf("WARNING: Failed to read kobject name at %p\n", (ULONG64)name_ptr);
}
else {
name[read < sizeof(name) - 1 ? read : sizeof(name) - 1] = '\0';
}
}
// 比较名称
if (strcmp(name, search_ctx->target_name) == 0) {
dprintf("Found kobject: %-30s %016llx\n", name, kobject_addr);
search_ctx->found = true;
// 如果需要显示属性,则调用process_module_properties函数
if (search_ctx->show_properties) {
// 创建模块属性处理上下文
ModulePropertiesContext mod_ctx = {
search_ctx->kobject_offsets,
search_ctx->module_kobject_offsets,
search_ctx->module_offsets,
search_ctx->target_name,
0
};
// 调用process_module_properties函数处理该kobject
process_module_properties(kobject_addr, &mod_ctx);
// 更新模块计数
search_ctx->module_count = mod_ctx.module_count;
}
// 已经找到目标kobject,停止遍历
return false;
}
return true;
}
// 按名称查找kobject
void SysModule::query_kobject_by_name(const char* name, ULONG64 next_node_addr, struct list_head next_node, bool show_properties) {
// 创建kobject名称搜索上下文
KobjectNameSearchContext search_ctx = {
&kobject_offsets,
name,
false,
show_properties,
&kobject_offsets,
&module_kobject_offsets,
&module_offsets,
0
};
// 使用通用链表遍历函数,传入next_node作为参数
traverse_generic_list(next_node_addr, next_node, kobject_offsets.entry_offset, search_kobject_by_name, &search_ctx);
if (!search_ctx.found) {
dprintf("No kobject found with name '%s'\n", name);
}
}
// 显示模块属性信息
void SysModule::show_module_properties(ULONG64 next_node_addr, struct list_head next_node, const char* module_name) {
// 检查偏移量是否已初始化
if (!kobject_offsets.initialized || !module_kobject_offsets.initialized || !module_offsets.initialized) {
dprintf("ERROR: Failed to initialize structure offsets\n");
return;
}
// 创建模块属性处理上下文
ModulePropertiesContext mod_ctx = {
&kobject_offsets,
&module_kobject_offsets,
&module_offsets,
module_name,
0, // module_count
0, // builtin_count
false, // builtin_header_printed
false // lkm_header_printed
};
// 使用通用链表遍历函数,传入next_node作为参数
traverse_generic_list(next_node_addr, next_node, kobject_offsets.entry_offset, process_module_properties, &mod_ctx);
// 输出模块总数
if (module_name == nullptr) {
dprintf("\nSummary:\n");
dprintf(" Built-in drivers: %d\n", mod_ctx.builtin_count);
dprintf(" LKM modules: %d\n", mod_ctx.module_count);
dprintf(" Total: %d\n", mod_ctx.builtin_count + mod_ctx.module_count);
}
else if (mod_ctx.module_count == 0 && mod_ctx.builtin_count == 0) {
dprintf("\nNo module found with name '%s'\n", module_name);
}
}
// 显示使用帮助
static void show_usage() {
dprintf(
"Linux Kernel System Module Commands:\n"
" !ndx.sysmodule List all kobjects (default)\n"
" !ndx.sysmodule -v Show module properties (version and srcversion)\n"
" !ndx.sysmodule -n <name> Search kobject by name\n"
" !ndx.sysmodule -n <name> -v Search kobject by name and show its properties\n"
" !ndx.sysmodule -a <address> Search module by address\n"
" !ndx.sysmodule -a <address> -v Search module by address and show its properties\n"
" !ndx.sysmodule -h, --help, ? Show this help\n"
);
}
// 按地址查询模块
void SysModule::query_module_by_address(ULONG64 addr, ULONG64 next_node_addr, struct list_head next_node) {
// 检查偏移量是否已初始化
if (!kobject_offsets.initialized) {
dprintf("ERROR: Failed to initialize structure offsets\n");
return;
}
// 输出标题行
dprintf("%-30s %-16s\n", "Kobject Name", "Address");
dprintf("%-30s %-16s\n", "------------", "-------");
// 创建kobject处理上下文
KobjectContext kobj_ctx = { &kobject_offsets, 0 };
// 直接调用process_kobject_node函数处理地址
bool found = process_kobject_node(addr, &kobj_ctx);
if (!found || kobj_ctx.kobject_count == 0) {
dprintf("\nNo valid kobject found at address 0x%llx\n", addr);
}
else {
dprintf("\nNote: The address shown is the kobject_addr used for address-based queries.\n");
}
}
// 解析和执行命令
void SysModule::parse_and_execute_command(const char* args, ULONG64 next_node_addr, struct list_head next_node, const KobjectOffsets& kobject_offsets) {
if (!args || !*args) {
// 无参数时显示所有kobject
list_all_kobjects(next_node_addr, next_node, kobject_offsets, *this);
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* kobject_name = strtok(nullptr, " ");
if (!kobject_name) {
dprintf("ERROR: Missing kobject name\n");
show_usage();
return;
}
// 检查是否有 -v 参数
char* next_token = strtok(nullptr, " ");
bool show_properties = false;
if (next_token && _stricmp(next_token, "-v") == 0) {
show_properties = true;
}
// 执行查询,如果有-v参数,则在查询时同时显示属性
query_kobject_by_name(kobject_name, next_node_addr, next_node, show_properties);
}
else if (_stricmp(token, "-a") == 0) {
// 按地址查询
char* addr_str = strtok(nullptr, " ");
if (!addr_str) {
dprintf("ERROR: Missing address\n");
show_usage();
return;
}
// 解析地址
ULONG64 addr;
if (!GetExpressionEx(addr_str, &addr, NULL)) {
dprintf("ERROR: Invalid address format '%s'\n", addr_str);
show_usage();
return;
}
// 检查是否有 -v 参数
char* next_token = strtok(nullptr, " ");
bool show_properties = false;
if (next_token && _stricmp(next_token, "-v") == 0) {
show_properties = true;
}
// 检查偏移量是否已初始化
if (!kobject_offsets.initialized || !module_kobject_offsets.initialized || !module_offsets.initialized) {
dprintf("ERROR: Failed to initialize structure offsets\n");
return;
}
if (show_properties) {
// 如果有-v参数,使用process_module_properties处理地址
ModulePropertiesContext mod_ctx = { &kobject_offsets, &module_kobject_offsets, &module_offsets, nullptr, 0 };
bool found = process_module_properties(addr, &mod_ctx);
if (!found || mod_ctx.module_count == 0) {
dprintf("\nNo valid module found at address 0x%llx\n", addr);
}
}
else {
// 执行按地址查询
query_module_by_address(addr, next_node_addr, next_node);
}
}
else if (_stricmp(token, "-v") == 0) {
// 显示模块属性信息
// 检查偏移量是否已初始化
if (!kobject_offsets.initialized || !module_kobject_offsets.initialized || !module_offsets.initialized) {
dprintf("ERROR: Failed to initialize structure offsets\n");
return;
}
// 创建模块属性处理上下文
ModulePropertiesContext mod_ctx = { &kobject_offsets, &module_kobject_offsets, &module_offsets, nullptr, 0 };
// 使用链表遍历函数,直接调用process_module_properties
traverse_generic_list(next_node_addr, next_node, kobject_offsets.entry_offset, process_module_properties, &mod_ctx);
// 输出模块总数
dprintf("\nTotal modules: %d\n", mod_ctx.module_count);
}
else if (_stricmp(token, "-h") == 0 || _stricmp(token, "--help") == 0 || _stricmp(token, "?") == 0) {
// 显示帮助信息
show_usage();
}
else {
dprintf("ERROR: Unknown option '%s'\n", token);
show_usage();
}
}
/**
* 检查是否为帮助命令,如果是则直接显示帮助并返回true
*/
static bool is_help_command(const char* args) {
if (!args || !*args) {
return false;
}
// 创建参数副本以避免修改原始字符串
char args_copy[256];
strncpy_s(args_copy, sizeof(args_copy), args, _TRUNCATE);
char* token = strtok(args_copy, " ");
if (!token) {
return false;
}
// 检查是否为帮助命令
if (_stricmp(token, "-h") == 0 || _stricmp(token, "--help") == 0 || _stricmp(token, "?") == 0) {
show_usage();
return true;
}
return false;
}
DECLARE_API(sysmodule) {
// 首先检查是否为帮助命令,如果是则直接显示帮助,无需初始化
if (is_help_command(args)) {
return;
}
ULONG read;
SysModule sysmodule;
// 确保结构体偏移量已初始化
const KobjectOffsets& kobject_offsets = sysmodule.get_kobject_offsets();
const KsetOffsets& kset_offsets = sysmodule.get_kset_offsets();
if (!kobject_offsets.initialized || !kset_offsets.initialized) {
dprintf("ERROR: Failed to initialize structure offsets\n");
return;
}
// 获取module_kset全局变量地址
ULONG64 module_kset_addr;
if (!GetExpressionEx("lk!module_kset", &module_kset_addr, NULL)) {
dprintf("ERROR: Failed to get module_kset address\n");
return;
}
// 读取kset.list字段
ULONG64 list_addr = module_kset_addr + kset_offsets.list_offset;
struct list_head kset_list;
if (!ExtReadMemory(list_addr, &kset_list, sizeof(kset_list), &read) ||
read != sizeof(kset_list)) {
dprintf("ERROR: Failed to read kset list head\n");
return;
}
// 读取kset_list.next指向的节点
ULONG64 next_node_addr = (ULONG64)kset_list.next;
struct list_head next_node;
if (!ExtReadMemory(next_node_addr, &next_node, sizeof(next_node), &read) ||
read != sizeof(next_node)) {
dprintf("ERROR: Failed to read next list node\n");
return;
}
// 调用命令解析函数处理参数,传入初始化好的参数
sysmodule.parse_and_execute_command(args, next_node_addr, next_node, kobject_offsets);
return;
}
4.实例测试
!ndx.sysmodule
为内核模块的时候显示对应的module地址
ls /sys/module
最后编辑:郭建程 更新时间:2025-09-19 18:39