Wracając do sposobu, pomysł był dość prosty - chciałem użyć metody debugowania "print it" (polegającej na wypisywaniu czego się da na stdout/do jakiegoś loga, a potem analizie tego), ale okazało się to utrudnione: raz że funkcja była wczytywana do pamięci i wywoływana z poziomu C, więc żadnego systemu importów etc nie miałem, więc printf/puts były niedostępne; dwa - nawet jeśli bym miał puts czy printf, to musiałbym wprowadziło by to zamieszanie z rejestrami, i pewnie masę czasu bym stracił na uganianie się za jakimś rejestrem który printf mi zmienił, a którego ja zapomniałem zapisać, etc.
Natomiast jak wiadomo bez problemu można cokolwiek wypisać z poziomu debuggera - np. GDB (ulubionego debuggera *nixowców)! Ale... (zawsze jest jakieś "ale") nawet pomimo iż porobiłem sobie skrypty do gdb które co-nie-co wypisywały (logowały do pliku konkretnie rzecz biorąc), to i tak po każdej zmianie w mojej funkcji zmieniały się adresy, musiałem przestawiać breakpointy, modyfikować skrypty, że nie wspomnę o tym że ciągłe alt-tabowanie między edytorem skryptu a disassemblerem wygodne też nie było.
Pomyślałem że fajnie by było sobie te instrukcje printujące wrzucić do źródła funkcji, i niech GDB sam sobie stawia tam breakpointy i wykonuje te skrypty. Lampy zamigotały, okno się otwarło, i przyleciała magiczna wróżka która podarowała mi program który to robi! Nie, niestety tak nie było, sam go sobie musiałem napisać ;D
Cały mechanizm jest wyjątkowo prosty (i podatny na pewne zawirowania, ale ja nie o tym...) i składa się z kilku części. Część pierwsza zawarta jest oczywiście w samym źródle funkcji, zawiera skrypty GDB, i wygląda mniej więcej tak:
GDB_BP
; echo Section name under ESI:
; print (char*)$esi
; ---GDB_BP
GDB_BP jest makrem, które zdefiniowane jest po prostu jako 4x nop, i pełni dwojaką funkcje - po pierwsze sam string "GDB_BP" jest wykrywany przez jedną część programu, a 4ry nopy przez drugą. Co to za "części" programu? Ano jest to druga część mechanizmu, którą stanowi program który:
1. Otrzymuje plik ze źródłem funkcji, wyszukuje wszystkie wystąpienia GDB_BP (oprócz takich z %macro w linii), po czym wczytuje skrypt gdb aż do linii z ---GDB_BP, i wrzuca ów skrypt na listę (cały skrypt jako jeden item na koniec listy, ważna będzie potem kolejność tych itemów)
2. Druga część otrzymuje wynik assemblacji źródła (czyli ciąg opcode'ów w przypadku NASM) oraz adres pod który ów plik (funkcja) będzie załadowana, wyszukuje w pliku sygnatury GDB_BP (czyli 4x nop w moim wypadku, można to ofc sobie zmienić), zamienia pierwszy nop z sygnatury na int3 (aby aplikacja zwróciła kontrolę do gdb w tym miejscu), po czym wylicza adres tego int3 i przypisuje n-temu skryptowi na liście (w przypadku n-tej sygnatury) ów adres
Po wykonaniu obu powyższych kroków generowany jest skrypt gdb który wygląda mniej więcej tak:
define gdbhelpfunc
set disassembly-flavor intel
set logging file gdbhelplog.txt
set logging overwrite
set logging on
display/1i $eip
set $endautoscan=0
while $endautoscan==0
c
set $endautoscan=1
if ($eip)==(WYLICZONY ADRES SKRYPTU+1)
SKRYPT ZE ŹRÓDŁA (bez poprzedzających średników oczywiście)
end
...kolejne takie ify...
end
end
set logging off
end
By the way...
On 22nd Nov'24 we're running a webinar called "CVEs of SSH" – it's free, but requires sign up: https://hexarcana.ch/workshops/cves-of-ssh (Dan from HexArcana is the speaker).
I teraz, odpalam gdb na programie który ładuje i używa funkcji, ładuje skrypt (source nazwa_skryptu.gdb), robię break na jakimś main, odpalam program, po czym po break'u odpalam funkcję gdbhelpfunc, i program sobie leci. A wszystko co ciekawe jest w pliku gdbhelplog.txt.
Metoda jest jak widać bardzo prosta, niemniej jednak mi na prawdę ułatwiła życie. Źródła programu są dostępne poniżej (C++, kod jakiś wyjątkowo bezpieczny nie jest, ale nvm). A, makro GDB_BP wygląda tak:
%macro GDB_BP 0
nop
nop
nop
nop
%endmacro
Download: gdbhelper.cpp (7kb)
P.S. Jednorożec do tej pory został znaleziony przez:
1. Netrix
2. deus
3. Patrykuss
4. mieszkos
5. doorsteps
6. Zombiak
7. Dab
8. makhzi
9. cyriel
Powodzenia wszystkim szukającym ;>
Comments:
to jeden z niewielu takich obrazków, które "widzę".
Rozstrojonej? Bo ja wiem, u mnie wygląda OK ;> Po za tym kto wie ;>
Wyślij mi na mejla screena z jednorożcem ;> (mejla podobny do adresu strony... gynvael NASERWIE coldwind.pl)
Co się śmiejesz ;> Mechanizm nie wykracza po za użycie printf i fread, więc jest wyjątkowo prosty ;>
pusha
push arg1
...
push argn
call printf
add esp, n*4
popa
ale nie wiem czy to bylo dopuszczalne w twoim przypadku, pewnie nie, skoro taka miazge zrobiles ;D
Ano właśnie nie było dopuszczalne, z paru powodów ;> Stąd ów mechanizm ;>
Sprawdziłem, odpisałem ;> Good work ;>
Obecnie 13 osób znalazło jednorożca, zaktualizowaną listę wrzucę razem z następnym postem.
Add a comment: