No Linux, com o ps
de procps(-ng)
:
ps -fwwp 2755
Em versões anteriores ao 4.2 do Linux, ainda é limitado (pelo kernel ( /proc/2755/cmdline
) para 4k) e você não pode obter mais, exceto pedindo o processo para informá-lo a você ou usar um depurador.
$ sh -c 'sleep 1000' $(seq 4000) &
[1] 31149
$ gdb -p $! /bin/sh
[...]
Attaching to program: /bin/dash, process 31149
[...]
(gdb) bt
#0 0x00007f40d11f40aa in wait4 () at ../sysdeps/unix/syscall-template.S:81
[...]
#7 0x00007f40d115c995 in __libc_start_main (main=0x4022c0, argc=4003, ubp_av=0x7fff5b9f5a88, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fff5b9f5a78)
at libc-start.c:260
#8 0x00000000004024a5 in ?? ()
#9 0x00007fff5b9f5a78 in ?? ()
#10 0x0000000000000000 in ?? ()
(gdb) frame 7
#7 0x00007f40d115c995 in __libc_start_main (main=0x4022c0, argc=4003, ubp_av=0x7fff5b9f5a88, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fff5b9f5a78)
at libc-start.c:260
(gdb) x/4003s *ubp_av
0x7fff5b9ff83e: "sh"
0x7fff5b9ff841: "-c"
0x7fff5b9ff844: "sleep 1000"
0x7fff5b9ff84f: "1"
0x7fff5b9ff851: "2"
[...]
0x7fff5ba04212: "3999"
0x7fff5ba04217: "4000"
Para imprimir o quarto argumento com até 5000 caracteres:
(gdb) set print elements 5000
(gdb) p ubp_av[3]
Se você quiser algo não intrusivo, pode tentar obter as informações de /proc/2755/mem
(observe que, se o kernel.yama.ptrace_scope
não estiver definido como 0, você precisará de permissões de superusuário para isso). Isso abaixo funciona para mim (imprime todos os argumentos e variáveis de ambiente), mas não há muita garantia que eu possa pensar (o erro e o manuseio inesperado de entrada são deixados como um exercício para o leitor):
$ perl -e '$p=shift;open MAPS, "/proc/$p/maps";
($m)=grep /\[stack\]/, <MAPS>;
($a,$b)=map hex, $m =~ /[\da-f]+/g;
open MEM, "/proc/$p/mem" or die "open mem: $!";
seek MEM,$a,0; read MEM, $c,$b-$a;
print((split /perl -le '$p=shift;open MAPS, "/proc/$p/maps";
($m)=grep /\[stack\]/, <MAPS>;
($a,$b)=map hex, $m =~ /[\da-f]+/g;
open MEM, "/proc/$p/mem" or die "open mem: $!";
seek MEM,$a,0; read MEM, $c,$b-$a;
$c =~ /.*ps -fwwp 2755
$ sh -c 'sleep 1000' $(seq 4000) &
[1] 31149
$ gdb -p $! /bin/sh
[...]
Attaching to program: /bin/dash, process 31149
[...]
(gdb) bt
#0 0x00007f40d11f40aa in wait4 () at ../sysdeps/unix/syscall-template.S:81
[...]
#7 0x00007f40d115c995 in __libc_start_main (main=0x4022c0, argc=4003, ubp_av=0x7fff5b9f5a88, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fff5b9f5a78)
at libc-start.c:260
#8 0x00000000004024a5 in ?? ()
#9 0x00007fff5b9f5a78 in ?? ()
#10 0x0000000000000000 in ?? ()
(gdb) frame 7
#7 0x00007f40d115c995 in __libc_start_main (main=0x4022c0, argc=4003, ubp_av=0x7fff5b9f5a88, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fff5b9f5a78)
at libc-start.c:260
(gdb) x/4003s *ubp_av
0x7fff5b9ff83e: "sh"
0x7fff5b9ff841: "-c"
0x7fff5b9ff844: "sleep 1000"
0x7fff5b9ff84f: "1"
0x7fff5b9ff851: "2"
[...]
0x7fff5ba04212: "3999"
0x7fff5ba04217: "4000"
\K[^(gdb) set print elements 5000
(gdb) p ubp_av[3]
].*$ perl -e '$p=shift;open MAPS, "/proc/$p/maps";
($m)=grep /\[stack\]/, <MAPS>;
($a,$b)=map hex, $m =~ /[\da-f]+/g;
open MEM, "/proc/$p/mem" or die "open mem: $!";
seek MEM,$a,0; read MEM, $c,$b-$a;
print((split /perl -le '$p=shift;open MAPS, "/proc/$p/maps";
($m)=grep /\[stack\]/, <MAPS>;
($a,$b)=map hex, $m =~ /[\da-f]+/g;
open MEM, "/proc/$p/mem" or die "open mem: $!";
seek MEM,$a,0; read MEM, $c,$b-$a;
$c =~ /.*%pre%%pre%\K[^%pre%].*%pre%[^%pre%]*$/s;
@a=unpack"L!*",substr$c,0,$-[0];
for ($i = $#a; $i >=0 && $a[$i] != $a+$-[0];$i--) {}
for ($i--; $i >= 0 && ($a[$i]>$a || $a[$i]==0); $i--) {}
$argc=$a[$i++];
print for unpack"(Z*)$argc",substr$c,$a[$i]-$a;' "$!"
{2,}/,$c)[-1])' "$!" | tr \0 \n | head
sh
-c
sleep 1000
1
2
3
4
5
6
7
[^%pre%]*$/s;
@a=unpack"L!*",substr$c,0,$-[0];
for ($i = $#a; $i >=0 && $a[$i] != $a+$-[0];$i--) {}
for ($i--; $i >= 0 && ($a[$i]>$a || $a[$i]==0); $i--) {}
$argc=$a[$i++];
print for unpack"(Z*)$argc",substr$c,$a[$i]-$a;' "$!"
{2,}/,$c)[-1])' "$!" | tr \0 \n | head
sh
-c
sleep 1000
1
2
3
4
5
6
7
(substitua "$!"
pelo id do processo). O exemplo acima usa o fato de o Linux colocar as strings apontadas por argv[]
, envp[]
e o nome do arquivo executado na parte inferior da pilha do processo.
O aspecto acima aparece nessa pilha para a string mais abaixo entre dois conjuntos de dois ou mais bytes NUL consecutivos. Não funciona se algum dos argumentos ou env strings estiver vazio, porque então você terá uma seqüência de 2 bytes NUL no meio daqueles argv ou envp. Além disso, não sabemos onde as strings argv param e onde começam os envp.
Uma solução para isso seria refinar essa heurística olhando para trás o conteúdo real de argv[]
(os ponteiros). Isto abaixo funciona na arquitetura i386 e amd64 para executáveis ELF pelo menos:
Basicamente, faz o mesmo que acima, mas uma vez que encontrou a primeira string de argv[]
(ou pelo menos uma das strings argv[]
ou envp[]
se houver vazios), ela sabe seu endereço, assim, ele olha para trás no topo da pilha para um ponteiro com o mesmo valor. Em seguida, fica olhando para trás até encontrar um número que não possa ser um ponteiro para eles, e isso é argc
. Então o próximo inteiro é argv[0]
. E sabendo argv[0]
e argc
, ele pode exibir a lista de argumentos.
Isso não funciona se o processo tiver escrito para o argv[]
possivelmente substituindo alguns delimitadores NUL ou se argc
for 0 ( argc
é geralmente pelo menos 1 para incluir argv[0]
), mas deve funcionar no caso geral, pelo menos para executáveis ELF.
No 4.2 e mais recente, /proc/<pid>/cmdline
não é mais truncado, mas ps
tem uma largura máxima de exibição de 128K.