Souhlasím s vámi. Root je bezedná studnice zajímavých informací.
Mimochodem, zkoušel jsem číst spousty jiných českých serverů, ale root je svým přístupem jedinečný a bohužel i osamocený. Všude jinde pozoruji snahy o regulace či dokonce cenzuru a především snahu o tendenční zkreslování informací. Tohle jsem na rootu kromě pár článků pana Kulhavého nikdy nepozoroval.
Jakoze informace, ze se muze v C/C++ prepsat argv a program nespadne a "ps" to pak vypisuje jinak... no dobre. Ale ten clanen? Hlavicka GPL u takove hovadiny, wtf? Neni to spis neco jako public-domain? To se u kazde ukazy ma psat do komentare licence? GPL je tam v anglictine, ale promenne a nazvy funkci v cestine vcetne diakritiky. To je maximalni lamizmus. Je to v C++17, proc proboha? Nikde se tam zadne C++ nepouziva, natoz C++17! (snad kvuli tej diakritice - ale o tom ten clanek neni, ze ne?). Prilozena je ukazka pouziti mysql.cc, co ma 5300 radku, WTF? Autor tim chce krejt hesla na cmdline. Kdo proboha zadava hesla v argumentech? Obrazek z terminalu + htop - co aspon pod nej napsat, "toto je htop"?
Nic proti autorovi, ale redakce rootu ma mit nejakou elementarni soudnost a vypousteni odkazu na takove veci zarazit uz kvuli autorovi (akorat z toho bude mit ostudu).
Tohle normalni standatni vec ktera nejak funguje na kazdem unixu.
Staci spustit prikaz "import password=abc123" a vy ve vypisu procesu uvidite jen "import password=".
PS: Zarucene to vice-mene je. Ale asi cca pred 15ti lety na Linuxu opravdu doslo k tomu, ze se zmenil ten mechanizmus. Drive stacilo zmenit pointer na string, dneska se pouze prepisuje obsah toho stringu.
Mně na tom přijde prasárna, že se program může zamaskovat a těžko se pak dohledá, s jakými parametry byl spuštěn (na binárku bude ukazovat /proc/$PID/exe
, ale to taky může vést na již smazaný soubor…).
Existuje nějaký způsob, jak root nebo vlastník procesu může zjistit, s jakými parametry a jaký příkaz byl spuštěn? Asi by to šlo přes auditd, ale bez něj?
proces měl vždy možnost změnit svoje env i argv, nevidím v tom nic špatně a řada aplikací toho využívá, ať už zmíněný fork či schování hesel.
Samotný linux si tyhle údaje nepamatuje, pouze má aktuální stav /proc. Nevím o jiné metodě než auditd,accton snoopy, Na Solarisu na to používám s oblibou dtrace, na linuxu by to asi šlo sledovat pro konkrétní strom přes valgrid.
Muzete cist /proc/PID/comm, ten zmena argv[] neovlivnuje. Pri cteni cmdline kernel kouka do pameti beziciho procesu a cte to rovnou od tamtud, takze by sel opatchovat linker, aby tato cast pameti byla jen pro cteni, ale moc velky smysl v tom nevidim, protoze proces to tak jako tak bude moci menit pres exec.
Pro jednoho zakaznika jsem todle chovani menil tak, aby si kernel udrzoval vychozi kopii, ale duvodem byla moznost priority inversion, ktera muze nastat pokud k cmdline pristupuje napriklad ps, netstat atd. a process se rozhodne zmenit mapovani sve pameti.
To svet nevidel ...
#include <iostream>
#include <wchar.h>
#include <locale.h>
using namespace std;
chybi mi tam aby to jeste pouzivalo javu, .net a nejaky javascriptovy engine.
Prvni by se pouzilo na usage, .net by treba mohl prepisovat stringy a javascriptovy kod to mohl cele ridit.
Ceckovy main() by spustil akorat ten javascript.
Sračky jako .net a javascript nebudu komentovat.
K těm knihovnám – chápu, že tohle je nad rámec toho příkladu a není to tam nezbytně nutné, nicméně chtěl jsem tam dát instrukce pro uživatele, aby věděl, co má spustit, aby si ověřil, jak to funguje. A chtěl jsem je napsat v češtině. A vzhledem k aktuálnímu letopočtu považuji unicode za základní a běžnou věc.
Jak tedy na to? Dá se to udělat nějak jednodušeji? Vím, že znaky s diakritikou jde vypsat i přes cout << "český text" << endl;
, ovšem to pracuje na úrovni bajtů a jakmile by člověk chtěl např. zarovnat/odsadit text pod sebe (přidat patřičný počet mezer), tak se to rozsype, takže by bylo potřeba celý program přepsat (tady by to bylo jedno, ale radši to píši rovnou tak, aby to fungovalo na úrovni znaků/unicodu). Co je tedy lepší? Přetížit si operátor <<
, aby podporoval vícebajtové znaky? A co s formátovacími řetězci? Tam by stejně byly tyhle funkce, ne?
Dovolím si snad konstruktivní kritiku.
Příklad je zjevně zacílený na začátečníky (zkušenějšímu by stačilo říct něco jako "stringy argv[] se dají přepsat" a bylo by vymalováno).
Pak by ukázky měly být pokud možno jednoduché, týkající se jen probíraného tématu, a rozhodně by neměly zabředávat do jiných oblastí které se probírané látky netýkají (psát správně česky je zajisté chvályhodné, ale do výkladu argv[] to fakt nepatří.) Místo toho by bylo naopak třeba hezké vědět, co se třeba stane když mám program ./x a v něm dám strcpy(argv[0], "/usr/sbin/httpd"); nebo strcpy(argv[0],"y"); - na to článek neodpovídá.
Takže závěr: držet se tématu, nekomplikovat.
P.S. Když prvňáčkům chci vysvětlit sčítání, tak taky budu sčítat jablka a ne zavádět pojem abelovská grupa.
Jinak co se týče těch dalších věcí: během psaní článku jsem zjistil akorát to, že když místo to místo 'x'
budu přepisovat nulovým bajtem, tak sice už není vidět, kolik parametrů původně bylo, ale jejich celková délka jde pořád zjistit (délka cmdline
se nemění):
Původně:
$ cat /proc/$(pidof ./přepis-cli-parametrů)/cmdline | hd 00000000 2e 2f 70 c5 99 65 70 69 73 2d 63 6c 69 2d 70 61 |./p..epis-cli-pa| 00000010 72 61 6d 65 74 72 c5 af 00 70 72 76 6e c3 ad 00 |rametr...prvn...| 00000020 64 72 75 68 c3 bd 00 74 c5 99 65 74 c3 ad 00 |druh...t..et...| 0000002f
Po přepsání:
$ cat /proc/$(pidof ./přepis-cli-parametrů)/cmdline | hd 00000000 2e 2f 70 c5 99 65 70 69 73 2d 63 6c 69 2d 70 61 |./p..epis-cli-pa| 00000010 72 61 6d 65 74 72 c5 af 00 00 00 00 00 00 00 00 |rametr..........| 00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |...............| 0000002f
Ještě si s tím zkusím trochu pohrát a článek následně rozšířím…
Nejde až tak o to, jestli je to z pohledu programu konstanta nebo ne – mne spíš překvapilo, že si systém nedrží ve své paměti kopii těchto hodnot a místo toho se dívá do paměti procesu. Neříkám, že mít možnost přepisu parametrů je špatné, má to i smysluplné využití, ale spíš bych čekal, že to bude nějaké API a bude to nějak řízené (tzn. s možností např. zalogovat, že došlo k přepisu), než že to bude založené na tom, že obě strany koukají na jedno místo v paměti.
Pokud nekdo touzi po jmene programu, ktere meni syscallem, ma k dispozici comm (/proc/PID/comm, ps -o comm, pctl(PR_SET_NAME)). Jakej duvod by melo nekam zaznamenavat, ze doslo k prepisu? Pokud si nekdo bude chtit takto "ukryt" process, muze ho execnout rovnou s "falesnym" argv a vysledek bude stejny.
Mimochodem, uplne stejne se chova i prostredi, protoze pro kernel je oboji jen pole ukazatelu na retezce, jehoz obsah ho nijak nezajima.
Ne, je to pole ukazatelu, coz je zrejme z protypu main a nebo si to muze zvedavec precist ve fs/exec.c (alespon myslim, neoveroval jsem). Vypada takhle:
ptr_to_str1
ptr_to_str2
ptr_to_str3
NULL /* Posledni prvek pole */
str1
str2
str3
Samozrejme je to v pameti za sebou, protoze nedava smysl, aby mezi ne kernel daval prazdne misto.
Reagoval jsem na to „pro kernel je oboji jen pole ukazatelu na retezce“.
Pole ukazatelů je to IMHO jen z pohledu programu, ale jádro toto pole ignoruje, neřeší a přistupuje přímo k té souvislé paměti a drží si svoje vlastní značky ( arg_start, arg_end, env_start, env_end
), kde začínají a končí parametry a proměnné prostředí. Budu si muset prostudovat zdrojáky procfs, ale zatím jsem to ověřil experimentálně:
printf("%p\n", argv[0]); printf("%p\n", argv[1]); argv[1]++; printf("%p\n", argv[0]); printf("%p\n", argv[1]); for (int i = 0; i < argc; i++) { printf("%d = %s\n", i, argv[i]); } argv[1][0] = '_'; for (int i = 0; i < argc; i++) { printf("%d = %s\n", i, argv[i]); }
Dejme tomu, že program byl spuštěn s jedním parametrem xxx
. První cyklus vypíše xx
a druhý _x
. Zatímco jádro ( ps aux
) vypisuje nejdřív xxx
a pak x_x
– což znamená, že jádro se dívá pořád na stejné místo v paměti, bez ohledu na to, že jsem do toho pole dal jiný ukazatel.
Trochu zvláštní je, že jádro tu svoji koncovou značku klidně překročí, pokud přemažu příslušný nulový bajt – a pak do seznamu parametrů začnou prosakovat proměnné prostředí, které následují v paměti hned za tím.