命令字:!ndx.sysclass

用法描述

!ndx.sysclass 命令用于查询和显示系统中的类信息,可以通过不同的筛选条件查找特定类或列出所有类。
支持的命令行参数:
-n 按名称查找类,支持通配符 *(匹配任意字符序列)和 ?(匹配单个字符)
-a 按内存地址查找类,需指定具体的内存地址
-h, –help 显示帮助信息,包含命令用法、参数说明及示例

开发过程

1. 找到关键全局变量

关键全局变量 作用
class_kset 统一管理系统中所有已注册的 “设备类”(struct class)(如 net 类、leds 类、tty 类),负责 “功能分类” 的创建与组织。

2. 相关内核源码和数据结构

cp->subsys.kobj.kset = class_kset;
作用:将当前设备类(struct class)对应的内核对象(kobj)所属的kset设置为class_kset。

error = kset_register(&cp->subsys);
注册kset,创建该kset对应的目录(即设备类的目录,如/sys/class/net/

error = class_add_groups(class_get(cls), cls->class_groups);
class_add_groups函数会遍历这些属性组,并调用sysfs相关接口,在设备类的sysfs目录(如/sys/class/net/)下创建对应的属性文件
调用过程为:class_add_groups -> sysfs_create_groups -> internal_create_groups -> internal_create_group -> create_files

subsys_private

struct subsys_private {
    struct kset subsys;
    struct kset *devices_kset;
    struct list_head interfaces;
    struct mutex mutex;

    struct kset *drivers_kset;
    struct klist klist_devices;
    struct klist klist_drivers;
    struct blocking_notifier_head bus_notifier;
    unsigned int drivers_autoprobe:1;
    struct bus_type *bus;

    struct kset glue_dirs;
    struct class *class;
};

kset

struct kset {
    struct list_head list;
    spinlock_t list_lock;
    struct kobject kobj;
    const struct kset_uevent_ops *uevent_ops;
} __randomize_layout;

class

struct class {
    const char        *name;
    struct module        *owner;

    const struct attribute_group    **class_groups;
    const struct attribute_group    **dev_groups;
    struct kobject            *dev_kobj;

    int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
    char *(*devnode)(struct device *dev, umode_t *mode);

    void (*class_release)(struct class *class);
    void (*dev_release)(struct device *dev);

    int (*shutdown_pre)(struct device *dev);

    const struct kobj_ns_type_operations *ns_type;
    const void *(*namespace)(struct device *dev);

    void (*get_ownership)(struct device *dev, kuid_t *uid, kgid_t *gid);

    const struct dev_pm_ops *pm;

    struct subsys_private *p;
};

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 "sysclass.h"

SysClass::SysClass() {
    // 初始化结构体偏移量
    kobject_offsets.initialized = false;
    kset_offsets.initialized = false;
    class_offsets.initialized = false;
    subsys_private_offsets.initialized = false;

    init_kobject_offsets();
    init_kset_offsets();
    init_class_offsets();
    init_subsys_private_offsets();
}

// 初始化kobject结构体偏移量
bool SysClass::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 SysClass::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;
    }

    if (GetFieldOffset("kset", "kobj", &kset_offsets.kobj_offset) != 0) {
        dprintf("WARNING: Failed to get kset.kobj offset\n");
        return false;
    }

    kset_offsets.initialized = true;
    return true;
}

// 初始化class结构体偏移量
bool SysClass::init_class_offsets() {
    if (class_offsets.initialized) {
        return true;
    }

    memset(&class_offsets, 0, sizeof(class_offsets));

    if (GetFieldOffset("class", "kobj", &class_offsets.kobj_offset) != 0) {
        dprintf("WARNING: Failed to get class.kobj offset\n");
        return false;
    }

    class_offsets.initialized = true;
    return true;
}

// 初始化subsys_private结构体偏移量
bool SysClass::init_subsys_private_offsets() {
    if (subsys_private_offsets.initialized) {
        return true;
    }

    memset(&subsys_private_offsets, 0, sizeof(subsys_private_offsets));

    if (GetFieldOffset("subsys_private", "subsys", &subsys_private_offsets.subsys_offset) != 0) {
        dprintf("WARNING: Failed to get subsys_private.subsys offset\n");
        return false;
    }

    if (GetFieldOffset("subsys_private", "class", &subsys_private_offsets.class_offset) != 0) {
        dprintf("WARNING: Failed to get subsys_private.class offset\n");
        return false;
    }

    subsys_private_offsets.initialized = true;
    return true;
}

// 类kobject处理上下文结构
struct ClassKobjectContext {
    const KobjectOffsets* offsets;
    const KsetOffsets* kset_offsets;
    const ClassOffsets* class_offsets;
    const SubsysPrivateOffsets* subsys_private_offsets;
    ULONG class_count;  // 统计类数量
    const ClassFilterParams* filter;  // 过滤参数
    SysClass* sys_class;  // SysClass实例指针
};

// 按名称查找类的上下文结构
// 类匹配结果结构体
struct ClassMatchResult {
    ULONG64 class_addr;
    char class_name[64];
};

struct ClassSearchContext {
    const char* search_name;
    bool found;
    bool is_exact_match;  // 是否为精确匹配(无通配符)
    std::vector<ClassMatchResult> matches;  // 存储所有匹配的类
    const KobjectOffsets* offsets;
    const KsetOffsets* kset_offsets;
    const ClassOffsets* class_offsets;
    const SubsysPrivateOffsets* subsys_private_offsets;
};

// 按名称查找类的回调函数
static bool search_class_by_name(ULONG64 kobject_addr, void* context) {
    ULONG read;
    ClassSearchContext* search_ctx = static_cast<ClassSearchContext*>(context);

    // 步骤1: 从kobject获取kset地址
    ULONG64 kset_addr = kobject_addr - search_ctx->kset_offsets->kobj_offset;

    // 步骤2: 从kset获取subsys_private地址
    ULONG64 subsys_private_addr = kset_addr - search_ctx->subsys_private_offsets->subsys_offset;

    // 步骤3: 从subsys_private获取class指针
    ULONG64 class_ptr_addr = subsys_private_addr + search_ctx->subsys_private_offsets->class_offset;

    // 读取class指针
    ULONG64 class_addr = 0;
    if (!ExtReadMemory(class_ptr_addr, &class_addr, sizeof(class_addr), &read) ||
        read != sizeof(class_addr) || class_addr == 0) {
        return true;  // 继续查找
    }

    // 读取kobject名称指针
    char* name_ptr = NULL;
    if (!ExtReadMemory(kobject_addr + search_ctx->offsets->name_offset, &name_ptr, sizeof(name_ptr), &read) ||
        read != sizeof(name_ptr)) {
        return true;  // 继续查找
    }

    // 读取名称字符串
    char name[64] = "<unknown>";
    if (name_ptr != NULL) {
        if (!ExtReadMemory((ULONG64)name_ptr, name, sizeof(name) - 1, &read)) {
            return true;  // 继续查找
        }
        else {
            name[read < sizeof(name) - 1 ? read : sizeof(name) - 1] = '\0';
        }
    }

    // 检查名称是否匹配(使用通配符匹配)
    if (SysClass::wildcard_match(name, search_ctx->search_name)) {
        search_ctx->found = true;

        // 添加匹配结果到列表
        ClassMatchResult match;
        match.class_addr = class_addr;
        strcpy_s(match.class_name, sizeof(match.class_name), name);
        search_ctx->matches.push_back(match);

        // 如果是精确匹配,找到第一个就停止
        if (search_ctx->is_exact_match) {
            return false;  // 停止查找
        }
    }

    return true;  // 继续查找
}

// 按地址查找类的上下文结构
struct ClassAddressSearchContext {
    ULONG64 search_addr;
    bool found;
    char class_name[64];
    const KobjectOffsets* offsets;
    const KsetOffsets* kset_offsets;
    const ClassOffsets* class_offsets;
    const SubsysPrivateOffsets* subsys_private_offsets;
};

// 按地址查找类的回调函数
static bool search_class_by_address(ULONG64 kobject_addr, void* context) {
    ULONG read;
    ClassAddressSearchContext* search_ctx = static_cast<ClassAddressSearchContext*>(context);

    // 步骤1: 从kobject获取kset地址
    ULONG64 kset_addr = kobject_addr - search_ctx->kset_offsets->kobj_offset;

    // 步骤2: 从kset获取subsys_private地址
    ULONG64 subsys_private_addr = kset_addr - search_ctx->subsys_private_offsets->subsys_offset;

    // 步骤3: 从subsys_private获取class指针
    ULONG64 class_ptr_addr = subsys_private_addr + search_ctx->subsys_private_offsets->class_offset;

    // 读取class指针
    ULONG64 class_addr = 0;
    if (!ExtReadMemory(class_ptr_addr, &class_addr, sizeof(class_addr), &read) ||
        read != sizeof(class_addr) || class_addr == 0) {
        return true;  // 继续查找
    }

    // 检查地址是否匹配
    if (class_addr == search_ctx->search_addr) {
        // 读取kobject名称指针
        char* name_ptr = NULL;
        if (ExtReadMemory(kobject_addr + search_ctx->offsets->name_offset, &name_ptr, sizeof(name_ptr), &read) &&
            read == sizeof(name_ptr) && name_ptr != NULL) {

            // 读取名称字符串
            if (ExtReadMemory((ULONG64)name_ptr, search_ctx->class_name, sizeof(search_ctx->class_name) - 1, &read)) {
                search_ctx->class_name[read < sizeof(search_ctx->class_name) - 1 ? read : sizeof(search_ctx->class_name) - 1] = '\0';
            }
            else {
                strcpy_s(search_ctx->class_name, sizeof(search_ctx->class_name), "<unknown>");
            }
        }
        else {
            strcpy_s(search_ctx->class_name, sizeof(search_ctx->class_name), "<unknown>");
        }

        search_ctx->found = true;
        return false;  // 找到了,停止查找
    }

    return true;  // 继续查找
}

// 处理类kobject的回调函数
static bool process_class_kobject(ULONG64 kobject_addr, void* context) {
    ULONG read;
    ClassKobjectContext* class_ctx = static_cast<ClassKobjectContext*>(context);

    // 步骤1: 从kobject获取kset地址
    ULONG64 kset_addr = kobject_addr - class_ctx->kset_offsets->kobj_offset;

    // 步骤2: 从kset获取subsys_private地址
    // kset是subsys_private中的subsys字段,所以subsys_private地址 = kset地址 - subsys_private.subsys偏移量
    ULONG64 subsys_private_addr = kset_addr - class_ctx->subsys_private_offsets->subsys_offset;

    // 步骤3: 从subsys_private获取class指针
    ULONG64 class_ptr_addr = subsys_private_addr + class_ctx->subsys_private_offsets->class_offset;

    // 读取class指针
    ULONG64 class_addr = 0;
    if (!ExtReadMemory(class_ptr_addr, &class_addr, sizeof(class_addr), &read) ||
        read != sizeof(class_addr) || class_addr == 0) {
        return true;  // 继续处理下一个
    }

    // 读取kobject名称指针(用于显示)
    char* name_ptr = NULL;
    if (!ExtReadMemory(kobject_addr + class_ctx->offsets->name_offset, &name_ptr, sizeof(name_ptr), &read) ||
        read != sizeof(name_ptr)) {
        return true;  // 继续处理下一个
    }

    // 读取名称字符串
    char name[64] = "<unknown>";
    if (name_ptr != NULL) {
        if (!ExtReadMemory((ULONG64)name_ptr, name, sizeof(name) - 1, &read)) {
            // 读取失败,使用默认名称
        }
        else {
            name[read < sizeof(name) - 1 ? read : sizeof(name) - 1] = '\0';
        }
    }

    // 应用过滤器(如果启用)
    if (class_ctx->filter && class_ctx->filter->use_filter) {
        if (!SysClass::wildcard_match(name, class_ctx->filter->name_pattern)) {
            return true;  // 不匹配过滤条件,跳过
        }
    }

    // 输出类信息(只显示名称和class地址)
    dprintf("  %-30s  0x%016llx\n", name, class_addr);
    class_ctx->class_count++;

    return true;  // 继续处理下一个
}

// 通用链表遍历函数
bool SysClass::traverse_generic_list(ULONG64 list_head_addr, const struct list_head& list_head,
    ULONG list_offset, ListNodeProcessor processor, void* context) {

    ULONG64 current_addr = (ULONG64)list_head.next;
    ULONG64 head_addr = list_head_addr;

    while (current_addr != head_addr) {
        // 计算包含该链表节点的结构体地址
        ULONG64 kobject_addr = current_addr - list_offset;

        // 调用处理函数
        if (!processor(kobject_addr, context)) {
            break;  // 处理函数返回false,停止遍历
        }

        // 读取下一个节点
        struct list_head next_node;
        ULONG read;
        if (!ExtReadMemory(current_addr, &next_node, sizeof(next_node), &read) ||
            read != sizeof(next_node)) {
            dprintf("ERROR: Failed to read next list node at 0x%llx\n", current_addr);
            return false;
        }

        current_addr = (ULONG64)next_node.next;

    }

    return true;
}

// 显示所有类
void SysClass::list_all_classes(ULONG64 next_node_addr, struct list_head next_node) {
    ClassKobjectContext context;
    context.offsets = &kobject_offsets;
    context.kset_offsets = &kset_offsets;
    context.class_offsets = &class_offsets;
    context.subsys_private_offsets = &subsys_private_offsets;
    context.class_count = 0;
    context.filter = nullptr;  // 不使用过滤器
    context.sys_class = this;

    dprintf("\nClasses:\n");
    dprintf("  %-30s  %s\n", "Name", "Class Address");
    dprintf("  %-30s  %s\n", "------------------------------", "----------------");

    // 遍历类链表
    traverse_generic_list(next_node_addr, next_node, get_kobject_offsets().entry_offset,
        process_class_kobject, &context);

    dprintf("\nTotal classes: %lu\n", context.class_count);
}

// 按名称查找类
bool SysClass::query_class_by_name(const char* class_name, ULONG64 next_node_addr, struct list_head next_node) {

    // 设置搜索上下文
    ClassSearchContext search_ctx;
    search_ctx.search_name = class_name;
    search_ctx.found = false;
    search_ctx.is_exact_match = (strchr(class_name, '*') == nullptr && strchr(class_name, '?') == nullptr);
    search_ctx.offsets = &kobject_offsets;
    search_ctx.kset_offsets = &kset_offsets;
    search_ctx.class_offsets = &class_offsets;
    search_ctx.subsys_private_offsets = &subsys_private_offsets;

    // 遍历链表查找匹配的类
    traverse_generic_list(next_node_addr, next_node, get_kobject_offsets().entry_offset,
        search_class_by_name, &search_ctx);

    if (search_ctx.found) {
        if (search_ctx.matches.size() == 1) {
            const auto& match = search_ctx.matches[0];
            dprintf("Found class: %s at address 0x%llx\n", match.class_name, match.class_addr);
        }
        else {
            dprintf("Found %zu matching classes:\n", search_ctx.matches.size());
            for (size_t i = 0; i < search_ctx.matches.size(); i++) {
                const auto& match = search_ctx.matches[i];
                dprintf("  [%zu] %s at 0x%llx\n", i + 1, match.class_name, match.class_addr);
            }
        }
        return true;
    }
    else {
        dprintf("Class '%s' not found\n", class_name);
        return false;
    }
}

// 按地址查找类
bool SysClass::query_class_by_address(ULONG64 target_addr, ULONG64 next_node_addr, struct list_head next_node) {

    // 设置搜索上下文
    ClassAddressSearchContext search_ctx;
    search_ctx.search_addr = target_addr;
    search_ctx.found = false;
    search_ctx.offsets = &get_kobject_offsets();
    search_ctx.kset_offsets = &get_kset_offsets();
    search_ctx.class_offsets = &get_class_offsets();
    search_ctx.subsys_private_offsets = &get_subsys_private_offsets();
    strcpy_s(search_ctx.class_name, sizeof(search_ctx.class_name), "<unknown>");

    // 遍历链表查找匹配的类
    traverse_generic_list(next_node_addr, next_node, get_kobject_offsets().entry_offset,
        search_class_by_address, &search_ctx);

    if (search_ctx.found) {
        dprintf("Found class at address 0x%llx: %s\n", target_addr, search_ctx.class_name);
        return true;
    }
    else {
        dprintf("Class at address 0x%llx not found\n", target_addr);
        return false;
    }
}

// 通配符匹配函数
bool SysClass::wildcard_match(const char* text, const char* pattern) {
    const char* t = text;
    const char* p = pattern;
    const char* star_text = nullptr;
    const char* star_pattern = nullptr;

    while (*t) {
        if (*p == '*') {
            star_text = t;
            star_pattern = ++p;
        }
        else if (*p == '?' || *p == *t) {
            t++;
            p++;
        }
        else if (star_pattern) {
            t = ++star_text;
            p = star_pattern;
        }
        else {
            return false;
        }
    }

    while (*p == '*') {
        p++;
    }

    return *p == '\0';
}

static void show_usage() {
    dprintf("Usage: !sysclass [options]\n");
    dprintf("Options:\n");
    dprintf("  -h, --help          Show this help message\n");
    dprintf("  -n <name>           Find class by name (supports wildcards * and ?)\n");
    dprintf("  -a <address>        Find class by address\n");
    dprintf("\n");
    dprintf("Examples:\n");
    dprintf("  !sysclass                       # List all classes\n");
    dprintf("  !sysclass -n \"input*\"           # Find classes starting with 'input'\n");
    dprintf("  !sysclass -n \"block\"            # Find class named 'block'\n");
    dprintf("  !sysclass -a 0xffff888012345678 # Find class at specific address\n");
}

static bool is_help_command(const char* args) {
    if (!args || strlen(args) == 0) {
        return false;
    }

    // 跳过前导空格
    while (*args && isspace(*args)) {
        args++;
    }

    return (strcmp(args, "-h") == 0 ||
        strcmp(args, "--help") == 0 ||
        strcmp(args, "help") == 0 ||
        strcmp(args, "?") == 0);
}

bool SysClass::parse_and_execute_command(const char* args, ULONG64 class_kset_addr) {
    // 获取kset的list字段地址
    ULONG64 kset_list_addr = class_kset_addr + get_kset_offsets().list_offset;

    // 读取kset的list_head结构
    struct list_head kset_list;
    ULONG read;
    if (!ExtReadMemory(kset_list_addr, &kset_list, sizeof(kset_list), &read) ||
        read != sizeof(kset_list)) {
        dprintf("ERROR: Failed to read kset list at 0x%llx\n", kset_list_addr);
        return false;
    }

    // 读取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 at 0x%llx\n", next_node_addr);
        return false;
    }

    if (!args || strlen(args) == 0) {
        // 默认行为:列出所有类
        list_all_classes(next_node_addr, next_node);
        return true;
    }

    // 检查是否为帮助命令
    if (is_help_command(args)) {
        show_usage();
        return true;
    }

    // 解析命令行参数
    std::string args_str(args);
    std::vector<std::string> tokens;

    // 简单的参数分割
    size_t start = 0;
    size_t end = 0;
    while ((end = args_str.find(' ', start)) != std::string::npos) {
        if (end != start) {
            tokens.push_back(args_str.substr(start, end - start));
        }
        start = end + 1;
    }
    if (start < args_str.length()) {
        tokens.push_back(args_str.substr(start));
    }

    if (tokens.empty()) {
        show_usage();
        return false;
    }

    // 处理不同的命令选项
    for (size_t i = 0; i < tokens.size(); i++) {
        const std::string& token = tokens[i];

        if (token == "-n" && i + 1 < tokens.size()) {
            const char* class_name = tokens[i + 1].c_str();
            return query_class_by_name(class_name, next_node_addr, next_node);
        }
        else if (token == "-a" && i + 1 < tokens.size()) {
            const char* addr_str = tokens[i + 1].c_str();
            ULONG64 addr = 0;

            // 解析地址
            if (strncmp(addr_str, "0x", 2) == 0 || strncmp(addr_str, "0X", 2) == 0) {
                addr = strtoull(addr_str, nullptr, 16);
            }
            else {
                addr = strtoull(addr_str, nullptr, 10);
            }

            if (addr == 0) {
                dprintf("ERROR: Invalid address: %s\n", addr_str);
                return false;
            }

            return query_class_by_address(addr, next_node_addr, next_node);
        }
    }

    dprintf("ERROR: Unknown command or missing arguments\n");
    show_usage();
    return false;
}

DECLARE_API(sysclass) {
    ULONG64 class_kset_addr = 0;

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

    dprintf("class_kset address: 0x%llx\n", class_kset_addr);

    SysClass sysClass;
    sysClass.parse_and_execute_command(args, class_kset_addr);
}

4. 实例测试

!ndx.sysclass

ls /sys/class

作者:郭建程  创建时间:2025-10-09 10:55
最后编辑:郭建程  更新时间:2025-10-10 18:37