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
   

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:

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

}
2009-04-05 11: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 12:16:19 = ged_
{
"Cały mechanizm jest wyjątkowo prosty", lol.
}
2009-04-06 21: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-06 23: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 00:11:22 = Gynvael Coldwind
{
@ged_
Ano właśnie nie było dopuszczalne, z paru powodów ;> Stąd ów mechanizm ;>
}
2009-04-08 12:27:57 = Notopro
{
Jednorożec znaleziony. Gynvael jak możesz to sprawdź pocztę :)
}
2009-04-08 22: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 12:43:30 = jarek
{
Unicorn made my day :)
}

Add a comment:

Nick:
URL (optional):
Math captcha: 2 ∗ 5 + 8 =