实验作业四:Socket与系统调用深度分析

实验环境:

Ubuntu 18.04.2 LTS

实验过程:

紧接实验三,当我们配置好环境之后,就可以使用 gdb 对 Socket 中的函数和系统调用函数进行跟踪分析了。

(1)如果实验三顺利完成的话,你应该有如下的目录结构:

 

 在此目录环境下输入以下命令:

 

开启 MenuOS。

(2)重开一个终端,输入 gdb 进入调试界面,然后分别输入以下命令,如下图所示:

实验作业四:Socket与系统调用深度分析_第1张图片

 

 这样就建立了 gdb 和 gdbserver 之间的连接。

(3)在 MenuOS中输入 help 会发现有 replyhi 命令和 hello 命令,我们进入 net/lab3 下的 main.c 文件可以找到对应的实现,然后我们会发现这些实现里面的函数都是封装好的定义在 syswrapper.h 文件中的,

这些封装好的宏函数才是真正调用了 Socket接口提供的函数。先来看一下 replyhi 命令的实现,其中一部分的函数的调用顺序是 Replyhi()->InitializeService()->PrepareSocket(),PrepareSocket()函数的实现是:

实验作业四:Socket与系统调用深度分析_第2张图片

 

 可以看到调用了Socket的接口函数socket(),那么它对应的系统调用函数呢?我们进入 linux-5.0.1/net 下打开 socket.c 文件,在里面搜索发现如下函数:

 

 经过查阅知道,Linux的系统调用都改为SYSCALL_DEFINE定义的了,这里的 SYSCALL_DEFINE3 中的3代表的是系统调用的参数个数,注意这里可以看到 SYSCALL_DEFINE3中返回了系统调用 __sys_socket()的结果。

对于 SYSCALL_DEFINE3,它是定义在 linux-5.0.1/include/linux/syscalls.h文件中的宏函数,具体如下:

实验作业四:Socket与系统调用深度分析_第3张图片

 

 其实 SYSCALL_DEFINEx函数内部还会调用一些其他函数,但最终还是会回到调用系统函数 __sys_socket()上,这样做的具体原因请看:

https://blog.csdn.net/hxmhyp/article/details/22699669。

(4)经过上一步的操作,我们可以找出许多系统调用函数,比如 __sys_bind、__sys_listen、__sys_connect、__sys_close等。通过查阅 Replyhi()函数和 Hello()函数的实现,可以发现 Replyhi()调用了 socket、bind、listen,Hello()调用了 socket、connect等。

因此分别在这些系统调用函数上设置断点。

实验作业四:Socket与系统调用深度分析_第4张图片

 

 上图是先输入replyhi命令,然后发现在 gdb 中就依次进入了相应的系统调用函数其实就是对应 Replyhi()函数中调用Socket接口函数的顺序,最后是调用 listen函数,因为是在一直监听,所以此时并未退出断点,我们手动退出,然后重新

设置socket、connect接口函数所对应的系统调用函数的断点。

实验作业四:Socket与系统调用深度分析_第5张图片

 

 可以看到,对于 Hello()函数也成功跟踪了其中的系统调用。

(5)其实在上一步,我们会在 socket.c 文件中找到另外一个函数:

实验作业四:Socket与系统调用深度分析_第6张图片

 

 上图是经过删减的一部分,可以发现其中的参数 call 就是操作码,通过它,我们决定对应的系统调用函数,这些操作码定义在 linux5.0.1/include/uapi/linux/net.h 文件中:

实验作业四:Socket与系统调用深度分析_第7张图片

 

 如果按照之前的socket对应__sys_socket的规则,那么socketcall对应的系统调用函数应该是 __sys_socketcall,但是在设置断点的时候,无论是 __sys_socketcall 还是 sys_socketcall 都是无法无法设置断点的,它会提示

函数未定义的错误。但是这个 socketcall 函数应该就是所有关于 Socket接口函数的总入口啊,我们查看 syscalls.h文件:

可以发现该函数已经被标记为过时了。

此时,我们打开与 socket.c 文件处于相同目录下的 compat.c 文件,搜索发现:

实验作业四:Socket与系统调用深度分析_第8张图片

 

 这应该是对32位兼容的版本,我们尝试对此设置断点:

 

 发现确实可以。但是当我们尝试输入 replyhi 命令和 hello 命令以对该断点进行捕捉时,却不能捕捉到,因为我们关于 Socket接口函数的调用默认是通过64位下的 __sys_xxx等,并不通过 __ia32_compact_sys_socketcall这一总入口。

不过我们可以重新编译生成32位下的rootfs,然后再进行跟踪:

实验作业四:Socket与系统调用深度分析_第9张图片

 

 此时,所有对Socket接口函数的调用就都通过__ia32_compat_sys_socketcall这个总入口了。

如果在每个断点处单步进去的话就会发现确实调用了Socket接口函数对应的系统调用函数:

 

 

 

 (6)对于32位系统是通过 int 0x80来触发系统调用的,而对于64位系统,则改为了 syscall。

在 linux-5.0.1/arch/x86/entry/entry_64.S 文件中可以找到如下说明:

实验作业四:Socket与系统调用深度分析_第10张图片

 

 具体流程应该是:start_kernel --> trap_init --> cpu_init --> syscall_init

实验作业四:Socket与系统调用深度分析_第11张图片

 

 当用户态程序发起系统调用时,对于x86-64位程序应该是直接跳到entry_SYSCALL_64。另外在和 entry_64.S 文件同样目录下的 syscalls 文件夹下还可以找到 syscall_64.tbl,里面定义了系统调用号:

实验作业四:Socket与系统调用深度分析_第12张图片

 

 可以发现和我们之前设置断点时使用的 entry ponint 的名字有些不同,多了__x64前缀,我们比较一下:

实验作业四:Socket与系统调用深度分析_第13张图片

 

 发现确实可以,而且发现之前报未定义错误的 __sys_socketcall 改为 __x64_sys_socketcall 就可以了。虽然可以在 __x64_sys_socketcall处设置断点,但是当我们输入命令 replyhi 和 hello 时,gdb却无法捕捉到,这就和我们之前的一样了,也就是在64位下系统调用并没有通过 __sys_socketcall 这一总入口,不知道是不是理解错了。

你可能感兴趣的:(实验作业四:Socket与系统调用深度分析)