命令字:!ndx.sysdevices

用法描述

用于 查询系统设备列表,设备名、内存地址定位目标设备
适用于内核设备模型排查、驱动绑定验证、设备资源分析等场景。
支持的命令行参数:
-n 按设备名筛选设备,支持通配符(*)匹配
-a 按设备的内存地址精确查询设备
-h / –help / ? 显示命令帮助文档(含参数说明、示例)

开发过程

1. 找到关键全局变量

关键全局变量 作用
devices_kset 统一管理系统中所有已注册的设备对象

使用dt命令查看,确保存在这个kset

2. 相关内核实现和数据结构

device_initialize初始化设备结构体(struct device)的核心成员
device_add将设备添加到系统中

device_initialize指定了对象的所属集合

kobject_add(&dev->kobj, dev->kobj.parent, NULL)
将kobject注册到内核对象系统中,并在 sysfs 文件系统中创建对应的目录,这里第二个参数用于指定父目录

kset

struct kset {
    struct list_head list;          // 链表头:串联子kobject,支持批量遍历
    spinlock_t list_lock;
    struct kobject kobj;            // 内嵌kobj:实现sysfs映射与层级管理
    const struct kset_uevent_ops *uevent_ops;
} __randomize_layout;

kobject

struct kobject {
    const char      *name;          // 名称:标识kobject(如对应sysfs目录名)
    struct list_head entry;         // 链表项:接入父kset的list链表,实现层级串联
    struct kobject  *parent;        // 父kobj:指定上层kobject,构成sysfs目录层级
    struct kset     *kset;          // 所属kset:关联到管理它的kset
    const struct kobj_type *ktype;  
    struct kernfs_node *sd;         // sysfs节点:映射到sysfs的目录/文件项
    struct kref     kref;           // 引用计数:管理kobject生命周期,防止提前释放
    // 以下为状态位(1bit):
    unsigned int state_initialized:1;   
    unsigned int state_in_sysfs:1;       
    unsigned int state_add_uevent_sent:1;
    unsigned int state_remove_uevent_sent:1;
    unsigned int uevent_suppress:1;      
};

device

dt lk!device
   +0x000 kobj             : kobject
   +0x040 parent           : Ptr64 device
   +0x048 p                : Ptr64 device_private
   +0x050 init_name        : Ptr64 Char
   +0x058 type             : Ptr64 device_type
   +0x060 bus              : Ptr64 bus_type
   +0x068 driver           : Ptr64 device_driver
   +0x070 platform_data    : Ptr64 Void
   +0x078 driver_data      : Ptr64 Void
   +0x080 mutex            : mutex
   +0x0b8 links            : dev_links_info
   +0x0f0 power            : dev_pm_info
   +0x260 pm_domain        : Ptr64 dev_pm_domain
   +0x268 em_pd            : Ptr64 em_perf_domain
   +0x270 pins             : Ptr64 dev_pin_info
   +0x278 msi              : dev_msi_info
   +0x288 dma_ops          : Ptr64 dma_map_ops
   +0x290 dma_mask         : Ptr64 Uint8B
   +0x298 coherent_dma_mask : Uint8B
   +0x2a0 bus_dma_limit    : Uint8B
   +0x2a8 dma_range_map    : Ptr64 bus_dma_region
   +0x2b0 dma_parms        : Ptr64 device_dma_parameters
   +0x2b8 dma_pools        : list_head
   +0x2c8 dma_mem          : Ptr64 dma_coherent_mem
   +0x2d0 cma_area         : Ptr64 cma
   +0x2d8 dma_io_tlb_mem   : Ptr64 io_tlb_mem
   +0x2e0 archdata         : dev_archdata
   +0x2e0 of_node          : Ptr64 device_node
   +0x2e8 fwnode           : Ptr64 fwnode_handle
   +0x2f0 devt             : Uint4B
   +0x2f4 id               : Uint4B
   +0x2f8 devres_lock      : spinlock
   +0x310 devres_head      : list_head
   +0x320 class            : Ptr64 class
   +0x328 groups           : Ptr64 Ptr64 attribute_group
   +0x330 release          : Ptr64    
   +0x338 iommu_group      : Ptr64 iommu_group
   +0x340 iommu            : Ptr64 dev_iommu
   +0x348 physical_location : Ptr64 device_physical_location
   +0x350 removable        : device_removable
   +0x354 offline_disabled : Pos 6816, 1 Bit
   +0x354 offline          : Pos 6817, 1 Bit
   +0x354 of_node_reused   : Pos 6818, 1 Bit
   +0x354 state_synced     : Pos 6819, 1 Bit
   +0x354 can_match        : Pos 6820, 1 Bit
   +0x354 dma_coherent     : Pos 6821, 1 Bit

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

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

    init_kobject_offsets();
    init_kset_offsets();
    init_device_offsets();
}

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

// 初始化device结构体偏移量
bool SysDevices::init_device_offsets() {
    if (device_offsets.initialized) {
        return true;
    }

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

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

    device_offsets.initialized = true;
    return true;
}

// 设备kobject处理上下文结构
struct DeviceKobjectContext {
    const KobjectOffsets* offsets;
    const DeviceOffsets* device_offsets;
    ULONG device_count;  // 统计设备数量
    const DeviceFilterParams* filter;  // 过滤参数
    SysDevices* sys_devices;  // SysDevices实例指针
};

// 按名称查找设备的上下文结构
// 设备匹配结果结构体
struct DeviceMatchResult {
    ULONG64 device_addr;
    char device_name[64];
};

struct DeviceSearchContext {
    const char* search_name;
    bool found;
    bool is_exact_match;  // 是否为精确匹配(无通配符)
    std::vector<DeviceMatchResult> matches;  // 存储所有匹配的设备
    const KobjectOffsets* offsets;
    const DeviceOffsets* device_offsets;
};

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

    // 计算device结构体地址
    ULONG64 device_addr = kobject_addr - search_ctx->device_offsets->kobj_offset;

    // 读取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 (SysDevices::wildcard_match(name, search_ctx->search_name)) {
        search_ctx->found = true;

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

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

    return true;  // 继续查找
}

// 按地址查找设备的上下文结构
struct DeviceAddressSearchContext {
    ULONG64 search_addr;
    bool found;
    char device_name[64];
    const KobjectOffsets* offsets;
    const DeviceOffsets* device_offsets;
};

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

    // 计算device结构体地址
    ULONG64 device_addr = kobject_addr - search_ctx->device_offsets->kobj_offset;

    // 检查地址是否匹配
    if (device_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->device_name, sizeof(search_ctx->device_name) - 1, &read)) {
                search_ctx->device_name[read < sizeof(search_ctx->device_name) - 1 ? read : sizeof(search_ctx->device_name) - 1] = '\0';
            }
            else {
                strcpy_s(search_ctx->device_name, sizeof(search_ctx->device_name), "<unknown>");
            }
        }
        else {
            strcpy_s(search_ctx->device_name, sizeof(search_ctx->device_name), "<unknown>");
        }

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

    return true;  // 继续查找
}

// 处理设备kobject的回调函数
static bool process_device_kobject(ULONG64 kobject_addr, void* context) {
    ULONG read;
    DeviceKobjectContext* dev_ctx = static_cast<DeviceKobjectContext*>(context);

    // 计算device结构体地址
    ULONG64 device_addr = kobject_addr - dev_ctx->device_offsets->kobj_offset;

    // 读取kobject名称指针
    char* name_ptr = NULL;
    if (!ExtReadMemory(kobject_addr + dev_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';
        }
    }

    // 输出设备信息(包含device地址)
    dprintf("  %-30s  0x%016llx\n", name, device_addr);
    dev_ctx->device_count++;

    return true;
}

// 链表遍历函数
bool SysDevices::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;
    ULONG read;

    while (current_addr != list_head_addr) {
        // 读取当前节点
        struct list_head current_node;
        if (!ExtReadMemory(current_addr, &current_node, sizeof(current_node), &read) ||
            read != sizeof(current_node)) {
            dprintf("WARNING: Failed to read list node at 0x%llx\n", current_addr);
            break;
        }

        // 计算包含该链表节点的结构体地址
        ULONG64 container_addr = current_addr - list_offset;

        // 调用处理函数
        if (!processor(container_addr, context)) {
            break;
        }

        // 移动到下一个节点
        current_addr = (ULONG64)current_node.next;

        // 防止无限循环
        if (current_addr == 0) {
            break;
        }
    }

    return true;
}

// 显示所有设备kobject
void SysDevices::list_all_devices(ULONG64 next_node_addr, struct list_head next_node) {
    DeviceKobjectContext context;
    context.offsets = &kobject_offsets;
    context.device_offsets = &device_offsets;
    context.device_count = 0;

    dprintf("\nDevices:\n");
    dprintf("  %-30s  %s\n", "Name", "Device Address");
    dprintf("  %-30s  %s\n", "------------------------------", "----------------");

    // 遍历设备链表
    traverse_generic_list(next_node_addr, next_node, kobject_offsets.entry_offset,
        process_device_kobject, &context);

    dprintf("\nTotal devices: %lu\n", context.device_count);
}

// 按名称查找设备
bool SysDevices::query_device_by_name(const char* device_name, ULONG64 devices_kset_addr) {
    ULONG read;

    // 读取kset.list字段
    ULONG64 kset_list_addr = devices_kset_addr + get_kset_offsets().list_offset;

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

    // 判断是否为精确匹配(无通配符)
    std::string pattern(device_name);
    bool is_exact_match = (pattern.find('*') == std::string::npos);

    // 设置查找上下文
    DeviceSearchContext search_ctx;
    search_ctx.search_name = device_name;
    search_ctx.found = false;
    search_ctx.is_exact_match = is_exact_match;
    search_ctx.matches.clear();
    search_ctx.offsets = &kobject_offsets;
    search_ctx.device_offsets = &device_offsets;

    // 读取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;
    }

    // 显示搜索提示信息
    dprintf("Searching for device '%s', please wait...\n", device_name);

    // 遍历设备链表查找
    traverse_generic_list(next_node_addr, next_node, kobject_offsets.entry_offset,
        search_device_by_name, &search_ctx);

    if (search_ctx.found && !search_ctx.matches.empty()) {
        if (is_exact_match) {
            // 精确匹配,只显示第一个结果
            const auto& match = search_ctx.matches[0];
            dprintf("Device found:\n");
            dprintf("  Name: %s\n", match.device_name);
            dprintf("  Address: 0x%016llx\n", match.device_addr);
        }
        else {
            // 通配符匹配,显示所有匹配的设备(表格格式)
            dprintf("\nDevices:\n");
            dprintf("  %-30s  %s\n", "Name", "Device Address");
            dprintf("  %-30s  %s\n", "------------------------------", "----------------");

            for (const auto& match : search_ctx.matches) {
                dprintf("  %-30s  0x%016llx\n", match.device_name, match.device_addr);
            }

            dprintf("\nTotal: %zu device(s) matched pattern '%s'\n",
                search_ctx.matches.size(), device_name);
        }
        return true;
    }
    else {
        dprintf("Device '%s' not found\n", device_name);
        return false;
    }
}

// 按地址查找设备
bool SysDevices::query_device_by_address(ULONG64 target_addr, ULONG64 devices_kset_addr) {
    ULONG read;

    // 读取kset.list字段
    ULONG64 kset_list_addr = devices_kset_addr + get_kset_offsets().list_offset;

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

    // 设置查找上下文
    DeviceAddressSearchContext search_ctx;
    search_ctx.search_addr = target_addr;
    search_ctx.found = false;
    search_ctx.offsets = &kobject_offsets;
    search_ctx.device_offsets = &device_offsets;

    // 读取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;
    }

    // 遍历设备链表查找
    traverse_generic_list(next_node_addr, next_node, kobject_offsets.entry_offset,
        search_device_by_address, &search_ctx);

    if (search_ctx.found) {
        dprintf("Device found:\n");
        dprintf("  Name: %s\n", search_ctx.device_name);
        dprintf("  Address: 0x%016llx\n", target_addr);
        return true;
    }
    else {
        dprintf("Device at address 0x%016llx not found\n", target_addr);
        return false;
    }
}

// 通配符匹配函数
bool SysDevices::wildcard_match(const char* text, const char* pattern) {
    std::string text_str(text);
    std::string pattern_str(pattern);

    if (pattern_str.empty()) {
        return text_str.empty();
    }

    // 检查是否包含通配符
    if (pattern_str.find('*') == std::string::npos) {
        // 没有通配符,精确匹配
        return text_str == pattern_str;
    }

    // 处理特殊情况:只有一个*
    if (pattern_str == "*") {
        return true;  // 匹配任何字符串
    }

    // 【重要】前后缀匹配:*abc* - 必须优先检查,避免被单独的前缀/后缀匹配拦截
    if (pattern_str.front() == '*' && pattern_str.back() == '*') {
        if (pattern_str.length() <= 2) {
            return true;  // ** 或 * 匹配任何字符串
        }
        std::string substring = pattern_str.substr(1, pattern_str.length() - 2);
        return text_str.find(substring) != std::string::npos;
    }

    // 后缀匹配:*abc
    if (pattern_str.front() == '*') {
        std::string suffix = pattern_str.substr(1);
        if (suffix.empty()) {
            return true;  // * 匹配任何字符串
        }
        if (text_str.length() < suffix.length()) {
            return false;
        }
        return text_str.substr(text_str.length() - suffix.length()) == suffix;
    }

    // 前缀匹配:abc*
    if (pattern_str.back() == '*') {
        std::string prefix = pattern_str.substr(0, pattern_str.length() - 1);
        if (prefix.empty()) {
            return true;  // * 匹配任何字符串
        }
        return text_str.substr(0, prefix.length()) == prefix;
    }

    // 精确匹配
    return text_str == pattern_str;
}

// 显示使用帮助
static void show_usage() {
    dprintf("Usage: !ndx.sysdevices [options]\n");
    dprintf("Options:\n");
    dprintf("  (no args)       List all devices\n");
    dprintf("  -n <name>       Search device by name (supports wildcards)\n");
    dprintf("  -a <address>    Search device by address\n");
    dprintf("  -h, --help, ?   Show this help message\n");
    dprintf("\nWildcard patterns for name search:\n");
    dprintf("  *               Match any string\n");
    dprintf("  prefix*         Match strings starting with 'prefix'\n");
    dprintf("  *suffix         Match strings ending with 'suffix'\n");
    dprintf("  *substring*     Match strings containing 'substring'\n");
    dprintf("\nExamples:\n");
    dprintf("  !ndx.sysdevices                    # List all devices\n");
    dprintf("  !ndx.sysdevices -n \"cpu0\"          # Search for device named 'cpu0'\n");
    dprintf("  !ndx.sysdevices -n \"cpu*\"          # Search for devices starting with 'cpu'\n");
    dprintf("  !ndx.sysdevices -n \"*usb*\"         # Search for devices containing 'usb'\n");
    dprintf("  !ndx.sysdevices -a 0xffff888012345678  # Search for device at address\n");
}

// 检查是否为帮助命令
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;
}

// 解析和执行命令
bool SysDevices::parse_and_execute_command(const char* args, ULONG64 devices_kset_addr) {
    if (!args || !*args) {
        // 无参数时显示所有设备
        ULONG read;

        // 读取kset.list字段
        ULONG64 kset_list_addr = devices_kset_addr + get_kset_offsets().list_offset;

        // 读取链表头
        struct list_head kset_list;
        if (!ExtReadMemory(kset_list_addr, &kset_list, sizeof(kset_list), &read) ||
            read != sizeof(kset_list)) {
            dprintf("ERROR: Failed to read kset list head 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;
        }

        // 显示所有设备
        list_all_devices(next_node_addr, next_node);
        return true;
    }

    // 创建参数副本以避免修改原始字符串
    char args_copy[256];
    strncpy_s(args_copy, sizeof(args_copy), args, _TRUNCATE);

    char* token = strtok(args_copy, " ");
    if (!token) {
        show_usage();
        return false;
    }

    // 解析命令选项
    if (_stricmp(token, "-n") == 0) {
        // 按名称查询
        char* device_name = strtok(nullptr, " ");
        if (!device_name) {
            dprintf("ERROR: Missing device name\n");
            show_usage();
            return false;
        }

        // 执行查询
        return query_device_by_name(device_name, devices_kset_addr);
    }
    else if (_stricmp(token, "-a") == 0) {
        // 按地址查询
        char* addr_str = strtok(nullptr, " ");
        if (!addr_str) {
            dprintf("ERROR: Missing address\n");
            show_usage();
            return false;
        }

        // 解析地址
        ULONG64 addr;
        if (!GetExpressionEx(addr_str, &addr, NULL)) {
            dprintf("ERROR: Invalid address format '%s'\n", addr_str);
            show_usage();
            return false;
        }

        // 执行查询
        return query_device_by_address(addr, devices_kset_addr);
    }
    else if (_stricmp(token, "-h") == 0 || _stricmp(token, "--help") == 0 || _stricmp(token, "?") == 0) {
        // 显示帮助信息
        show_usage();
        return true;
    }
    else {
        dprintf("ERROR: Unknown option '%s'\n", token);
        show_usage();
        return false;
    }
}

DECLARE_API(sysdevices) {
    // 首先检查是否为帮助命令,如果是则直接显示帮助,无需初始化
    if (is_help_command(args)) {
        return;
    }

    ULONG64 devices_kset_addr = 0;

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

    dprintf("devices_kset address: 0x%llx\n", devices_kset_addr);

    // 创建SysDevices实例
    SysDevices sysDevices;

    // 参数解析逻辑
    sysDevices.parse_and_execute_command(args, devices_kset_addr);
}

4. 实例测试

!ndx.sysdevices

作者:郭建程  创建时间:2025-09-29 15:45
最后编辑:郭建程  更新时间:2025-10-13 16:46