Zine.net online

Witaj na Zine.net online Zaloguj się | Rejestracja | Pomoc
w Szukaj

mgrzeg.net - Admin on Rails :)

Co Ty wiesz o zabijaniu... usług?

Wyobraźmy sobie następującą sytuację: mamy usługę, którą chcemy chronić przed administratorem systemu – a właściwie przed możliwością jej wyłączenia. Administrator śmieje nam się w nos, że skoro jest administratorem systemu, to możemy mu co najwyżej... skoczyć po piwo ;), jak będzie chciał, to i tak ją sobie zatrzyma. Zapewne ma rację, a już na pewno wtedy, gdy ma fizyczny dostęp do komputera, jednak zależy nam na tej usłudze, więc możemy naszemu niesfornemu administratorowi trochę sprawę utrudnić. Jak to zrobić? Oto jedno z możliwych rozwiązań.

"Koń, z drzewa koń na biegunach..."

Wyobraźmy sobie, że usługa, na której nam zależy ma strażnika. Niech to będzie inna usługa, która obserwuje poczynania administratora, i jak tylko chce on ją wyłączyć, lub zmienić typ startu, to strażnik przywraca ją do stanu, na którym nam zależy. Cwany administrator szybko się zorientuje, że coś jest nie tak i postanowi wyłączyć naszego strażnika, żeby móc później spokojnie wyłączyć usługę. A zatem mamy sytuację:
Usługa G1 ---> monitoruje usługę ---> U (G1--->U).
I faktycznie, po wyłączeniu strażnika G1 można zrobić, co się chce z usługą U. Dodajmy jednak do zabawy jeszcze jednego strażnika G2 i ustawmy:
G1 ---> U <--- G2
G1 <---> G2
A zatem obaj straźnicy monitorują usługę U i przy okazji dbają o siebie nawzajem. Proste?
Zobaczmy jak to można zrealizować.

Instalacja usługi

Samą usługę przygotowujemy korzystając z kreatora usług i standardowej procedury, opisanej na przykład w moim tekście o Viście, więc opis pomijam.
Pierwszy krok w naszej zabawie to instalacja programowa usługi. Do tego celu można użyć klasy AssemblyInstaller z przestrzeni System.Configuration.Install:


Hashtable mySavedState = new Hashtable();
string[] commandLineOptions = new string[1]{"/LogFile=install.log"};
AssemblyInstaller myAssemblyInstaller = new
            AssemblyInstaller(Assembly.GetExecutingAssembly(), commandLineOptions);
myAssemblyInstaller.UseNewContext = true;
myAssemblyInstaller.Install(mySavedState);
myAssemblyInstaller.Commit(mySavedState);

Deinstalacja usługi różni się od powyższego kodu tylko wywołaniem w odpowiednim miejscu metody Uninstall zamiast Install obiektu myAssemblyInstaller.
Moglibyśmy użyć narzędzia installutil, a właściwie metody InstallHelper klasy ManagedInstallerClass, z której installutil.exe korzysta, jednak dokumentacja zniechęca nas do tego.
Dodatkowo po zainstalowaniu usługi uruchamiamy ją, korzystając z pomocniczej klasy ServiceController:


ServiceController sc = new ServiceController(serviceName);
if (sc.Status != ServiceControllerStatus.Running)
  sc.Start();
sc.Refresh();

Monitorowanie innych usług

Tu z pomocą przychodzi nam WMI, a właściwie bardzo sympatyczne narządko dostępne na stronach Microsoftu i opisane w tekście ze stycznia 2006 r  w TechNet Magazine.

Okno WMI Code Creatora

Z jego pomocą generujemy odpowiedni kod, dla przykładu:


using System;
using System.Management;
using System.Windows.Forms;

namespace WMISample
{
  public class WMIReceiveEvent
  {
    public WMIReceiveEvent()
    {
      try
      {
        WqlEventQuery query = new WqlEventQuery(
            "SELECT * FROM __InstanceModificationEvent WITHIN 10 WHERE TargetInstance.Name = 'TlntSvr'");

        ManagementEventWatcher watcher = new ManagementEventWatcher(query);
        Console.WriteLine("Waiting for an event...");

        watcher.EventArrived +=
            new EventArrivedEventHandler(
            HandleEvent);

        // Start listening for events
        watcher.Start();

        // Do something while waiting for events
        System.Threading.Thread.Sleep(10000);

        // Stop listening for events
        watcher.Stop();
        return;
      }
      catch (ManagementException err)
      {
        MessageBox.Show("An error occurred while trying to receive an event: " + err.Message);
      }
    }

    private void HandleEvent(object sender,
        EventArrivedEventArgs e)
    {
      Console.WriteLine("__InstanceModificationEvent event occurred.");
    }

    public static void Main()
    {
      WMIReceiveEvent receiveEvent = new WMIReceiveEvent();
      return;
    }

  }
}

Wybrałem wersję asynchroniczną, stąd dodatkowa metoda HandleEvent obsługująca zdarzenie __InstanceModificationEvent. Zauważmy, ze zdarzenia możemy zawężać do bardziej szczegółowych dodając odpowiednie warunki w zapytaniu WqlEventQuery. Dla przykładu zmiana stanu usługi na Stopped lub Paused może być wyrażona poprzez:
AND (TargetInstance.State = \"Stopped\" OR TargetInstance.State = \"Paused\")
A zmiana trybu startu:
AND (TargetInstance.StartMode = \"Manual\" OR TargetInstance.StartMode = \"Disabled\")"
W reakcji na odpowiednie zdarzenia możemy odpowiednio: uruchamiać ponownie obserwowaną usługę (odpowiedni przykład już jest w rozdziale ‘Instalacja usługi’) oraz przywracać tryb uruchomienia na automatyczny. Zauważmy w tym miejscu, że przy instalacji nie zmienialiśmy trybu startu usługi na automatyczny – pozostawiam to jako proste ćwiczenie. Do zmiany trybu startu rozszerzamy klasę ServiceController o interesujące nas metody:


public void ChangeStartupType(ServiceStartMode value){
  if (ServiceName != null)
  {
    string path = "Win32_Service.Name=\"" + ServiceName + "\"";
    ManagementPath p = new ManagementPath(path);
    ManagementObject mo = new ManagementObject(p);
    object[] parameters = new object[1];
    parameters[0] = Enum.GetName(typeof(ServiceStartMode), value);
    mo.InvokeMethod("ChangeStartMode", parameters);
  }
}

I to właściwie tyle, jeśli idzie o prostą implementację.
Przykładowa implementacja dostępna jest do pobrania w przykładach do tekstów (ServiceGuard). Oto kilka uwag dotyczących sposobu użycia:
Instalacja i użycie.
W katalogu bin dostępna jest skompilowana wersja usługi oraz jej kopia z innymi parametrami startowymi. Nazwy usług dostępne są w pliku konfiguracyjnym pod parametrem ServiceName, usługi do obserwacji jako lista ServicesToWatch. Poprzez proste kopiowanie plików można tworzyć dodatkowe usługi, należy jednak pamiętać o zmianie parametrów startowych. Żeby zainstalować daną usługę, wystarczy uruchomić ją z wiersza poleceń z parametrem –i, do usunięcia należy skorzystać z parametru –u.
Zauważmy, że tworząc dłuższy cykl usług czynimy go bardziej odpornym na próby zatrzymania. Wykorzystywanie zdarzeń WMI jest jednak kosztowne i obciąża pamięć i procesor.

Dalsze rozszerzenia

Możliwości dalszych rozszerzeń jest wiele. Po pierwsze – klonowanie usług. W przypadku, gdy adminowi uda się zatrzymać odpowiednio dużo usług z naszego cyklu, możemy pokusić się o programowe tworzenie dodatkowych usług o przypadkowych nazwach plików i usług, i dołączać je do cyklu, czyniąc sprawę jeszcze bardziej beznadziejną. Można pokusić się o inną implementację i oprzeć się np. na sterownikach, zamiast usługach, jednak to zupełnie inny temat, zmierzający w kierunku tworzenia rootkitów.

Ciekaw jestem jakie macie pomysły na obejście przedstawionego rozwiązania, zapraszam do komentarzy :)

Opublikowane 2 lipca 2007 12:11 przez mgrzeg
Filed under: ,

Attachment(s): http://zine.net.pl/Misc/ZineNet.ServiceGuard.v.0.0.1.0.zip

Powiadamianie o komentarzach

Jeżeli chciałbyś otrzymywać email gdy ta wypowiedź zostanie zaktualizowana, to zarejestruj się tutaj

Subskrybuj komentarze za pomocą RSS

Komentarze:

 

ucel said:

Trojana piszesz?

:P

lipca 3, 2007 10:46
 

mgrzeg said:

Alez skad! Tekst ma charakter czysto edukacyjny i raczej zalezalo mi na ciekawym przykladzie ilustrujacym uzycie zdarzen WMI i naprawde fajnego toola do generacji kodu.

lipca 3, 2007 11:53

Co o tym myślisz?

(wymagane) 
(opcjonalne)
(wymagane) 

  
Wprowadź kod: (wymagane)
Wyślij

Subskrypcje

W oparciu o Community Server (Personal Edition), Telligent Systems