命令字:!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 15:08
最后编辑:郭建程  更新时间:2025-09-19 18:39