疫情影响不能回临港海大,在家无聊搞Linux系列(一)
目前Linux4.19获取IDT的方法和之前几个版本相同,网上也已经有了很多帖子写了方法,如果已经掌握的朋友可以直接看我的下一篇博客:Linux4.19-通过IDT获取sys_call_table实现系统调用劫持:https://blog.csdn.net/qq_41208289/article/details/106013258
我用的是目前最新的Debian10,内核为Linux4.19.0-8-686(32位x86机器)
网上很多博客获取sys_call_table的方法在新的Linux中均已失效,我这个方法目前在Linux4.19中有效。
以下为正文:
我们知道,IDT(Interupt Description Table)里面放的是各种中断的处理函数地址,当中断来的时候会先查这个表,然后执行相关的程序,而这个表在内存中就是一个数组,数组中的每个元素对应着一个中断,例如系统调用是0x80中断,当该中断触发时,会查找IDT的0x80处的表项,获取0x80中断处理程序的地址,再执行相关的操作。
而IDT表的每个元素,其实是一个8字节的结构体,从源代码可以看出如下定义:
struct gate_struct {
u16 offset_low;
u16 segment;
struct idt_bits bits;
u16 offset_middle;
#ifdef CONFIG_X86_64 // 此处为x64多的一部分内容
u32 offset_high;
u32 reserved;
#endif
} __attribute__((packed));
后两项是64位机器多的两项,我用的是32机,因此只需看前四项,其中idt_bits为标志位,占两个字节,因此4项一共是8个字节。其中offset_middle与offset_low拼接成的32位地址即为这个表项对应的中断号的处理程序地址。
如何获取IDT表的地址?
知道了表结构,那么如何获取到表地址呢?IDT表的地址其实是专门存放在一个6字节寄存(在x86中为idtr)中的,从源代码可以看出其中的6字节数据为:
struct desc_ptr {
unsigned short size;
unsigned long address;
} __attribute__((packed));
其中的高32位即为idt表的地址。获取这个寄存器的值用C语言是不行的,需要用到汇编指令,因此这里需要采用内联汇编实现。
struct
{
unsigned short size;
unsigned int addr; // 高32位为idt表地址
}__attribute__((packed)) idtr; // idtr是48位6字节寄存器
void get_addr_idt(void)
{
asm("sidt %0":"=m"(idtr)); //通过idtr获取IDT表的地址
printk(KERN_ALERT "idt table adr: 0x%x\n", idtr.addr);
}
代码解读:通过sidt指令将idtr寄存器中的内容赋值给idtr结构体中去,此时通过idtr.addr即可获取IDT表的地址。
总结
至此我们已经获取到了IDT表的地址,上面我们讲到IDT表每个表项为8字节,因此0x80中断的表项地址=IDT表地址+0x80*8
下一篇博客我将继续讲解最新的Debian10 Linux4.19在获取到IDT表地址后,如何获取sys_call_table的地址。