C语言 Linux x86_64汇编套接字编程

jum4pzuy  于 2023-08-03  发布在  Linux
关注(0)|答案(3)|浏览(111)

我在学组装。
按照我学习任何一门新语言的通常步骤,我已经学会了使用汇编语言进行网络连接。
很遗憾,这并不顺利,因为我在第0步几乎失败了,这将获得一个可以开始通信的套接字。
汇编代码应大致等于以下C代码:

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

int main(){
    int sock;
    sock = socket(AF_INET, SOCK_STREAM, 0);
}

字符串
(Let我们忽略它现在没有关闭套接字的事实。)
这就是我到目前为止所做的:

  • 已检查the manual。这意味着我需要做一个socketcall(),这一切都很好。问题开始于它需要一个int来描述它应该进行哪种socketcall。调用manpage也没有多大帮助,因为它只描述了:

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

  • 然而,在原始的系统调用列表中没有这样的调用--据我所知,socket()accept()bind()listen()等。是来自libnet的调用,而不是来自内核的。这让我非常困惑,所以我决定编译上面的C代码并用strace检查它。结果如下:
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3

  • 虽然这并没有让我更了解socket()**是什么,但它确实解释了它的参数。对于女巫,我似乎没有找到适当的文件(再次)。我以为PF_INETSOCK_STREAMIPPROTO_IP会在<sys/socket.h>中定义,但是我的grep-ing似乎没有找到任何用处。所以我决定使用gdbdisass main来查找值。这给出了以下输出:
Dump of assembler code for function main:
   0x00000000004004FD  <+0>:  push  rbp
   0x00000000004004FE  <+1>:  mov   rbp,rsp
   0x0000000000400501  <+4>:  sub   rsp,0x10
   0x0000000000400505  <+8>:  mov   edx,0x0
   0x000000000040050A <+13>:  mov   esi,0x1
   0x000000000040050F <+18>:  mov   edi,0x2
   0x0000000000400514 <+23>:  call  0x400400 <socket@plt>
   0x0000000000400519 <+28>:  mov   DWORD PTR [rbp-0x4],eax
   0x000000000040051C <+31>:  leave
   0x000000000040051D <+32>:  ret
End of assembler dump.

  • 根据我的经验,这意味着socket()EDXPF_INET)、ESISOCK_STREAM)和EDIIPPROTO_IP)获取参数。这对于系统调用来说是奇怪的(因为Linux系统调用的约定是使用EAX/RAX作为调用号,其他寄存器按升序存储参数,例如。RBXRCXRDX ...)。这是CALL-艾德而不是INT 0x80'd的事实也意味着这实际上不是一个系统调用,而是从一个共享对象调用的东西。或者别的什么
  • 但话说回来。在寄存器中传递参数对于CALL-ed来说是非常奇怪的。据我所知,通常情况下,被调用的东西的参数应该是PUSH-ed到堆栈上,因为编译器不知道他们会尝试使用什么寄存器。
  • 当使用ldd检查生成的二进制文件时,这种行为变得更加奇怪:
linux-vdso.so.1 (0x00007fff4a7fc000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f56b0c61000)
/lib64/ld-linux-x86-64.so.2 (0x00007f56b1037000)

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

这就是我的点子用光的地方。
所以我要求如下:

  • 描述x86-64 linux内核的实际系统调用及其相关编号的文档。(最好作为C的头文件。)
  • 定义PF_INETSOCK_STREAMIPPROTO_IP的头文件,因为它真的让我很困扰,我无法在自己的系统上找到它们。
  • 也许是x86-64 Linux上汇编中的网络教程。(对于x86-32,很容易找到素材,但由于某种原因,我没有找到64位的素材。
kh212irz

kh212irz1#

64位调用约定确实使用寄存器来传递参数,无论是在用户空间还是在系统调用中。如您所见,用户空间约定为rdirsirdxrcxr8r9。对于系统调用,使用r10而不是rcx,后者被syscall指令破坏。请参阅维基百科或ABI文档了解更多详情。
各种常量的定义都隐藏在头文件中,但是如果您安装了必要的开发包,则可以通过文件系统搜索轻松找到这些头文件。您应该查看/usr/include/x86_64-linux-gnu/bits/socket.h/usr/include/linux/in.h
至于系统调用列表,对google one来说是微不足道的,比如this。当然,你也可以随时查看内核源代码。

f45qwnt8

f45qwnt82#

socket.asm

; Socket

; Compile with: nasm -f elf socket.asm

; Link with (64 bit systems require elf_i386 option): ld -m elf_i386 socket.o -o socket

; Run with: ./socket
 
%include    'functions.asm'
 
SECTION .text
global  _start
 
_start:
 
    xor     eax, eax            ; init eax 0
    xor     ebx, ebx            ; init ebx 0
    xor     edi, edi            ; init edi 0
    xor     esi, esi            ; init esi 0
 
_socket:
 
    push    byte 6              ; push 6 onto the stack (IPPROTO_TCP)
    push    byte 1              ; push 1 onto the stack (SOCK_STREAM)
    push    byte 2              ; push 2 onto the stack (PF_INET)
    mov     ecx, esp            ; move address of arguments into ecx
    mov     ebx, 1              ; invoke subroutine SOCKET (1)
    mov     eax, 102            ; invoke SYS_SOCKETCALL (kernel opcode 102)
    int     80h                 ; call the kernel
 
    call    iprintLF            ; call our integer printing function (print the file descriptor in EAX or -1 on error)
 
_exit:
 
    call    quit                ; call our quit function

字符串
more docs...

b91juud3

b91juud33#

这是x86系统。如果要用于x86_64系统,请将x86寄存器更改为x86_64。例如将'eax'改为'rax'或将'esp'改为'rsp'。并更改eax(rax)中的syscall值,参见https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md

[bits 32]

global _start
section .data
   msg: db "Socket Failed To Create!",0xa,0
   len: equ $-msg

   msg1: db "Socket Created",0xa,0
   len1: equ $-msg1

   msg2: db "Recv Or Send Failed",0xa,0
   len2: equ $-msg2

   msg3: db "Shutdown Socket Failed",0xa,0
   len3: equ $-msg3

   DATASIZE:        equ 5

   SOCK_STREAM:     equ 1
   AF_INET:         equ 2
   AF_INET:         equ 2
   INADDR_ANY:      equ 0
   MSG_WAITALL:     equ 0x100
   MSG_DONTWAIT:    equ 0x40
   SHUT_RDWR:       equ 2

    SYS_SOCKET:     equ 1       ; sys_socket(2)
    SYS_BIND:       equ 2       ; sys_bind(2)
    SYS_CONNECT:    equ 3       ; sys_connect(2)
    SYS_LISTEN:     equ 4       ; sys_listen(2)
    SYS_ACCEPT:     equ 5       ; sys_accept(2)
    SYS_GETSOCKNAME:equ 6       ; sys_getsockname(2)
    SYS_GETPEERNAME:equ 7       ; sys_getpeername(2)
    SYS_SOCKETPAIR: equ 8       ; sys_socketpair(2)
    SYS_SEND:       equ 9       ; sys_send(2)
    SYS_RECV:       equ 10      ; sys_recv(2)
    SYS_SENDTO:     equ 11      ; sys_sendto(2)
    SYS_RECVFROM:   equ 12      ; sys_recvfrom(2)
    SYS_SHUTDOWN:   equ 13      ; sys_shutdown(2)
    SYS_SETSOCKOPT: equ 14      ; sys_setsockopt(2)
    SYS_GETSOCKOPT: equ 15      ; sys_getsockopt(2)
    SYS_SENDMSG:    equ 16      ; sys_sendmsg(2)
    SYS_RECVMSG:    equ 17      ; sys_recvmsg(2)
    SYS_ACCEPT4:    equ 18      ; sys_accept4(2)
    SYS_RECVMMSG:   equ 19      ; sys_recvmmsg(2)
    SYS_SENDMMSG:   equ 20      ; sys_sendmmsg(2)

struc sockaddr_in, -0x30
    .sin_family:    resb 2  ;2bytes
    .sin_port:      resb 2  ;2bytes
    .sin_addr:      resb 4  ;4bytes
    .sin_zero:      resb 8  ;8bytes
endstruc

struc socket, -0x40
    .socketfd       resb 4
    .connectionfd   resb 4
    .count          resb 4
    .data           resb DATASIZE
endstruc

section .text

_start:
    push ebp
    mov ebp, esp
    sub esp, 0x400 ;1024byte

    xor edx, edx    ;or use cdq
    ;
    ; int socket(int domain, int type, int protocol);
    ; domain: The domain argument specifies a communication domain
    ; 
    push edx                        ; Push protocol
    push dword SOCK_STREAM          ; Push type
    push dword AF_INET              ; Push domain
    mov ecx, esp                    ; ECX points to args
    mov ebx, SYS_SOCKET             ;
    mov eax, 0x66                   ; socketcall()
    int 0x80

    cmp eax, 0
    jl .socket_failed
    mov [ebp + socket.socketfd], eax

    ;
    ; fill struct sockaddr_in serv_addr;
    ;
    mov word [ebp + sockaddr_in.sin_family], AF_INET
    mov word [ebp + sockaddr_in.sin_port], 0x3905
    mov dword [ebp + sockaddr_in.sin_addr], INADDR_ANY

    push dword [ebp + sockaddr_in.sin_addr]
    push word  [ebp + sockaddr_in.sin_port]
    push word  [ebp + sockaddr_in.sin_family]

    mov ecx, esp

    ;
    ; int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    ;
    push byte 0x10                  ; sizeof(struct sockaddr)
    push ecx                        ; pointer struct sockaddr
    push dword [ebp + socket.socketfd]
    mov ecx, esp                    ; ECX points to args
    mov ebx, SYS_BIND               ;
    mov eax, 0x66
    int 0x80

    cmp eax, 0
    jne .socket_failed

    ;
    ;   int listen(int sockfd, int backlog);
    ;
    push dword 0x10
    push dword [ebp + socket.socketfd]
    mov ecx, esp
    mov ebx, SYS_LISTEN
    mov eax, 0x66
    int 0x80
    cmp eax, 0
    jne .socket_failed


    ;
    ; int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    ;
    xor ebx, ebx
    push ebx
    push ebx
    push dword [ebp + socket.socketfd]
    mov ecx, esp
    mov ebx, SYS_ACCEPT
    mov eax, 0x66
    int 0x80
    cmp eax, -1
    je .socket_failed
    mov [ebp + socket.connectionfd], eax

    mov dword [ebp + socket.count], 0
.again:
    lea edi, [ebp + socket.data]
    mov ecx, DATASIZE
    mov eax, 0
    rep stosd

    lea eax, [ebp + socket.data]
    ;
    ; ssize_t recv(int sockfd, const void *buf, size_t len, int flags);
    ;
    push dword MSG_WAITALL
    push dword DATASIZE
    push eax
    push dword [ebp + socket.connectionfd]
    mov ecx, esp
    mov ebx, SYS_RECV
    mov eax, 0x66
    int 0x80
    cmp eax, 0
    jle .recv_or_send_failed

    mov edx, eax
    lea ecx, [ebp + socket.data]
    call printk

    inc dword [ebp + socket.count]
    cmp dword [ebp + socket.count], 5
    jle  .again
.break:
    ;
    ; int shutdown(int sockfd, int how);
    ;
    push dword SHUT_RDWR
    push dword [ebp + socket.socketfd]
    mov ecx, esp
    mov ebx, SYS_SHUTDOWN
    mov eax, 0x66
    int 0x80
    cmp eax, 0
    jne .shutdown_failed

    ;
    ; int close(int fd)
    ;
    mov ebx, [ebp + socket.connectionfd]
    mov eax, 0x06
    int 0x80
    cmp eax, 0
    jne .shutdown_failed

    jmp .success

.shutdown_failed:
    mov edx, len3
    mov ecx, msg3
    call printk
    jmp .end
.recv_or_send_failed:
    mov edx, len2
    mov ecx, msg2
    call printk
    jmp .end
.socket_failed:

    mov edx, len
    mov ecx, msg
    call printk
    jmp .end

.success:
    mov edx, len1
    mov ecx, msg1
    call printk
    jmp .end
.end:
   leave

   mov     ebx,0               ;first syscall argument: exit code
   mov     eax,1               ;system call number (sys_exit)
   int     0x80                ;call kernel

   ret

   
; EDX: message length
; ECX: pointer to message to write
printk:
    pusha
    mov     ebx,1               ;first argument: file handle (stdout)
    mov     eax,4               ;system call number (sys_write)
    int     0x80                ;call kernel
    popa
    ret

字符串

相关问题