2011-05-07:

Simplified Assembly Loader

asm:x86
Simplified Assembly Loader to prościutki programik (napisany zresztą "na kolanie" w niecałą godzinę) którego zadaniem jest ułatwienie rozpoczęcia nauki assembly x86 pod win32 i "linuxami". Będę z niego korzystał w kursie assembly który planuje wrzucać na youtube). Jak każdy mój tool, asmloader jest open source (tym razem licencja MIT, dla odmiany).

Z mojego doświadczenia wynika, że początek nauki assembly, to w zasadzie nie nauka assembly, a:
- próba wyboru kompilatora / assemblera
- nauka stworzenia hello world w pod ten assembler
- stwierdzenie, że kurs jest pod tasm a my mamy nasm
- stwierdzenie, że nasm nie rozumie składni at&t
- stwierdzenie, że w zasadzie na 20 linii programu w masm/fasm tylko 3 to faktycznie instrukcje assembly
- stwierdzenie, że windows nie ma int 21h i assembly 16-bitowy kiepsko na nim działa
- stwierdzenie, że windows nie ma również int 80h
- etc.

Powyższe rzeczy są ofc ważne, ale fajnie by było to uprościć na samym początku do np.:
- jak najmniejszego overhead'a jeśli chodzi o strukturę pliku źródłowego, czyli np. żadnych tablic importów, sekcji, etc
- zunifikowania API - do zrobienia pierwszych kroków wystarczy w zasadzie z 5 funkcji i fajnie by było gdyby było można je tak samo wywołać pod Windowsem jak i "Linuxami"
Oczywiście, zunifikowane API wprowadza nowe API, co też cudowne nie jest (chociaż niby możnaby rozwiązać to implementując np. API Linuxa pod Windowsem, ale nvm).

Wracając do Simplified Assembly Loader aka asmloader - jest to prosta appka której podaje się nazwę pliku zawierającego 32-bitowy kod maszynowy (bez żadnych nagłówków) i która:
1. Alokuje pamięć Read/Write/Execute na podany plik (+- 0x101 bajtów na stub + padding).
2. Ładuje tam kod maszynowy (na offset +0x100).
3. Skacze do niego.
Oraz dodatkowo, udostępnia API (5 funkcji: exit, putchar, getchar, printf, scanf; taak, to są standardowe funkcje ze standardowej biblioteki C, więc bez problemu można znaleźć ich opis).

Jeśli chodzi o warunki początkowe, to nie można czynić żadnych założeń co do wartości rejestrów, oprócz dwóch:
- EBX zawiera pointer na tablicę API (o tym za chwilę)
- ESP wskazuje na prawidłowy stos (tzn nie trzeba ESP inicjować w żaden sposób, można od razu korzystać ze stosu)

Jeśli chodzi o odwołania do API, to robi się to używając call [ebx+NR_FUNKCJI * 4], gdzie * 4 wynika z wielkości pointera (4 bajty aka 32 bity), a NR_FUNKCJI to:
0 - exit
1 - putchar
2 - getchar
3 - printf
4 - scanf
Np. call [ebx+1*4] wywoła putchar.

Funkcje są cdecl, czyli:
1. Argumenty wrzuca się na stos (stos rośnie w stronę niższych adresów, więc przyjmuje się, że argumenty wrzuca się "od tyłu").
2. Caller (programista) musi zdjąć argumenty ze stosu po tym jak funkcja wróci.
3. To co funkcja zwróci jest w EAX.

Np. wypisanie literki 'H' to (w składni nasm):

push dword 'H' ; wrzucenie na stos literki 'H' (kodu ASCII ofc)
call [ebx+1*4] ; wywołanie putchar
add esp, 4     ; wyczyszczenie stosu

Jeśli chodzi o stub i padding, to stub zawiera kod który wrzuca do EBX adres tablicy API, a padding to po prostu 0xCC (int3), czyli jeśli ktoś zapomni o exit lub ret (lepiej użyć exit, ew trzeba zadbać o zachowanie rejestrów które się używa), to program się crashnie.

Jeśli chodzi o assembler który polecam do kompletu, to jest nim nasm (Netwide Assembler), ale w zasadzie może być to dowolny assembler który umie wygenerować beznagłówkowy kod maszynowy (np. fasm z format binary i use32 też się nada).

To chyba na tyle. Na koniec linki:
asmloader.c (źródło, licencja MIT)
asmloader.exe (binarka pod Win32)

Programik kompiluje się pod GCC (MinGW, "linuxowe" GCC) i MSVC++ (cl.exe), np.
gcc asmloader.c -o asmloader

Jeśli chodzi o potencjalny rozwój, to raczej za dużo go nie będzie. Co najwyżej planuje dodać tryb 64-bitowy, oraz jakieś proste API do wypisania wszystkich rejestrów i RDTSC.

And that's that :)

Comments:

2011-05-07 09:11:34 = Sebaa
{
Wspomniałeś o kursie assembly.. Wnioskuje, że coś się niebawem ruszy w tej kwestii? ;>
}
2011-05-07 10:32:38 = Gynvael Coldwind
{
@Sebaa
Szczerze, to już ruszyło (rzuć okiem na moje uploady na YT). Ale wrzucę dzisiaj jeszcze posta o tym ;)
}
2011-05-07 10:52:26 = Sebaa
{
Oho, jest :) Dzięki za info, gdybyś nie powiedział to pewnie sam bym nie zajrzał ;D
}
2011-05-07 10:53:42 = anonim
{
e tam, ja bym wolal wersje do downloadu, zeby pozniej to ogladac offline. ;>
}
2011-05-07 12:47:29 = norbi123
{
@anonim
a co za problem pobrac z Youtube? w gruncie rzeczy ladujac film na YT juz go masz na dysku, pewnie nawet o tym nie wiedzac:)
}
2011-05-07 14:38:49 = nek
{
łatwo z YT zassac film dopisując -get, do domeny youtube
}
2011-05-07 15:22:27 = anonim
{
o tym ze mozna sciagac z utube to wiem, ale czesto wiaze sie to z obnizeniem jakosci filmu, zreszta samego utuba niezbyt lubie ;]
}
2011-05-07 16:31:06 = olo16
{
@anonim:
Ja tam żadnego obniżenia jakości nie mam, zależy czym ściągasz. Ja polecam do tego JDownloader, tyle że to bardziej kombajn do ściągania, co może niektórych ucieszyć a dla innych będzie tylko niepotrzebnie zwiększać wagę.

A tak w ogóle to witam, to chyba mój pierwszy komentarz, bloga obserwuję od niecałego miesiąca.
}
2011-05-08 12:13:05 = olo16
{
Co do samego loadera: czy jeśli z programu wychodzimy funkcją quit(), to pamięć zaalokowana na kod nie jest zwalniana?
}
2011-05-08 14:44:22 = Gynvael Coldwind
{
@download
W zasadzie wszystko na temat zostało powiedziane ;)
Natomiast jeśli będzie dużo głosów za downloadem, to postaram się żeby opcja downloadu videocastów również była.
(Zresztą, pisałem o tym w komentarzach w następnym poście)

@olo16
Witam witam ;)
Ad loader i quit(), a raczej exit() - system operacyjny wie dokładnie ile pamięci proces ma zaalokowane, więc, przy wyjściu procesu, może tą pamięć zwolnić. W zasadzie jeśli chodzi o zwykłą alokację pamięci i otwieranie handli, to można ufać systemowi operacyjnemu że tą pamięć zwolni i handle zamknie.
Jakieś 15 lat temu (za czasów Windows 95 i 98) był z tym większy problem afair, i memory leaki faktycznie się zdarzały.
}
2011-05-08 14:47:22 = Gynvael Coldwind
{
Btw, jeśli się ofc chce być pedantycznym, to można po prostu dopisać jakąś funkcję dealokującą, i w rzucić ją w atexit(), e.g.:
void clean_at_exit() { tutaj dealkoacja, jesli cos jest zaalokowane ofc; }
atexit(clean_at_exit);
Or similar (ofc to wymaga przynajmniej jednej zmiennej globalnej, ale co zrobić). Lib C (w którym jest implementacja tego mechanizmu) zadba o to by clean_at_exit() bylo wywołane.
}
2011-05-08 19:24:05 = olo16
{
Ok, to jeśli chodzi o systemowe funkcje do alokacji. A jak z malloc i new?
New to podobno w większości implementacji wrapper na malloc, co byłoby logiczne.
Za to malloc musi wywoływać funkcje systemowe, no bo jak inaczej ma alokować.

Widziałem że po malloc też nie zawsze wywołujesz free, więc rozumiem że jest tak samo - OS wszystko sobie zwolni? Heh, we wszystkich kursach i książkach zawsze straszą wyciekami - w zasadzie to dobrze, wyrabia dobre nawyki, ale widzę że wycieki pamięci to raczej problem jeśli program często alokuje i nie zwalnia, wtedy może mu się wyczerpać pamięć.
}
2011-05-08 19:27:18 = olo16
{
PS. jakby się chciało być pedantycznym, natomiast nie chciałoby się zwalniać samemu, to kiedyś się zastanawiałem, że w C++ można by globalnie przeciążyć new, rejestrować alokacje w globalnej tablicy i utworzyć globalny obiekt, który w destruktorze zwalnia wszystkie alokacje z tej tablicy.
}
2011-05-09 07:07:56 = anonim
{
czyli zaimplementowac prymitywny garbage collector ;]
}
2011-05-18 16:10:04 = asd.c
{
Niezbyt związane z Loaderem, ale dla tych co nie widzieli:
http://www.wykop.pl/link/744031/emulator-komputera-napisany-w-js-linux-odpalony-w-przegladarce/

Ciekawe jak to działa :p

P.S. kod cpux86.js bardzo ciekawy :D
}
2012-05-10 12:30:43 = Vincent
{
Kontakt emailowy emo3232@interia.pl

Gdy przepisałem słowo w słowo hello world.
To zapisałem i użyłem następujących komend
nasm hell.asm - działa
asmloader hell.asm
Cytuje "Simplified Assembly Loader v0.0.1 by gynvael.coldwind//vx"
"Code loaded at 0x00030100 <334 bytes>"

Pojawienie się błędu "Program asmloader.exe przestał działać."
Debuguj lub zamknij program

Nie działa mi w ogóle ten loader. Gdy wyrzucę wypisz_xyz i wypisz_hello to i tak program się skonfiguruje tylko że wtedy po wpisaniu A,g nie dostaję żadnego efektu.
}
2013-06-29 16:48:21 = Brak_pomysłu_na_nick
{
Mi też się program sypie. Kompilowałem pod linuxem. Wywala się przy pierwszym wywołaniu funkcji w załadowanym kodzie (tzn. funkcji z API), debugger pokazuje, że ebx=0.
}
2013-06-29 20:35:49 = Gynvael Coldwind
{
@Brak_pomysłu_na_nick
Podrzuć kod który się wysypuje, to rzucę okiem.
}
2013-06-30 12:28:42 = Brak_pomysłu_na_nick
{
To był ten kod, który wypisywał literę H z kursu assembly na youtube.

push 'H'
call [ebx + 4]
add esp, 4
push 0x0A
call [ebx + 4]
add esp, 4
push 0
call [ebx]
add esp, 4

Sypie się na pierwszym call'u, próbuje skoczyć do 0x4, w ebx jak już mówiłem jest 0.
}
2013-06-30 14:09:01 = Gynvael Coldwind
{
@Brak pomysłu na nick
Hmm, mógłbyś mi tą binarkę asmloader'a z plikiem core podrzucić?

Żeby wygenerować core zrób tak:
ulimit -c unlimited
<i tutaj odpal asmloader na tym programie co się wysypuje>

Mój e-mail: gynvael@coldwind.pl

}
2013-07-23 08:13:11 = czystezlo
{
Wydaje mi się, że liczysz na to, że ktoś stworzy serwis do nauki asm i użyje tego programu jako cgi.
}
2013-10-05 16:27:43 = tomek
{
Nie mogę skompilować źródła. Błąd:

nick@domena:~/Folder/C$ gcc asmloader.c -o asmloader
asmloader.c: In function ‘LoadBinary’:
asmloader.c:134:4: error: #error Not implemented.
asmloader.c: In function ‘main’:
asmloader.c:191:4: error: #error Not implemented.

Dodatkowe informacje:

gcc version 4.7.3 (Ubuntu/Linaro 4.7.3-1ubuntu1)
Target: x86_64-linux-gnu
}
2013-10-05 20:44:29 = tomek
{
Gdyby ktoś miał podobny problem i architekturę, to do gcc należy doinstalować
sudo apt-get install libc6-dev-i386
i kompilować:
gcc -m32 -o asmloader asmloader.c
}
2016-06-01 18:13:33 = DK CLAPka
{
@Vincent
*clap* *clap* *clap*

zrobiłeś:
nasm cuź.asm ; Output -> cuź lub cuź.BIN
asmloader cuź.ASMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM ; zamiast . czy .bin

P.S.
Wiem, że to dawny komentarz, ale musiałem coś na ten temat wspomnieć.
.
.
.
.
.
.
.
.
.
.
.
.
.






















*clap*
}
2017-04-02 16:44:32 = AleksanderGaming
{
Rozumiem, że z tym kodem mogę robić co mi się podoba? W sensie mogę go normalnie użyć jako procedury przy aplikacji wykonującej kod jakiegoś pliku binarnego? Ostatnio trochę pracuję(a raczej bawię się) nad pewną aplikacją, a po dodaniu kodu do aplikacji i przetestowaniu kod pańskiego asmloadera okazał się niesamowicie przydatny, zwłaszcza że moja aplikacja nie będzie odczytywała żadnych skomplikowanych formatów plików, tylko najprostsze, bez rozszerzenia, ewentualnie z rozszerzeniami *.bin i *.com.
}
2017-04-02 16:51:37 = Gynvael Coldwind
{
@AleksanderGaming
Asmloader jest na licencji MIT, więc rzuć najlepiej okiem na samą licencje (sekcja "-- License Stuff" w kodzie źródłowym: https://github.com/gynvael/asmloader/blob/master/asmloader.c) :)
Ta licencja generalnie pozawala na większość (wszystkie?) ciekawe zastosowania, i stawia jeden wymóg:
"The [..] copyright notice and this permission notice shall be included in all copies or substantial portions of the Software."
}
2017-04-02 17:00:32 = AleksanderGaming
{
Jeżeli dobrze zrozumiałem: licencja musi obowiązywać również moją aplikację?
}
2017-04-02 17:14:14 = Gynvael Coldwind
{
@AleksanderGaming
Nie, to nie GPL. Po prostu musisz kopię licencji (łącznie z copyright notice) dołączyć do swojego programu (jako plik tekstowy; zazwyczaj dokleja się na koniec swojej licencji albo robi jakiś osobny plik na to).
}
2017-04-02 17:17:37 = AleksanderGaming
{
Dzięki :)
}

Add a comment:

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