Zawsze możesz powiedzieć swojej powłoce, aby poinformowała aplikacje, jaki kod powłoki prowadzi do ich wykonania. Na przykład, zsh
przekazując te informacje do $SHELL_CODE
zmiennej środowiskowej za pomocą preexec()
haka ( printenv
użytego jako przykład, którego użyłbyś getenv("SHELL_CODE")
w swoim programie):
$ preexec() export SHELL_CODE=$1
$ printenv SHELL_CODE
printenv SHELL_CODE
$ printenv SHELL_CODE
printenv CODE
$ $(echo printenv SHELL_CODE)
$(echo printenv SHELL_CODE)
$ for i in SHELL_CODE; do printenv "$i"; done
for i in SHELL_CODE; do printenv "$i"; done
$ printenv SHELL_CODE; : other command
printenv SHELL_CODE; : other command
$ f() printenv SHELL_CODE
$ f
f
Wszystkie te byłyby wykonywane printenv
jako:
execve("/usr/bin/printenv", ["printenv", "SHELL_CODE"],
["PATH=...", ..., "SHELL_CODE=..."]);
Zezwolenie printenv
na pobranie kodu zsh, który prowadzi do wykonania printenv
tych argumentów. To, co chciałbyś zrobić z tymi informacjami, nie jest dla mnie jasne.
Dzięki bash
, funkcja najbliższa do zsh
's preexec()
używałaby jej $BASH_COMMAND
w DEBUG
pułapce, ale zauważ, że bash
robi pewien poziom przepisywania w tym (a w szczególności refaktoryzuje niektóre białe spacje używane jako separator) i to jest stosowane do każdego polecenia (no, niektóre) uruchom, a nie całą linię poleceń wprowadzoną w wierszu poleceń (zobacz także functrace
opcję).
$ trap 'export SHELL_CODE="$BASH_COMMAND"' DEBUG
$ printenv SHELL_CODE
printenv SHELL_CODE
$ printenv $(echo 'SHELL_CODE')
printenv $(echo 'SHELL_CODE')
$ for i in SHELL_CODE; do printenv "$i"; done; : other command
printenv "$i"
$ printf '%s\n' "$(printenv "SHELL_CODE")"
printf '%s\n' "$(printenv "SHELL_CODE")"
$ set -o functrace
$ printf '%s\n' "$(printenv "SHELL_CODE")"
printenv "SHELL_CODE"
$ print${-+env } $(echo 'SHELL_CODE')
print${-+env } $(echo 'SHELL_CODE')
Zobacz, jak niektóre spacje, które są ogranicznikami w składni języka powłoki, zostały ściśnięte do 1 i jak nie pełna linia poleceń nie zawsze jest przekazywana do polecenia. Prawdopodobnie nie jest to przydatne w twoim przypadku.
Pamiętaj, że nie radziłbym robić tego rodzaju rzeczy, ponieważ potencjalnie wyciekasz poufne informacje do każdego polecenia, jak w:
echo very_secret | wc -c | untrustedcmd
wyciekłby ten sekret do obu wc
i untrustedcmd
.
Oczywiście, możesz zrobić coś takiego dla innych języków niż shell. Na przykład w C można użyć makr eksportujących kod C, który wykonuje polecenie do środowiska:
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#define WRAP(x) (setenv("C_CODE", #x, 1), x)
int main(int argc, char *argv[])
{
if (!fork()) WRAP(execlp("printenv", "printenv", "C_CODE", NULL));
wait(NULL);
if (!fork()) WRAP(0 + execlp("printenv", "printenv", "C_CODE", NULL));
wait(NULL);
if (argc > 1 && !fork()) WRAP(execvp(argv[1], &argv[1]));
wait(NULL);
return 0;
}
Przykład:
$ ./a.out printenv C_CODE
execlp("printenv", "printenv", "C_CODE", NULL)
0 + execlp("printenv", "printenv", "C_CODE", NULL)
execvp(argv[1], &argv[1])
Zobacz, jak niektóre miejsca zostały skondensowane przez preprocesor C, jak w przypadku bash. W większości, jeśli nie we wszystkich językach, ilość miejsca używanego w separatorach nie robi różnicy, więc nie jest zaskakujące, że kompilator / interpreter korzysta tutaj z pewnej swobody.