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...
If want to improve your binary file and protocol skills, check out the workshop I'll be running between April and June → Mastering Binary Files and Protocols: The Complete Journey
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: