命令字:!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
最后编辑:郭建程 更新时间:2025-10-10 18:37