Intel CPUID指令简介

  • 一、CPUID简介
    • 1.1 CPUID 功能简介
    • 1.2 处理器是否支持 CPUID指令
    • 1.3 指令返回基本信息
    • 1.4 指令返回扩展信息
  • 二、CPUID指令参数输入为01H
    • 2.1 简介
    • 2.2 返回值EAX内容分析
    • 2.3 返回值EBX内容分析
    • 2.4 返回值ECX内容分析
    • 2.5 返回值EDX内容分析
  • 三、 linux中的CPUID指令
    • 3.1 linux应用层调用cpuid指令
    • 3.2 linux内核中调用cpuid指令
  • 总结
  • 参考资料

一、CPUID简介

1.1 CPUID 功能简介

从名字就可以知道该指令是用来读取处理器的各种标识和特性信息(比如:CPU型号和支持的功能),并将指令执行完后返回的信息保存在 EAX, EBX, ECX,和 EDX 寄存器中。

CPUID指令有两组功能:一组返回的是基本信息,另一组返回的是扩展信息。

该指令有一个输入参数(可能会有两个),该参数会传递给EAX(ECX)寄存器,一般情况下只输入一个参数,根据输入参数的不同,返回给EAX, EBX, ECX, and EDX寄存器的信息也不一样。简介如下:

1.2 处理器是否支持 CPUID指令

EFLAGS寄存器的bit21位表明处理器是否支持CPUID指令,如果可以设置或者清除该标志位,那么代表处理器支持CPUID指令。

1.3 指令返回基本信息

CPUID指令的第一组功能:
确定CPUID指令输出处理器的基本信息以及EAX寄存器所能接受的最大的输入值:
输入参数为 00 H ,汇编伪代码为:
MOV EAX, 00H
CPUID

该指令会返回处理器能接受的最大输入参数 Maximum(即EAX寄存器能接受的最大输入值),为了返回有效的CPU基本信息,因此输入参数必须得在0~ Maximum之间,同时该返回值也是保存在EAX寄存器中。以及供应商ID字符串(Vendor Identification String)
如下图所示:

从图中可以看出,供应商ID字符串以EBX、EDX和ECX的形式返回。
对于Intel处理器,该字符串为" GenuineIntel ",并表示为:

接下来我们来简单的演示一下,在 intel 主机上执行 lscpu命令:

对于AMD处理器,该字符串为"AuthenticAMD "

1.4 指令返回扩展信息

CPUID指令的第二组功能:
确定CPUID指令输出处理器的扩展信息以及EAX寄存器所能接受的最大的输入值:
输入参数为 80000000H ,汇编伪代码为:
MOV EAX, 80000000H
CPUID

该指令会返回处理器能接受的最大输入参数 Maximum(即EAX寄存器能接受的最大输入值),为了返回有效的CPU扩展信息,因此输入参数必须得在80000000H ~ Maximum之间,同时该返回值也是保存在EAX寄存器中。

二、CPUID指令参数输入为01H

2.1 简介

接下来我们主要来介绍输入参数为01是CPUID的功能。

这一章节的内容都与下篇文章intel LBR和BTS有关:Intel x86_64 LBR & BTS功能

我这里先简单的介绍下:简单来说就是LBR和BTS用来记录CPU产出的跳转指令:包括call、ret、jmp、jxx(间接跳转)、中断、异常。利用这个功能,就可以保存每个分支跳转指令的起始地址和目的地址。这样就可以了解到当前CPU执行代码的流程情况。
举一个简单的例子jmp指令:

4004d3: eb 03    jmp 4004d8
当执行下面这条jmp指令时,那么将产生一个 from 4004d3 to 4004d8的跳转,那么产生的LBR记录项就是
{ From:4004d3 ,To:4004d8 } /* jmp指令 */

再举一个简单的例子关于call ,ret 指令:

int add(int a, int b)
{return (a+b);
}int main(void)
{return add(1,2);
}
上述两个函数将会产生两条LBR记录项:
{From : main , to : add} /* call 指令*/
{From : add , to : main} /* ret 指令*/

在二进制安全中,有很多攻击都是hook控制流,比如隐藏进程,隐藏文件,隐藏tcp,提权等等,使正在运行的系统按照攻击者的流程运行下去,这样就和系统正常的控制转移过程将会不一样,比如在Linux系统上,由于攻击者hook掉一些函数,当用户使用ls、top、ps、netstat等命令时,那么将会过滤掉一些进程,文件,tcp连接等等,这样用户调用shell命令时,便看不到过滤的进程,文件,tcp连接等,那么攻击者就会利用到这些进程、文件、tcp连接,对系统危害极大。

CPU当前正在产生的分支、中断和异常等跳转指令提供一种方法来确定系统当前运行中的控制转移过程。我们根据这些跳转指令便能够分析出系统是否被攻击了。
画个简图(只是简图描述的不太准确)来描述一下:

上图中的不正常的跳转指令是操作系统text中不存在的跳转指令,那么我们就可以通过LBR,BTS获取CPU正在产生的跳转指令来对比操作系统中text中的跳转指令来判断系统是否被攻击了,描述的比较简单,下篇文章我将详细介绍。

从上文可以得知01参数属于第一组功能:返回处理器基本信息。

输入参数为 01 H ,汇编伪代码为:
MOV EAX, 01H
CPUID

2.2 返回值EAX内容分析


EAX返回的内容主要是 处理器的Model ID、 Family ID 、Processor Type、Stepping ID 。

2.3 返回值EBX内容分析

此处省略。。。。。。

2.4 返回值ECX内容分析


在这里主要解释一下3个参数,与LBR和BTS有关,有关LBR和BTS可看下文链接:
Intel LBR & BTS 功能介绍
PDCM:Perfmon and Debug Capability 。该值为1表示处理器支持性能和调试能力。
DS-CPL:CPL Qualified Debug Store。该值为1表示处理器支持对Debug Store特性的扩展,允许根据当前系统处于的特权等级对 branch message 进行过滤。(0:表示内核态,3:表示用户态)
DTES64:64-bit DS Area。该值为1表示处理器支持在DS Area存放64位地址。

2.5 返回值EDX内容分析


在这里主要解释一下2个参数,与LBR和BTS有关:
DS:Debug Store。处理器支持将调试信息写入内存驻留缓冲区。BTS和PEBS使用该特性。可以理解为处理器是否支持BTS和 PEBS功能。
MSR :Model Specific Registers RDMSR and WRMSR Instructions。CPU是否支持指令rdmsr/wrmsr来读写MSR寄存器

三、 linux中的CPUID指令

3.1 linux应用层调用cpuid指令

我写了一个简单的应用程序来获取处理器厂商ID(vendor ID)和 family ,如下:

#include <stdio.h>#define X86_VENDOR_INTEL       0
#define X86_VENDOR_AMD         1
#define X86_VENDOR_UNKNOWN     2#define QCHAR(a, b, c, d) ((a) + ((b) << 8) + ((c) << 16) + ((d) << 24))
#define CPUID_INTEL1 QCHAR('G', 'e', 'n', 'u')
#define CPUID_INTEL2 QCHAR('i', 'n', 'e', 'I')
#define CPUID_INTEL3 QCHAR('n', 't', 'e', 'l')
#define CPUID_AMD1 QCHAR('A', 'u', 't', 'h')
#define CPUID_AMD2 QCHAR('e', 'n', 't', 'i')
#define CPUID_AMD3 QCHAR('c', 'A', 'M', 'D')#define CPUID_IS(a, b, c, ebx, ecx, edx)    \(!((ebx ^ (a))|(edx ^ (b))|(ecx ^ (c))))static inline void cpuid(int op, unsigned int *eax, unsigned int *ebx,unsigned int *ecx, unsigned int *edx)
{asm volatile("cpuid" //asm 表示内核汇编,执行cpuid指令, volatile 表示告诉gcc编译器不要优化代码 : "=a" (*eax),   //第一个冒号后面: 是输出参数。"=b" (*ebx),   //输出操作数约束应该带有一个约束修饰符 "=",指定它是输出操作数"=c" (*ecx),"=d" (*edx): "0" (*eax)   //第二个冒号后面: 是输入参数  Intel手册也说明ecx有时候也作为输入参数: "memory");
}static int x86_vendor(void)
{unsigned eax = 0x00000000;unsigned ebx, ecx = 0, edx;cpuid(0, &eax, &ebx, &ecx, &edx);if (CPUID_IS(CPUID_INTEL1, CPUID_INTEL2, CPUID_INTEL3, ebx, ecx, edx))printf("GenuineIntel\n");return X86_VENDOR_INTEL;if (CPUID_IS(CPUID_AMD1, CPUID_AMD2, CPUID_AMD3, ebx, ecx, edx))printf("AuthenticAMD\n");return X86_VENDOR_AMD;return X86_VENDOR_UNKNOWN;
}static int x86_family(void)
{unsigned eax = 0x00000001;unsigned ebx, ecx = 0, edx;int x86;cpuid(1, &eax, &ebx, &ecx, &edx);x86 = (eax >> 8) & 0xf;if (x86 == 15)x86 += (eax >> 20) & 0xff;return x86;
}int main()
{unsigned int eax = 0;unsigned int ebx = 0;unsigned int ecx = 0;unsigned int edx = 0;cpuid(0, &eax, &ebx, &ecx, &edx);printf("EBX ← %x (“Genu”)EDX ← %x (“ineI”) ECX ← %x (“ntel”)\n", ebx, edx ,ecx);int vendor = x86_vendor();int family = x86_family();printf("%d %d \n", vendor, family);return 0;}

让我们来看看输出结果:



结果显示与Intel手册,lscpu命令显示的一致,感兴趣的同学还可以打印跟多的cpu信息,在这里我就不继续介绍了。
补充一下:

内联汇编的基本格式
asm ( assembler template : output operands                  /* optional */: input operands                   /* optional */: list of clobbered registers      /* optional */);

3.2 linux内核中调用cpuid指令

除了在应用层通过汇编指令调用cpuid指令外,还可以在内核模块直接调用cpuid函数接口
该接口定义在arch/x86/include/asm/processor.h (内核版本3.10.0)中:

/** Generic CPUID function* clear %ecx since some cpus (Cyrix MII) do not set or clear %ecx* resulting in stale register contents being returned.*/
static inline void cpuid(unsigned int op,unsigned int *eax, unsigned int *ebx,unsigned int *ecx, unsigned int *edx)
{*eax = op;*ecx = 0;__cpuid(eax, ebx, ecx, edx);
}#define __cpuid            native_cpuidstatic inline void native_cpuid(unsigned int *eax, unsigned int *ebx,unsigned int *ecx, unsigned int *edx)
{/* ecx is often an input as well as an output. */asm volatile("cpuid": "=a" (*eax),"=b" (*ebx),"=c" (*ecx),"=d" (*edx): "0" (*eax), "2" (*ecx): "memory");
}

现在我写个简单的内核模块来测试一下cpuid指令:

#include <linux/kernel.h>
#include <linux/module.h>//内核模块初始化函数
static int __init lkm_init(void)
{unsigned int eax = 0;unsigned int ebx = 0;unsigned int ecx = 0;unsigned int edx = 0;cpuid(0, &eax, &ebx, &ecx, &edx);printk("EBX:%xh(“Genu”) EDX:%xh(“ineI”) ECX:%xh(“ntel”)\n", ebx, edx ,ecx);return 0;
}//内核模块退出函数
static void __exit lkm_exit(void)
{printk(KERN_DEBUG "exit\n");
}module_init(lkm_init);
module_exit(lkm_exit);MODULE_LICENSE("GPL");

Makefile文件:

obj-m := kcpuid.oall:make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modulesclean:make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean


可以看出和intel手册显示的一致。感兴趣的同学可以写个内核模块在这里获取更多的cpu信息。

总结

这条指令支持的内容十分丰富,在这里我只介绍了CPUID指令的一点点内容,并在应用层和内核模块各自写了一个简单的程序验证了一下,在下篇文章我会介绍这条指令的其它内容(与LBR和BTS有关),大家感兴趣的可以自行阅读 intel vol2 chapter3 3.2小节 关于CPUID指令的详细介绍。

参考资料

https://blog.csdn.net/bin_linux96/article/details/104236808
https://blog.csdn.net/razor87/article/details/8711712
https://blog.csdn.net/subfate/article/details/50719396
linux内核源码 3.10.0
Intel官方手册 vol2
intel官方手册 vol3

Intel x86_64 CPUID指令介绍相关推荐

  1. Intel处理器CPUID指令学习

    前文<Intel处理器Family.Model.Stepping等的学习>只是简单讲了CPU的标识等内容(仅针对Intel,本文也是),但其读取方法未涉及.本文就此未完事宜来了解读取的方法 ...

  2. linux C语言调用Intel处理器CPUID指令的实例

    在之前写的文章中,仅简单讲了一下CPUID指令,通过该指令可以获取很多和处理器相关的信息,如处理器的系列.型号.内存地址是多少位,等等.本文在Linux环境下,使用C语言内嵌汇编的手段使用CPUID指 ...

  3. Intel x86_64使用cpuid指令获取CPU信息

    文章目录 前言 一.cpuid指令简介 1.1 cpuid指令功能 1.2 cpuid指令代码 二.获取处理器信息 2.1 输入参数为0H 2.2 输入参数为01H 2.3 输入参数为0x800000 ...

  4. Intel CPU的CPUID指令

    Intel有一个超过100页的文档,专门介绍cpuid这条指令,可见这条指令涉及内容的丰富. 记得去年的时候,曾经有个"英布之剑"问过我这条指令,当时并没有给出一个满意的回答,现在 ...

  5. Intel x86_64 LBR BTS功能

    Intel x86_64 LBR & BTS功能 前言 一.DEBUGCTL MSR 1.1 MSR寄存器简介 1.2 IA32_DEBUGCTL MSR 寄存器 1.3 LBR 1.4 BT ...

  6. Intel CPU的CPUID指令(转载)

    Intel CPU的CPUID指令 Intel有一个超过100页的文档,专门介绍cpuid这条指令,可见这条指令涉及内容的丰富. 记得去年的时候,曾经有个"英布之剑"问过我这条指令 ...

  7. 【Optimizaition/x86】Intel CPU的CPUID指令获取的C实现

    Date: 2018.7.22 1.参考: https://www.cnblogs.com/DxSoft/articles/5053954.html https://blog.csdn.net/lis ...

  8. INTEL处理器识别和CPUID指令(二) CPUID指令

    CPUID指令有两套函数,第一套函数返回处理器的基本信息,第二套函数返回处理器的扩展信息.图一总结了CPUID指令所能输出的处理器的基本信息.CPUID指令的输出完全依赖于EAX寄存器的内容,根据EA ...

  9. INTEL处理器识别和CPUID指令(一) CPUID指令与状态寄存器的演变

    按:本系列翻译自INTEl官方的CPUID指令文档<Intel® Processor Identification and the CPUID Instruction>,由于我自身英语水平 ...

最新文章

  1. 闲谈嵌入式的复杂性2
  2. python【力扣LeetCode算法题库】面试题40- 最小的k个数
  3. Linux中如何用命令打开文件夹
  4. MongoDB 连接数高产生原因及解决
  5. python mysql股票分析_一颗韭菜的自我修养:用Python分析下股市,练练手
  6. 音视频技术开发周刊 | 172
  7. java在线预览txt、word、ppt、execel,pdf代码
  8. 常系数线性递推的第n项及前n项和(转载)
  9. 实例47:python
  10. bin文件怎么转换成文本文档_怎么把pdf文件转换成word文档?这样转很简单
  11. java metric_java版的Metric工具介绍
  12. plotly包安装_Plotly(一)安装指南
  13. MVC学习笔记:MVC实现用户登录验证ActionFilterAttribute用法并实现统一授权
  14. 【代码优化】for-each代替普通的for循环或者while循环
  15. Python爬虫防封杀方法集合
  16. 计算机上可以插键盘吗,电脑外接键盘好用吗 哪些键盘可以外接电脑使用
  17. 一文教你如何挑选深度学习GPU
  18. 一维数组、二维数组的大小、长度与偏移
  19. Python 全局变量、局部变量、静态变量 详解
  20. CentOS 7 安装Ibus中文输入法

热门文章

  1. MATLAB 设置小的tick
  2. 一个金融行业站SEO优化方案
  3. 将要被人工智能取代的十大职业
  4. 守株待兔都赚钱了,几十个IP怎么比几万IP还赚钱
  5. PON为什么被称为无源光网络?不同PON技术的主要区别是什么?
  6. css 光影掠过文字显现动画
  7. jquery寻找父子兄弟节点
  8. 聊一聊GNU/Linux 与开源文化的那些人和事
  9. 国信证券开源自研的微服务开发框架 Zebra
  10. Linux 终端文件管理器 —— ranger