Como ignorar funções bash chamadas 'command', 'builtin' e 'unset'?

6

Estou ciente de que os aliases podem ser ignorados, citando o próprio comando.

No entanto, parece que se os comandos internos forem "sombreados" por funções com os mesmos nomes, não há como executar o comando interno subjacente, exceto ... usando um comando interno. Se você puder chegar a ele.

Para citar a página man bash (em LESS='+/^COMMAND EXECUTION' man bash ):

COMMAND EXECUTION
       After a command has been split into words, if it results  in  a  simple
       command  and  an  optional list of arguments, the following actions are
       taken.

       If the command name contains no slashes, the shell attempts  to  locate
       it.   If  there  exists a shell function by that name, that function is
       invoked as described above in FUNCTIONS.  If the name does not match  a
       function,  the shell searches for it in the list of shell builtins.  If
       a match is found, that builtin is invoked.

Então, é possível recuperar o seguinte, sem iniciar um novo shell?

unset() { printf 'Haha, nice try!\n%s\n' "$*";}
builtin() { printf 'Haha, nice try!\n%s\n' "$*";}
command() { printf 'Haha, nice try!\n%s\n' "$*";}

Eu nem adicionei readonly -f unset builtin command . Se é possível recuperar-se do que foi dito acima, considere isto uma questão bônus: você ainda pode se recuperar se todas as três funções estiverem marcadas como somente leitura?

Eu fiz essa pergunta no Bash, mas também estou interessado em aplicá-la a outros shells.

    
por Wildcard 10.11.2017 / 06:34

2 respostas

5

can you still recover if all three functions are marked readonly?

Sim, normalmente você pode, embora isso não signifique que você deva.

Assim como você pode anular as variáveis somente leitura anexando um depurador e chamando unbind_variable , como mostrado na resposta do anishsane a essa pergunta , você também pode cancelar funções readonly passando seus nomes para unbind_func usando um depurador .

Esta não é uma abordagem razoável quando eles não são apenas de leitura (se é que realmente é). Nessa situação, você deve usar a solução do cuonglm , que aproveita como unset é tratado no POSIX mode. Essa solução é algo que você pode usar na vida real.

Como não há garantias reais de que seu shell se comportará de maneira razoável depois de contornar readonly com um depurador, sugiro evitá-lo sempre que quiser uma alternativa mais razoável, como desistir e reiniciar seu shell ou substituir seu shell por um novo usando exec , está disponível.

Com isso dito, aqui está o método do anishsane adaptado para desfazer funções em vez de uma variável:

cat <<EOF | sudo gdb
attach $$
call unbind_func("unset")
call unbind_func("builtin")
call unbind_func("command")
detach
EOF

Observe que $$ está expandido no ID do processo do shell, porque nenhuma parte de EOF em <<EOF é citada.

Eu testei isso no Bash 4.3.48 (1) -release no Ubuntu 16.04 LTS, e funcionou. Você precisa de gdb para isso, embora possa ser adaptado para outros depuradores. Como anishsane comentou , a canalização de cat tem como objetivo evitar um deadlock em que o processo que fornece entrada para gdb é aquele que gdb parou. Acredito que alcança esse objetivo, porque em um pipeline de dois ou mais comandos, Bash executa cada comando em uma subcamada. Mas não tenho certeza se é o caminho mais robusto. Em última análise, no entanto, não há nenhuma garantia real de que isso funcione de qualquer maneira, já que é totalmente razoável que o Bash assuma as variáveis de leitura e as funções não mudem. Na prática, meu palpite é que isso praticamente sempre funciona.

Para usar essa técnica como escrita, você precisa de sudo instalado e precisa poder sudo para raiz. Você pode, naturalmente, substituí-lo por outro método de elevação de privilégio. Dependendo de qual sistema operacional você está executando e como está configurado, você poderá omitir sudo e executar gdb como você mesmo, em vez de como root. Por exemplo, o kernel do Linux irá consultar o valor de /proc/sys/kernel/yama/ptrace_scope , que você pode fazer. definir através sysctl e pode ler ou (como root) escrever, para determinar quais processos podem depurar outros processos. Se o valor for 1 , somente o pai direto de um processo - ou qualquer processo executado como raiz - poderá depurá-lo. Os sistemas GNU / Linux mais recentes definiram como 1 , e é por isso que incluí sudo .

Essa descrição do comportamento do kernel do Linux é um pouco simplificada, em que outros valores de ptrace_scope são permitidos e em que a relação requerida por 1 pode ser ajustada. Consulte a documentação relevante para obter detalhes completos.

    
por 10.11.2017 / 09:01
4

Quando bash está no modo posix, alguns builtins são considerados especiais, o que é compatível com o padrão POSIX.

Uma coisa especial sobre esses builtins especiais, eles são encontrados antes da função no processo de consulta do comando. Aproveitando essa vantagem, você pode tentar:

$ unset builtin
Haha, nice try!
builtin
$ set -o posix
$ unset builtin
$ builtin command -v echo
echo

embora não funcione se set for substituído por uma função chamada set :

$ set() { printf 'Haha, nice try!\n%s\n' "$*";}
$ set -o posix
Haha, nice try!

Nesse caso, basta definir POSIXLY_CORRECT para fazer bash entrar no modo posix e, em seguida, você terá todos os recursos especiais:

$ POSIXLY_CORRECT=1
    
por 11.11.2017 / 02:51