什麼程序創建了這個 X11 視窗?
給定一個 X11 視窗 ID,有沒有辦法找到創建它的程序的 ID?
當然,這並不總是可能的,例如,如果視窗通過 TCP 連接。對於這種情況,我想要與遠端端關聯的 IP 和埠。
之前在 Stack Overflow 上問過這個問題,建議的方法是使用該
_NET_WM_PID
屬性。但這是由應用程序設置的。如果應用程序不能正常執行,有沒有辦法做到這一點?
除非您的 X-server 支持X-Resource v1.2 擴展
XResQueryClientIds
,否則我知道沒有簡單的方法可以可靠地請求程序 ID。不過還有其他方法。如果您面前只有一扇窗戶,但還不知道它的 ID — 很容易找到它。只需在相關視窗旁邊打開一個終端,在
xwininfo
那裡執行並點擊該視窗。xwininfo
將向您顯示視窗 ID。所以讓我們假設你知道一個window-id,例如0x1600045,並且想要找到擁有它的程序是什麼。
檢查該視窗屬於誰的最簡單方法是為其執行 XKillClient,即:
xkill -id 0x1600045
看看哪個程序剛剛死掉。但前提是你當然不介意殺死它!
另一種簡單但不可靠的方法是檢查其
_NET_WM_PID
和WM_CLIENT_MACHINE
屬性:xprop -id 0x1600045
這就是工具喜歡
xlsclients
和xrestop
做的事情。不幸的是,此資訊可能不正確,不僅因為該過程是邪惡的並改變了那些,還因為它是錯誤的。例如,在一些 Firefox 崩潰/重新啟動之後,我看到了
_NET_WM_PID
指向一個程序的孤立視窗(我猜是來自 flash 外掛),該程序很久以前就死了。另一種方法是執行
xwininfo -root -tree
並檢查相關視窗的父級屬性。這也可能會給你一些關於視窗起源的提示。
但!雖然您可能找不到創建該視窗的程序,但仍有一種方法可以找到該程序從何處連接到 X-server。這種方式適用於真正的黑客。:)
低位歸零(即 0x1600000)的視窗 id 0x1600045 是“客戶群”。並且為該客戶端分配的所有資源 ID 都是“基於”它的(0x1600001、0x1600002、0x1600003 等)。X-server 將有關其客戶端的資訊儲存在客戶端中
$$ $$數組,並且對於每個客戶端,它的“基礎”儲存在客戶端中$$ i $$->clientAsMask 變數。要找到對應於該客戶端的 X-socket,您需要使用 連接到 X-server
gdb
,遍歷客戶端$$ $$數組,找到客戶端clientAsMask
並列印其套接字描述符,儲存在 ((OsCommPtr)(clients$$ i $$->osPrivate))->fd. 可能連接了很多 X 客戶端,所以為了不手動檢查它們,讓我們使用 gdb 函式:define findclient set $ii = 0 while ($ii < currentMaxClients) if (clients[$ii] != 0 && clients[$ii]->clientAsMask == $arg0 && clients[$ii]->osPrivate != 0) print ((OsCommPtr)(clients[$ii]->osPrivate))->fd end set $ii = $ii + 1 end end
找到socket後,就可以查看,連接了誰,最後找到程序。
警告:不要從 X-server 內部將 gdb 附加到 X-server。gdb 掛起它附加的程序,因此如果您從 X-session 內部附加到它,您將凍結您的 X-server 並且將無法與 gdb 互動。您必須切換到文本終端 (
Ctrl+Alt+F2
) 或通過 ssh 連接到您的機器。例子:
- 查找 X-server 的 PID:
$ ps ax | grep X 1237 tty1 Ssl+ 11:36 /usr/bin/X :0 vt1 -nr -nolisten tcp -auth /var/run/kdm/A:0-h6syCa
- 視窗 id 是 0x1600045,所以客戶群是 0x1600000。附加到 X-server 並找到該客戶端的客戶端套接字描述符。您需要為 X-server 安裝調試資訊(用於 rpm-distributions 的 -debuginfo 包或用於 deb 的 -dbg 包)。
$ sudo gdb (gdb) define findclient Type commands for definition of "findclient". End with a line saying just "end". > set $ii = 0 > while ($ii < currentMaxClients) > if (clients[$ii] != 0 && clients[$ii]->clientAsMask == $arg0 && clients[$ii]->osPrivate != 0) > print ((OsCommPtr)(clients[$ii]->osPrivate))->fd > end > set $ii = $ii + 1 > end > end (gdb) attach 1237 (gdb) findclient 0x1600000 $1 = 31 (gdb) detach (gdb) quit
- 現在您知道客戶端連接到伺服器套接字 31。使用
lsof
查找該套接字是什麼:$ sudo lsof -n | grep 1237 | grep 31 X 1237 root 31u unix 0xffff810008339340 8512422 socket
(這裡“X”是程序名,“1237”是它的pid,“root”是它執行的使用者,“31u”是一個套接字描述符)
在那裡您可能會看到客戶端通過 TCP 連接,然後您可以轉到它所連接的機器並在
netstat -nap
那裡檢查以找到該程序。但很可能你會在那裡看到一個 unix 套接字,如上所示,這意味著它是一個本地客戶端。 4. 要為該 unix 套接字找到一對,您可以使用MvG 的技術 (您還需要安裝核心的調試資訊):$ sudo gdb -c /proc/kcore (gdb) print ((struct unix_sock*)0xffff810008339340)->peer $1 = (struct sock *) 0xffff810008339600 (gdb) quit
- 現在您知道了客戶端套接字,使用
lsof
它來查找持有它的 PID:$ sudo lsof -n | grep 0xffff810008339600 firefox 7725 username 146u unix 0xffff810008339600 8512421 socket
而已。保留該視窗的程序是“firefox”,程序 ID 為 7725
2017 年編輯:現在有更多選項,如誰擁有這個 unix socketpair 的另一端?. 對於 Linux 3.3 或更高版本以及
lsof
4.89 或更高版本,您可以將上面的第 3 點到第 5 點替換為:lsof +E -a -p 1237 -d 31
找出誰在 ID 為 1237 的 X-server 程序的 fd 31 上的套接字的另一端。