Skip to content

Instantly share code, notes, and snippets.

@disconnect3d
Last active August 10, 2022 17:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save disconnect3d/6a234e01196234af68fa74bddf14c46b to your computer and use it in GitHub Desktop.
Save disconnect3d/6a234e01196234af68fa74bddf14c46b to your computer and use it in GitHub Desktop.
Pwndbg sprint tasks (10.08.2022)

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 :).

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.

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: pwndbg/pwndbg#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 pwndbg/pwndbg#1047
  • Oraz pomyśleć, czy nie nazwać tego argumentu inaczej
  • Warto zbadać co się stanie jeśli będziemy klikać dalej - czy pokażą się kolejne adresy? Czy powinniśmy renderować poprzednie czy kolejne wartości? Do przedyskutowania

Poprawienie parsowania adresów

TL;DR: pwndbg/pwndbg#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 (którą trzeba dodać jako zależność)

Kod plus minus:

# 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?
  1. 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
  1. Powinniśmy pozwalać na export patchy do pliku
  2. 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: pwndbg/pwndbg#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ć: pwndbg/pwndbg#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 pwndbg/pwndbg#939
  • Mamy też PR z poprzedniego sprintu z EuroPython 2022, który zaczął to dodawać: pwndbg/pwndbg#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, 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

# 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment