Wyrażenie "assemblerowe źródło" mi jakoś nie leży, ale po 15 minutach straconych na próbę wymyślenia czegoś lepszego, co określało by źródło kawałka funkcji stworzonej w assemblerze, i jednocześnie było dość zwięzłe, stwierdziłem że musi zostać. Wracając do tematu, a w zasadzie dopiero go rozpoczynając, dzisiaj będzie o pewnym sposobie którym się wspomogłem podczas gonitwy za bugiem w pewnej dość rozbudowanej funkcji napisanej w asmie (dialekt NASM). Ów funkcja miała wyszukiwać w obrazie biblioteki (w pamięci, MACH-O) adres podanej metody z podanej klasy (objc), z podanej biblioteki na OS X, i składała się z trzech czy czterech zagłębień pętli. No i oczywiście nie działała jak należy, a ręczne debugowanie tego przyjemne nie było (potem się okazało że cały pomysł nie do końca działa, ale to historia na inny dzień).

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
   

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:

2009-04-05 12:04:41 = mt3o
{
Ten jednorożec to gdzieś w okolicach tej rozstrojonej grafiki gór?
}
2009-04-05 12:08:16 = mt3o
{
edit: znalazłem skurczybyka. cwane.
to jeden z niewielu takich obrazków, które "widzę".

}
2009-04-05 13:00:44 = Gynvael Coldwind
{
@mt3o
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)
}
2009-04-06 14:16:19 = ged_
{
"Cały mechanizm jest wyjątkowo prosty", lol.
}
2009-04-06 23:52:29 = Gynvael Coldwind
{
@ged_
Co się śmiejesz ;> Mechanizm nie wykracza po za użycie printf i fread, więc jest wyjątkowo prosty ;>
}
2009-04-07 01:30:35 = ged_
{
proste to by bylo przekazanie adresu printfa jako parametr/w zmiennej/przez rejestr i potem:

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
}
2009-04-07 02:11:22 = Gynvael Coldwind
{
@ged_
Ano właśnie nie było dopuszczalne, z paru powodów ;> Stąd ów mechanizm ;>
}
2009-04-08 14:27:57 = Notopro
{
Jednorożec znaleziony. Gynvael jak możesz to sprawdź pocztę :)
}
2009-04-09 00:47:03 = Gynvael Coldwind
{
@Notopro
Sprawdziłem, odpisałem ;> Good work ;>

Obecnie 13 osób znalazło jednorożca, zaktualizowaną listę wrzucę razem z następnym postem.
}
2009-04-11 14:43:30 = jarek
{
Unicorn made my day :)
}

Add a comment:

Nick:
URL (optional):
Math captcha: 9 ∗ 2 + 1 =