该模块是我2010年硕士论文的一部分。 论文的主题是Linux内核中的Keylogging 。 主要思想是找到一种方法来拦截针对x64 arch Linux内核的系统调用,尤其是对于2.6.34.7-61.fc13.x86_64内核。
引言
关于x32 arch的系统调用拦截有很多不同的文章。 作为研究的一部分,我遇到了如何通过Linux内核模块拦截对x86_64 arch的系统调用的问题。
我们如何截获系统调用?
- 找出系统调用表地址。
- 用新的替换原来的系统调用。
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 。
聚苯乙烯