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 :)