Linux x86平台获取sys_call_table,上岸蚂蚁金服

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Linux运维全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上运维知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注运维)
img

正文


内核模块使用 kallsyms\_lookup\_name(推荐) 或者 kprobe:



unsigned long * __sys_call_table = (unsigned long *)kallsyms_lookup_name(“sys_call_table”);


下面举例说明一下x86\_64平台下其他获取sys\_call\_table的方案。


## 一、根据call \*sys\_call\_table来获取


(1)



// linux-3.10/arch/x86/kernel/entry_64.S

/*
* Register setup:
* rax system call number
* rdi arg0
* rcx return address for syscall/sysret, C arg3
* rsi arg1
* rdx arg2
* r10 arg3 (–> moved to rcx for C)
* r8 arg4
* r9 arg5
* r11 eflags for syscall/sysret, temporary for C
* r12-r15,rbp,rbx saved by C code, not touched.
*
* Interrupts are off on entry.
* Only called from user space.
*
* XXX if we had a free scratch register we could save the RSP into the stack frame
* and report it properly in ps. Unfortunately we haven’t.
*
* When user can change the frames always force IRET. That is because
* it deals with uncanonical addresses better. SYSRET has trouble
* with them due to bugs in both AMD and Intel CPUs.
*/

ENTRY(system_call)

system_call_fastpath:

movq %r10,%rcx
call *sys_call_table(,%rax,8) # XXX: rip relative
movq %rax,RAX-ARGOFFSET(%rsp)


在进入system\_call时,中断被禁用。system\_call仅从用户空间调用。系统调用通过指令syscall来执行。


在x86\_64架构的用户空间下进行系统调用时,常用的寄存器如下:


rax:系统调用号(syscall number)放置在rax寄存器中,用于指定要调用的特定系统调用。  
 rdi:第一个参数(arg0)。在系统调用期间,用户提供的第一个参数通常存储在rdi寄存器中。  
 rsi:第二个参数(arg1)。用户提供的第二个参数通常存储在rsi寄存器中。  
 rdx:第三个参数(arg2)。用户提供的第三个参数通常存储在rdx寄存器中。  
 r10、r8、r9:第四、五、六个参数(arg3、arg4、arg5)。用户提供的第四、五、六个参数通常存储在r10、r8和r9寄存器中。


rcx:系统调用的返回地址。在进行系统调用时,将返回地址存储在rcx寄存器中,以便在系统调用完成后返回到正确的位置。  
 r11:用于存储eflags寄存器的值。在进行系统调用前,将当前eflags寄存器的值保存在r11寄存器中,以便在系统调用完成后恢复它。


用户空间函数调用:



rax:返回值 参数传递:rdi,rsi,rdx,rcx,r8,r9


用户空间系统调用:



rax:系统调用号 参数传递:rdi,rsi,rdx,r10、r8、r9 (rcx -> r10)


(2)  
 在x86\_64架构中,调用sys\_call\_table的机器码操作是通过间接调用(indirect call)指令来实现的。具体的操作码是ff 14 c5,其表示的汇编指令是callq \*%rax。


这条指令的作用是从rax寄存器中获取一个指针地址,然后跳转到该地址执行代码。在这种情况下,我们假设rax寄存器中存储了sys\_call\_table的地址,以便通过间接调用来调用相应的系统调用函数。


我们通过crash调试便可以获取到sys\_call\_table的地址:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/60c6f40fea4c47a780e58abec7f7fa6d.png)  
 由上面的汇编代码:



system_call
–>system_call_fastpath
–>call *sys_call_table(,%rax,8)


我们反汇编system\_call\_fastpath:



crash> dis system_call_fastpath
0xffffffff816b4fb3 <system_call_fastpath>: cmp $0x146,%rax
0xffffffff816b4fb9 <system_call_fastpath+6>: ja 0xffffffff816b5081
0xffffffff816b4fbf <system_call_fastpath+12>: mov %r10,%rcx
0xffffffff816b4fc2 <system_call_fastpath+15>: callq *-0x7e941120(,%rax,8)
0xffffffff816b4fc9 <system_call_fastpath+22>: mov %rax,0x20(%rsp)


可以看到在地址0xffffffff816b4fc2,调用call \*sys\_call\_table(,%rax,8),然后读取内存地址0xffffffff816b4fc2的值:



crash> rd -64 0xffffffff816b4fc2
ffffffff816b4fc2: 48816beee0c514ff …k.H


call的操作码是0xff14c5,后面就是sys\_call\_table的地址0x816beee0  
 因此sys\_call\_table的地址是0xffffffff816beee0。


备注:第三节使用了rdmsr来获取 system\_call 符号的值。


(3)  
 我们也可以借助vmlinux来objdump来获取其地址:



./extract-vmlinux vmlinuz-3.10.0-693.el7.x86_64 > vmlinux

objdump -d vmlinux > vmlinux.txt

vim vmlinux.txt


我们根据 ff 14 c5 指令码来搜索,上一条指令且是movq %r10,%rcx:



movq %r10,%rcx
call \*sys\_call\_table(,%rax,8)  # XXX:	 rip relative


ffffffff816b4fbf: 4c 89 d1 mov %r10,%rcx
ffffffff816b4fc2: ff 14 c5 e0 ee 6b 81 callq *-0x7e941120(,%rax,8)


可以看到 ff 14 c5 机器码后面的地址就是sys\_call\_table的地址0x816beee0(x86\_64是小端机器)。  
 因此sys\_call\_table的地址是0xffffffff816beee0。


## 二、使用dump\_stack



#include <linux/module.h>
#include <linux/kallsyms.h>
#include <linux/kernel.h>

static int __init lkm_init(void)
{
dump_stack();
return 0;
}

static void __exit lkm_exit(void)
{

}

module_init(lkm_init);
module_exit(lkm_exit);

MODULE_LICENSE(“GPL”);



[ 7666.386756] Call Trace:
[ 7666.386761] [] dump_stack+0x19/0x1b
[ 7666.386762] [] lkm_init+0x9/0x1000 [sys_call_table]
[ 7666.386764] [] do_one_initcall+0xb8/0x230
[ 7666.386766] [] load_module+0x1f64/0x29e0
[ 7666.386769] [] ? ddebug_proc_write+0xf0/0xf0
[ 7666.386770] [] ? copy_module_from_fd.isra.42+0x53/0x150
[ 7666.386772] [] SyS_finit_module+0xa6/0xd0
[ 7666.386774] [] system_call_fastpath+0x16/0x1b


我们就可以看到 system\_call\_fastpath+0x16 的地址是0xffffffff816b4fc9,因此system\_call\_fastpath的地址是:



0xffffffff816b4fc9 - 0x16 = 0xffffffff816b4fb3


我们可以从/proc/kallsyms验证:



cat /proc/kallsyms | grep system_call_fastpath

ffffffff816b4fb3 t system_call_fastpath



system_call_fastpath:
#if __SYSCALL_MASK == ~0
cmpq $__NR_syscall_max,%rax
#else
andl $__SYSCALL_MASK,%eax
cmpl $__NR_syscall_max,%eax
#endif
ja badsys
movq %r10,%rcx
call *sys_call_table(,%rax,8) # XXX: rip relative


这里我们简单点,从上面的crash可以看到:



crash> dis system_call_fastpath
0xffffffff816b4fb3 <system_call_fastpath>: cmp $0x146,%rax
0xffffffff816b4fb9 <system_call_fastpath+6>: ja 0xffffffff816b5081
0xffffffff816b4fbf <system_call_fastpath+12>: mov %r10,%rcx
0xffffffff816b4fc2 <system_call_fastpath+15>: callq *-0x7e941120(,%rax,8)


call \*sys\_call\_table 在 system\_call\_fastpath 的 0xf(15)处。



#include <linux/module.h>
#include <linux/kallsyms.h>
#include <linux/kernel.h>

static int __init lkm_init(void)
{
int i;
unsigned char *ptr;

ptr = (unsigned char \*)(0xffffffff816b4fc9 - 0x16 + 0xf);

for (i = 0; i < 8; i ++) {
		//printk("%02x ", (unsigned char)ptr[i]);
		printk("%02x ", (unsigned char)\*(ptr + i));
}

return 0;

}

static void __exit lkm_exit(void)
{

}

module_init(lkm_init);
module_exit(lkm_exit);

MODULE_LICENSE(“GPL”);



(unsigned char)ptr[i]) = (unsigned char)*(ptr + i))



insmod sys_call_table.ko

[# dmesg -c
[ 8416.858466] ff 14 c5 e0 ee 6b 81 48


可以看到 ff 14 c5 机器码后面的地址就是sys\_call\_table的地址0x816beee0(x86\_64是小端机器)。  
 因此sys\_call\_table的地址是0xffffffff816beee0。


或者直接搜索:



#include <linux/module.h>
#include <linux/kallsyms.h>
#include <linux/kernel.h>

static int __init lkm_init(void)
{
int i, j;
unsigned char *ptr;

ptr = (unsigned char \*)(0xffffffff816b4fc9 - 0x16 + 0xf);

for (i = 0; i < 20; i ++) {

	if( ((unsigned char)ptr[i] == 0xff) && ((unsigned char)ptr[i + 1] == 0x14) && ((unsigned char)ptr[i +2] == 0xc5) ){
		printk("0x%x ", \*(unsigned int \*)(ptr+i+3));
		break;
	}
} 


return 0;

}

static void __exit lkm_exit(void)
{

}

module_init(lkm_init);
module_exit(lkm_exit);

MODULE_LICENSE(“GPL”);



insmod sys_call_table.ko

dmesg -c

[ 280.502659] 0x816beee0


因此sys\_call\_table的地址是0xffffffff816beee0。


## 三、根据MSR\_LSTAR寄存器


我们来看一下内核的启动过程:



start_kernel()
–>trap_init()
–>cpu_init()
–>syscall_init()



#define MSR_STAR 0xc0000081 /* legacy mode SYSCALL target */
#define MSR_LSTAR 0xc0000082 /* long mode SYSCALL target */
#define MSR_CSTAR 0xc0000083 /* compat mode SYSCALL target */


| Register




| Address | Architectural MSR Name / Bit Fields (Former MSR Name) | MSR/Bit Description |
| --- | --- | --- |
| C000\_0081H | IA32\_STAR | System Call Target Address (R/W) |
| C000\_0082H | IA32\_LSTAR | IA-32e Mode System Call Target Address (R/W)  Target RIP for the called procedure when SYSCALL is executed in 64-bit mode. |
| C000\_0083H | IA32\_CSTAR | IA-32e Mode System Call Target Address (R/W)  Not used, as the SYSCALL instruction is not recognized in compatibility mode. |


我们主要看MSR\_LSTAR寄存器:  
 IA32\_LSTAR 是 IA-32e 模式下的系统调用目标地址寄存器,用于存储在 64 位模式下执行 SYSCALL 指令时被调用过程的目标 RIP(指令指针)。


当在 64 位模式下执行 SYSCALL 指令时,系统将从 IA32\_LSTAR 寄存器中加载 RIP 的值,以跳转至相应的系统调用处理程序。


为了使内核接收传入的系统调用,它必须通过将其地址写入IA32\_LSTAR MSR寄存器 来注册将在发生系统调用时执行的代码的地址。



/* May not be marked __init: used by software suspend */
void syscall_init(void)
{
/*
* LSTAR and STAR live in a bit strange symbiosis.
* They both write to the same internal register. STAR allows to
* set CS/DS but only a 32bit target. LSTAR sets the 64bit rip.
*/
wrmsrl(MSR_STAR, ((u64)__USER32_CS)<<48 | ((u64)__KERNEL_CS)<<32);
wrmsrl(MSR_LSTAR, system_call);
wrmsrl(MSR_CSTAR, ignore_sysret);

/* Flags to clear on syscall */
wrmsrl(MSR_SYSCALL_MASK,
X86_EFLAGS_TF|X86_EFLAGS_DF|X86_EFLAGS_IF|
X86_EFLAGS_IOPL|X86_EFLAGS_AC);
}


这行代码把system\_call入口地址存入到MSR\_LSTAR寄存器。syscall指令会把该地址加载到到%rip寄存器,从该地址开始执行。


syscall指令:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/4011639e50e84767a8654524b5974269.png)  
 SYSCALL 指令用于在特权级别 0(内核模式)下调用操作系统的系统调用处理程序。它通过从 IA32\_LSTAR MSR 寄存器加载 RIP(同时将 SYSCALL 指令后面的指令地址保存在 RCX 中)来实现这一功能。IA32\_LSTAR MSR 寄存器的值是一个规范地址(canonical address),通过 WRMSR 指令确保其始终包含一个规范地址。



SYSCALL 指令:RIP = IA32_LSTAR MSR 寄存器 = system_call


过程如下:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/6396aa590b62461f87ed6139b8013409.png#pic_center)


特别说明一下,因为初始化时,掩码中包含中断标志位X86\_EFLAGS\_IF,所以syscall指令执行时,中断是禁止的。


![](https://img-blog.csdnimg.cn/img_convert/9a8cb5f8c0ec69e6499adead0da6e95b.png)



最全的Linux教程,Linux从入门到精通

======================

1.  **linux从入门到精通(第2版)**

2.  **Linux系统移植**

3.  **Linux驱动开发入门与实战**

4.  **LINUX 系统移植 第2版**

5.  **Linux开源网络全栈详解 从DPDK到OpenFlow**



![华为18级工程师呕心沥血撰写3000页Linux学习笔记教程](https://img-blog.csdnimg.cn/img_convert/59742364bb1338737fe2d315a9e2ec54.png)



第一份《Linux从入门到精通》466页

====================

内容简介

====

本书是获得了很多读者好评的Linux经典畅销书**《Linux从入门到精通》的第2版**。本书第1版出版后曾经多次印刷,并被51CTO读书频道评为“最受读者喜爱的原创IT技术图书奖”。本书第﹖版以最新的Ubuntu 12.04为版本,循序渐进地向读者介绍了Linux 的基础应用、系统管理、网络应用、娱乐和办公、程序开发、服务器配置、系统安全等。本书附带1张光盘,内容为本书配套多媒体教学视频。另外,本书还为读者提供了大量的Linux学习资料和Ubuntu安装镜像文件,供读者免费下载。



![华为18级工程师呕心沥血撰写3000页Linux学习笔记教程](https://img-blog.csdnimg.cn/img_convert/9d4aefb6a92edea27b825e59aa1f2c54.png)



**本书适合广大Linux初中级用户、开源软件爱好者和大专院校的学生阅读,同时也非常适合准备从事Linux平台开发的各类人员。**

> 需要《Linux入门到精通》、《linux系统移植》、《Linux驱动开发入门实战》、《Linux开源网络全栈》电子书籍及教程的工程师朋友们劳烦您转发+评论




**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注运维)**
![img](https://img-blog.csdnimg.cn/img_convert/96567598f65cb4c6aebaa09a320d166d.jpeg)

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**


![华为18级工程师呕心沥血撰写3000页Linux学习笔记教程](https://img-blog.csdnimg.cn/img_convert/9d4aefb6a92edea27b825e59aa1f2c54.png)



**本书适合广大Linux初中级用户、开源软件爱好者和大专院校的学生阅读,同时也非常适合准备从事Linux平台开发的各类人员。**

> 需要《Linux入门到精通》、《linux系统移植》、《Linux驱动开发入门实战》、《Linux开源网络全栈》电子书籍及教程的工程师朋友们劳烦您转发+评论




**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注运维)**
[外链图片转存中...(img-HrNxClIR-1713349390954)]

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
  • 13
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值