Proč nemůžete/nechcete použít trik LD_PRELOAD?
Příklad kódu zde:
/*
* File: soft_atimes.c
* Author: D.J. Capelis
*
* Compile:
* gcc -fPIC -c -o soft_atimes.o soft_atimes.c
* gcc -shared -o soft_atimes.so soft_atimes.o -ldl
*
* Use:
* LD_PRELOAD="./soft_atimes.so" command
*
* Copyright 2007 Regents of the University of California
*/
#define _GNU_SOURCE
#include <dlfcn.h>
#define _FCNTL_H
#include <sys/types.h>
#include <bits/fcntl.h>
#include <stddef.h>
extern int errorno;
int __thread (*_open)(const char * pathname, int flags, ...) = NULL;
int __thread (*_open64)(const char * pathname, int flags, ...) = NULL;
int open(const char * pathname, int flags, mode_t mode)
{
if (NULL == _open) {
_open = (int (*)(const char * pathname, int flags, ...)) dlsym(RTLD_NEXT, "open");
}
if(flags & O_CREAT)
return _open(pathname, flags | O_NOATIME, mode);
else
return _open(pathname, flags | O_NOATIME, 0);
}
int open64(const char * pathname, int flags, mode_t mode)
{
if (NULL == _open64) {
_open64 = (int (*)(const char * pathname, int flags, ...)) dlsym(RTLD_NEXT, "open64");
}
if(flags & O_CREAT)
return _open64(pathname, flags | O_NOATIME, mode);
else
return _open64(pathname, flags | O_NOATIME, 0);
}
Z toho, co jsem pochopil... je to do značné míry trik LD_PRELOAD nebo modul jádra. Není mnoho středních možností, pokud to nechcete spouštět pod emulátorem, který dokáže zachytit vaši funkci nebo provést přepsání kódu ve skutečném binárním souboru, aby se dostal do vaší funkce.
Za předpokladu, že nemůžete upravit program a nemůžete (nebo nechcete) upravit jádro, je nejlepší přístup LD_PRELOAD, za předpokladu, že vaše aplikace je poměrně standardní a ve skutečnosti se nesnaží zlomyslně překonat. vaše odposlech. (V takovém případě budete potřebovat jednu z dalších technik.)
Valgrind lze použít k zachycení jakéhokoli volání funkce. Pokud potřebujete zachytit systémové volání ve vašem hotovém produktu, nebude to k ničemu. Pokud se však pokoušíte zachytit během vývoje, může to být velmi užitečné. Tuto techniku jsem často používal k zachycení hašovacích funkcí, abych mohl kontrolovat vrácený hash pro účely testování.
V případě, že si nejste vědomi, Valgrind se používá hlavně k hledání úniků paměti a dalších chyb souvisejících s pamětí. Ale základní technologií je v podstatě x86 emulátor. Emuluje váš program a zachycuje hovory na malloc/free atd. Dobrá věc je, že jej nemusíte znovu kompilovat, abyste jej mohli používat.
Valgrind má funkci, kterou nazývají Function Wrapping , který slouží k ovládání odposlechu funkcí. Podrobnosti naleznete v části 3.2 příručky Valgrind. Můžete nastavit zalamování funkcí pro jakoukoli funkci, kterou chcete. Jakmile je hovor zachycen, je vyvolána vámi poskytnutá alternativní funkce.
Nejprve eliminujme některé ne-odpovědi, které dali jiní lidé:
- Použijte
LD_PRELOAD
. Jo, řekl jsi "KroměLD_PRELOAD
." ..." v otázce, ale pro některé lidi to zjevně nestačí. Není to dobrá volba, protože funguje pouze v případě, že program používá knihovnu libc, což nemusí nutně platit. - Použijte Systemtap. Ano, v otázce jste řekli "Kromě ... Linux Kernel Modules", ale zjevně to některým lidem nestačí. Toto není dobrá volba, protože musíte nahrát vlastní modul jádra, což je velká bolest v zadku a také vyžaduje root.
- Valgrind. Funguje to, ale funguje to jako simulace CPU, takže je to opravdu pomalé a opravdu složité. Dobře, pokud to děláte jen pro jednorázové ladění. Není to opravdu volba, pokud děláte něco, co stojí za to produkce.
- Různé věci týkající se auditu systémových volání. Nemyslím si, že protokolování systémových volání se počítá jako jejich „zachycení“. Jasně chceme upravit parametry syscall / návratové hodnoty nebo přesměrovat program přes nějaký jiný kód.
Existují však i další možnosti, které zde zatím nebyly zmíněny. Všimněte si, že jsem ve všech těchto věcech nový a ještě jsem nic z toho nevyzkoušel, takže se mohu v některých věcech mýlit.
Přepište kód
Teoreticky byste mohli použít nějaký vlastní zavaděč, který přepíše instrukce syscall, aby místo toho přešel na vlastní obslužný program. Ale myslím si, že implementace by byla absolutní noční můra.
ksondy
kprobes jsou nějaký druh systému instrumentace jádra. Mají přístup pouze pro čtení k čemukoli, takže je nemůžete použít k zachycení systémových volání, pouze je zaznamenat.
ptrace
ptrace je API, které debuggery jako GDB používají k ladění. Existuje PTRACE_SYSCALL
možnost, která pozastaví provádění těsně před/po systémových voláních. Odtud můžete dělat v podstatě, co chcete, stejným způsobem jako GDB. Zde je článek o tom, jak upravit parametry syscall pomocí ptrace. Zdá se však, že má vysokou režii.
Seccomp
Seccomp je systém navržený tak, aby vám umožňoval filtrovat systémová volání. Argumenty nemůžete upravit, ale můžete zablokovat je nebo vrátit vlastní chyby. Filtry Seccomp jsou programy BPF. Pokud nejste obeznámeni, jsou to v podstatě libovolné programy, které mohou uživatelé spouštět na virtuálním počítači v prostoru jádra. Tím se vyhnete přepínání kontextu uživatel/kernel, díky kterému jsou rychlejší než ptrace.
I když nemůžete upravovat argumenty přímo z vašeho programu BPF, můžete vrátí SECCOMP_RET_TRACE
což spustí ptrace
ing rodič zlomit. Je to tedy v podstatě stejné jako PTRACE_SYSCALL
kromě toho, že spustíte program v prostoru jádra, abyste se rozhodli, zda chcete skutečně zachytit systémové volání na základě jeho argumentů. Mělo by to být rychlejší, pokud chcete zachytit pouze některá systémová volání (např. open()
s konkrétními cestami).
Myslím, že tohle je asi nejlepší varianta. Tady je o tom článek od stejného autora jako ten výše. Všimněte si, že používají klasické BPF místo eBPF, ale myslím, že můžete použít i eBPF.
Edit:Ve skutečnosti můžete použít pouze klasické BPF, ne eBPF. Je o tom článek na LWN.
Zde je několik souvisejících otázek. První z nich rozhodně stojí za přečtení.
- Může eBPF upravit návratovou hodnotu nebo parametry syscall?
- Zachyťte pouze systémové volání pomocí PTRACE_SINGLESTEP
- Je to dobrý způsob, jak zachytit systémová volání?
- Minimální režijní způsob zachycení systémových volání bez úpravy jádra
Zde je také dobrý článek o manipulaci se systémovými voláními pomocí ptrace.
Některé aplikace mohou oklamat strace/ptrace, aby se nespustily, takže jedinou skutečnou možností, kterou jsem měl, je použití systemtap
Systemtap může v případě potřeby zachytit spoustu systémových volání kvůli shodě divokých karet. Systemtap není C, ale samostatný jazyk. V základním režimu by vám systemtap měl bránit v hloupostech, ale také může běžet v "expertním režimu", který umožňuje vývojáři používat C, pokud je to vyžadováno.
Nevyžaduje to, abyste opravovali své jádro (nebo byste alespoň neměli), a jakmile je modul zkompilován, můžete jej zkopírovat z testovací/vývojové krabice a vložit (přes insmod) do produkčního systému.
Ještě jsem nenašel linuxovou aplikaci, která by našla způsob, jak obejít/vyhnout se zachycení systemtapem.