owned this note
owned this note
Published
Linked with GitHub
# 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
```