C Linux x86_64汇编套接字编程
大家好。 因此,我正在学习汇编。C Linux x86_64汇编套接字编程,c,linux,sockets,assembly,x86-64,C,Linux,Sockets,Assembly,X86 64,大家好。 因此,我正在学习汇编。按照我学习的任何新语言的常规学习步骤,我已经与汇编建立了联系 遗憾的是,这并不顺利,因为我在第0步几乎失败了,这将获得一个可以开始通信的套接字 汇编代码应大致等于以下C代码: #include <stdio.h> #include <sys/socket.h> int main(){ int sock; sock = socket(AF_INET, SOCK_STREAM, 0); } #包括 #包括 i
按照我学习的任何新语言的常规学习步骤,我已经与汇编建立了联系 遗憾的是,这并不顺利,因为我在第0步几乎失败了,这将获得一个可以开始通信的套接字 汇编代码应大致等于以下C代码:
#include <stdio.h>
#include <sys/socket.h>
int main(){
int sock;
sock = socket(AF_INET, SOCK_STREAM, 0);
}
#包括
#包括
int main(){
int袜子;
sock=socket(AF\u INET,sock\u STREAM,0);
}
(让我们忽略一个事实,即它现在还没有关闭套接字。)
以下是我迄今为止所做的:
- 检查过了。这意味着我需要做一个
这一切都很好。问题开始于它需要一个socketcall()
来描述它应该进行什么样的socketcall。这些电话也没有多大帮助,因为它只描述了:int
- 然而,在最初的系统调用列表中没有这样的调用——据我所知,
,socket()
,accept()
,bind()
,等等都是来自listen()
的调用,而不是来自内核的调用。这让我完全困惑,所以我决定编译上面的libnet
代码,并用C
检查它。这产生了以下结果: 套接字(PF_INET、SOCK_STREAM、IPPROTO_IP)=3strace
- 虽然这并没有让我更进一步地了解什么是
,但它确实解释了它的参数。对于witch,我似乎没有找到合适的文档(再次)。我原以为socket()
,PF\u INET
,SOCK\u STREAM
将在IPPROTO\u IP
中定义,但我的
-ing对它们似乎没有任何用处。所以我决定使用grep
和gdb
来查找值。这产生了以下输出: 主功能的汇编程序代码转储: 0x00000000004004fd:推送rbp 0x00000000004004fe:mov rbp,rsp 0x0000000000400501:子rsp,0x10 0x0000000000400505:mov edx,0x0 0x000000000040050a:mov esi,0x1 0x000000000040050f:mov edi,0x2 0x0000000000400514:调用0x400400 0x0000000000400519:mov DWORD PTR[rbp-0x4],eax 0x000000000040051c:离开disassmain
0x000000000040051d:ret
汇编程序转储结束 - 根据我的经验,这意味着
从socket()
(EDX
)、PF_INET
(ESI
)和SOCK_STREAM
(EDI
)获取其参数。这对于系统调用来说是很奇怪的(linux系统调用的惯例是使用IPPROTO_IP
/EAX
作为调用号,并使用其他寄存器作为参数的递增顺序,例如RAX
,RBX
,RCX
…)。这是beingRDX
-ed而不是CALL
'd这一事实也意味着这实际上不是一个系统调用,而是从共享对象调用的东西。或者别的什么int0x80
- 但话说回来。对于
-ed的对象,在寄存器中传递参数是非常奇怪的。通常,据我所知,被调用对象的参数应该是调用
推到堆栈上,因为编译器不知道他们将尝试使用哪些寄存器
- 当使用
检查生成的二进制文件时,这种行为变得更加奇怪: 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)ldd
- 似乎没有网络库的链接
- 描述
linux内核的实际系统调用及其相关编号的文档。(最好作为x86-64
的头文件)C
- 定义
,PF_INET
,SOCK_STREAM
的头文件,因为我在自己的系统上找不到它们,这让我非常恼火IPPROTO_IP
- 也许是关于在
linux上汇编网络的教程。(对于x86-64
很容易找到材料,但出于某种原因,我的64位内容是空的。)x86-32
谢谢 64位调用约定确实使用寄存器在用户空间和系统调用中传递参数。如您所见,用户空间约定是
rdi
,rsi
,rdx
,rcx
,r8
,r9
。对于系统调用,使用r10
代替被syscall指令阻塞的rcx
。有关更多详细信息,请参阅或ABI文档
各种常量的定义隐藏在头文件中,但如果您安装了必要的开发包,则可以通过文件系统搜索轻松找到头文件。您应该查看/usr/include/x86\u 64-linux-gnu/bits/socket.h
和/usr/include/linux/in.h
至于系统调用列表,它对于googleone来说是微不足道的,比如。当然,您也可以随时查看内核源代码
socket.asm
);插座
);使用:nasm-f elf socket.asm编译
);链接(64位系统需要elf_i386选项):ld-m elf_i386 socket.o-o socket
);使用:./socket运行
%包括“functions.asm”
第节.案文
全球启动
_开始:
xor eax, eax ; init eax 0
xor ebx, ebx ; init ebx 0
xor edi, edi ; init edi 0
xor esi, esi ; init esi 0
_插座:
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)
_出口:
call quit ; call our quit function
这是针对x86系统的。如果您想使用x86_64系统
[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