Przeczytaj stos innego procesu?

16

Próbuję odczytać stos procesu potomnego, ale bez powodzenia. Wiem, że to możliwe ptrace, aleptrace interfejs pozwala na odczyt tylko jednego słowa na raz, a ja próbuję zeskanować większą część stosu.

Próbowałem również odczytać /proc/$pid/memz granic stosu wyodrębnionego z /proc/$pid/mapspliku po pierwszym użyciu ptrace do dołączenia do niego (jak sugerowano tutaj ), ale odczyt nadal się nie udaje (nawet gdy działa jako root), chociaż ten sam kod kończy się powodzeniem przy próbie odczyt z różnych części procesu (np. sterty).

Co ja robię źle? Czy jest jakaś inna opcja?

użytkownik4537
źródło
Czy sprawdziłeś waitpidpomiędzy ptrace(PTRACE_ATTACH,…)i read(w przeciwnym razie istnieje możliwość wyścigu)? Jaki błąd readpowraca? Czy dziecko robi coś szczególnego z mapowaniem pamięci - czy możesz wypróbować kod z prostym dzieckiem, takim jak sleep?
Gilles „SO- przestań być zły”
Użyłem funkcji czekaj po ptrace i umieściłem skan w dziecku, aby zmusić go do czekania.
user4537
Czy to tylko w systemie Linux? Solaris ma również system plików / proc, ale jest całkowicie odmienny od Linuksa, filozoficznie. Wiele „plików binarnych”.
Bruce Ediger,
po prostu zrób system („pstack pid ”) i przeanalizuj wynik ..
vrdhn
Zobacz ps: pełne polecenie jest zbyt długie dla niektórych przykładów
Stéphane Chazelas

Odpowiedzi:

5

ptraceInterfejs pozwala na odczyt tylko jednego słowa na raz, a ja próbuję zeskanować większą część stosu

Więc skorzystaj z pętli. Naprawdę nie rozumiem, z czym to stanowi problem ptrace, używam go cały czas do zdalnego dostępu do procesów.

Używam czegoś takiego:

static int memcpy_from_target(pid_t pid, char *dest, long src, size_t n)
{
    static int const align = sizeof(long) - 1;

    while (n)
    {
        size_t todo = MIN(n, sizeof(long) - (src & align));
        long data = ptrace(PTRACE_PEEKTEXT, pid, src - (src & align), 0);
        if (errno)
        {
            perror("ptrace_peektext (memcpy_from_target)");
            return -1;
        }
        memcpy(dest, (char *)&data + (src & align), todo);

        dest += todo; src += todo; n -= todo;
    }

    return 0;
}
sam hocevar
źródło
Cześć Sam, chociaż twój kod to zrobi (a właściwie to właśnie teraz robię), ma duży narzut wydajności.
user4537
@ user4536: Rozumiem. Mam na myśli inną strategię, o której napiszę, kiedy będę mieć czas, aby ją zapisać. Jakie są twoje typowe rozmiary stosów?
sam hocevar
Naprawdę trudno powiedzieć, ponieważ moje badania nie zakładają określonego rozmiaru stosu, ale ze względu na ten argument powiedzmy, że ma on co najmniej kilka stron. Czy mógłbyś podać wskazówki dotyczące swojej strategii? W każdym razie dzięki za pomoc!
user4537 13.03.11
1

Oto kolejna strategia, która może wymagać ulepszenia, ale powinna być bardziej wydajna przy dużych porcjach danych. Chodzi o wykonanie wywołań systemowych w zdalnym procesie w celu pobrania zawartości stosu. Będzie wymagał określonego kodu architektury, ale jeśli kierujesz reklamy tylko na x86 / x86_64, nie powinno to być zbyt wielkim problemem.

  1. Utwórz nazwany potok, na przykład "/tmp/fifo"w procesie wywoływania.
  2. Wejdź do śledzonego procesu, aż powróci on z wywołania systemowego, używając PTRACE_SYSCALLpolecenia krok, waitpid()oczekiwania i PTRACE_GETREGS/PTRACE_PEEKTEXT lub sprawdzić aktualnie wykonywany kod operacji.
  3. Wykonaj kopię zapasową rejestrów zdalnego procesu i niewielkiego obszaru stosu.
  4. Wykonaj wywołań systemowych na zdalnym procesu nadrzędnymi swój stack z własnymi danymi: open("/tmp/fifo"), write()zawartość stosu,close() deskryptor.
  5. Przywróć stan procesu zdalnego.
  6. Przeczytaj dane fifo z procesu połączenia.

Mogą istnieć bardziej eleganckie alternatywy dla nazwanej rury, ale nie mogę teraz wymyślić żadnej. Używam tylko syscall, ponieważ zdalne wstrzykiwanie kodu jest dość niewiarygodne w nowoczesnych systemach z powodu różnych zabezpieczeń. Wadą jest to, że zawiesi się, dopóki zdalny proces nie wykona wywołania systemowego (co może stanowić problem w przypadku niektórych programów, które w większości wykonują obliczenia).

W tym pliku źródłowym można zobaczyć trochę wolnego kodu implementującego większość pracy . Informacje zwrotne na temat kodu są mile widziane!

sam hocevar
źródło
1

Kolejna sugestia.

Kiedy / jeśli zostanie zaakceptowany w głównym drzewie jądra Linuksa, będziesz mógł użyć łatki Cross Memory Attach Christophera Yeoha . Zobacz na przykład dokumentację dla process_vm_readv .

sam hocevar
źródło
1

Możesz łatwo odczytać stos innego procesu za pomocą systemu plików proc (w tym celu potrzebujesz dostępu do konta root). Przed arbitralnym odczytaniem z / proc / pid / mem należy zajrzeć do / proc / pid / map. Prosty odczyt w tym pliku pokazuje wiele wpisów. Interesuje nas pozycja oznaczona jako stos. Gdy to otrzymasz, musisz przeczytać dolną i górną granicę stosu. Teraz wystarczy otworzyć plik / proc / pid / mem, przejść do dolnej granicy stosu i odczytać właściwy rozmiar danych.

Ajay Brahmakshatriya
źródło
1
Jesteś pewien, że masz na myśli, memsa nie maps? (Nie widzę żadnych memswpisów w moim /procsystemie plików.) OP wspomniał już o czytaniu granic stosu /proc/$pid/maps- co sugerujesz, że robią inaczej?
JigglyNaga,
Edytowałem literówkę. Zrobiłem dokładnie to, o czym wspomniałem w odpowiedzi i zrzuciłem 132 KB danych stosu. Potrzebujemy więcej informacji na temat tego, co OP zrobił źle. Być może OP może udostępnić kod, którego użył do odczytu granic stosu. Jeśli nie odpowie, podzielę się moim.
Ajay Brahmakshatriya
0

Możesz spróbować lsstack . Wykorzystuje ptrace, tak jak każdy inny udany program „czytaj stos innego procesu”. Nie mogłem uruchomić programu przy użyciu / proc / $ pid / mem reading. Uważam, że nie możesz tego zrobić w ten sposób, choć logicznie powinieneś.

Bruce Ediger
źródło