我想複製一個在 Linux 上不相關進程中運行的檔案描述符。我知道關於發送訊息(2)和SCM_RIGHTS
(例如https://stackoverflow.com/questions/4489433/sending-file-descriptor-over-unix-domain-socket-and-select) ,但這僅在其他進程合作時才有效。我需要一個不需要其他進程積極合作的解決方案。我還知道我可以先建立檔案描述符,保留副本,然後再建立另一個進程,但我需要一個解決方案,讓另一個進程建立自己的檔案描述符。
我可以看到文件描述符:
$ ls -l /proc/13115/fd/3
lrwx------ 1 pts pts 64 2013-05-04 13:15 /proc/13115/fd/3 -> socket:[19445454]
但是,open("/proc/13115/fd/3", O_RDWR)
在另一個進程中執行會回傳錯誤沒有這樣的設備或地址。還有其他有效的方法嗎?可能與追蹤?
答案1
這是設計使然:與其他進程共享檔案描述符是明確的。預設情況下,檔案描述子與進程本身的記憶體一樣私有。
像往常一樣,如果您有權利追蹤在此過程中,您可以做任何您喜歡的事情,包括呼叫sendmsg
.傳統上,呼叫ptrace
需要以相同的用戶ID運行;安全限制,例如 SELinux、功能、監獄等可以使ptrace
限制更加嚴格。例如,在預設的 Ubuntu 配置下,非 root 程序只能呼叫ptrace
自己的後代(透過 AppArmor)。
穩健地使用ptrace
有點棘手:您必須注入正確的數據,確保不要覆蓋任何內容,並自行清理。因此,我的建議是以迂迴方式註入程式碼,並使用現有工具觸發該程式碼。
編寫一個包含程式碼的小型共享庫sendmsg
,並將LD_PRELOAD
其提供給其他進程。這是一些未經測試的缺少錯誤檢查的骨架程式碼。
int pts_gift_fd (char *path, int fd) {
int sock;
struct sockaddr_un addr = {0};
struct msghdr msg = {0};
struct iovec iov = {0};
addr.sun_family = AF_UNIX;
strlcpy(addr.sun_path, path, sizeof(addr.sun_path));
/* Populate msg, iov as in the code you've already found */
sock = socket(AF_UNIX, SOCK_STREAM, 0);
connect(sock, (struct sockaddr*)&addr, sizeof(addr));
sendmsg(sock, &msg, 0);
close(sock);
}
然後,要觸發程式碼,請執行gdb -n -pid 13115 -batch -x /dev/stdin
並向popen
其提供以下輸入(其中%d
是您想要取得的 fd,並且%s
是您之前建立並正在偵聽的 unix 套接字的路徑):
call pts_gift_fd("%s", %d)
detach
quit
答案2
您不能這樣做的原因與您無法存取不相關進程的記憶體的原因相同,因為檔案描述符是另一個進程的記憶體的一部分。存在有關此類內容的資訊的唯一原因/proc
是因為核心在那裡提供了它,並且它是只讀的(因此有方法可以檢查影印本進程記憶體)。
如果它與文件相關,您當然可以嘗試存取該文件。如果它是套接字,你可以用它來窺探它libpcap或從中派生出的東西。
情況基本上是這樣的:檔案描述符(再次)是進程記憶體的一部分。描述符有一個存在於內核空間中的底層緩衝區;當進程從描述符讀取或寫入時,它正在向該緩衝區寫入或從該緩衝區寫入。對於資料輸出,核心適當地將緩衝區刷新(到硬體);對於傳入的數據,當進程清空緩衝區時,它會(從硬體)重新填充緩衝區。 AFAIK,這些緩衝區不能被其他進程訪問,儘管有一些方法(例如 libpcap)閱讀由特定核心介面確定的某種形式的數據,就像 proc 介面可以從進程的用戶空間記憶體提供一些數據一樣。