Linux x86_64 汇编套接字编程

Wol*_*fer 5 c sockets linux assembly x86-64

大家好。

所以我正在学习汇编。
按照我通常学习的任何新语言的学习步骤,我已经到达了使用程序集的网络。

遗憾的是,情况不太好,因为我在第 0 步几乎失败了,这将获得一个可以开始通信的套接字。

汇编代码应大致等于以下 C 代码:

#include <stdio.h>
#include <sys/socket.h>

int main(){
        int sock;
        sock = socket(AF_INET, SOCK_STREAM, 0);
}
Run Code Online (Sandbox Code Playgroud)

(让我们忽略它现在没有关闭套接字的事实。)

所以这是我到目前为止所做的:

  • 检查了手册。这意味着我需要做一个socketcall()这一切都很好。问题开始于它需要一个int描述它应该进行什么样的套接字调用的。调用手册对此也没有多大帮助,因为它仅描述了以下内容:

在某些架构上——例如 x86-64 和 ARM——没有 socketcall() 系统调用;相反,socket(2)、accept(2)、bind(2) 等实际上是作为单独的系统调用实现的。

  • 然而,在系统调用的原始列表中没有这样的调用——据我所知socket()accept()bind()listen()、 等是来自libnet而不是来自内核的调用。这让我很困惑,所以我决定编译上面的C代码并使用strace. 这产生了以下结果:

    套接字(PF_INET,SOCK_STREAM,IPPROTO_IP)= 3

  • 虽然这并没有让我更接近于知道它是什么socket() 但它确实解释了它的论点。对于女巫,我似乎没有找到合适的文档(再次)。我认为PF_INETSOCK_STREAMIPPROTO_IP就在被定义<sys/socket.h>,但我grep似乎对他们-ing没有发现使用的东西。所以我决定通过gdbdisass main查找值一起使用来实现它。这给出了以下输出:

    汇编代码转储功能主要:0x00000000004004fd <0>:推RBP 0x00000000004004fe <1>:MOV RBP,RSP 0x0000000000400501 <4>:子RSP,为0x10 0x0000000000400505 <8>:MOV EDX,为0x0 0x000000000040050a <13 >:MOV ESI,为0x1 0x000000000040050f <18>:MOV EDI,0X2 0x0000000000400514 <23>:呼叫0x400400 0x0000000000400519 <28>:MOV DWORD PTR [RBP-为0x4],EAX 0x000000000040051c <31>:假
    0x000000000040051d <+ 32>: ret
    汇编程序转储结束。

  • 根据我的经验,这意味着socket()EDX( PF_INET), ESI(SOCK_STREAM ) 和EDI( IPPROTO_IP)。这将是奇怪的系统调用(如Linux的系统调用约定是使用EAX/RAX递增的顺序呼叫号码和其他寄存器的参数,如:RBXRCXRDX...)。这是 beaing CALL-ed 而不是INT 0x80'd 的事实也意味着这实际上不是系统调用,而是从共享对象调用的东西。或者其他的东西。

  • 但话又说回来。对于CALL-ed 的东西,在寄存器中传递参数非常奇怪。通常,据我所知,被调用事物的参数应该被PUSH放到堆栈上,因为编译器不知道它们会尝试使用哪些寄存器。

  • 当使用以下命令检查生成的二进制文件时,这种行为变得更加奇怪ldd

    linux-vdso.so.1 (0x00007fff4a7fc000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f56b0c61000) /lib64/ld-linux-x86-64.so.2001000007 )

  • 似乎没有网络库的链接。

这就是我没有想法的地方。

所以我要求以下内容:

  • 描述 x86-64linux 内核的实际系统调用及其相关编号的。(最好作为 . 的头文件C
  • 定义的头文件 PF_INET,SOCK_STREAMIPPROTO_IP因为我无法在我自己的系统上找到它们,这让我很烦恼。
  • 也许是x86-64linux 上的程序集网络教程。(为了x86-32很容易找到材料,但出于某种原因,我想出了 64 位的东西。)

谢谢!

Jes*_*ter 8

64 位调用约定确实使用寄存器在用户空间和系统调用中传递参数。正如您所看到的,用户空间约定是rdi, rsi, rdx, rcx, r8, r9。对于系统调用,r10使用 ,而不是rcx被系统调用指令破坏。有关更多详细信息,请参阅维基百科或 ABI 文档。

各种常量的定义隐藏在头文件中,但假设您安装了必要的开发包,则可以通过文件系统搜索轻松找到头文件。你应该查看/usr/include/x86_64-linux-gnu/bits/socket.h/usr/include/linux/in.h

至于系统调用列表,google一下就很简单了,比如这个。当然,您也可以随时查看内核源代码。