Catatan : Saya sekarang mengelola lsof
pembungkus yang menggabungkan kedua pendekatan yang dijelaskan di sini dan juga menambahkan informasi untuk rekan-rekan koneksi TCP loopback di https://github.com/stephane-chazelas/misc-scripts/blob/master/lsofc
Linux-3.3 dan yang lebih baru.
Di Linux, karena kernel versi 3.3 (dan asalkan UNIX_DIAG
fitur dibangun di dalam kernel), rekan dari soket domain unix yang diberikan (termasuk soket pasang) dapat diperoleh dengan menggunakan API berbasis netlink baru .
lsof
sejak versi 4.89 dapat menggunakan API itu:
lsof +E -aUc Xorg
Akan mencantumkan semua soket domain Unix yang memiliki proses yang namanya dimulai dengan Xorg
kedua ujungnya dalam format yang mirip dengan:
Xorg 2777 root 56u unix 0xffff8802419a7c00 0t0 34036 @/tmp/.X11-unix/X0 type=STREAM ->INO=33273 4120,xterm,3u
Jika versi Anda lsof
terlalu lama, ada beberapa opsi lagi.
The ss
utilitas (dari iproute2
) yang menggunakan yang API yang sama untuk mengambil dan menampilkan informasi dalam daftar unix domain sockets pada sistem termasuk informasi rekan.
Soket diidentifikasi oleh nomor inode mereka . Perhatikan bahwa ini tidak terkait dengan inode sistem file dari file socket.
Misalnya di:
$ ss -x
[...]
u_str ESTAB 0 0 @/tmp/.X11-unix/X0 3435997 * 3435996
dikatakan bahwa soket 3435997 (yang terikat ke soket ABSTRAK /tmp/.X11-unix/X0
) terhubung dengan soket 3435996. -p
Opsi ini dapat memberi tahu Anda proses mana yang membuat soket itu terbuka. Itu melakukan itu dengan melakukan beberapa readlink
s /proc/$pid/fd/*
, sehingga hanya bisa melakukan itu pada proses yang Anda miliki (kecuali Anda root
). Misalnya di sini:
$ sudo ss -xp
[...]
u_str ESTAB 0 0 @/tmp/.X11-unix/X0 3435997 * 3435996 users:(("Xorg",pid=3080,fd=83))
[...]
$ sudo ls -l /proc/3080/fd/23
lrwx------ 1 root root 64 Mar 12 16:34 /proc/3080/fd/83 -> socket:[3435997]
Untuk mengetahui proses apa yang telah 3435996, Anda dapat mencari entri sendiri di output dari ss -xp
:
$ ss -xp | awk '$6 == 3435996'
u_str ESTAB 0 0 * 3435996 * 3435997 users:(("xterm",pid=29215,fd=3))
Anda juga dapat menggunakan skrip ini sebagai pembungkus lsof
untuk dengan mudah menampilkan informasi yang relevan di sana:
#! /usr/bin/perl
# lsof wrapper to add peer information for unix domain socket.
# Needs Linux 3.3 or above and CONFIG_UNIX_DIAG enabled.
# retrieve peer and direction information from ss
my (%peer, %dir);
open SS, '-|', 'ss', '-nexa';
while (<SS>) {
if (/\s(\d+)\s+\*\s+(\d+) ([<-]-[->])$/) {
$peer{$1} = $2;
$dir{$1} = $3;
}
}
close SS;
# Now get info about processes tied to sockets using lsof
my (%fields, %proc);
open LSOF, '-|', 'lsof', '-nPUFpcfin';
while (<LSOF>) {
if (/(.)(.*)/) {
$fields{$1} = $2;
if ($1 eq 'n') {
$proc{$fields{i}}->{"$fields{c},$fields{p}" .
($fields{n} =~ m{^([@/].*?)( type=\w+)?$} ? ",$1" : "")} = "";
}
}
}
close LSOF;
# and finally process the lsof output
open LSOF, '-|', 'lsof', @ARGV;
while (<LSOF>) {
chomp;
if (/\sunix\s+\S+\s+\S+\s+(\d+)\s/) {
my $peer = $peer{$1};
if (defined($peer)) {
$_ .= $peer ?
" ${dir{$1}} $peer\[" . (join("|", keys%{$proc{$peer}})||"?") . "]" :
"[LISTENING]";
}
}
print "$_\n";
}
close LSOF or exit(1);
Sebagai contoh:
$ sudo that-lsof-wrapper -ad3 -p 29215
PERINTAH PID PENGGUNA UKURAN PERANGKAT FD TIPE / OFF NAMA NAMA
xterm 29215 stephane 3u unix 0xffff8800a07da4c0 0t0 3435996 type = STREAM <-> 3435997 [Xorg, 3080, @ / tmp / .X11-unix / X0]
Sebelum linux-3.3
Linux API lama untuk mengambil informasi soket unix adalah melalui /proc/net/unix
file teks. Ini mencantumkan semua soket domain Unix (termasuk soketnya). Bidang pertama di sana (jika tidak disembunyikan untuk non-pengguna super dengan kernel.kptr_restrict
parameter sysctl) seperti yang sudah dijelaskan oleh @Totor berisi alamat kernel dari suatu unix_sock
struktur yang berisi peer
bidang yang menunjuk ke rekan yang sesuai unix_sock
. Ini juga merupakan lsof
keluaran untuk DEVICE
kolom pada soket Unix.
Sekarang mendapatkan nilai peer
bidang itu berarti bisa membaca memori kernel dan mengetahui offset peer
bidang itu berkaitan dengan unix_sock
alamat.
Beberapa gdb
berbasis dan systemtap
berbasis solusi telah diberikan tetapi mereka memerlukan gdb
/ systemtap
dan Linux kernel simbol debug untuk kernel yang berjalan yang diinstal yang umumnya tidak terjadi pada sistem produksi.
Hardcoding of the offset sebenarnya bukan pilihan karena bervariasi dengan versi kernel.
Sekarang kita dapat menggunakan pendekatan heuristik untuk menentukan offset: minta alat kita membuat dummy socketpair
(lalu kita tahu alamat kedua rekan), dan cari alamat rekan di sekitar memori di ujung lainnya untuk menentukan offset.
Berikut ini adalah skrip proof-of-concept yang tidak hanya menggunakan perl
(berhasil diuji dengan kernel 2.4.27 dan 2.6.32 pada i386 dan 3.13 dan 3.16 pada amd64). Seperti di atas, ini berfungsi sebagai pembungkus lsof
:
Sebagai contoh:
$ that-lsof-wrapper -aUc nm-applet
PERINTAH PID PENGGUNA UKURAN PERANGKAT FD TIPE / OFF NAMA NAMA
nm-applet 4183 stephane 4u unix 0xffff8800a055eb40 0T0 36.888 type = STREAM -> 0xffff8800a055e7c0 [dbus-daemon, 4190, @ / tmp / dbus-AiBCXOnuP6]
nm-applet 4183 stephane 7U unix 0xffff8800a055e440 0T0 36.890 type = STREAM -> 0xffff8800a055e0c0 [Xorg, 3080, @ / tmp / .X11-unix / X0]
nm-applet 4183 stephane 8u unix 0xffff8800a05c1040 0T0 36201 type = STREAM -> 0xffff8800a05c13c0 [dbus-daemon, 4118, @ / tmp / dbus-yxxNr1NkYC]
nm-applet 4183 stephane 11u unix 0xffff8800a055d080 0t0 36219 type = STREAM -> 0xffff8800a055d400 [dbus-daemon, 4118, @ / tmp / dbus-yxxNr1NkYC]
nm-applet 4183 stephane 12u unix 0xffff88008228808e08e08bl0e0822880e0822880e0822880e08 type0 STREAM -> 0xffff8800a055d400 / run / dbus / system_bus_socket]
nm-applet 4183 stephane 13u unix 0xffff88022e0f80c0 0t0 37025 type = STREAM -> 0xffff88022e29ec00 [dbus-daemon, 2268, / var / run / dbus / system_bus_socket]
Ini skripnya:
#! /usr/bin/perl
# wrapper around lsof to add peer information for Unix
# domain sockets. needs lsof, and superuser privileges.
# Copyright Stephane Chazelas 2015, public domain.
# example: sudo this-lsof-wrapper -aUc Xorg
use Socket;
open K, "<", "/proc/kcore" or die "open kcore: $!";
read K, $h, 8192 # should be more than enough
or die "read kcore: $!";
# parse ELF header
my ($t,$o,$n) = unpack("x4Cx[C19L!]L!x[L!C8]S", $h);
$t = $t == 1 ? "L3x4Lx12" : "Lx4QQx8Qx16"; # program header ELF32 or ELF64
my @headers = unpack("x$o($t)$n",$h);
# read data from kcore at given address (obtaining file offset from ELF
# @headers)
sub readaddr {
my @h = @headers;
my ($addr, $length) = @_;
my $offset;
while (my ($t, $o, $v, $s) = splice @h, 0, 4) {
if ($addr >= $v && $addr < $v + $s) {
$offset = $o + $addr - $v;
if ($addr + $length - $v > $s) {
$length = $s - ($addr - $v);
}
last;
}
}
return undef unless defined($offset);
seek K, $offset, 0 or die "seek kcore: $!";
my $ret;
read K, $ret, $length or die "read($length) kcore \@$offset: $!";
return $ret;
}
# create a dummy socketpair to try find the offset in the
# kernel structure
socketpair(Rdr, Wtr, AF_UNIX, SOCK_STREAM, PF_UNSPEC)
or die "socketpair: $!";
$r = readlink("/proc/self/fd/" . fileno(Rdr)) or die "readlink Rdr: $!";
$r =~ /\[(\d+)/; $r = $1;
$w = readlink("/proc/self/fd/" . fileno(Wtr)) or die "readlink Wtr: $!";
$w =~ /\[(\d+)/; $w = $1;
# now $r and $w contain the socket inodes of both ends of the socketpair
die "Can't determine peer offset" unless $r && $w;
# get the inode->address mapping
open U, "<", "/proc/net/unix" or die "open unix: $!";
while (<U>) {
if (/^([0-9a-f]+):(?:\s+\S+){5}\s+(\d+)/) {
$addr{$2} = hex $1;
}
}
close U;
die "Can't determine peer offset" unless $addr{$r} && $addr{$w};
# read 2048 bytes starting at the address of Rdr and hope to find
# the address of Wtr referenced somewhere in there.
$around = readaddr $addr{$r}, 2048;
my $offset = 0;
my $ptr_size = length(pack("L!",0));
my $found;
for (unpack("L!*", $around)) {
if ($_ == $addr{$w}) {
$found = 1;
last;
}
$offset += $ptr_size;
}
die "Can't determine peer offset" unless $found;
my %peer;
# now retrieve peer for each socket
for my $inode (keys %addr) {
$peer{$addr{$inode}} = unpack("L!", readaddr($addr{$inode}+$offset,$ptr_size));
}
close K;
# Now get info about processes tied to sockets using lsof
my (%fields, %proc);
open LSOF, '-|', 'lsof', '-nPUFpcfdn';
while (<LSOF>) {
if (/(.)(.*)/) {
$fields{$1} = $2;
if ($1 eq 'n') {
$proc{hex($fields{d})}->{"$fields{c},$fields{p}" .
($fields{n} =~ m{^([@/].*?)( type=\w+)?$} ? ",$1" : "")} = "";
}
}
}
close LSOF;
# and finally process the lsof output
open LSOF, '-|', 'lsof', @ARGV;
while (<LSOF>) {
chomp;
for my $addr (/0x[0-9a-f]+/g) {
$addr = hex $addr;
my $peer = $peer{$addr};
if (defined($peer)) {
$_ .= $peer ?
sprintf(" -> 0x%x[", $peer) . join("|", keys%{$proc{$peer}}) . "]" :
"[LISTENING]";
last;
}
}
print "$_\n";
}
close LSOF or exit(1);