Linux'ta Keylogging, Bölüm 1: Çekirdek Tuş İşlemeden X Sunucusuna
Bir tuş vuruşunun çekirdek klavye sürücüsü, input altyapısı, evdev ve /dev/input/event düğümleri üzerinden X sunucusu girdi hattına ve XKB keymap'e nasıl aktığını ve logger'ların nereye bağlandığını inceliyoruz.
Linux'ta Keylogging, Bölüm 1: Çekirdek Tuş İşlemeden X Sunucusuna#
Linux'ta bir tuş vuruşu, terminalinizde karaktere dönüşmeden önce uzun bir yol kat eder. Yolculuk klavyedeki elektrik sinyaliyle başlar, scancode olur, çekirdek keycode'una dönüşür, bir aygıt düğümünde input event'e çevrilir, X event'i olur ve sonunda uygulamanızın metne çevirdiği keysym'e ulaşır.
Bu yolun her aşaması, birinin dinleyebileceği bir noktadır. Linux'ta keylogging'in temel fikri budur: sihir gerekmez, aşamalardan birine oturup geçeni okumak yeter.
Bu ilk bölüm yolun haritasını çıkarır. İkinci bölüm, bu harita üzerine gerçek logger'ları kuracak.
Kapsam ve etik#
Buradaki her şey kendi makineleriniz ve lab VM'leriniz içindir. Paylaşımlı bir sistemde başka bir kullanıcının tuş vuruşlarını yazılı izin olmadan okumak çoğu yerde suçtur ve her yerde işten kovulmanın en hızlı yoludur. Aynı bilgi, savunduğunuz sistemlerdeki logger'ları tespit etmek için de gereklidir; bu çerçevede tutun.
Yolun tamamına kuşbakışı#
tuşa basış | v [ klavye denetleyicisi ] IRQ1 + scancode (PS/2) veya USB HID usage | v [ çekirdek sürücüsü: atkbd / usbhid ] scancode -> keycode çevirisi | v [ input altyapısı çekirdeği ] struct input_event üretir | v [ evdev işleyicisi ] /dev/input/eventN düğümünü açar | +------------------> okuma izni olan herhangi bir süreç | (klasik userspace logger noktası) v [ X sunucusu girdi sürücüsü ] evdev veya libinput | v [ XKB keymap ] keycode + grup + seviye -> keysym | v [ X protokolü KeyPress event ] odaktaki pencereye iletilir | v [ uygulama araç seti ] GTK/Qt keysym'i metne çevirir
Bu diyagramı aklınızda tutun. Bu serideki her savunma ve saldırı numarası, bu kutulardan birine asılır.
Aşama 1: çekirdekte tuş işleme#
Bir tuşa dokunduğunuzda klavye denetleyicisi bir kesme (interrupt) üretir. PS/2 klavyede bu IRQ1'dir ve atkbd sürücüsü tarafından işlenir. USB klavyede ise usbhid tarafından işlenen bir HID raporudur. İki sürücü de sonunda aynı işi yapar: donanıma özgü scancode'ları tek bir donanımdan bağımsız keycode alanına çevirir.
Çekirdek her girdi aygıtı için bir çeviri tablosu tutar. Canlı görebilirsiniz:
sudo showkey --scancodes # birkaç tuşa basın, ham değerleri not edin, çıkmak için Ctrl+C sudo showkey --keycodes # aynı tuşlar, sürücü çevirisinden sonra
Scancode "ikinci sıranın üçüncü tuşu indi" der. Keycode "KEY_Q dediğimiz tuş indi" der. Çekirdek artık klavyenin PS/2, USB veya Bluetooth olmasıyla ilgilenmez; bu katmanın üstündeki her şey keycode konuşur.
Sonradan okumak isterseniz ilgili kaynak dosyaları:
- PS/2 yolu için
drivers/input/keyboard/atkbd.c - USB klavyeler için
drivers/hid/usbhid/ - Keycode isimleri için
include/uapi/linux/input-event-codes.h
Aşama 2: input altyapısı ve evdev#
Input altyapısı, fare, klavye, touchpad ve insan girdisi bildiren diğer her şey için çekirdeğin orta katmanıdır. Olay arayüzünün adı evdev'dir ve /dev/input/ altında aygıt başına bir karakter aygıtı açar:
cat /proc/bus/input/devices
Kısaltılmış tipik çıktı:
I: Bus=0011 Vendor=0001 Product=0001 Version=ab41 N: Name="AT Translated Set 2 keyboard" P: Phys=isa0060/serio0/input0 S: Sysfs=/devices/platform/i8042/serio0/input/input3 U: Uniq= H: Handlers=sysrq kbd event2 leds B: EV=120013
Handlers= satırı, klavyenize ait /dev/input/eventN düğümünü söyler. Her olay sabit boyutlu bir struct'tır:
struct input_event { struct timeval time; __u16 type; /* EV_KEY, EV_REL, EV_ABS, ... */ __u16 code; /* KEY_A, KEY_B, ... */ __s32 value; /* 0 bırakma, 1 basma, 2 tekrar */ };
Üç noktaya dikkat:
- Klavye tuşları için
typeher zamanEV_KEY'dir. valuebasma, bırakma ve otomatik tekrarı ayırt eder. Sadece basmaları sayan bir logger,value == 0kayıtlarını yok sayarak gürültüsünü yarıya indirir.- Ekranda hiçbir pencere odakta olmasa bile olaylar gelir. Çekirdek hangi uygulamanın göründüğünü bilmez ve umursamaz.
Ayrıca bir olay tek başına karar bildirmez; çekirdek olabilecek bir olay fırlatır ve sonuna SYN_REPORT (type == EV_SYN, code == SYN_REPORT) koyar. Bir KEY_CTRL basması, ardından KEY_A basması, sonra SYN_REPORT gelirse hepsi tek bir "fiziksel an" sayılır. Gerçekçi bir logger, eşleme yapmadan önce SYN_REPORT sınırını bekler; aksi hâlde Shift ile A basan bir kullanıcıyı "Shift tek başına, sonra A tek başına" olarak görür ve modifier durumunu kaybeder.
Aşama 3: /dev/input/event* düğümünü kendiniz okuyun#
Ham olayları okumak, düğüm üzerinde okuma izni gerektirir. Çoğu dağıtımda bu root veya input grubu üyeliği demektir. C ile minimal bir okuyucu:
#include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <linux/input.h> int main(void) { int fd = open("/dev/input/event2", O_RDONLY); struct input_event ev; while (read(fd, &ev, sizeof(ev)) == sizeof(ev)) { if (ev.type == EV_KEY && ev.value == 1) printf("keycode %u basildi\n", ev.code); } close(fd); return 0; }
Derleyin, root olarak çalıştırın, yazın ve keycode akışını izleyin. Bu program zaten bir userspace keylogger'ın yüzde seksenidir. Eksik yüzde yirmi, bir keycode-karakter tablosu ve yazılacak bir dosyadır; ikisi de ikinci bölümde.
Bir ioctl daha önemlidir: EVIOCGRAB. Bir aygıtı grab eden süreç olayları münhasır alır; X sunucusu artık onları görmez. Meşru kullanımlar vardır (ekran kilitleri, sanal klavyeler), ama düşmanca kullanımlar da vardır; sizin başlatmadığınız bir grab bu yüzden kırmızı bayraktır.
Okuyucu mantığı: ham akış nasıl tüketilir#
Minimal okuyucu bilinçli olarak eksik bırakılmıştır; üretim seviyesinde bir evdev tüketicisi dört şeyi daha yapmak zorundadır.
Birincisi, aygıt kimliğini ve yeteneklerini sormaktır. EVIOCGBIT ile aygıtın hangi olay tiplerini gönderdiğini sorarsınız; klavyede EV_KEY, EV_MSC, EV_LED, EV_SYN görünmelidir, farede EV_REL. Yani tek bir /dev/input/event* listesinden gerçekten klavye olanları EVIOCGBIT çıktısında EV_KEY biti aramakla seçersiniz; bazı sistemlerde web kamerası tuşları, güç tuşları ve tümleşik touchpad'ler de event* altındadır.
İkincisi, modifier durumunu kendiniz sürdürmek zorundasınızdır. Çekirdek size "Shift basılı" diye bir bit göndermez; size KEY_LEFTSHIFT için basma, bırakma ve tekrar olayları gönderir. Logger, her modifier keycode için bir "şu an basılı mı" durum matrisi tutar ve her tuş basışında bütün matrisi yeniden değerlendirir. Bu olmadan "Ctrl+Shift+K" göremezsiniz; sadece "Ctrl basıldı", "Shift basıldı", "K basıldı" görürsünüz ve bu akış X11 tarafındaki ile aynı keysym çeviri mantığından yoksundur.
Üçüncüsü, value == 2 tekrarlarını ayırt etmektir. Otomatik tekrar, basılı tuşun periyodik olarak yeniden olay göndermesidir. Şifre denemesi yapan kullanıcının tek uzun basışı, kötü yazılmış bir logger'da "aaaaaaaaaaa" olarak görünür. Politika genellikle şöyledir: tuş vuruşu sayımında tekrarları dahil etmek "ham alışkanlık" raporu için, metni kopyalamak için ise yalnızca ilk basmayı işlemek için kullanılır.
Dördüncüsü, birden fazla aygıtı tek tek FD'lerle izleyip hepsini tek zaman çizgisinde birleştirmektir. Harici klavye, yerleşik klavye ve bazen sanal klavye aynı anda açıktır. poll/select ile bütün FD'leri dinleyip olayları struct timeval damgasına göre sıralamasını koruyan bir tüketici, "hangi aygıtta yazıldı" bilgisini de korur. Tespit açısından bu sıralama izni de var demektir; kayıtlar arasında karıştıkça hangi oturumu izlediğiniz kaybolur.
Aygıt düğümleri ayrıca stabil değildir. Aygıt ekleyip çıkardıkça event2, event3 olur. Üretim okuyucuları, /dev/input/by-id/ altındaki sembolik bağları kullanır:
ls -l /dev/input/by-id/
Bu bağlar, donanım yoluna göre isimlenir ve klavye fişten çekilip geri takılsa bile aynı sembolden izlenir. Bir logger by-id yolunu kullanıyorsa, klasör yapısı değiştiğinde de çalışmaya devam eder; bu yüzden tespit kuralları by-id yolunu açan süreçleri ayrıca aramalıdır.
Aşama 4: X sunucusu girdi hattı#
Aygıt düğümlerinin üstünde X sunucusu oturur. Aynı evdev düğümlerini bir girdi sürücüsü üzerinden okur:
evdevsürücüsü: eski olanı, çekirdek evdev'in ince bir sarmalayıcısı.libinputsürücüsü: çoğu dağıtımda güncel varsayılan, Wayland kompozitörleriyle paylaşılır.
Oturumunuzun hangisini kullandığını kontrol edin:
grep -i "input driver" /var/log/Xorg.0.log # veya modern sistemlerde: grep -ri "libinput\|evdev" ~/.local/share/xorg/Xorg.0.log | head
X sunucusu sürücüden keycode alır ve tarihsel nedenlerle üstüne 8 ekler (X protokolü 1-7 arası keycode'ları rezerve etmiştir). Yani çekirdek keycode 24 (KEY_Q), X keycode 32 olur. Bu +8 kayması, evtest çıktısıyla xev çıktısını ilk karşılaştıran herkesin kafasını karıştırır.
Bundan sonra hat şöyle ayrılır:
- X sunucusu bir
KeyPress/KeyReleaseX event'i üretir. - Hangi pencerenin girdi odağına sahip olduğuna karar verir.
- Olayı o pencereye sahip istemciye iletir.
- İstemci araç seti (GTK, Qt) üstte kendi input method katmanını çalıştırır.
XInput2 ham olayları: neden ikinci kanca noktası#
X protokolünde iki girdi olayı ailesi vardır. Klasik çekirdek, KeyPress/KeyRelease ve Keymap olaylarını her uygulamaya pencere bazlı yollar. XInput2 ise ek olarak "raw" denen iki olay sınıfı verir: XI_RawKeyPress ve XI_RawKeyRelease. Bu iki olay, sunucunun herhangi bir pencereye dağıtmasından önce yakalanır, yani dinleyen istemci hangi pencerenin odakta olduğunu seçmek zorunda değildir.
Ham olaylar üç özelliğiyle öne çıkar:
- Olay, X sunucusunda işlenme aşamasındadır; modifier ve keymap dönüşümü tam uygulanmamıştır. Bu, dinleyenin kendi dönüştürme mantığını çalıştırmasını zorunlu kılar.
- Ham olayı dinleyen istemci, aktivite gösteren her tuşu alır: bilinmeyen semboller, numlock gibi özel tuşlar, X'in odaklamadığı pencerelere yazılanlar dahil olmaz ama aynı X oturumunun bütün normal pencerelerine yazılanlar gelir.
- Dinleyici sadece X display'ine erişmek zorundadır; kök olmaya veya
/dev/inputaçmaya gerek yoktur. Bu,evtest/lsoftabanlı işletim sistemi tespitlerinin yanından geçer; size lazım olan işaret, istemci bağlantıları arasına sıkışmış bir XI2 dinleyicisidir.
Hızlı bakmak için:
xinput test-xi2 --root
Çıkışta XIDeviceEvent ile raw_event_code alanları gelir. Bu alan, evdev code alanı ile aynıdır (+8 kayması ham tarafta olmaz). Yani XInput2 ham olayı, evdev okuyucu ile aynı keycode'u verir, fakat ona ulaşmak için ayrıcalık gerekmez; yalnızca X oturumunu bilinmesi yeter.
Bu ayrım, savunma tarafında kritiktir: /dev/input/event* açıklarını tarayan bir denetim, XInput2 tabanlı bir dinleyiciyi bulamaz. O dinleyiciye yaklaşmak için X sunucusunun istemci tablosuna, xdpyinfo -queryExtensions çıktısında XInputExtension uzantısının aktif olduğuna ve aynı oturumda XI2 seçimlerine bakan araçlara ihtiyaç vardır. xinput list --long, xinput test-xi2 istemcilerini doğrudan listelemez ama root olanlar xrestop benzeri araçlarla hangi istemcinin hangi event mask'le kayıt olduğunu gorebilir.
XRecord ise aynı fikrin daha eski yüzüdür: sunucuda dolaşan her olayı çoğaltmayı vaat eder. Hatta XIRawEvents ve XRecord ikisi de aynı amacı görür ve birlikte kapalı olmayan tek duvar, kompozitörün gizlenmiş olmasıdır. Yani "X11 dinleyicisi tespit etmek kolay" iddiasına temkinli yaklaşın; bir evdev okuyucu auditd ile yakalanır, XI2 dinleyici ise genelde X oturumu bildiriminde saklı kalır.
Aşama 5: XKB ve keymap#
Ham X keycode ile ekranda gördüğünüz karakter arasında X Keyboard Extension, yani XKB oturur. XKB derlenmiş bir keymap tutar ve şunu yanıtlar: "bu keycode, mevcut shift seviyesi ve aktif grup verildiğinde hangi keysym'dir?"
Canlı izleyin:
xev -event keyboard
a tuşuna Shift'li ve Shift'siz basıp state ve keysym alanlarını karşılaştırın. Sonra:
setxkbmap -print setxkbmap -query
Keymap'te seviyeler (sade, Shift, AltGr, Shift+AltGr) ve gruplar (arasında geçiş yaptığınız düzenler, us/tr gibi) vardır. Keysym hâlâ karakter değildir; a, A, adiaeresis veya BackSpace gibi bir isimdir. Keysym'i metne istemci çevirir; ğ harfini çekirdek hiç tanımamasına rağmen Türkçe karakter yazabilmenizin nedeni budur.
Metin kurallardan keymap derlemek:
setxkbmap -layout tr -variant f xmodmap -pke | head -20 # keycode başına eski görünüm
LD_PRELOAD shim'i: üçüncü kanca noktası ve sınırları#
LD_PRELOAD, dinamik bağlayıcıya "bu kütüphaneyi diğerlerinden önce yükle" demenin yoludur. Shim, libX11'in veya araç setinin girdi fonksiyonlarını yeniden tanımlar; XNextEvent, XINextEvent, gdk_event gibi. Uygulama bu fonksiyonu çağırdığında shim'in kopyası ayağa kalkar, olayları okur ve sonraki gerçek fonksiyonu dlsym(RTLD_NEXT, ...) ile çağırır. Şema kabaca şöyledir:
// shim içinde typedef int (*XNextEvent_t)(Display*, XEvent*); int XNextEvent(Display* d, XEvent* ev) { static XNextEvent_t real; if (!real) real = (XNextEvent_t)dlsym(RTLD_NEXT, "XNextEvent"); int r = real(d, ev); if (ev->type == KeyPress) kaydet(ev); return r; }
Bu şekil, tek uygulamanın tuşlarını toplar; X oturumu veya /dev/input izni gerekmez. Fakat çalışması için üç şart vardır:
- Hedef süreç dinamik bağlı olmalıdır. Statik bağlanmış bir ikili, LD_PRELOAD'dan habersizdir.
- setuid/setgid ikililerde bağlayıcı LD_PRELOAD'i yok sayar; AT_SECURE bayrağı nedeniyle yüklenmez. Bu, parolalar için
su/sudohedefli bir shim'in neden sessiz çalışmadığını açıklar. - Hedef oturumda LD_PRELOAD'un kurulabilmesi gerekir: kullanıcının shell startup dosyasına, systemd unit'in
Environment=satırına veya wrapper betiğe yazma erişimi.
Ayrıca iki sessiz zayıflık daha vardır. Birincisi, araç setleri her zaman X protokol fonksiyonunu doğrudan çağırmaz; GTK, GdkDisplayX11 üzerinden XInput2'yi, Qt ise XCB/QPA katmanını kullanır. Sadece XNextEvent sarmalayıp XINextEvent sarılmamış bir shim, modern bir uygulamada yarım çalışır. İkincisi, bazı uygulamalar X sunucusundan gelen olayları aynı olay döngüsünde sıraya koyup ilerler; shim olayın içeriğini değişmeksizin çoğaltmak zorundadır, aksi hâlde davranışı bozulur ve kurban süreç kendini kapatır; kayıt akışı kesilir.
Tespit açısından LD_PRELOAD shim'i çoğu zaman en sessiz seçenektir; çünkü yeni bir dinleyici süreci yoktur, başka bir ikili içinden konuşur. İz sürmek için:
# prosesin çevresindeki shared library sırası cat /proc/<pid>/maps | grep -E "\.so" | head # LD_PRELOAD hangi değerdeydi? tr '\0' '\n' < /proc/<pid>/environ | grep -i preload # kimlik doğrulama ikilileri için güvenlik bayrakları grep -i preload /proc/<pid>/status | head
/proc/<pid>/environ çoğu zaman eski değer tutar; süreç başladıktan sonra LD_PRELOAD ortadan kaldırılmış olabilir. Daha kalıcı iz, /proc/<pid>/maps içindeki tanımadığınız .so yollarıdır ve shell startup dosyalarında, /etc/ld.so.preload içinde veya kullanıcı .bashrc'lerinde arama yapmaktır.
Logger'lar nereye bağlanır#
Diyagramdaki üç kutuya karşılık gelen üç klasik kanca noktası:
-
Aygıt düğümü okuyucusu.
/dev/input/eventNaç ve oku. Root veyainputgrubu gerekir. Makinedeki her tuş vuruşunu görür: diğer X oturumlarına, konsollara ve parola istemlerine yazılanlar dahil. XKB görmez, bu yüzden keycode'ları karaktere kendisi çevirmelidir. -
X protokolü dinleyicisi. XInput2 ham olaylarını seçer; root gerektirmez, yalnızca X display erişimi. Sadece o X sunucusundan geçeni görür, ama keycode tabanlı olmaları nedeniyle karakter çeviri işi size kalır; XRecord, aynı akışın daha eski yoludur.
-
Kütüphane araya girme.
XNextEventgibi X olay fonksiyonlarını veya araç seti girdi fonksiyonlarını saran bir shim'iLD_PRELOADile yükleme. Sadece tek bir uygulamanın tuşlarını görür, ama oturumunu miras alır ve kurban uygulamayı zehirli bir sarmalayıcıyla başlatırsa ek ayrıcalık istemez.
Her kancanın farklı bir maliyeti, farklı bir ayrıcalık gereksinimi ve farklı bir kör noktası vardır. İkinci bölüm üçünü de lab ortamında kurmayı anlatıyor.
Yetki sınırları: kim neyi okuyabilir#
Linux'ta girdi aygıtları, ayrıcalık modelinin tipik kıvrımlarını taşıyan bir yüzeydir. Düğüm izinleri, grup üyeliği, logind oturumları ve portal token'ları aynı makinede farklı kararlar üretir.
Birincisi, karaktero özel düğümlerin dosya izinleridir:
ls -l /dev/input/event* /dev/input/by-id/
Çoğu dağıtımda owner root, group input, mode 0660'dır. Yani input grubundaki bir kullanıcı, parolasını girmeden klavye dinleyebilir. Bu, tesadüfen grup üyeliği alan bir servisin veya elden bırakılmış bir kullanıcı hesabının sessiz logger zemini olabildiğini gösterir.
İkincisi, logind ACLs'dir. Bir oturum aktifken, o oturumun kullanıcısına /dev/input/event* ve /dev/uinput üzerinde ACL (u:user:ali:rw gibi) yazılır; odak kaybedince çekilir. Bu model, "oturum açan kullanıcı kendi klavyesini izler" öncülüne dayanır. Üretim sunucularında oturum açan bir hizmet hesabına geçici ACL verilmesi, o hizmet için sessiz logger zemini oluşturur.
Üçüncüsü, capability ve setuid kenarlarıdır. Bir ikiliye CAP_SYS_RAWIO veya CAP_INPUT gibi capability verilmişse, normalde root olmayan bir süreç de evdev düğümlerini açabilir. getcap -r /usr/bin /usr/local/bin 2>/dev/null ile bu ikilileri gezin. Bu durum, 2026'da yaygın değildir ama "neden bu kullanıcı okuyabiliyor" sorusunun cevabı genelde bir capability'dir.
Dördüncüsü, portal token'larıdır. Wayland oturumlarında RemoteDesktop/libei akışı, kullanıcının açık izniyle geçici bir girdi enjeksiyonu hakkı verir. Token, süreç yaşamı sona erdiğinde veya oturum bittiğinde geçersiz olur. Bu token'ları portal dosya tanımlayıcıları, EIS soket dosyaları ve $XDG_RUNTIME_DIR içinden izleyebilirsiniz:
ls -la $XDG_RUNTIME_DIR | grep -i -E "eis|remotedesktop|portal"
Savunanlar için pratik sonuç: ayrıcalık denetimini root/input grubu ile sınırlı tutmayın. capability'leri, logind ACL'lerini ve portal session varlıklarını da envantere ekleyin.
Wayland portal duvarı#
Wayland'ın tasarım amacı, istemcilerin birbirini gözetlemesini engellemektir. Girdi olayları, yalnızca odaktaki pencereye sahip istemciye gönderilir ve her istemci, diğer pencerelerin girdisini göremez. Bu, XRecord/XI2 ham olayları gibi "herkese izni olmayan bir dinleme kanalı" bırakmayı protokol seviyesinde mümkün kılmaz. Yani bir Wayland oturumunda X11'deki gibi resmi bir "global girdi dinleyicisi" API'si yoktur.
Bu duvarın pratik sonucu: bir Wayland keylogger'ı çalıştırmak için protokol dışına çıkmak gerekir. Çıkışlar şunlardır:
/dev/input/event*okumak: logind ACL veyainputgrubu gerektirir; root'suz tam dinleme için yoldur.EVIOCGRABile aygıtı kendisine almak: kompozitör göremez; tespit açısından güçlü sinyal, çünkü odaklı penceredeki tuşlar da artık uygulamaya düşmez.- sanal aygıt üzerinden enjeksiyon ile kurban süreci, klavyeyi kendi penceresine çekmeye zorlamak: dolaylı ve gürültülüdür.
RemoteDesktop portalı ise bu duvarın "izinli delik"idir. Kullanıcı onayıyla oturum açıldıktan sonra libei oturumu üzerinden girdi enjekte edilebilir veya alıntılanabilir. Meta'nın EIS (libei bağlayıcısı) soket dosyası, $XDG_RUNTIME_DIR altında görünür ve sadece izinli süreçlerce açılır. Bu, kötüye kullanımın yeni meşru yüzeyi olduğu anlamına gelir: bir süreç, oturum katılımcısı olmadan EIS soketine dokunamıyorsa bile, kullanıcı portal izin düğmesine bastığında token ekranından bir sanal girdi aygıtı doğabilir.
Ekran görüntüsü portalıyla karşılaştırma faydalıdır. PipeWire üzerinden ekran paylaşımı başlatıldığında, ekranda görünen içerik başka bir sürece taşınabilir; bu ekrana yazılan parolayı da görebilir, ama "tuş vuruşu akışını" protokol olarak taşımaz. Portal duvarının yan etkisi, ekran paylaşımının keylogger ile karıştırılmaması gerektiğidir: ekran paylaşımı görseldir, keylogger tuş akışıdır; tespit soruları ve artifact'leri farklıdır.
Savunma açısından Wayland'da tespitte işe yarayanlar:
# hangi süreçler input nodes'u tutuyor? sudo lsof /dev/input/event* 2>/dev/null # uinput yolu aktif mi? ls -l /dev/uinput # EIS/portal soketleri var mı? ls -la $XDG_RUNTIME_DIR | grep -i -E "eis|remotedesktop|portal" # logind oturumları için ACL'leri kontrol et loginctl list-sessions
Bir süreç, Wayland oturumunda /dev/input/event* açıyorsa, meşru masaüstü servisleri (libinput, PipeWire tabanlı ekran paylaşımı için portal yardımcısı) dışındakiler gerekçelendirilmelidir. Bu soru listesi, "kullanıcı tuşlarını kim kaydediyor olabilir" sorusunun Wayland cevabıdır.
Savunanlar için tespit notları#
Aynı harita nereye bakacağınızı da söyler:
# klavye aygıtlarını şu anda kim açık tutuyor? sudo lsof /dev/input/event* 2>/dev/null # input grubunda hangi süreçler var? getent group input # XRecord tarzı bir istemci bağlı mı? xdpyinfo -queryExtensions | grep -i record
Karşılığını veren ek alışkanlıklar:
- Hassas makinelerde
/dev/input/open syscall'larınaauditdwatch'ı koyun. inputgrubunun beklenmedik üyelerini önemsiz değil bulgu sayın.- Wayland'de kompozitör girdiye sahiptir ve istemci başına izolasyon X11 dinleyici noktasını tamamen kaldırır; XWayland bir kısmını canlı tutar, karışık oturumlar ikinci bir bakışı hak eder.
- Sizin etmediğiniz bir grab (bilinmeyen bir PID'den
EVIOCGRAB) güçlü bir sinyaldir. - LD_PRELOAD shim'leri için
/proc/*/mapsiçinde tanımadığınız.soarayın ve/etc/ld.so.preloaddosyasını sürpriz arama listesine ekleyin. /dev/uinputaçan süreçleri kendi başına bulgu sayın; EIS/libei dışındaki bir sanal aygıt, enjeksiyon yolu demektir.
Sanal aygıtlar üzerine bir not#
Aynı altyapı ters yönde de çalışır. uinput modülü bir userspace sürecinin sanal girdi aygıtı yaratmasına ve çekirdeğe olay enjekte etmesine izin verir; olaylar az önce okuduğunuz hattın aynısından akar:
sudo modprobe uinput ls /dev/uinput
Test araçları, uzak masaüstü sunucuları ve makro daemon'ları buna dayanır. Şu anlama da gelir: "sadece gerçek klavyeden gelen olaylara güvenirim" kontrolü, hattın size bedavaya verdiği bir şey değildir; X sunucusu seviyesinde uinput'ten gelen evdev olayları donanım olaylarıyla birebir aynı görünür. Tespit kuralları tasarlarken bunu aklınızda tutun; enjekte edilmiş girdinin, saatlerce tuşa basmadan logger hedefini simüle etmenin de yolu olduğunu unutmayın.
2026'da bu hat ne kadar değişti?#
Diyagramdaki kutu isimleri 2026'da da geçerli; değişen, bazı kutuların içinde ne olduğu ve kimlerin geçebildiği.
tuş basışı | v [ evdev / libinput ] libinput, X sunucusu ve Wayland kompozitörün | ortak girdi kütüphanesi; ham olayları okur, | donanım farkını giderir v [ kompozitör / X sunucusu ] Wayland'de kompozitör, X'te sunucu tek otorite | +--- klasik: /dev/input/eventN okuyucusu (root veya input grubu) | +--- X oturumunda: XRecord / XI2 raw dinleme hâlâ mümkün | +--- Wayland oturumunda: global dinleme protokolde yok; | dinleyen tek taraflı yol yine /dev/input'u okumak | +--- uzak masaüstü: RemoteDesktop portalı + libei, onaylı | kullanıcının sanal girdi oturumunu açar v [ uygulama ] wl_keyboard / wl_seat üzerinden olayları alır
Üç pratik nokta:
- libinput paylaşıldı. Eski
evdevX sürücüsünün yerini büyük dağıtımlarda libinput aldı. X'te ve Wayland'de aynı kütüphane çalışır, yani keycode-keysym dönüşümü iki tarafta da tutarlıdır; "X'te çalışıyor ama Wayland'de çalışmıyor" logger'larının çoğu ayrıcalık modelinden tökezler, ayrıştırmadan değil. - /dev/uinput yolu daraldı ama yok olmadı. systemd-uaccess kuralları fiziksel konsoldaki aktif kullanıcıya
uinputveinputdüğümlerini ACL ile verir; grup üyeliği kadar gevşek değildir. Yine de yetkili bir oturum, libei/EIS üzerindenuinput'e sanal aygıt yazabilir; olaylar hâlâ gerçek aygıt gibi görünür. - Portal yolu yeni bir "meşru dinleme" noktası yarattı. RemoteDesktop portalı bir kullanıcı oturumuna girdi enjeksiyonu hakkı verir; bu, kötü amaçlı kullanım açısından uinput ile aynı gücü taşır ama kullanıcının onayıyla ve kompozitörün yönlendirmesiyle açılır. Yani 2026'da meşru uzaktan kontrol ile kötü niyetli enjeksiyonu ayırmak, hakları hangi oturumun ve hangi portal token'ının tuttuğuna bakmayı gerektirir.
Savunma tarafında sonuç net: /dev/input/event* açan süreçlerin envanteri değişmedi; değişen, apartman girişinde libei/PipeWire gibi servislerin de bu düğümlerle oynuyor olması. PipeWire ekran ve mikrofon akışını temsil eder, tuş vuruşlarını taşımaz; ekran görüntüsüyle keylogger'ı karıştırmamak gerekir. Tespit kuralları yazarken yeni soruyu ekleyin: "bu süreci hangi portal oturumu ve hangi yetki çerçevesi çalıştırıyor?"
İzleme noktası seçimi: maliyet tablosu#
| Kanca noktası | Ayrıcalık | Ne görür | Kör noktası |
|---|---|---|---|
/dev/input/eventN | root veya input grubu veya logind ACL | Tüm oturumlar, parola istemleri dahil ham keycode | XKB uygulamaz, karaktere çevirme işi size kalır |
| X protokolü dinleme (XRecord/XI2) | X display erişimi | Yalnızca o X sunucusundan geçeni, ham keycode | Wayland oturumlarında çalışmaz; kompozitör arkasındaki pencereleri görmez |
LD_PRELOAD shim | Hedef oturumu miras alır | Tek uygulamanın metinlerini | Statik ikililerde, setuid'lerde ve farklı kullanıcıdaki oturumlarda çalışmaz |
| RemoteDesktop/libei üzerinden enjeksiyon | Portal onayı | Sentetik girdi; gerçek girdiden ayırt edilemez | Onaysız oturumlarda erişemez |
Bir tutar notu#
Bu haritadaki hiçbir kutuyu "güvenli" veya "güvensiz" diye etiketlemeyin; hepsi birlikte bir güven sınırı tanımlar. /dev/input/eventN okumak ayrıcalık gerektirir ama çoğu günlük kullanıcı gözlemi onun dışında kalır. X sunucusu girdiyi tek kullanıcıya yönlendirir ama dinleme uzantısını herkes deneyebilir. Kompozitör keylogger'ı protokol dışına iter ama oturum açan kullanıcı hâlâ kendi ev düğümlerini açabilir. Bu üçü 2026'da aynı masada oturan gerçekler; biri diğerini geçersiz kılmaz. Hangi oturumun anahtarlığa ve uinput'e eriştiğini bilmeyen bir makinede keylogger hâlâ tek bir satır kod uzağa kurulabilir.
Sınırı nerede çizmeli#
Ev sahibi ile saldırgan arasındaki ayırım, 2026'da daha da ince bir çizgidir: kimlik doğru mu, oturum kime ait, hangi yetkilerle başlatıldı. input grubu, uinput ACL'leri, portal token'ları ve X oturumunun varlığı hepsi bu tek soruya cevap verir. Tespit kuralı yazarken o soru listesini başlığa koyun, alttaki teknik katman çalışması için haritaya bakın.
Lab alıştırmaları#
/proc/bus/input/devicesiçinde klavyenizi bulun ve event düğümünü not edin.sudo evtest /dev/input/eventNçalıştırıp tuşlara basın; birkaç keycode'uinput-event-codes.hisimlerine eşleyin.- Yukarıdaki C okuyucusunu derleyin ve çıktısını
evtestile karşılaştırın. xevçalıştırıp aynı tuşlar için X keycode'unu yazın; +8 kaymasını doğrulayın.setxkbmap trile düzen değiştirin ve aynı keycode'un nasıl farklı keysym verdiğini izleyin.- Bir terminal, tarayıcı ve ekran kilidi başlatmadan önce ve sonra
lsof /dev/input/event*çıktısına bakın. xinput test-xi2 --rootçalıştırıp aynı tuşları dinleyin;evtestvexevçıktılarındaki keycode farkını +8 ve 0 kayması üzerinden eşleştirin.- Bir paylaşımlı makinede
getcap -r /usr/bin /usr/local/bin 2>/dev/nullile girdi hakkı olan ikilileri bulun; capabiliteleriniz yoksa bu izin modelini sınıfta tartışın. /dev/input/by-id/listesini kaydedin, harici klavyeyi fişten çıkarıp takın ve sembolik bağların nasıl değiştiğini izleyin.- Wayland oturumunda
xinput test-xi2 --root'un hata verip vermediğini kontrol edin; aynı makinede X11 oturumu açıp farkı yazın.
İkinci bölüm bunun üstüne ne kuruyor#
İkinci bölüm bu haritayı çalışan koda çevirir: tam keycode tablosu ve modifier takibiyle ham evdev okuyucusu, XInput2 ham olaylarını kullanan bir X11 dinleyicisi ve bir LD_PRELOAD shim'i; her biri tespit açısıyla birlikte. Bu bölümdeki hat mantıklı geldiyse, sonraki bölümdeki logger'lar basit tesisat gibi okunacak.
Ek: uinput ve libei - madalyonun enjeksiyon yüzü#
evdev varsa uinput da vardır: aynı çekirdek istemci arayüzü sanal girdi aygıtları yaratabilir. /dev/uinput, EVIOCGRAB benzeri bir grab kabul eder ve tuş vuruşlarını keyifle sentezler. libinput kullanan bir Wayland kompozitörü çalışan bir masaüstünde, uinput ile yaratılan aygıtlar libinput tarafından tıpkı fiziksel klavyeler gibi ele alınır; yani aynı yumruktan atılan tuş, odaktaki istemciye kompozitörün ek izni olmadan ulaşır. Bu, evdev keylogging'in gerçek karşılığıdır: iki problem aynı yetki sınırını paylaşır.
Wayland ikinci bir yol ekler. Remote desktop uygulamaları (girdi emülasyonu) portalların meşru kullanım alanıdır; portal ile kompozitör arasında koşan protokol libei'dir. Bir RemoteDesktop oturumu katılımcısı - örneğin bir uzak masaüstü yardımcısı - kompozitörden bir soket çifti alır ve sentez girdi olaylarını bu soket üzerinden gönderir. Oturum açık bir portal onayından geldiği için enjeksiyon kapsamı ve süresi sınırlıdır. Modern kompozitörler ayrıca oturum aktifken masaüstü kabuğunun ekranda gösterdiği bir gösterge (küçük bir işaret/rozet) sağlar.
Savunucular için iki somut tutamak:
uinputdüğümü erişimi: evdev gibi aynı grup (input/video) kurallarına tabi; ama DIY sistemlerde sıklıkla gereğinden geniş verilir. Olay anında denetim:ls -l /dev/uinputve FD tutanlar (/proc/*/fd,lsof).- Portal izin kayıtları: portal günlüğü (
journalctl --user -u xdg-desktop-portal*) hangi uygulamanın hangi yeteneği tuttuğunu kaydeder; masaüstü ayarlarında aktif izinleri listeleyen bir bölüm olmalıdır. Beklenmedik bir uygulamanın RemoteDesktop izni varsa, önce o oturum kapatılmalıdır.
Çekirdek girdi iç yapısı: evdev, uinput ve input sınıfı#
Kullanıcı tarafının gördüğü her şey üç çekirdek yapısına dayanır: struct input_dev (aygıtın kendisi), struct input_handler (olayların aygıttan çıkış yolu) ve struct input_handle (ikisinin bağlantısı). Üçünü de drivers/input/input.c içinde görebilirsiniz.
klavye donanımı | IRQ / USB kesintisi v atkbd / usbhid (drivers/hid/*, drivers/input/keyboard/*) | input_event() v input çekirdeği (input.c) -> struct input_dev, olay tipleri | -> her input_handler'a yönlendirir +----------------+--------------------+ | | | v v v input_kbd evdev işleyicisi uinput (tty sürücü) (evdev.c) (uinput.c) | | ^ v v | /dev/tty /dev/input/eventN kullanıcı alanından write()
Bu diyagram, çekirdek katmanının tam keylogging yüzeyidir. evdev.c ve uinput.c, aygıt düğümünün bir katman altında durur ve kullanıcı alanının geri kalanını anlamanın iki anahtarıdır.
evdev.c, /dev/input/eventN düğümlerini üretir. Her açık dosya tanımlayıcısına ait kendi olay kuyruğu, kendi tuş durum bit haritası ve kendi grab durumu vardır. Bu yüzden bir dinleyici ve libinput aynı aygıtı aynı anda okurken birbirini bozmaz: çekirdek her olay için dosya başına kopya tutar. Kuyruk taşması (örneğin otomatik tekrar sırasında EV_KEY selinde) sessiz kayıp olarak değil, SYN_DROPPED ve ardından tam senkron olarak görünür.
/* evdev okuyucu, sınırlı örnek */ struct input_event ev[64]; for (;;) { int n = read(fd, ev, sizeof(ev)); if (n <= 0) break; n /= sizeof(struct input_event); for (int i = 0; i < n; i++) { if (ev[i].type == EV_KEY) handle_key(ev[i].code, ev[i].value); if (ev[i].type == EV_SYN && ev[i].code == SYN_REPORT) flush(); } }
uinput.c aynanın diğer yüzüdür: bir süreç UI_SET_EVBIT, UI_SET_KEYBIT ve UI_DEV_CREATE ioctl'leriyle sanal bir aygıt kaydeder, sonra olay enjekte etmek için struct input_event yazar. Sanal klavye input çekirdeğine gerçek sürücülerin geldiği fonksiyondan geldiği için libinput, Xorg ve her evdev okuyucusunun gözünde donanım gibidir. /proc/bus/input/devices içindeki bus alanında BUS_VIRTUAL görünür ama olay akışı içinde "sentetik" işareti yoktur. Düğümün kimliğini kontrol etmeden enjekte edilen girdi ile donanım girdisi ayırt edilemez.
Güvenlik matrisi: aygıt katmanı#
| Yüzey | Kim okuyabilir/yazabilir | Ne görür | Tespit tutamağı |
|---|---|---|---|
/dev/input/event* | root, input, koltuk ACL | ham keycode, tüm oturumlar | lsof, auditd open kuralı |
/dev/uinput | root, input, koltuk ACL | izin verilen sentetik aygıtlar | düğüm listesi, D-Bus seat izinleri |
input_kbd (tty) | root, tty katmanı | konsol tuş vuruşları | tty open üzerinde auditd, showkey |
| dosya başına evdev kuyruğu | fd'yi tutan kişi | akışın kopyası | süreç /proc/<pid>/fd dağılımı |
| evdev üzerinden sysrq | root | SyRq kombinasyonları | /proc/sys/kernel/sysrq maskesi |
libinput: tek girdi kütüphanesi, birden çok kompozitör#
libinput öncesi dönemde Xorg ve Wayland, ham evdev olaylarını ayrı ayrı ayrıştırıyordu. libinput dağınık işleri tek kütüphaneye verdi: ucuz touchpad'lerde quirks, tap-to-click, iki parmak kaydırma, klavye düzenleri, aygıt hotplug ve hangi olayların ortak bir "seat"e ait olduğuna karar.
/dev/input/event* | v +------------------------------+ | libinput (logind/libseat) | | - udev ile aygıt tarama | | - olay filtreleme | | - pointer/gesture mantığı | +------------------------------+ | libinput_event (keyboard_key, pointer_motion, device_added...) v +------------------+ +--------------------------+ | Xorg (xserver) | | Wayland kompozitörü | | libinput driver | | wlroots/wayfire vb. | +------------------+ +--------------------------+
libinput'un kendisini inceleyen komutlar:
libinput list-devices: libinput'un o an gördüğü aygıtlar, yetenek ve quirks uygulama durumu ile.sudo libinput debug-events: libinput'un kompozitöre verdiği her olay, zaman damgalı. Evdev'den loglayan bir logger'ın gördüğü akışla karşılaştırma için en iyi aynadır.libinput record session.yml: evdev trafiğini kaydediplibinput replayile başka makinede yürütmek. Labda modifier kenar durumlarını çoğaltırken işe yarar.libinput quirks list /dev/input/eventN: aygıta hangi udev hwdb quirk girdilerinin eşleştiği.
Seat, libinput'un ana kavramıdır. Her fiziksel aygıt bir seat'e atanır, çoğu zaman seat0. Seat, aygıtları ve odak modelini birlikte sahiplenir. Wayland'ın wl_seat globali bunu doğrudan yansıtır: bir seat, bir klavye odağı, bir pointer odağı, bir touch odağı. X11'de seat kavramı yoktu; bu yüzden izin modeli /dev/input grup üyeliği ve logind ACL'lerine karıştı.
Tespit için çıkarım: libinput tabanlı yığında gerçekçi logger üç yerden birinde durur. Evdev şeklinde olanı (libinput ile aynı fd'ye ihtiyaç duyar, grab semantiği üzerinde kavga eder), bir XWayland sunucusundaki X11 protokol casusu, ya da bir araç setinin içine gömülü LD_PRELOAD. Arka planda cron'la libinput debug-events koşturan bir süreç de gözlem açısından logger'a benzer artıralaklar üretir: her girdi düğümünde fazladan açık fd ve kayan bir keycode akışı. Ekip bilmiyorsa çalışan libinput araçlarını şüpheli sayın.
udev: izinler, kurallar ve uaccess etiketi#
udev, /dev/input modlarını politikaya çeviren katmandır. Bir kural satırı dört parçadır: eşleşme (SUBSYSTEM=="input"), atama (GROUP="input" veya MODE="0660"), etiket (TAG+="uaccess") ve eylem kancası. İlgili yollar:
/usr/lib/udev/rules.d/60-input-id.rules (aygıt kimliği, ID_INPUT*) /usr/lib/udev/rules.d/73-seat-classes.rules /etc/udev/rules.d/*.rules (yerel geçersiz kılmalar)
udev'ün klavyenize ne yapacağını görmenin pratik yolu:
udevadm info --path=/dev/input/event3 --query=all | grep -E "GROUP|MODE|TAGS|UDEV_" udevadm trigger --dry-run --verbose /dev/input/event3
TAG+="uaccess", grup tabanlı erişimi koltuk tabanlı erişime çevirir. Aygıt uaccess etiketi taşıyorsa logind, o koltuktaki aktif yerel oturum kullanıcısına ACL yazar; oturum ölünce kaldırır. logind'siz seatd kullanan bir sistemde eşdeğer kanca, GROUP="seat" veren udev kuralları ve fd'yi kompozitöre teslim eden seatd yardımcısıdır.
Bir güvenlik incelemesinde tutulacak udev matrisi:
| Kural seçimi | Klavyeyi kim okur | Risk |
|---|---|---|
MODE="0660", GROUP="input" | tüm input üyeleri | unutulmuş grup üyeleri dinleme zemini olur |
sadece TAG+="uaccess" | o koltuktaki aktif oturum | ele geçirilen oturum tüm koltuğu devralır |
| ikisi birden | üyeler + oturum | en geniş yüzey, en yaygın DIY varsayılanı |
MODE="0600", etiket yok | root + (TakeDevice ile) bir fd | en sıkı; çok kullanıcılı dev makinelerde kırıcılık |
Pratik kural: getent group input artık tek hikaye değil. Her düğüm için udevadm info çıktısını denetleyin, grup suçlamadan önce loginctl seat-status seat0 ACL'lerine bakın.
udev hwdb ve libinput quirks#
Aynı udev veritabanı, libinput'un quirks girdisini besler. Touchpad'ler ve laptoplar, dokun-tıkla sorunları, hayalet tıklama ve hotplug için hwdb geçersiz kılmaları gerektirir. Güvenlik açısından iki not.
- Quirks olay içeriğini değiştirir. Bir saldırgan enjekte tuşları normal yazma içine saklamak istese, libinput'un filtreleme quirks uyguladığı bir aygıt yolu normal değildir. Gerçekçi saklama yeri değildir ama sentetik bir aygıtı doğrularken
libinput quirks listneden gerektiğini açıklar. - Bir saldırgan udev kuralını değiştirerek düğüme dokunmadan erişimi değiştirebilir. Kurallar üç yerde durur (
/etc/udev/rules.d,/run/udev/rules.d,/usr/lib/udev/rules.d) veudevinfoyalnızca birleşmiş sonucu gösterir./etc/udev/rules.düzerinde klasör izleme makul bir tespit primitifidir.
Wayfire, wlroots ve kompozitör seat modeli#
Wayfire (wlroots tabanlı) gibi kompozitörler /dev/input'u kendileri okumaz. libinput olaylarını alıp seat'e koyar ve odaklı wl_surface'e dağıtır. Hattın görünür kısmı:
evdev -> libinput -> wlroots/seat -> wayfire shell | v wl_keyboard.enter / key / leave | v klavye odağındaki istemci
Wayfire yapılandırması ~/.config/wayfire.ini altındadır. Input bölümü klavye düzeni, tekrar hızı ve odak davranışını tanımlar:
[input] layout = us,tr repeat_rate = 30 click_method = clickfinger
Bu keylogger çerçevesinde önemli olan, istemci tarafı bir logger'ın ateşlemesi için neyin doğru olması gerektiğidir. Wayland'da sıradan bir X istemcisi XInput2 ham olaylarını açamaz. Başka istemcilerin tuşlarını da göremez. Yapabildiği, /dev/input/event*'i herhangi bir yerel kullanıcı gibi okumaktır; bu da önceki bölümle aynı yetki sorusudur.
Wayfire'in seat modeli karışık oturumlar için tek bir şey söyler: XWayland çalışıyorsa, o oturum içindeki X11 istemcileri X11 kurallarına tabidir ve kendi X sunucularında XInput2 ham erişimleri vardır. Yani XWayland açık bir Wayland oturumu, X11 deliklerinden birini açık tutar. Tespit pratiğine pgrep -a Xwayland ve XWayland üzerinden koşan uygulamaları ekleyin.
journald: savunma tarafının cam vitrinli kasası#
journald bir keylogger değildir ama kanıt taşır. Birkaç yer, yumuşak yeniden başlatmayı atlatan izler bırakır:
journalctl -b # şu anki önyükleme journalctl -b -1 # önceki önyükleme journalctl _COMM=evtest _COMM=libinput journalctl --user _COMM=xdg-desktop-portal
Storage=persistent ile journald /var/log/journal'a (kalıcı), aksi halde /run/log/journal'a (yalnız RAM) yazar. Yapılandırması /var/log/journal'ı silen ya da storage'ı none yapan bir logger iki imza bırakır: yeniden başlatmalar arasında kayıp geçmiş ve değiştirilmiş bir /etc/systemd/journald.conf. Daha güçlü seçenek systemd-journal-remote ile uzak bir journal'a, ya da append-only bir sunucuya akıtmaktır; yerel root logger, yerel logları her zaman düzenleyebilir.
Çevrimdışı analiz için journalctl --file /var/log/journal/*/*.journal, pencere kesimi için journalctl --since "..." --until "...". Canlı savunmada ikinci terminalde journalctl -f ve evtest birlikte hangi aygıt düğümlerinin açıldığını izlemek için yeterlidir.
LD_PRELOAD'a karşı LD_AUDIT ve loader sertleştirmesi#
LD_PRELOAD en gürültülü kancadır. İki daha az bilinen kuzen farklı iş yapar:
LD_AUDIT=./audit.so: loader geri çağrılarını alan bir denetim kütüphanesi yükler. Bir audit modülü, tek fonksiyonları geçersiz kılmadan sembol bağlama ve çağrı akışını gözlemleyebilir; bu onu hem daha sessiz bir enjeksiyon noktası hem de daha sessiz bir tespit kaynağı yapar. Tıpkı preload gibi/proc/<pid>/environiçinde arayın.LD_LIBRARY_PATH=: öncelik kayması. Saldırgan, arama yolunda öne gelen kötü amaçlı birlibX11.sobırakır ve her yeni süreç onu yükler. Yüzey olarak preload ile aynı etkiyi üretir ancak wrapper betiğinde saklandığı için environ taramalarında daha zor görünür.
Loader'ı bir kerede kapatan sertleştirme bayrakları:
readelf -d /usr/bin/target | grep -E "FLAGS|FLAGS_1" # sertleştirilmiş derlemede BIND_NOW, NODELETE, ORIGIN beklenir grep -E "NoNewPrivs|CapEff|Seccomp" /proc/<pid>/status
NoNewPrivs: 1 ve /dev/input/* açma filtreleri, hangi logger çeşidini bilmenize gerek kalmadan klasik okuyucuyu öldürür. Savunmayı sürüme karşı bağımsız kılmanın az sayıdaki yolundan biridir.
XTest ve XInput2: X11 gözlem-enjeksiyon ikilisi#
Gözlem tarafı XInput2 ile zaten oradaydı. İkizi XTest, enjeksiyon tarafını verir. XTestFakeKeyEvent, X sunucusuna gerçek bir klavyeden gelmiş gibi teslim edilen sanal tuşlar üretir. Otomasyon araçları bunu kullanır:
xdotool key ctrl+a ctrl+c # XTest üzerinden
xdotool lab makinesinde meşrudur. Tespit sorusu şudur: "bu olay hangi uzantıyla üretildi ve sunucu bunu hangi aygıta bağladı?" XI2 ham çıktısında XTest kaynaklı olaylar, gerçek bir klavyeyle eşleşmeyen bir device id gösterir; xinput list --short çıktısında XTEST aygıtları "Virtual core XTEST keyboard" olarak görünür. Karşılaştırma kuralı: her gerçek klavyede evtest karşılığı yok ama xev tuş gösteriyorsa, tuşlar XTest ile üretildi demektir, donanım seviyesi logger değil.
| Özellik | XI2 ham olayı | XTest enjeksiyonu |
|---|---|---|
| X sunucusu gerçek keycode görür | ilişkisiz | evet |
| kompozitör/çekirdek aygıt görür | hayır | sanal aygıt yok |
| evtest'te görünür | hayır | hayır |
xinput list içinde | dinleyici görünmez | Virtual core XTEST |
| savunmacıya faydası | kim dinliyor denetimi | sentez kanıtı |
systemd-logind: oturumlar, seat'ler ve neden önemi#
Logind, /dev/input/event* üzerindeki ACL'yi kimin alacağına karar veren katmandır. Üç ismi vardır: user, session, seat.
kullanıcı (uid) ---- seat0 ---- oturum (x11|wayland|tty) | | | +-- /dev/input, /dev/uinput üzerinde ACL +-- kompozitörün TakeDevice() D-Bus çağrıları
Komut envanteri:
loginctl list-sessions: kim hangi oturumda.loginctl show-session <id>:Type,Class,Service, durum.loginctl seat-status seat0: bu seat şu an hangi oturumlara sahip.loginctl session-status <id>: CC, PAM yapılandırması, ACL açısından ayrıntılar.
ACL yalnızca aktif ve aynı seat'teki oturuma verilir. Kullanıcının normal oturumunda koşan bir logger ACL'yi varsayılan olarak devralır. Ayrı bir tty oturumunda veya SSH üzerindeki logger devralmaz; bu yüzden SSH üzerinden doğurma deseninin logger'ı sessizce düşürdüğü sebebi budur.
seatd: systemd'siz sistemlerde aynı seat problemi#
seatd, seat sahipliğini isteyene veren küçük bir daemon'dır. Arayüzü tek bir sokettir (/run/seatd-sock veya wlroots'un libseat'i) ve ortam dosyaları kompozitöre SEATD_SOCK benzeri değişkenler üretir.
cat /run/seatd-sock ls -l /run/seatd* 2>/dev/null grep -r "seatd" ~/.config/wayfire.ini ~/.config/sway/config 2>/dev/null
Wayfire, sway ve cage, logind yerine seatd ile koşabilir; ACL hikayesi "aktif oturum kullanıcısına ACL" yerine "kompozitör seat'in tüm düğümlerine sahiptir" olur. Bu daha sessiz bir sınır: koltuktaki tüm /dev/input/event*'i açma hakkı yalnızca kompozitördedir. logind'siz seatd sisteminde, bir girdi düğümüne açılmamış fd, durdurulup incelenecek bir bulgudur.
PolicyKit ve portallar: devredilen erişimin yaşadığı yer#
polkit, bir servis ile ayrıcalıklı eylem arasına girer. Konunun kuralları genelde etrafındaki yardımcılar içindir:
ls /usr/share/polkit-1/actions | grep -E "login|freedesktop|portal" pkaction --action-id org.freedesktop.login1.manage-unit
Bir politika aracısı "X kullanıcısı S oturumunda A eylemini şimdi yapabilir mi" diye sorar. Konuyla ilgili olanlar org.freedesktop.login1.manage-unit tarzı kira izinleri ve portal aracısı eylemleridir. Dikkat edilecek yanlış yapılandırmalar: org.freedesktop.login1.inhibit-handle-power-key:yes geniş verilirse, ya da bir portal aracı kaydeden desktop girdisi zaman aşımı kullanmazsa.
Portallar bir katman yukarıda. Wayland'da istemciler xdg-desktop-portal'dan RemoteDesktop, ScreenCast veya InputCapture ister. Portal bir kez onay ister, ardından bir libei soketi verir. Bundan sonra:
istemci uygulaması -> org.freedesktop.portal.RemoteDesktop.CreateSession -> SelectDevices(keyboard|pointer|touchscreen) -> Start -> $XDG_RUNTIME_DIR içinde EIS soketi (libei) -> kompozitör sentez veya yakalanmış olayları alır
Çekirdek seviyesindeki fark: portal kaynaklı her olay ham /dev/input yazımı değil, bir libei EIS akışı olarak gelir. Savunucu, kendi D-Bus özellikleri üzerinden verilmiş portal oturumlarını denetleyebilir (org.freedesktop.portal.RemoteDesktop.AvailableDeviceTypes, /org/freedesktop/portal/desktop/session/... alt oturum yolları). Günlerdir açık kalan bir RemoteDesktop oturumu, Wayland'ın XTest daemon'ı karşılığıdır; kayıtta açık kullanıcı onayı vardır.
Güvenlik matrisi: hangi kanca hangi platformda#
| Platform | evdev okuyucu | XInput2 raw | XTest | uinput aygıtı | libei/RemoteDesktop |
|---|---|---|---|---|---|
| X11 (klasik) | evet, root | evet, oturum | evet | evet, root | evet, portal |
| Wayland içinde XWayland | evet, root | kendi X sunucu oturumu | evet | evet, root | evet, portal |
| Wayland oturumu | evet, root veya ACL | hayır | hayır | evet, root | evet, portal |
| seatd'li Wayland, systemd yok | evet, root veya kompozitör | hayır | hayır | evet | evet, portal |
| Yalnız terminal (görüntü yok) | evet, root | hayır | hayır | evet, root | hayır |
Katman katman okuma listesi#
drivers/input/input.c: input çekirdeği, olay yönlendirme, handler'lar.drivers/input/evdev.c: evdev açılışı, kuyruklar, grab.drivers/input/uinput.c: sanal aygıt yaratma ve olay enjeksiyonu.lib/libinput/src/evdev.c: libinput'un udev taraması ve quirks uygulaması.xserver/hw/xfree86/common/xf86Events.c: Xorg odak teslimi ve XTEST bağlantısı.xserver/hw/xwayland: Wayland üzerindeki X istemcileri için seat-klavye eşlemi.
Lab matrisi: tek lab, on kontrol#
| # | Kontrol | Komut/açıklama |
|---|---|---|
| 1 | klavye düğümleri | ls /dev/input/by-path/ yol açıklaması |
| 2 | aygıtı kim görüyor | sudo lsof /dev/input/event* fd listesi |
| 3 | libinput görünümü | libinput list-devices aygıt envanteri |
| 4 | ACL durumu | getfacl /dev/input/event* koltuk izinleri |
| 5 | logind oturumu | loginctl show-session self oturum türü |
| 6 | udev kural zinciri | udevadm info --path=... --query=all düğüm politikası |
| 7 | XTEST varlığı | xinput list --short sanal aygıt kontrolü |
| 8 | libei soketleri | `ls $XDG_RUNTIME_DIR |
| 9 | journal referansı | `journalctl -b |
| 10 | uinput envanteri | ls -l /dev/uinput düğüm izinleri |
Sınırlı örnek: seat'e duyarlı asgari okuyucu#
Aşağıdaki okuyucu, bu bölümdeki tek yeni koddur; tek olay kuyruğu, tek seat varsayımı ve yazma yolu olmayacak şekilde sınırlıdır. Yukarıdaki kaynak tartışmasını çerçevelemek için gösterilir, üzerine inşa edilecek temel değildir.
#include <libinput.h> int main(void) { struct libinput *li = libinput_path_create_context(NULL, NULL); while (struct libinput_event *ev = libinput_get_event(li)) { if (libinput_event_get_type(ev) == LIBINPUT_EVENT_KEYBOARD_KEY) { struct libinput_event_keyboard *kbd = libinput_event_get_keyboard_event(ev); printf("key %u %s\n", libinput_event_keyboard_get_key(kbd), libinput_event_keyboard_get_key_state(kbd) == LIBINPUT_KEY_STATE_PRESSED ? "down" : "up"); } libinput_event_destroy(ev); } libinput_unref(li); }
Parçaları tek bir tespit yürüyüşünde birleştirme#
Yürüyüşü sırayla çalıştırın. Yalnızca okuma amaçlı komutlar kullanır ve her adım, makineyi başkasıyla paylaşıyorsanız bir işaret bırakır.
1. ls -l /dev/input/* -> düğüm başına GROUP/MODE not edin 2. getfacl /dev/input/event* -> seat ACL'lerini not edin 3. loginctl show-session self -> seat, tür, aktif durum 4. sudo lsof /dev/input/* -> her açık fd, pid bazında 5. ls $XDG_RUNTIME_DIR -> eis soketleri, wayland soketleri 6. journalctl -b | grep -iE "input|libei|uinput" 7. xinput list --short -> sanal XTEST aygıtları 8. pgrep -a Xwayland -> XWayland köprüsü taze mi?
Yanıtlar birbiriyle çelişiyorsa, örneğin bir seat ACL'si artık var olmayan bir oturuma aitse, olağan şüpheli kalan bir sudo servisi veya yanlış kurulmuş bir udev kuralıdır. Bunların her biri canlı bir logger'dan daha sessiz bir klavye yoludur.
Sık sorular, kısa cevaplar#
evtest logger kurar mı? Hayır, libinput ile aynı fd'yi kullanır; fark şu ki evtest yazdırır ve çıkar, logger kalıcılık kurar. Tespit kuralı, evtest-benzeri API'leri zamanlı bir yapılandırma, dosya veya servis olarak aramalıdır.
Wayland istemcisi, xdg-desktop-portal üzerinden başka istemcinin tuşlarını görebilir mi? Yalnızca RemoteDesktop izni varsa. İzin uygulama bazlıdır ve kompozitör aktifken gösterge verir. "Gösterge yok, RemoteDesktop oturumu yok" anlamlı bir olumsuzdur.
uinput tehlikeli mi çünkü tuş üretebilir? Üretebilir, ama önce düğümü almak zorundadır; bu da evdev ile aynı GROUP/ACL denetimine çöker. /dev/input/*'i kilitlediyseniz, aynı kural bloğunu okuduğunuz sürece /dev/uinput'u da incelemişsinizdir.
XInput2 ham olayları Wayland'da doğrudan çalışır mı? Hayır. XI2 ham dinleyicileri yalnızca X sunucusu olaylarını ve yalnızca o sunucunun X istemcilerini alır. Wayland'da istemci izolasyonu protokol tarafından zorlanır; casusluk kanalı gizli değildir, yoktur.
Kısa sertleştirme, özet tablo#
| Önlem | Ne kapatır |
|---|---|
/dev/input/* ve /dev/uinput için ACL denetimi | sessiz evdev okuyucusu |
| udev kurallarında TAG+="uaccess" sınırı | geniş grup üyeliği |
| auditd açık fd watch'ı | kalıcı dinleyici evtest benzeri servis |
| XWayland'ı gerekirse kapatma | X11 ham olayları |
NoNewPrivs + seccomp profili | amaca özel logger'lar |
uzak journal (systemd-journal-remote) | yerel log manipülasyonu |
| RemoteDesktop izin takvimi | uzun ömürlü portal oturumları |
Lab Kontrolü: Evdev Olaylarını İzlemek#
Aşağıdaki blok hiçbir veri kaydetmez ve tuş basımını metin olarak yazmaz. Sadece çekirdeğin nasıl sinyal ürettiğini görmek için evdev olay tiplerini listeler. Orijinal işletimsel keylogger tarifini dışarıda bırakıyoruz.
sudo evtest /dev/input/event0
Örnek çıktı şöyle okunur:
Input device name: "AT Translated Set 2 keyboard"
Supported events:
Event type 0 (EV_SYN)
Event type 1 (EV_KEY)
Key events:
type 1 (EV_KEY), code 30 (KEY_A), value 1
type 1 (EV_KEY), code 30 (KEY_A), value 0
code alanı hangi tuşun basıldığını gösterir; value ise basma / bırakma / tekrar durumudur (1=basma, 0=bırakma, 2=tekrar). Bu davranış Debian, Fedora, Arch veya özel bir live ISO fark etmeksizin aynıdır.
Bayt seviyesine ham bakış için cat /dev/tty0 kullanma; doğru event düğümüne karşı evtest kullan.
Minimal ve Zararsız Evdev Okuyucu#
Bu örnek yalnızca ham evdev olaylarının userspace'e nasıl ulaştığını gösterir. Tuşları metne çevirmez, durum tutmaz, dosyaya yazmaz ve dışarıya aktarmaz.
/* evdump.c. Thread yok, sleep yok, write yok. */ #include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <linux/input.h> int main(int argc, char **argv) { if (argc < 2) { fprintf(stderr, "usage: %s <input-device>\n", argv[0]); return 2; } int fd = open(argv[1], O_RDONLY | O_NONBLOCK); if (fd < 0) { perror("open"); return 1; } struct input_event ev; int count = 0; while (count < 16 && read(fd, &ev, sizeof(ev)) == (ssize_t)sizeof(ev)) { if (ev.type == EV_KEY) { printf("KEY: code=%3u value=%d\n", ev.code, ev.value); } else if (ev.type == EV_SYN) { printf("SYN: code=%3u value=%d\n", ev.code, ev.value); } else { printf("EVT: type=%2u code=%3u value=%d\n", ev.type, ev.code, ev.value); } count++; } close(fd); return 0; }
Kullanım:
gcc -o evdump evdump.c ./evdump /dev/input/event3
Her EV_KEY olayı için code hangi fiziksel tuş olduğunu, value ise basma / bırakma / tekrar durumunu verir (1=basma, 0=bırakma, 2=tekrar). EV_SYN bir çerçevenin sınırını işaretler; bu yüzden tahminle okumak yerine tam çerçeve beklenir.
LD_PRELOAD ile Wayland: Bir PoC Analizi#
Neden Wayland'daki bir keylogger PoC'ı hâlâ bir iddia, tam bir çözüm değil?
Yazıda iddia edilen şey basit: hedefin başına LD_PRELOAD=libwayland-keylogger.so koyup Wayland soketi üzerinden doğal akışı okumak. Tek bakışta sanki tüm uygulamaların wl_keyboard olaylarını yakalıyormuşsun gibi görünür. Bu bir bilgi eksikliği aracıdır: 2026 bağlamında bu PoC, her desktop oturumunda çalışan genel bir çözüm değildir.
Aishou/wayland-keylogger deposu bu yüzden dikkat çekiyor: klasik LD_PRELOAD enjeksiyon kalıbını kullanıyor. Mekanik olarak yaptığı şu:
hedef süreç | v +---------------------+ | LD_PRELOAD=.so | | Wayland soketi | +---------+-----------+ | v wl_keyboard olayları, stderr'e basılır
Perde arkasında olanlar (kaynak koddan doğrulandı):
Keylogger.cpp:99-107içindekiMyHandleKeyboardKeytaşma yapmaz; yalnızcastatedeğerine bakıpstderr'ePressed key %d/Released key %dbasar. Olay sayacı dışında hiçbir şeyi eşlemez.Keylogger.cpp:129-148içindemy_wl_proxy_create(eski yol) vemy_wl_proxy_marshal_array_constructor(yeni yol),interface == &wl_keyboard_interfacekarşılaştırmasıyla klavye nesnesini yakalar veg_keyboard_to_logiçine koyar.Keylogger.cpp:150-162içindemy_wl_proxy_add_listener, hedefwl_keyboardise gerçek dinleyiciyi saklayıp yerinemy_keyboard_listeneryapısını koyar.Keymap,enter,leave,modifiers,repeat_infoyalnızca paslanır; kayıt yapılmaz.stderr'e düşen bu kayıt, yazarınREADME.md:21-25içinde söylediği gibi~/.profileveya~/.bashrciçineexport LD_PRELOAD=...soyazılarak kalıcı hale getirilebilir. Üretimde dosya yazma, gizlenme ve dışa aktarım yoktur; PoC yalnızca terminale basar.- Yazarın kendi notu dürüsttür: 75 dakikada, Wayland protokolünü bilmeden yazıldı; test edilen son sürüm Wayland/Weston 1.4.0'dır (
init_hooks,Keylogger.cpp:28-38). Program her başlatmada bunustderr'e yazar: iç yapı değişirse güncelleme gerekir.
0. Wayland'in kendi kodu ne diyor? (protocol/wayland.xml, src/wayland-client.c)#
PoC'u anlamak için Wayland deposunu klonlamak yeter (wayland-src/protocol/wayland.xml, wayland-src/src/wayland-client.c):
wl_seat(sürüm 11): bir klavye + fare + dokunmatik grubudur; klavye odağını ve fare odağını tutar.get_keyboardisteği bu seat'e ait birwl_keyboardnesnesi döndürür. İstemci yalnızca kendiwl_keyboardproxy'sini görür; komşu istemcinin nesnesi ona hiç gelmez.wl_keyboardmantıksal durumu dört parçadır: aktif yüzey (olmayabilir), mantıksal olarak basılı tuşlar, aktif değiştiriciler, aktif grup. Varsayılan: yüzey null, tuşlar boş, değiştirici ve grup 0.- Olaylar ve kuralları:
keymap:fd+format(no_keymapveyaxkb_v1) +size.xkb_v1ise istemci xkb keycode için olaya 8 ekler. Sürüm 7'den berifdalıcıdaMAP_PRIVATEile maplenmelidir.enter (serial, surface, keys): aktif yüzeyi ayarlar. Compositor, zaten aktif yüzeyi varsa bu olayı göndermemelidir.leave (serial, surface): tüm değerleri varsayılana sıfırlar. Compositor, aktif yüzey argümana eşit değilse göndermemelidir.key (serial, time, key, state):keyplatforma özgü koddur; anlamı içinkeymapgerekir. Compositor, hemen öncesinde aktif yüzey yoksa göndermemelidir; basılı tuşa tekrar basıldı, bırakılmamış tuşa bırakıldı diyemez. Sürüm 10'dan berirepeatedsözde-durumu vardır.modifiers (serial, depressed, latched, locked, group): yerleşim ve kilit durumunu günceller.repeat_info (rate, delay): sürüm 4'ten beri vardır;rate0 ise tekrar kapalıdır.release: sürüm 3'ten beri yıkıcı istek.
- İstemci tarafı (
wayland-client.c:66):struct wl_proxyiçindedisplay,queue,refcount,user_data,dispatcher,versionvardır.wl_proxy_add_listener (659)tek kural koyar: proxy'de dinleyici veya dispatcher varsa-1döner; wrapper iseaborteder. Yani PoC'un sahte dinleyicisi ikinci kez kurulamaz; dispatcher kullanan toolkit (ör. dil bağları) PoC'u anında bozar.
1. Güvensiz varsayım: çıplak Linux oturumu#
X11'de uinput üzerinden sahte tuş üretmek mümkündü; çünkü olay yetkileri çoğu zaman sıkı denetlenmiyordu. Wayland mimarisi ise compositor tabanlıdır: wayland.xml içindeki wl_keyboard.enter / leave sözleşmesi açıktır — compositor key olayını yalnızca hemen öncesinde aktif yüzeyi olan istemciye gönderir; odak dışı istemciye key gitmez, leave sonrası durum sıfırlanır.
Bir LD_PRELOAD kancası devreye girdiğinde yakalanan şey asla "tüm tuşlar" değildir. En fazla, enjekte edilen sürecin kendi wl_keyboard proxy'sinden geçen akıştır; README.md:15 içindeki "Full visibility into program-to-compositor communication" cümlesi bile yalnızca o programla compositor arasını anlatır. PoC bunu stderr'e basmakla kalır; standart bir dışa aktarım, oturum takibi veya kalıcı üretim hattı üretmez.
1b. Kaydettiği şey neden metin değil?#
MyHandleKeyboardKey yalnızca (serial, time, key, state) dörtlüsünden key ve state kullanır. key ham platform kodudur; xkb_v1 keymap'te anlama kavuşmak için 8 eklenmesi, modifiers ve group ile birleştirilmesi, keymap fd'sinin (MAP_PRIVATE, sürüm 7+) okunması gerekir. PoC bunların hiçbirini yapmaz: keymap, modifiers, enter içindeki keys dizisi ve repeat_info yalnızca asıl dinleyiciye paslanır. Yani ele geçen, hangi pencerede, hangi yerleşimle, hangi değiştiriciyle yazıldığı bilinmeyen bir sayı akışıdır. Metne çevirme işi saldırgana kalır ve odak bilgisi olmadan yanlış çevrilir.
2. LD_PRELOAD nereye kadar ulaşır?#
LD_PRELOAD yalnızca tek bir süreç için geçerlidir. Hedefi başlatırken bu değişkeni bilmiyorsan, yalnızca o sürecin içinde kalırsın. Aynı compositor altında çalışan komşu uygulamaları göremezsin.
Kalıcılık için akla gelen ilk numara bellidir: ~/.bashrc veya ~/.profile içine export LD_PRELOAD=... yazmak. Fakat karşında şunlar var:
- Bu, etkileşimsiz (non-interactive) oturumlarda çalışmaz.
- Flatpak / Snap uygulamaları kendi sandbox ortam listesiyle çalışır; dışarıdan verilen
envdeğişikliği genelde içeri geçmez. - PID ve mount ad alanına (namespace) bağlı uygulamalarda kontrol akışı yalıtılmıştır; kanca komşu ad alanına sızmaz.
Dolayısıyla PoC, bir sandbox içinde X11'deki kadar serbest dolaşamaz; etki alanı enjekte edildiği kabukla sınırlı kalır.
3. Daha yıkıcı değil, daha kırılgan#
~/.profile dosyasına yazılmış bir LD_PRELOAD satırı, oturum geneli bir dinleme sağlamaz. Kırılganlık envanteri kaynak koddan çıkar:
- Sürüm kayması: PoC Wayland/Weston 1.4.0 ile test edildi; güncel
wl_keyboardsürüm 11'dir (repeat_infov4,releasev3,repeateddurumu v10,wl_seat:namev2).wl_keyboard_interfacesembolü veya marshal imzası değişirseinterface ==kontrolü tutmaz. - Tek dinleyici kuralı:
wl_proxy_add_listenerzaten dinleyicisi olan proxy'de-1döner. Toolkit dispatcher kullanıyorsa veya dinleyiciyi erken kuruyorsa sahte dinleyici hiç kurulamaz. dlsym/dlvsymkancası (hook_table,Keylogger.cpp:181-201): uygulamagetenv("LD_PRELOAD")bakabilir,/proc/self/mapstarayabilir; PoC dagetenvve ilgili çağrıları gizlemeye çalışır. YazarınREADME.md:29içindeki dediği gibi bu kedi-fare oyunudur; güvenli sistem saldırıyı baştan engeller (SELinux, sandbox), sonradan tespit etmeye çalışmaz.- Statik bağ,
setuid/setgid(AT_SECURE), tembel başlatma ve çözümlenmiş yol farkı kancayı kör eder. KeyLoggerDataher klavye içinnewile ayrılıp hiç bırakılmaz; uzun süreli gizli kullanımda bellek izi bırakır.
Üretimde her seferinde "biraz veri, biraz sessizlik" üretmesi bu yüzdendir: oturumlar arası geçişte, odak değişiminde ve compositor yeniden başladığında kanca kopar. Anlık bir oturum dersi verir, sürekli bir ara katman kurmaz.
4. Portallar: 2026'daki yeni sınır#
Modern Linux masaüstünde genel kısayollar, ekran paylaşımı ve uzaktan girdi portaldan gelir. xdg-desktop-portal altındaki RemoteDesktop, ScreenCast ve InputCapture akışlarının her biri kullanıcı onayı ve oturum yaşam döngüsü (session lifecycle) ile çalışır. libei üzerinden okuma / yazma için portal izni şarttır.
Bu izin yoksa, uygulamanın kendisi girdi enjekte edemez veya başka oturumun komutunu çalıştıramaz; tutarlı bir izin denetimi vardır. PoC, bir portal üzerinden girdi enjekte edebiliyorsa bu, onay akışından geçtiği anlamına gelir; sessizce tarama yaptığı anlamına gelmez.
2026 kontrolü şöyle özetlenebilir:
| Dönem | 2026 kontrolü |
|---|---|
Çıplak wl_keyboard dinleme | Sandbox fark etmeden LD_PRELOAD vurabilir, ama yalnızca kendi sürecini görür |
| Portal + libei | Kullanıcı onayı + sandbox'lı enjeksiyon gerekir |
| Flatpak / netns | Doğrudan saldırı yüzeyi oturuma kadar daralır |
Bu yüzden rastgele bir Wayland uygulamasının ~/.profile satırıyla işbirliği yapmasını beklemek bugün pratikte işe yaramaz. Portal, polkit ve strace / euid denetimleri meşru yüzeyi korur.
5. Gerçek kullanımda tablo ne diyor?#
- Flatpak / Snap: sandbox ortamı dışarıdan
LD_PRELOADkabul etmediği için etkileşimli kısayol hilesi gerçek bir dinlemeye dönüşmez.README.md:11içindeki iki gereksinimden ikincisi ("shared library somewhere on the system") sandbox içinde sağlanamaz. - Wayland compositor: oturum kaydı (session recording) yoktur; odak dışı tuş akışı protokole hiç girmez.
enterolmadankeygelmez;leavesonrası durum sıfırlanır. - XWayland: yalnızca X sunucusu içindeki X istemcileri için
XInput2ham olayları görünür; saf Wayland istemcisine giden yol yine kapalıdır.
Lab'de nasıl doğrulanır, savunmada ne aranır?#
PoC'u lab'de koşmak (compile betiği: g++ -Wall -pipe -shared -fpic *.cpp *.c -lwayland-client -o libwayland-keylogger.so) ve tek hedefte denemek, iddiayla gerçeği ayırır: yalnızca enjekte edilen uygulamanın stderr çıktısında [wayland-keylogger] Pressed key %d satırları görülür; komşu uygulamada hiçbir şey olmaz.
Savunmada kalıcı izler:
# başlatma ortamında preload var mı? tr '\0' '\n' < /proc/<pid>/environ | grep -i preload # bilinmeyen .so eşlemesi var mı? cat /proc/<pid>/maps | grep -E "\.so" | grep -v -E "^.*(libc|libwayland|libxkb|libGL|ld-linux)" # PoC imzası stderr/journal içinde mi? journalctl --user -b | grep -i "wayland-keylogger"
README.md:31 içindeki ana fikir 2026'da da geçerlidir: gerçek güvenlik, sonradan tespitle değil, saldırıyı baştan engelleyen mekanizmayla gelir (sandbox, MAC/SELinux, portal onayı). Wayland'ın tek başına "sihirli" olduğu iddiası da, PoC'un "her şeyi gördüğü" iddiası da yanlıştır.
Sonuç#
Bu PoC bir yol göstericidir, tam teşekküllü bir keylogger değil. Wayland'ın kendi kodu (wayland.xml + wayland-client.c) tasarım felsefesini doğrular: hiçbir istemci diğerini göremez; compositor her oturumu enter / leave / key sözleşmesiyle izole eder. PoC'un gördüğü, yalnızca bulaştığı sürecin kendi klavye proxy'sidir ve kaydettiği, metne çevrilmemiş ham sayı akışıdır.
Savunma tarafında etik çizgi aynı kalır: yalnızca kendi makinen ve lab VM'lerin. Olayı araştırırken evtest ile hangi düğümün açık olduğuna, /dev/uinput üzerinden kimlerin sanal aygıt üretebildiğine, hangi kullanıcının input grubunda olduğuna ve hangi uygulamanın elinde aktif bir RemoteDesktop izni tuttuğuna bak. Başarılı bir PoC, gerçek bir problemi işaret eder; ama 2026 masaüstünde bu problem "her tuşu sessizce toplamak" değil, "onaylı bir oturumu kötüye kullanmak"tır.
Ne düşünüyorsun?
Tepki bırakarak geri bildirim ver