socket()を追う
-
socket()
はどこで実装されているか? - とりあえずプログラムを書いてみる
#include <stdio.h> #include <unistd.h> #include <arpa/inet.h> #include <netinet/in.h> #include <sys/socket.h> #include <string.h> int main(int argc, char **argv) { struct sockaddr_in dst; int sock; char dat[] = "hogehoge"; memset(&dst, 0, sizeof(dst)); dst.sin_port = htons(8080); dst.sin_family = AF_INET; dst.sin_addr.s_addr = inet_addr("127.0.0.1"); sock = socket(AF_INET, SOCK_STREAM, 0); connect(sock, (struct sockaddr *) &dst, sizeof(dst)); printf("%d\n",sock); send(sock, dat, strlen(dat), 0); close(sock); return 0; }
- gdbで
socket()
で止めてどういうアセンブラが動いているか見てみる
- gdb-peda$ disas
- 0x00007ffff7ed71a0 <+0>: endbr64
- => 0x00007ffff7ed71a4 <+4>: mov eax,0x29
- 0x00007ffff7ed71a9 <+9>: syscall
- 0x00007ffff7ed71ab <+11>: cmp rax,0xfffffffffffff001
- 0x00007ffff7ed71b1 <+17>: jae 0x7ffff7ed71b4 <socket+20>
- 0x00007ffff7ed71b3 <+19>: ret
- 0x00007ffff7ed71b4 <+20>: mov rcx,QWORD PTR rip+0xc0c85 # 0x7ffff7f97e40
- 0x00007ffff7ed71bb <+27>: neg eax
- 0x00007ffff7ed71bd <+29>: mov DWORD PTR fs:rcx,eax
- 0x00007ffff7ed71c0 <+32>: or rax,0xffffffffffffffff
- 0x00007ffff7ed71c4 <+36>: ret
- システムコールを呼んでいるだけっぽい
-
eaxに入れられている0x29はsyscall_64.tblのsocketの番号に一致する
$ rg 'int socket'
で宣言を探す/* Create a new socket of type TYPE in domain DOMAIN, using protocol PROTOCOL. If PROTOCOL is zero, one is chosen automatically. Returns a file descriptor for the new socket, or -1 for errors. */ extern int socket (int __domain, int __type, int __protocol) __THROW;
/* Create a new socket of type TYPE in domain DOMAIN, using
- protocol PROTOCOL. If PROTOCOL is zero, one is chosen automatically.
- Returns a file descriptor for the new socket, or -1 for errors. */
extern int __socket (int __domain, int __type,
- int __protocol);
libc_hidden_proto (__socket)
libc_hidden_proto
はlibc内部からその関数を呼べるようにするもの(?)
- 実装を探す
- 内部でsocketシステムコールを呼んでいるのでOS依存→
sysdeps/
以下int __socket (int fd, int type, int domain) { #ifdef __ASSUME_SOCKET_SYSCALL return INLINE_SYSCALL (socket, 3, fd, type, domain); #else return SOCKETCALL (socket, fd, type, domain); #endif } libc_hidden_def (__socket) weak_alias (__socket, socket)
- 内部でsocketシステムコールを呼んでいるのでOS依存→
INLINE_SYSCALL
からはINTERNAL_SYSCALL
とかたどって最終的に- i386では
int 0x80
されていた nr
は引数の数
- i386では
#undef INTERNAL_SYSCALL
#define INTERNAL_SYSCALL(name, err, nr, args...) \
internal_syscall##nr (SYS_ify (name), err, args)
...
#undef internal_syscall0
#define internal_syscall0(number, err, dummy...) \
({ \
unsigned long int resultvar; \
asm volatile ( \
"syscall\n\t" \
: "=a" (resultvar) \
: "0" (number) \
: "memory", REGISTERS_CLOBBERED_BY_SYSCALL); \
(long int) resultvar; \
})
...
send(), sendto(), sendmsg()
の実装も読む- 同じように読んだらええやろと思って開いたら実装がない
- stubになってる
/* Send N bytes of BUF to socket FD. Returns the number sent or -1. */
ssize_t
__send (int fd, const void *buf, size_t n, int flags)
{
__set_errno (ENOSYS);
return -1;
}
libc_hidden_def (__send)
weak_alias (__send, send)
stub_warning (send)
- いろいろパターンを変えて探す
$ rg 'sendmsg \('
__libc_sendmsg
からシステムコールを呼んでいる- 調べてみるとこの3つの関数はCancellation pointらしい
- アセンブラで見てみるとなんかマルチスレッドに関係してそうな関数を呼び出している
ssize_t
__libc_sendmsg (int fd, const struct msghdr *msg, int flags)
{
# ifdef __ASSUME_SENDMSG_SYSCALL
return SYSCALL_CANCEL (sendmsg, fd, msg, flags);
# else
return SOCKETCALL_CANCEL (sendmsg, fd, msg, flags);
# endif
}
weak_alias (__libc_sendmsg, sendmsg)
weak_alias (__libc_sendmsg, __sendmsg)
Backlinks
SecHack365
SecHack365は学生(と社会人)が、1年間指導を受けながらセキュリティに関連したり関連しなかったりするテーマで何かをつくる長期ハッカソン。無料で、交通費も全部出る。筆者は2018年度に、「ライブラリ・リンカ・ローダ・コンパイラetcを連携させたセキュリティ機能の開発」というテーマで参加した。