# Sprint Pwndbg (10.08.2022) Hi! Witaj na stronie sprintu Pwndbg. Poniżej możesz przeczytać opisy przykładowych rzeczy, które można by dodać lub usprawnić w Pwndbg :). * Repo projektu: https://github.com/pwndbg/pwndbg/ Sprint zaczniemy od małego wstępu "co to jest Pwndbg" i "co mogę z tym zrobić", a następnie podzielimy się na zadania wylistowane poniżej (ofc można dodać również i swoje). ### Jak rozpocząć debugowanie programu w GDB? ```bash $ echo 'int main() { puts("Hello world"); )}' > a.c $ gcc a.c $ gdb --quiet ./a.out pwndbg> break main pwndbg> continue ``` Przydatne komendy: ``` break <symbol albo adres> - ustawia breakpoint na danym adresie run [<argumenty>] - uruchamia procesu z danymi argumentami (jeśli nie ustawiliśmy breakpointa, to proces może się skończyć) starti [<argumenty>] - uruchamia proces i zatrzymuje się na jego pierwszej instrukcji continue - kontynuuje wykonywanie programu si - skrót od "step instruction", czyli: wykonaj dokładnie jedną instrukcję (wchodząc również do funkcji które są wywoływane) ni - skrót od "next instruction", czyli: wykonaj jedną instrukcję, ale jeśli jest ona wywołaniem funkcji (np. instrukcja "call") to wykonaj całe wnętrze i zatrzymaj się dopiero na następnej instrukcji w funkcji w której jesteśmy info break - wyświetl ustawione breakpointy delete <numer-breakpointa> - usuwa dany breakpoint; jeśli nie podamy numeru, to usunie wszystkie print <wyrazenie> - oblicz i wypisz dane wyrażenie x/10i $rip - komenda GDB "examine", posiadająca różne formaty; tu: wyświetlająca 10 instrukcji rozpoczynając od adresu z rejestru RIP (IP == Instruction Pointer), który wskazuje na instrukcję którą program będzie właśnie wykonywać ``` Polecam również [GDB cheatsheet](https://darkdust.net/files/GDB%20Cheat%20Sheet.pdf). #### Taski na sprincie Zadania mają różną trudność i wymagają różnej wiedzy. Prostsze zadania mogą pomóc w rozeznaniu się w strukturze projektu lub różnych schematach, np. jak wygląda "komenda". PS: Na samym dole dodałem kilka przykładów róznych API w Pwndbg, które mogą się przydać oraz informacji o samym GDB. Taski / kto co robi (poniżej jest dłuższy opis tasków): * [x] [Poprawienie opisu oraz testy komendy `memfrob`](#Poprawienie-opisu-komendy-memfrob) -- artcz * [x] [Sprzątanie po Pythonie 2](#Sprzątanie-po-Pythonie-2) -- Ivellios * [x] [Rejestr X30 na architekturze aarch64](#Rejestr-X30-na-architekturze-aarch64) -- arcz * [ ] [Dodanie możliwości `telescope --reversed`](#Dodanie-możliwości-telescope---reversed) * [x] [Poprawienie parsowania adresów](#Poprawienie-parsowania-adresów) -- Nika * [ ] [Dodanie komendy `patch <adres> <dane lub instrukcje>`](#Dodanie-komendy-patch-ltadresgt-ltdane-lub-instrukcjegt) * [ ] [Dodanie testów do wybranych funkcjonalności](#Dodanie-testów-do-wybranych-funkcjonalności) * [ ] [Dodanie funkcjonalności/komendy "dump memory as code"](#Dodanie-funkcjonalnościkomendy-“dump-memory-as-code”) -- vesim * [x] [Dziwny bug #1008](https://github.com/pwndbg/pwndbg/issues/1008) - arcz * [x] [Dziwny bug 2 #1042](https://github.com/pwndbg/pwndbg/issues/1042) - disconnect3d * [ ] [Renderowanie argumentów printf-podobnych funkcji](#Renderowanie-argumentów-printf-podobnych-funkcji) * [ ] Bundlowanie Pwndbg w jeden skrypt - Hubert * [ ] Telemetria (eh :D) --- ## Poprawienie opisu komendy `memfrob` Komenda `memfrob` robi XORa danej pamięci z kluczem `'*'`. Powinniśmy to ująć w jej opisie, tak, żeby `memfrob --help` lub `help memfrob` jasno opisywało co to robi. * Komendę memfrob możemy znaleźć szukając w projekcie funkcji `memfrob` naturalnie :) ## Sprzątanie po Pythonie 2 Stare wersje Pwndbg wspierały Pythona 2, ale od pewnej wersji usunęliśmy support dla Pythona 2, gdyż obecnie żadne nowe distro nie buduje GDB z Pythonem 2. W kodzie Pwndbg pozostały jednak rzeczy, które potrzebne były jedynie w Pythonie 2, a teraz możemy się ich pozbyć. Na przykład: - komentarze odnośnie kodowania plików (`-*- coding: utf-8 -*-` itp.) - w sumie to nie związane z Py2, ale niektóre pliki mają też shebang (`#!/usr/bin/env python`), który jest zbędny, gdyż nigdy nie są uruchamiane bezpośrednio Być może coś jeszcze? ## Rejestr X30 na architekturze aarch64 Aarch64 to 64-bitowa architektura ARM (czyli to, co mamy m.in. w telefonach czy Macbookach M1/M2). Na tej architekturze jest dość dużo rejestrów - X0-X30 - i obecnie chyba źle wyświetlamy jeden z nich. Tu więcej info: https://github.com/pwndbg/pwndbg/issues/1039 To wszystko można przetestować lokalnie na architekturze x64 używając QEMU user emulation. Aby to zrobić musimy: - zainstalować paczkę z QEMU user (`sudo apt install qemu-user`) - zainstalować kompilator gcc na aarch64, aby cross-kompilować przykładowy program napisany w C do AARCH64 (`sudo apt install gcc-11-aarch64-linux-gnu`) - stworzyć przykładowy program (`echo 'int main() {}' > main.c`) - skompilować go - wtedy możemy uruchomić ten program pod qemu: `qemu-aarch64 -L <sciezka-do-/lib-aarch64> ./a.out` - dodając flagę `-g 1234` QEMU wystawi gdbserver na danym porcie (tu: 1234) - wtedy możemy w GDB zrobić `target remote :1234` aby podłączyć się do tego gdbservera i debugować emulowany proces ## Dodanie możliwości `telescope --reversed` * TL;DR: Trzeba wysłać PR z patchem, który jest w https://github.com/pwndbg/pwndbg/issues/1047 * Oraz pomyśleć, czy nie nazwać tego argumentu inaczej * Warto zbadać co się stanie jeśli będziemy klikać <enter> dalej - czy pokażą się kolejne adresy? Czy powinniśmy renderować poprzednie czy kolejne wartości? Do przedyskutowania ## Poprawienie parsowania adresów TL;DR: https://github.com/pwndbg/pwndbg/issues/1050 ## Dodanie komendy `patch <adres> <dane lub instrukcje>` Przydałaby się komenda do "patchowania" kodu, tak, żeby można było w łatwy sposób zmienić kod programu na inny - na przykład usunąć "ifa" z programu, albo zrobić tak, żeby dana funkcja zawsze zwracała zero (lub inną wartość). Przykładowo, `patch 0x1234 nop; nop; nop` (na x86/x64) powinno wpisać pod adres `0x1234` zasemblowane instrukcje `nop; nop; nop`, czyli wartość `0x90` trzy razy, bo instrukcja `nop`, która jest tak zwanym "no operation", tzn. nic nie robi, odpowiada właśnie bajtowi 0x90 na architekturach x86/x64. Przydatne rzeczy: - musimy stworzyć nową komendę w pliku `pwndbg/commands/patch.py` - możemy Do tego zadania przyda się biblioteka [`pwntools`](http://docs.pwntools.com/en/latest/) (którą trzeba dodać jako zależność) Kod plus minus: ```py # prawdopodobnie trzeba string arch jakoś zmapować z nazw Pwndbg na nazwy architektur używane przez Pwntools import pwnlib arch = pwndbg.arch.current data = pwnlib.asm.asm(assembly_code, arch=arch) pwndbg.memory.write(address, data) ``` Potencjalne rozszerzenia: 1. Zamiast pobierać architekturę za każdym razem podczas wykonywania komendy, moglibyśmy ustawiać `pwnlib.context.context.arch = ...` gdy ustawiane jest `pwndbg.arch.current` 2. Można rozszerzyć interfejs tak, aby pozwolić wprowadzać bajty instrukcji heksadecymalnie, zamiast tylko poprzez instrukcje asemblera; na przykład: - `patch 0x1234 0x90` - powinno wpisać pod `0x1234` bajt `0x90` (czyli instrukcję `nop` na x86/x64) - `patch 0x1234 9090` - powinno wpisać dwie instrukcje `nop` - Do zastanowienia się: czy powinniśmy supportować hexy bez prefixu `0x`? 3. Można zapisywać listę wszystkich dodanych patchy i wtedy: - `patch --list` - listowałoby zaaplikowane patche - `patch --disable <id...>` - wyłączałoby dane patche - `patch --enable <id...>` - włączałoby dane patche - `patch --revert <id...>` - pozwołiłoby zrevertować dany patch - `patch --revert-all` - cofałoby wszystkie patche 4. Powinniśmy pozwalać na export patchy do pliku 5. Dodanie testów :) Potencjalne problemy: - Co z rebase'owaniem binarki (ASLR/PIE)? ## Dodanie testów do wybranych funkcjonalności - obecnie Pwndbg ma dość mało testów - testy na CI są uruchamiane przez `PWNDBG_GITHUB_ACTIONS_TEST_RUN=1 sudo --preserve-env ./tests.sh` i są zaimplementowane w `./tests/` Potrzebujemy testów m.in. dla wielu komend: - `hexdump ...` - nawigacja po programie - `nextret`, `nextsyscall`, `stepsyscall`, etc. - wyszukanie wartości w programie: `search ...` - zmieniania uprawnień do pamięci - `mprotect` - listowanie layoutu pamięci - `vmmap` - również dla targetów z QEMU user emulation - również dla targetów z QEMU kernel - komend wyświetlających pamięć w ciekawy sposób - `probeleak`, `leakfind` - (bardzo trudne) funkcjonalności związane ze stertą (heap) z Pull Requesta, dla różnych wersji glibc i architektur ## Dodanie funkcjonalności/komendy "dump memory as code" W skrócie, można by dodać, żeby niektóre komendy, jak na przykład `dq`, `dw`, `dd`, `db` pozwalały na zdumpowanie danych w formie kodu, tak, żeby można było łatwo wkleić je np. jako tablicę w C? * TL;DR: https://github.com/pwndbg/pwndbg/issues/1018 * Przy okazji być może dałoby się zrefaktoryzować te parsery komend dq itd., żeby nie było tyle powtórzen tego samego kodu? ## Dziwny bug #1008 Ktoś zgłosił bug, w którym jak ustawimy rejestr RIP (Instruction Pointer) na niezmapoawny adres, to Pwndbg się crashuje. Trzeba by to sprawdzić, zdebugować i naprawić: https://github.com/pwndbg/pwndbg/issues/1008 (lub zamknąć jeśli to nieaktualne). ## Renderowanie argumentów printf-podobnych funkcji Chodzi o to, żeby gdy jesteśmy na instrukcji `call printf` wyświetlać ładnie argumenty tego printfa. * Issue na to jest stworzone w https://github.com/pwndbg/pwndbg/issues/939 * Mamy też PR z poprzedniego sprintu z EuroPython 2022, który zaczął to dodawać: https://github.com/pwndbg/pwndbg/issues/939 * Trzeba sprawdzić, czy obsługuje to wszystkie przypadki, czy nie jest zbugowane, czy się nie crashuje gdy coś się nie zgadza * No i dodać testy # Co nieco informacji o GDB GDB (GNU Debugger) to konsolowy debugger na Linuxa, który obsługuje debugowanie programów napisanych w przeróżnych językach, na wiele różnych targetów - architektur czy ABI. Można tu np. zobaczyć wewnątrz GDB wykonanie komend: * `show arch` * `show osabi` * `show language` - warto wiedzieć, że "wybrany" język wpływa na to jakie wyrażenia możemy używać w GDB debugując dany program GDB pod spodem korzysta pod spodem z wywołania systemowego [`ptrace`](https://man7.org/linux/man-pages/man2/ptrace.2.html), które pozwala na śledzenie procesów na Linuxie oraz na czytanie i modyfikację ich stanu (pamięci i rejestrów procesora). Wcześniej wymienione `arch`, `osabi` czy `language`, to właściwie "parametry" GDB. Takich parametrów jest **sporo** i można je wszystkie wyświetlić komendą `show`, a co niektórych zmieniać wartość poprzez komendę `set <parametr> <wartosc>`. # Useful Pwndbg APIs ```py # Reading memory # NOTE: may raise gdb.error(...) if `addr` is not readable or mapped in the target pwndbg.memory.write(addr, data) # write `data` bytes into memory, e.g. b'asdf' data = pwndbg.memory.read(addr, count) # read `count` bytes from memory, returns e.g. b'asdf' # Useful constants pwndbg.arch.endian # endianess used by the architecture pwndbg.arch.ptrsize # size of a pointer on a given architecture pwndbg.arch.ptrmask # # Getting useful info about process pwndbg.arch.current # name of target architecture (string) pwndbg.regs.current # list of target registers pwndbg.heap.current # currently used heap pwndbg.vmmap.get() # get list of memory maps mapped into the process pwndbg.auxv.get() # get ELF Auxiliary Vectors info pwndbg.file.get(filepath) # download a file from the target's filesystem pwndbg.symbol.get(address) # fetch text name for a symbol at given address pwndbg.disasm.get(address, n) # disassebmle N instructions at address pwndbg.chain.get(addr, limit, offset, ...) # recursively dereference address pwndbg.string.get(addr, ...) # returns a printable C-string from address ```