命令字:!ndx.version
用法描述:
显示 Linux 内核版本信息,类似于 cat /proc/version 命令的输出。
开发过程:
1.找到关键全局变量
在fs\proc\version.c文件下,proc_create_single在目录/proc下创建了version文件,并且注册了一个回调函数version_proc_show用于用户读取 /proc/version 文件时(例如通过 cat /proc/version 命令),生成并输出该文件的具体内容。
所以继续看version_proc_show的实现细节
show函数中的详细信息来自utsname(),这个方法返回的是结构体指针,然后输出对应的字段
linux_proc_banner则是一个字符数组,用于规范内核版本信息的输出格式。
继续查看utsname,返回值类型为结构体new_utsname指针,通过一系列字段获取,我们需要找到currnet的来源。
current是通过current_thread_info这个函数获取thread_info 结构体地址,再获取thread_info中的结构体(task_struct)task字段,即为current。
但是我们没有相关的方式来获取current但是我们可以读取相同结构(task_struct)的全局变量,来查找的对应的版本相关字段。
init_task 是一个非常关键的全局变量,代表内核启动时创建的第一个进程。可以读取这个全局变量,做到一致的显示,我们可以试着读取这个变量。
先检查一下这个变量的对应数据结构,用x lk!显示全局变量地址,再用dt命令检查这个地址显示出的是结构体task_struct。
2.相关数据结构
init_task(task_struct)->nsproxy(nsproxy)->uts_ns(uts_namespace*)->name(new_utsname)
括号后是这个字段对应的类型。
然后读取下面这三个字段并且显示
3.读取变量和相关偏移,输出信息
DECLARE_API(version)
{
ULONG read;
// 获取init_task(Linux 内核中的一个全局变量,代表系统的第一个进程)
ULONG64 init_task_addr = 0;
GetExpressionEx("lk!init_task", &init_task_addr, NULL);
if (init_task_addr == 0) {
dprintf("Failed to get init_task address. Please fix symbol.\n");
return;
}
// 使用init_task作为当前任务
ULONG64 current_ptr = init_task_addr;
// 获取字段偏移
ULONG nsproxy_offset, uts_ns_offset, name_offset;
GetFieldOffset("task_struct", "nsproxy", &nsproxy_offset);
GetFieldOffset("nsproxy", "uts_ns", &uts_ns_offset);
GetFieldOffset("uts_namespace", "name", &name_offset);
// 读取nsproxy指针
ULONG64 nsproxy_ptr = 0;
ExtReadMemory(current_ptr + nsproxy_offset, &nsproxy_ptr, sizeof(nsproxy_ptr), &read);
if (nsproxy_ptr == 0) {
dprintf("Failed to read nsproxy pointer.\n");
return;
}
// 读取uts_ns指针
ULONG64 uts_ns_ptr = 0;
ExtReadMemory(nsproxy_ptr + uts_ns_offset, &uts_ns_ptr, sizeof(uts_ns_ptr), &read);
if (uts_ns_ptr == 0) {
dprintf("Failed to read uts_ns pointer.\n");
return;
}
// 读取utsname结构体
ULONG64 utsname_addr = uts_ns_ptr + name_offset;
// 获取utsname字段偏移
ULONG sysname_offset, release_offset, version_offset;
GetFieldOffset("new_utsname", "sysname", &sysname_offset);
GetFieldOffset("new_utsname", "release", &release_offset);
GetFieldOffset("new_utsname", "version", &version_offset);
// 读取各个字段
char sysname[65] = { 0 };
char release[65] = { 0 };
char version[65] = { 0 };
ExtReadMemory(utsname_addr + sysname_offset, sysname, 64, &read);
ExtReadMemory(utsname_addr + release_offset, release, 64, &read);
ExtReadMemory(utsname_addr + version_offset, version, 64, &read);
// 输出版本信息
dprintf("%s version %s %s\n", sysname, release, version);
}
4.实例测试
!ndx.version
cat /proc/version
最后编辑:郭建程 更新时间:2025-09-11 16:05