Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[Q] source code of connect system call

79 views
Skip to first unread message

Hiroki Kashiwazaki

unread,
May 14, 2004, 7:55:22 AM5/14/04
to
柏崎@北海道です。

ネットワークプログラミングをして遊んでいる今日このごろなのですが、
connect のソースを読んでみたいなーとか考えて、libcのソースを get
してきました (glibc_2.2.5.orig.tar.gz)。が、展開して探してみても
connectがどこにあるのか探し当てる事は出来ませんでした。connectは
system callだから、カーネルなのかしらんと、カーネルソースを取得
してきたのですが、これまた探し当てる事が出来ませんでした。

どこにあるのか、どのように探すのが適切であるのか、情報をお寄せ頂
ければ幸いです。

--
柏崎 礼生 (Hiroki Kashiwazaki)@HUIIC
Ph.D candidate in the Division of Electronics & Information
Engineering, Hokkaido University
mailto:r...@cc.hokudai.ac.jp
Tel:+81-11-706-2998

dev_...@anet.ne.jp

unread,
May 14, 2004, 8:30:02 AM5/14/04
to
Hiroki Kashiwazaki wrote:
>
> どこにあるのか、どのように探すのが適切であるのか、情報をお寄せ頂
> ければ幸いです。
>

ローカルに lxr 環境があると便利なんですがねぇ。
ちょっと古いカーネルですが
http://bernia.disca.upv.es/lxr/http/source/net/socket.c
の1114行目です。

IIJIMA Hiromitsu

unread,
May 14, 2004, 9:57:14 AM5/14/04
to
いいじまです。

Linux の実機がないので未確認のまま書いてますが…

> connect のソースを読んでみたいなーとか考えて、libcのソースを get
> してきました (glibc_2.2.5.orig.tar.gz)。が、展開して探してみても
> connectがどこにあるのか探し当てる事は出来ませんでした。connectは
> system callだから、カーネルなのかしらんと、カーネルソースを取得
> してきたのですが、

本題は解決の方向に向かっているとして、問題部分はまず libc にあるはずです。

x86 Linux の場合、アセンブラレベルでのシステムコールの呼び出しには、
レジスタに値をセットして int 0xNN 命令を使うと記憶しています。
バッファ・オーバーフローの exploit code ではこの直接呼び出しが一般的です。

ところがこれは C 言語からは直接には関数呼び出しとして扱えませんので
(インラインアセンブラという方法はありますが)、そのための wrapper が
必要です。そして、wrapper を提供するのは libc です。

ただ、libc を読んでも、もしかしたら connect 関数はアセンブラで書かれて
いて、純粋に int 0xNN への wrapper にしかなっていない、という可能性も
あります。
#アセンブラソースも grep しました?>>柏崎さん

で、それを確認したら今度はカーネルソースをあたる、と。

========================================================================
飯嶋 浩光 / でるもんた・いいじま http://www.ht.sakura.ne.jp/~delmonta/
IIJIMA Hiromitsu, aka Delmonta mailto:delm...@ht.sakura.ne.jp

Shinji KONO

unread,
May 14, 2004, 7:48:15 PM5/14/04
to
河野真治 @ 琉球大学情報工学です。

In article <40A4D03A...@ht.sakura.ne.jp>, IIJIMA Hiromitsu <delm...@ht.sakura.ne.jp> writes
> 本題は解決の方向に向かっているとして、問題部分はまず libc にあるはずです。

「まず」としては正解なんだけど....

> x86 Linux の場合、アセンブラレベルでのシステムコールの呼び出しには、
> レジスタに値をセットして int 0xNN 命令を使うと記憶しています。
> バッファ・オーバーフローの exploit code ではこの直接呼び出しが一般的です。

それを見たいんだったら、gdb で、一回connect を呼び出してから、
x/20i connect で disassemeble するのが簡単です。何故一回呼び出すか
というと、dynamic link されているから...

で、
0x400ff2f0 <__libc_connect>: mov %ebx,%edx
0x400ff2f2 <__libc_connect+2>: mov $0x66,%eax
0x400ff2f7 <__libc_connect+7>: mov $0x3,%ebx
0x400ff2fc <__libc_connect+12>: lea 0x4(%esp,1),%ecx
0x400ff300 <__libc_connect+16>: int $0x80
0x400ff302 <__libc_connect+18>: mov %edx,%ebx
0x400ff304 <__libc_connect+20>: cmp $0xffffff83,%eax
0x400ff307 <__libc_connect+23>: jae 0x400ff30a <__libc_connect+26>
みたいなのが見れるので... 0x66 = 102 がシステムコール番号
だってのがわかります。

libc のソース見てもいいんだけどさ。

> #アセンブラソースも grep しました?>>柏崎さん

これもかなり正解に近いんですが、cpp を通るので、.s でなくて、.S
だったりします。Linux には、システムコールの番号は、libc と
entry.S に分離して記述されているんです。BSDだと、このあたりは
config がやるんですが...
linux-2.6.5/arch/i386/kernel/entry.S


> で、それを確認したら今度はカーネルソースをあたる、と。

で、そこを見ても connect というのはなくて... 102 という番号を頼りに、

.long sys_fstatfs /* 100 */
.long sys_ioperm
.long sys_socketcall
.long sys_syslog

とかいうのがあるので、sys_socketcall だってのがわかるわけですね。
で、そいつは、socket.c にあって、
linux-2.6.5/net/socket.c

switch(call)
{
case SYS_SOCKET:
err = sys_socket(a0,a1,a[2]);
break;
case SYS_BIND:
err = sys_bind(a0,(struct sockaddr __user *)a1, a[2]);
break;
case SYS_CONNECT:
err = sys_connect(a0, (struct sockaddr __user *)a1, a[2])
;
break;
ってなっているので、ようやっと、sys_connect にいるとわかる。
最初から別なシステムコールにしなかったのは、拡張性を考えてかな。

でも、それでも、行きつけない。何故なら、

err = sock->ops->connect(sock, (struct sockaddr *) address, addrlen,
sock->file->f_flags);

となっていて、実は、indirect call だから。そりゃそうなんで、connect
といっても、TCPとは限らない。decnet (ふる~) かも知れないし。
もちろん、sock はファイルディスクリプタテーブルを指していて、ops
は、そこに入っている関数へのポインタの表です。

この表は、デバイスとプロトコルに分散しているので... このindirect
callがどこを指しているかを見付けるのは、割と楽しい作業です...
この構造体に代入しているところを見付けるのは大変なので。

で、ソースを追わずに、remote debug するのが直接的でよろしい。
kernel をgdbでのぞいても良いですが... Virtual PC でやりたい
んだけど、まだ、やってないです。

結果的には、

struct proto_ops inet_stream_ops = {
.....
.connect = inet_stream_connect,

とかいうのがあって、(だんだんめんどくさくなってきた...)
あと何段かindirectがあって、

In /Users/kono/src/linux/linux-2.6.5/net/ipv4/tcp_ipv4.c
2623: .name = "TCP",
2624: .close = tcp_close,
2625: .connect = tcp_v4_connect,
2626: .disconnect = tcp_disconnect,
2627: .accept = tcp_accept,

ってのがあるから、

In /Users/kono/src/linux/linux-2.6.5/net/ipv4/tcp_ipv4.c
753:/* This will initiate an outgoing connection. */
754:int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
755:{
756: struct inet_opt *inet = inet_sk(sk);

だってのがわかりますね。

---
Shinji KONO @ Information Engineering, University of the Ryukyus
河野真治 @ 琉球大学工学部情報工学科

Shinji KONO

unread,
May 14, 2004, 7:53:15 PM5/14/04
to
河野真治 @ 琉球大学情報工学です。

In article <3989746...@insigna.ie.u-ryukyu.ac.jp>, ko...@ie.u-ryukyu.ac.jp (Shinji KONO) writes


> みたいなのが見れるので... 0x66 = 102 がシステムコール番号
> だってのがわかります。

ちなみに、0x64 = 100 を覚えておくと便利。(何が?) 他には、1024
= 2^10 とか、0x1000 = 4096 とか。65536 とか...

日常生活で「ぴったり256だ」とかいう機会はあるが、「ぴったり65536
だ」という機会はまだないです。

Hiroki Kashiwazaki

unread,
May 17, 2004, 8:29:19 PM5/17/04
to
柏崎@北海道です。

こんがらがりつつある今日このごろなので、阿呆かと思われるかも
しれませんがお付き合い下さい。

At Fri, 14 May 2004 21:30:02 +0900,
dev_...@anet.ne.jp wrote:

> http://bernia.disca.upv.es/lxr/http/source/net/socket.c
> の1114行目です。

疑問点は 2つあって、

・ asmlinkage 型は何か、どこで定義されているのか
・ sys_connect 関数がどうして connect 関数を意味するのか

というところです。

Shinji KONO

unread,
May 17, 2004, 10:00:12 PM5/17/04
to
河野真治 @ 琉球大学情報工学です。

In article <86zn86...@soro1.cc.hokudai.ac.jp>, Hiroki Kashiwazaki <r...@cc.hokudai.ac.jp> writes
> 柏崎@北海道です。

君は僕の授業を受けるとちょうど良いかも.... (ってか僕の記事は
届いてないか?)

> ・ asmlinkage 型は何か、どこで定義されているのか

これは、macro で、i386のヘッダーで定義されています。
あんまりたいしたことは、やってないはず。

> ・ sys_connect 関数がどうして connect 関数を意味するのか

kernel 内部の system call のテーブルに格納されていて、
kernel trap の後、テーブルを参照して分岐するルーチンが
entry.S に書かれてます。

Hiroki Kashiwazaki

unread,
May 18, 2004, 7:07:50 AM5/18/04
to
柏崎@北海道です。

At Tue, 18 May 2004 02:00:12 +0000 (UTC),
Shinji KONO wrote:

> 君は僕の授業を受けるとちょうど良いかも.... (ってか僕の記事は
> 届いてないか?)

届いていました、が後回しにしていました(ごめんなさいごめんなさい)。
河野先生の記事をじっくり読んで、ようやく理解にたどり着けた感じが
あります。

いやでもそれはともかく河野先生の授業は受けたいです。とりあえず、
WWW 上の講義資料は全部読ませて頂こうと。

> kernel 内部の system call のテーブルに格納されていて、
> kernel trap の後、テーブルを参照して分岐するルーチンが
> entry.S に書かれてます。

まだあやふやなのですが、書かれたソースから、それが実現されるまでの
流れとしては

・ソースに記述された connect 関数

・コンパイル時にシステムコールとして認識され、
arch/i386/kernel/entry.S に記述されているように呼び出す関数
(sys_socketcall) を得る ? そのときに sys_socketcall に与える
引数 (SYS_CONNECT) はどこから得られる ?

・sys_socketcall ( SYS_CONNECT, hogehoge ) を呼び出す

・sys_connect を呼び出す

・socket file descriptorに適切な関数を呼び出す

という理解で良いのでしょうか。ここらへんがあやふやでどうにも。

IIJIMA Hiromitsu

unread,
May 18, 2004, 7:30:04 AM5/18/04
to
いいじまです。

> まだあやふやなのですが、書かれたソースから、それが実現されるまでの
> 流れとしては
>
> ・ソースに記述された connect 関数
> ↓
> ・コンパイル時にシステムコールとして認識され、
> arch/i386/kernel/entry.S に記述されているように呼び出す関数
> (sys_socketcall) を得る ? そのときに sys_socketcall に与える
> 引数 (SYS_CONNECT) はどこから得られる ?
> ↓
> ・sys_socketcall ( SYS_CONNECT, hogehoge ) を呼び出す
> ↓
> ・sys_connect を呼び出す
> ↓
> ・socket file descriptorに適切な関数を呼び出す
>
> という理解で良いのでしょうか。ここらへんがあやふやでどうにも。

えっと、ちょっと違います。
河野さんの説明してくれた手順を再度追ってみましょう。

まず、C の関数としての connect() は、「普通の libc の関数」です。
C 言語で connect() を呼び出す適当なコード(動かなくても文法的に正しけれ
ばよい)を書いて、gcc -O6 --fomit-frame-pointer -S というオプションでア
センブラソースを吐き出させて、読んでみてください。全く同じように、printf()
を呼び出すコードも書いて、同じようにアセンブラソースを書いて、読み比べて
みてください。ほぼ同じコードだということがわかると思います。つまり、コン
パイラはこの部分については全く関知しません。

ところがこの connect() はアセンブラで書かれていて、
レジスタ EAX に 0x66 を、EBX に 3 を、ECX には呼び出し元から
渡されたディスクリプタをセットして、割り込み 0x80 を発生させる
というコードが書かれています。この「EAX=0x66」「EBX=3」「割り込み 0x80」
というのは、カーネルが好き勝手に決めている数値で、他のシステムコールと重
複卯していなければ特にこの数値でなくてもかまいません。

この割り込み発生で、制御がカーネルに渡ります。

制御を受け取ったカーネルは、entry.S に書いてある関数ポインタ配列から、
EAX==0x66 に対応する関数を探し出します。これが、sys_socketcall です。

で、

> switch(call)
> {
> case SYS_SOCKET:
> err = sys_socket(a0,a1,a[2]);
> break;
> case SYS_BIND:
> err = sys_bind(a0,(struct sockaddr __user *)a1, a[2]);
> break;
> case SYS_CONNECT:
> err = sys_connect(a0, (struct sockaddr __user *)a1, a[2])
> ;
> break;

というコードにたどりつくわけですが、おそらく SYS_CONNECT が、割り込みを
発生させるときに使った EBX=3 という値に対応しているのでしょう。で、EBX
に 3 以外の値をセットすると、同じ int 0x80、EAX=0x66 が socket() の呼び
出しになったり、bind() の呼び出しになったりする、と。(すいません、実際
のコードは追っていないので憶測です。)

で、そのあと呼び出される関数は、最初に socket() でソケットを生成したとき
に、ソケットの種類に応じて、ディスクリプタテーブル上の関数ポインタには、
○○の処理を行う関数はどれ、○○の処理を行う関数は○○、という値が設定さ
れていますので、それを参照して呼び出しを行うと、最終的に tcp_v4_connect()
にたどりつく、というわけです。


P.S.
このへんのアセンブラの初歩って、昔は MS-DOS で int86x() なんかをよく使っ
たものですが、今は何を使って勉強するのが素直なんだろう…

Shinji KONO

unread,
May 18, 2004, 8:45:49 AM5/18/04
to
河野真治 @ 琉球大学情報工学です。

In article <40A9F3BC...@ht.sakura.ne.jp>, IIJIMA Hiromitsu <delm...@ht.sakura.ne.jp> writes
> この割り込み発生で、制御がカーネルに渡ります。


> というコードにたどりつくわけですが、おそらく SYS_CONNECT が、割り込みを
> 発生させるときに使った EBX=3 という値に対応しているのでしょう。

asmlinkage なんだけど、

linux-2.6.5/include/asm-i386/linkage.h
で、

#define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0)))

となっていて、(CPP_ASMLINKAGE)がどこかは知らないんだけど、
(実際に kernel を作って、-E フラグで cpp してやれば消えると期待します)
このregparmてのが、register parameter だってのは、

info gcc で探すと、

`regparm (NUMBER)'
On the Intel 386, the `regparm' attribute causes the compiler to
pass up to NUMBER integer arguments in registers EAX, EDX, and ECX
instead of on the stack. Functions that take a variable number of
arguments will continue to be passed all of their arguments on the
stack.

となっているのがわかります。

In article <86pt92...@soro1.cc.hokudai.ac.jp>, Hiroki Kashiwazaki <r...@cc.ho
kudai.ac.jp> writes


> ・コンパイル時にシステムコールとして認識され、
> arch/i386/kernel/entry.S に記述されているように呼び出す関数
> (sys_socketcall) を得る ? そのときに sys_socketcall に与える
> 引数 (SYS_CONNECT) はどこから得られる ?

ってのは、

0x400ff2f0 <__libc_connect>: mov %ebx,%edx
0x400ff2f2 <__libc_connect+2>: mov $0x66,%eax

^^^^^ これが sys_socketcall の番号
0x400ff2f7 <__libc_connect+7>: mov $0x3,%ebx
^^^^^ これが sys_connect の番号
0x400ff2fc <__libc_connect+12>: lea 0x4(%esp,1),%ecx
^^^^^^^^^^^ これがconnectの引数


0x400ff300 <__libc_connect+16>: int $0x80
0x400ff302 <__libc_connect+18>: mov %edx,%ebx
0x400ff304 <__libc_connect+20>: cmp $0xffffff83,%eax
0x400ff307 <__libc_connect+23>: jae 0x400ff30a <__libc_connect+26>

になるはずなんだけど、これらは、int 0x80で、kernel に割り込んだ後、
entry.S の
#define SAVE_ALL \
cld; \
pushl %es; \
pushl %ds; \
pushl %eax; \
pushl %ebp; \
pushl %edi; \
pushl %esi; \
pushl %edx; \
pushl %ecx; \
pushl %ebx; \
で、スタックに積まれているので、そこから読んで来るんじゃないかと。
たぶん、最後に積まれた二つのレジスタが、

asmlinkage long sys_socketcall(int call, unsigned long __user *args)

の二つの引数になるんじゃないかな。

> ・sys_socketcall ( SYS_CONNECT, hogehoge ) を呼び出す
> ↓
> ・sys_connect を呼び出す
> ↓
> ・socket file descriptorに適切な関数を呼び出す
> という理解で良いのでしょうか。ここらへんがあやふやでどうにも。

ここはあってると思う。

Hiroki Kashiwazaki

unread,
May 19, 2004, 12:34:53 AM5/19/04
to
柏崎@北海道です。

# 響きの悪い頭にお付き合い頂いて恐縮の至りです…。

ようやくクリアになりつつありますが、もう何点か。
理解のための僕の思考過程をまとめると、以下のようになります。

・ connect を使うのに <sys/socket.h> を include する。
・ <sys/socket.h> には extern int connect ... の宣言がある。
・ コンパイルする時に libc とリンクが行われる
・ libc で connect は アセンブラでコーディングされているので、
connectのCのソースを探そうとしても見つからない。

・ connectのアセンブラソースには


> レジスタ EAX に 0x66 を、EBX に 3 を、ECX には呼び出し元から
> 渡されたディスクリプタをセットして、割り込み 0x80 を発生させる

というコードが書かれてある。
[でもこれはどこにある ?]

・ arch/i386/kernel/entry.S に記述されているように呼び出す関数から
EAX==0x66に対応する sys_socketcall を得る。sys_socketcall に与える
引数 には EBX==3 が定められる。

・sys_socketcall ( EBX==3==SYS_CONNECT , hogehoge ) を呼び出す



・sys_connect を呼び出す

・socket file descriptorに適切な関数を呼び出す

もうここまでこれば別に connect のアセンブラソースはどうでもいい
じゃろという事にもなるんですが、欲を出して connect のソースを探
すと、libcの sysdeps/unix/sysv/linux/connect.S がそれに当たるよ
うに思われます。

で、connect.Sに記述されているのは

#define socket connect
#define __socket __libc_connect
#define NARGS 3
#include <socket.S>
weak_alias (__libc_connect, __connect)

というもので、NARGS の 3 というあたりがふむふむな感じですが、他の
部分 (weak_alias やらその他マクロ) をさらに追跡していっても、もう
ここらで引き上げておけと心の中の声が叫んでいる状態です。

で、疑問なのが

1. connect.S をさらに追跡していった先に connect のソースはあり
そうなのか。それともこれはすでに間違っているのか。

というのが一番重要な疑問で、そしてもう一つ以前からずっと思っていた
事で、今回の話とは全然関係なさそうですが、

2. どうしてマクロには __ が多用されるのか。

という疑問があります。情報へのポインタでもいいので、何か情報をお寄
せ頂ければ幸いです。

Hiroki Kashiwazaki

unread,
May 19, 2004, 9:41:28 PM5/19/04
to
柏崎@北海道です。

# 片山@PFUさんからメールでご指摘を頂き、自己フォローアップ。

At Wed, 19 May 2004 13:34:53 +0900,
Hiroki Kashiwazaki wrote:

> ・ connectのアセンブラソースには
> > レジスタ EAX に 0x66 を、EBX に 3 を、ECX には呼び出し元から
> > 渡されたディスクリプタをセットして、割り込み 0x80 を発生させる
> というコードが書かれてある。
> [でもこれはどこにある ?]

glibc の sysdeps/unix/sysv/linux/{arch}/socket.S にありました。
sysdeps/unix/sysv/linux/connect.S で include されてるのだし、すぐに
読めば分かったろうに…。

内容は以下の通り。

ENTRY (__socket)

/* Save registers. */
movl %ebx, %edx

movl $SYS_ify(socketcall), %eax /* System call number in %eax. */

/* Use ## so `socket' is a separate token that might be #define'd. */
movl $P(SOCKOP_,socket), %ebx /* Subcode is first arg to syscall. */
lea 4(%esp), %ecx /* Address of args is 2nd arg. */

/* Do the system call trap. */
int $0x80

/* Restore registers. */
movl %edx, %ebx

/* %eax is < 0 if there was an error. */
cmpl $-125, %eax
jae SYSCALL_ERROR_LABEL

/* Successful; return the syscall's value. */
L(pseudo_end):
ret

PSEUDO_END (__socket)

IIJIMA Hiromitsu

unread,
May 20, 2004, 1:31:45 PM5/20/04
to
いいじまです。

> > ・ connectのアセンブラソースには
> > > レジスタ EAX に 0x66 を、EBX に 3 を、ECX には呼び出し元から
> > > 渡されたディスクリプタをセットして、割り込み 0x80 を発生させる
> > というコードが書かれてある。

枝葉末節ですが、ソース読んで上記記述のミスに気づきました。ECX に渡される
のは、ディスクリプタの値ではなく、引数3つを記録しているスタック上の領域
へのポインタです。つまり、connect() は
int connect(s, saddr, len);
と定義されていますが、呼び出されたときに、ESP が示すアドレスには、
int32_t stack[4] = {戻り先アドレス, s, saddr, len};
という形でデータが格納されています。
で、ECX には、この表記でいうと &stack[1] に相当するものがセットされます。

MS-DOS での open() や execvp() の実装を知っている人間からすると、ちょっと
汚いコードに感じますね。まあ Linux の場合は MS-DOS と違って libc とカーネ
ルは実質的に一体のものだからいいんですけど。


> ENTRY (__socket)
>
> /* Save registers. */
> movl %ebx, %edx
>
> movl $SYS_ify(socketcall), %eax /* System call number in %eax. */
>
> /* Use ## so `socket' is a separate token that might be #define'd. */
> movl $P(SOCKOP_,socket), %ebx /* Subcode is first arg to syscall. */
> lea 4(%esp), %ecx /* Address of args is 2nd arg. */

========================================================================

0 new messages