User-Mode Linux (UML)

Czym jest UML?

  1. UML jest maszyną wirtualną Linuksa dla Linuksa.
  2. UML oparty jest na Linuxowych wywołaniach systemowych

Zastosowania

  1. Zabezpieczenia w sieci
  2. Ustawianie honeypot-ów
  3. Testowanie aplikacji sieciowych
  4. Nauka Linuksa
  5. Testowanie i odpluskwianie programów

Jak działa UML?

Do uruchomienia UML potrzebujemy dwóch jąder:

  1. Guest kernel - jądro gościa, uruchamiane przez maszynę wirtualną
  2. Host kernel - jadro gospodarza, na ktorym dziala maszyna wirtualna

UML korzysta z jądra systemu tylko do emulowania wirtualnego sprzetu.

UML działa w dwóch trybach: TT i SKAS.
Historycznie pterwszym był tryb TT. Tryb SKAS powstał w celu usunięcia problemów związanych z trybem TT

Tryb TT (Tracing Thread)

  1. Każdy proces jest procesem w jadrze gospodarza
  2. Istnieje specjalny wątek, zwany wątkiem śledzącym, który śledzi wywołania systemowe wszystkich procesów UML
  3. Wątek śledzący anuluje wywołania systemowe i zmusza proces do wejścia do jądra UML, znajdującego się w przestrzeni adresowej tego procesu

Problemy związane z trybem TT:

  1. Możliwośc pisania do jądra UML, gdyż znajdował się w przestrzeni adresowej każdego procesu i domyślnie był zapisywalny
  2. Do obsługi wywołań systemowych wykorzystywane były sygnały, co znacznie spowalniało działanie UML

Tryb SKAS (Separate Kernel Address Space)

Jądra mają oddzielną przestrzeń adresową.

Aby uruchomić UML w trybie SKAS należy zainstalować łatę na jądro gospodarza.
Łata ta implementuje wsparcie dla przestrzeni adresowej używanej przez UML, oraz kilka niezbędnych dodatków dla ptrace

W trybie SKAS z poziomu gospodarza widoczne są tylko 4 procesy:

  1. UML kernel thread - utrzymuje oddzielna przestrzeń adresową, wykonuje kod jądra, przechwytuje wywołania systemowe dla wątków UML
  2. UML userspace thread - wykonuje kod wszystkich procesów UML
  3. Ubd driver asynchronous IO thread
  4. Write SIGIO emulation thread

Jak zainstalować UML?

Wykorzystać gotowy system plików

Gotowy system plików do ściągnięcia, wraz z jądrem można znaleźć np. na http://user-mode-linux.sourceforge.net/
Na tej samej stronie znajduje się również sposób uruchomienia UML. Niestety w takuruchomionym UML nie da się śledzić procesów jądra.

Budowanie ze źródła

  1. Najpierw należy zdobyć jądro Linuxa, np.:
    host% wget http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.16.tar.bz2
    		
    i je rozpakować. W naszym wypadku:
    host% bunzip2 linux-2.6.16.tar.bz2 
    host% tar xf linux-2.6.16.tar 
    host% cd linux-2.6.16
    		
  2. Następnie konfigurujemy UML:
    host% make defconfig ARCH=um
    host% make menuconfig ARCH=um
    		
    Aby móc używiać gdb, w celu odpluskiania jądra musimy ustawić flagi CONFIG_DEBUG_INFO oraz CONFIG_FRAME_POINTER. makeconfig
  3. Teraz możemy zbudować UML:
    host% make ARCH=um
    		
  4. Możemy teraz uruchomić UML
    host% ./linux
    		

Management Console (MConsole)

Przydatnym narzędziem do zarządzania UML jest Management Console.
Jest to niskopoziomowy interfejs do jądra.
Dzięki MConsole można m.in.:

pełna lista instrukcji i sposobów ich wywołania znajduje się na: http://user-mode-linux.sourceforge.net/old/mconsole.html

aby uruchomić MConsole należy uruchomić UML-a z opcją

umid=identyfikator
	
A następnie uruchomić MConsole poleceniem:
host% uml_mconsole identyfikator
	

Tworzenie systemu pliów

W łatwy sposób można w UML tworzyc i dołączać nowe systemy plików:

  1. Tworzymy pusty plik: host% dd if=/dev/zero of=new_filesystem seek=100 count=1 bs=1M
  2. Uruchamiamy UML z dodatkowym parametrem: ubd*=new_filesystem upewniając się uprzednio, ze ubd* nie jest z niczym powiązany.
  3. Tworzymy system plików: host# mkreiserfs /dev/ubd/*
  4. A na końcu go montujemy: UML# mount /dev/ubd/4 /mnt

Współdzielenie systemmu plików między maszynami wirtualnymi

UML udostępnia pliki typu COW (copy-on-write).
Umożliwiają one współdzielenie systemu plików między maszynami wirtualnymi.
IDEA: Współdzielony system pliku służy tylko do odczytu. Wszelkie zmiany przechowywane są przez każdą z maszyn z osobna. Maszyny czytają najświeższe posiadane przez siebie zawartości plików

Dokładniejsze informacje można uzyskać na stronie: http://user-mode-linux.sourceforge.net/old/shared_fs.html

Odpluskwianie jądra i modułów przy użyciu UML

Odpluskwianie jądra

Aby móc śledzić działanie jądra należy zbudować UML ze źródła.
Dla tak zbudowanego UML-a można używać gdb, jak dla każdego innego procesu:

% gdb obj/linux     
GNU gdb Red Hat Linux (6.3.0.0-1.122rh)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".

(gdb) b start_kernel
Breakpoint 1 at 0x80493c3: file /home/jdike/linux/2.6/test/linux-2.6.17/init/main.c, line 461.
(gdb) r
Starting program: /home/jdike/linux/2.6/test/linux-2.6.17/obj/linux 
Reading symbols from shared object read from target memory...done.
Loaded system supplied DSO at 0x615000
Detaching after fork from child process 24108.
Checking that ptrace can change system call numbers...OK
Detaching after fork from child process 24109.
Checking syscall emulation patch for ptrace...OK
Detaching after fork from child process 24110.
Checking advanced syscall emulation patch for ptrace...OK
Checking for tmpfs mount on /dev/shm...OK
Checking PROT_EXEC mmap in /dev/shm/...OK
Checking for the skas3 patch in the host:
  - /proc/mm...not found
Detaching after fork from child process 24111.
  - PTRACE_FAULTINFO...not found
Detaching after fork from child process 24112.
  - PTRACE_LDT...not found
UML running in SKAS0 mode
Adding 25427968 bytes to physical memory to account for exec-shield gap

Breakpoint 1, start_kernel ()
    at /home/jdike/linux/2.6/test/linux-2.6.17/init/main.c:461
461             smp_setup_processor_id();
(gdb) n
469             local_irq_disable();
(gdb) 

	

Breakpoint'y

Czasami UML pomija breakpoint'y ustawione w gdb (błąd gdb (?)). http://user-mode-linux.sourceforge.net proponuje 2 rozwiązania tego problemu:

  1. Uruchomić UML-a normalnie, bez gdb. Kiedy chcesz przyjrzeć się dokładnie jakiemuś kawałkowi kodu, dołączasz gdb do głównego pid-u UML-a:
    gdb ./linux `cat ~/.uml/UMID/pid`
  2. Jeśli i tak nie udało się dotrzeć do miejsca, które chcemy obejrzeć, można w tym miejscu ustawić nieskończoną pętle: while(stop_here) sleep(1); Ze zmienną globalną stop_here ustawioną na 1.

    Kiedy UML zastyga, dołączamy gdb i ustawiamy stop_here na 0, kiedy nasz proces śpi.

Odpluskwianie modułów

Przy użyciu UML możemy także śledzić działanie poszczególnych modułów

Najpierw dodajemy moduł do UML-a:

  1. Kompilujemy moduły: make modules_install INSTALL_MOD_PATH=mods ARCH=um
  2. Tworzymy katalog na moduły w UML-u: UML# mkdir -p /lib/modules/`uname -r`
  3. Kopiujemy moduły, np: host% scp -r mods/lib/modules/2.6.18-rc3-mm2/kernel/ root@UML:/lib/modules/2.6.18-rc3-mm2
  4. W UML-u uruchamiamy depmod: UML# depmod -a
  5. Dodajemy moduł do jądra, np.: UML# modprobe loop

Następnie musimy zlokalizować, gdzie w pamięci znajduje się moduł: