Linux内核模块中的系统调用拦截

该模块是我2010年硕士论文的一部分。 论文的主题是Linux内核中的Keylogging 。 主要思想是找到一种方法来拦截针对x64 arch Linux内核的系统调用,尤其是对于2.6.34.7-61.fc13.x86_64内核。


引言


关于x32 arch的系统调用拦截有很多不同的文章。 作为研究的一部分,我遇到了如何通过Linux内核模块拦截对x86_64 arch的系统调用的问题。


我们如何截获系统调用?


  1. 找出系统调用表地址。
  2. 用新的替换原来的系统调用。

Syscall表地址


IDT( 中断描述能力)限制中断处理程序中断代码 。 在保护模式下,IDT是存储在内存中的描述符数组。 每个处理器都有一个特殊的IDTR寄存器。 寄存器由IDT物理地址和IDT长度组成。 第一个假设是从IDTR寄存器获取IDT地址,然后计算syscall表地址。 但是,这种假设是错误的,因为在这种情况下,我们获得了x32处理程序地址。


第二个假设更有趣。 在继续之前,我想描述一下MSR(Macodell Specific Register)。 MSR是x86指令集中用于调试,程序执行跟踪,计算机性能监视和切换某些CPU功能的各种控制寄存器中的任何一个。 让我们谈谈MSR_LSTAR - 0xc0000082 (长模式SYSCALL目标)。 您可以在/usr/include/asm/msr-index.h获得完整列表。


MSR_LSTAR存储用于x86-64体系结构的系统调用条目。 您可以获得地址:


 int i, lo, hi; asm volatile("rdmsr" : "=a" (lo), "=d" (hi) : "c" (MSR_LSTAR)); system_call = (void*)(((long)hi<<32) | lo); 

让我们走得更远。 我有地址并正在搜索\xff\x14\xc5\xff\x14\xc5是一个魔术数字。 如果查看内核2.6.34.7-61.fc13.x86_64代码,尤其是函数system_call ,则会发现接下来的4个字节是syscall_table地址。


我们知道syscall表地址,这意味着我们可以获取syscall处理程序地址并替换/拦截它。


源代码:


 unsigned char *ptr; for (ptr=system_call, i=0; i<500; i++) { if (ptr[0] == 0xff && ptr[1] == 0x14 && ptr[2] == 0xc5) return (void*)(0xffffffff00000000 | *((unsigned int*)(ptr+3))); ptr++; } 

系统调用拦截


我遇到了一个问题。 更改系统调用表中的地址时出错。 幸运的是,这很容易做到:


  • 禁用内存保护。
  • 重写系统调用处理程序地址。
  • 启用内存保护。

如果要禁用存储器保护,应该知道:寄存器CR0由标志组成。 这些标志管理处理器行为。 标志WP是Write Protect ,它是CR0的第48位。


禁用内存保护:


 asm("pushq %rax"); asm("movq %cr0, %rax"); asm("andq $0xfffffffffffeffff, %rax"); asm("movq %rax, %cr0"); asm("popq %rax"); 

启用内存保护:


 asm("pushq %rax"); asm("movq %cr0, %rax"); asm("xorq $0x0000000000001000, %rax"); asm("movq %rax, %cr0"); asm("popq %rax"); 

结论



一方面,它足以应付系统调用拦截,但另一方面,我不确定自2010年以来没有任何变化。因此,请按原样使用它。 源代码位于github.com/ultral/linux-keylogger


聚苯乙烯


Source: https://habr.com/ru/post/zh-CN437182/


All Articles