Windows servisleri hoş şeylerdir. Interaktif değildirler, mesaj göstermelerini, kullanıcı ile iletişime geçmelerini sağlayamazsınız ama bunun haricinde bütün bilgisayarın kontrolüne sahiptirler ve çalışmaları için birinin login etmiş olması gerekmez.
Tek sorun, standart bir windows servisi kurduğunuzda, onu bir de "install" etmek gerekir. Aslında gerekmez ama Microsoft bu konuda pek açıklama yapmamış, taslaklarına da pek bir opsiyon koymamıştır.
Bu yazıda kendi kendini kurabilen, kaldırabilen bir windows servisi nasıl yazılır onu anlatacağım.
Öncelikle yeni bir windows servisi oluşturalım. Visual Studio'da CTRL+SHIFT+N tuşlarına basarak yeni projemizi oluşturalım:
Adını değiştirmeyi unutmayın. Sonradan değiştirmek için bir çok değişiklik yapmak gerekir; assembly name, klasör adları, namespace adları, falan filan.. Servis projemiz servisimizin "Design" görünümü ile açılır. Çok güzel, çünkü buraya hemen bir şey eklemek istiyoruz. Sağ tuşa tıklayıp, "Installer" sisteminin bu servise eklenmesini isteyelim.
Hemen ardından da ServiceInstaller1'i seçim, servis seçeneklerimizi değiştirelim. "Description", yani açıklama yazmadan önce çektim fotoğrafı yanlışlıkla, isterseniz oraya da bir açıklama yazabilirsiniz.
Servisin prosesi için bir ayar değişikliğimiz var. Hemen hemen her zaman için servisler LocalService(yönetici olmayan interaktif olmayan kullanıcı) veya LocalSystem(yönetici interaktif kullanıcı) adıyla açılır. Bu hesaplar üstüne açtığınız servislerde bir kullanıcı adı veya şifre sağlamak zorunda da değilsinizdir.
Şimdi servisimizin adını değiştirelim. Solution Explorer'da seçin, F2'ye basın, yeni ismi yazın. Size aynı isimdeki kod elemanlarını da değiştirip değiştirmeyeceğini soracak, tabii kabul edin.
Ardından F7'ye basıp, servisin asıl yapması gerekenleri ekleyebilirsiniz. O kısım beni ilgilendirmiyor, benimkini de sonra ekleyeceğim.
Tabii şimdi bir de bu installer class'ları kullanarak, asıl kurulumu yapmamız lazım. Proje'ye referans ekleyelim. Yani aslında zaten eklenmiş olması lazım, ama arada Visual Studio sapıtıyor.
"System.Configuration.Install" namespace'inin referanslar arasında olduğundan emin olalım.
Şimdi işimiz Program.cs ile.. Aslında gayet standart, ServiceBase listesini oluşturup sonra bu servisi çalıştıran bir kısım var. Bazı durumlarda bunu da yapmalı, ne zaman? Servis olarak açıldığında...
Servis olarak açıldığını anlamanın da inanılmaz basit bir yolu var. Servisler, interaktif olarak çalışmaz. Bundan dolayı bu kısmı bir "if" içine alıp, interaktif değilse burayı çalıştıracağından emin olalım...
Sadece "interaftif değilsen, servisi çalıştır, işin bitince de 'return' et" deyiveriyoruz.
Ya interaktifse? O zaman önce yönetici olup olmadığını kontrol edelim. Yönetici olarak çalıştırılmamışsa, programcı bir şeyi test ediyor demektir, Servisimizin "Start()" fonksiyonunu çalıştıralım ki normalde ne yapacaksa onu yapsın servis... Yönetici olarak çalıştırılmışsa, servisin kurulu olup olmadığını kontrol edelim. Kurulu ise kaldırsın, yoksa kendini adam gibi bir yere kopyalayıp kursun.
İşte bu kadar.. Legal programlar için tatlı bir özelliktir bu ama, tabii ki kendini servis olarak çabucak kuruveren bir trojan da aynı derecede tatlıdır.
Tek sorun, standart bir windows servisi kurduğunuzda, onu bir de "install" etmek gerekir. Aslında gerekmez ama Microsoft bu konuda pek açıklama yapmamış, taslaklarına da pek bir opsiyon koymamıştır.
Bu yazıda kendi kendini kurabilen, kaldırabilen bir windows servisi nasıl yazılır onu anlatacağım.
Öncelikle yeni bir windows servisi oluşturalım. Visual Studio'da CTRL+SHIFT+N tuşlarına basarak yeni projemizi oluşturalım:
![hpnPZ.jpg](https://kek.gg/i/hpnPZ.jpg)
Adını değiştirmeyi unutmayın. Sonradan değiştirmek için bir çok değişiklik yapmak gerekir; assembly name, klasör adları, namespace adları, falan filan.. Servis projemiz servisimizin "Design" görünümü ile açılır. Çok güzel, çünkü buraya hemen bir şey eklemek istiyoruz. Sağ tuşa tıklayıp, "Installer" sisteminin bu servise eklenmesini isteyelim.
![4rcx6Z.jpg](https://kek.gg/i/4rcx6Z.jpg)
Hemen ardından da ServiceInstaller1'i seçim, servis seçeneklerimizi değiştirelim. "Description", yani açıklama yazmadan önce çektim fotoğrafı yanlışlıkla, isterseniz oraya da bir açıklama yazabilirsiniz.
![3zZdLY.jpg](https://kek.gg/i/3zZdLY.jpg)
Servisin prosesi için bir ayar değişikliğimiz var. Hemen hemen her zaman için servisler LocalService(yönetici olmayan interaktif olmayan kullanıcı) veya LocalSystem(yönetici interaktif kullanıcı) adıyla açılır. Bu hesaplar üstüne açtığınız servislerde bir kullanıcı adı veya şifre sağlamak zorunda da değilsinizdir.
![6jqpx9.jpg](https://kek.gg/i/6jqpx9.jpg)
Şimdi servisimizin adını değiştirelim. Solution Explorer'da seçin, F2'ye basın, yeni ismi yazın. Size aynı isimdeki kod elemanlarını da değiştirip değiştirmeyeceğini soracak, tabii kabul edin.
Ardından F7'ye basıp, servisin asıl yapması gerekenleri ekleyebilirsiniz. O kısım beni ilgilendirmiyor, benimkini de sonra ekleyeceğim.
Tabii şimdi bir de bu installer class'ları kullanarak, asıl kurulumu yapmamız lazım. Proje'ye referans ekleyelim. Yani aslında zaten eklenmiş olması lazım, ama arada Visual Studio sapıtıyor.
"System.Configuration.Install" namespace'inin referanslar arasında olduğundan emin olalım.
Şimdi işimiz Program.cs ile.. Aslında gayet standart, ServiceBase listesini oluşturup sonra bu servisi çalıştıran bir kısım var. Bazı durumlarda bunu da yapmalı, ne zaman? Servis olarak açıldığında...
Servis olarak açıldığını anlamanın da inanılmaz basit bir yolu var. Servisler, interaktif olarak çalışmaz. Bundan dolayı bu kısmı bir "if" içine alıp, interaktif değilse burayı çalıştıracağından emin olalım...
Sadece "interaftif değilsen, servisi çalıştır, işin bitince de 'return' et" deyiveriyoruz.
Kod:
if (!Environment.UserInteractive)
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new ZwService()
};
ServiceBase.Run(ServicesToRun);
return;
}
Ya interaktifse? O zaman önce yönetici olup olmadığını kontrol edelim. Yönetici olarak çalıştırılmamışsa, programcı bir şeyi test ediyor demektir, Servisimizin "Start()" fonksiyonunu çalıştıralım ki normalde ne yapacaksa onu yapsın servis... Yönetici olarak çalıştırılmışsa, servisin kurulu olup olmadığını kontrol edelim. Kurulu ise kaldırsın, yoksa kendini adam gibi bir yere kopyalayıp kursun.
Kod:
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
namespace ZwService
{
static class Program
{
static **** Main()
{
if (!Environment.UserInteractive)
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new ZwService()
};
ServiceBase.Run(ServicesToRun);
return;
}
[Color="blue"] //Şu anki identity için Windows Principallarını alıyoruz.[/color]
[Color="blue"] //Eğer giriş yapılan kullanıcı "admin" ise, "Yönetici Olarak Çalıştır" seçerek çalıştırılmıştır.[/color]
if (new System.Security.Principal.WindowsPrincipal(System.Security.Principal.WindowsIdentity.GetCurrent()).IsInRole(System.Security.Principal.WindowsBuiltInRole.Administrator))
{
[Color="blue"] //Appdata klasörü altına yeni bir klasör açıyorum, buraya kuracak veya zaten kurulmuşsa bu klasörü kaldıracağım.[/color]
string kurulumKlasoru = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ZwUpdate");
[Color="blue"] //Dosyamız da bu klasörün hemen altında olacak.[/color]
string kurulumYeri = System.IO.Path.Combine(kurulumKlasoru, "ZwUpdate.exe");
[Color="blue"] //Servis kontrolörü oluşturmaya çalışıyorum. Kurulu muyum, değil miyim buradan anlayacağım.[/color]
System.ServiceProcess.ServiceController kontrol = new System.ServiceProcess.ServiceController("ZwUpdateServer");
[Color="blue"] //Eğer servis aslında kurulmamışsa, burası InvalidOperationException verecek.[/color]
try
{
[Color="blue"] //Status'u "Stopped" yani durmuş değil ise, durdurmaya çalışalım.[/color]
[Color="blue"] //Aslında o anda servis "durmaya hazırlanıyor" da olabilir, n'olur n'olmaz bu stop komutunu da[/color]
[Color="blue"] //try..catch içine alalım.[/color]
if (kontrol.Status != ServiceControllerStatus.Stopped) try
{
kontrol.Stop();
}
catch { }
[Color="blue"] //Bu noktaya gelmişsek kesinlikle servisimiz kuruludur. Şimdi kaldıralım.[/color]
System.Configuration.Install.ManagedInstallerClass.InstallHelper(new string[] { "/u", kurulumYeri });
[Color="blue"] //Bu kadar.. /u ve kurulu olduğu yeri parametre olarak veriyoruz, yetiyor.[/color]
[Color="blue"] //Şimdi de recursive olarak kurulum klasöründe ne varsa silelim, klasörü de kaldıralım.[/color]
System.IO.Directory.Delete(kurulumKlasoru, true);
Console.WriteLine("Servis başarıyla kaldırıldı.");
}
catch (InvalidOperationException)
{
[Color="blue"] //Buraya düşmüşse, servis kurulu değildir, bizim kurmamız gerekir.[/color]
[Color="blue"] //Önce gerekli klasörü açalım..[/color]
System.IO.Directory.CreateDirectory(kurulumKlasoru);
[Color="blue"] //Sonra kendimizi kopyalayalım.[/color]
[Color="blue"] //Biz şu anda çalışan prosesin, ana modülünün dosyasıyız. Ondan eminiz çünkü şu anda bir dll'den çalışmıyoruz.[/color]
System.IO.File.Copy(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName, kurulumYeri);
[Color="blue"] //Şimdi kuralım.. Gene tek komutluk bir işlem...[/color]
System.Configuration.Install.ManagedInstallerClass.InstallHelper(new string[] { kurulumYeri });
[Color="blue"] //Servisimiz kuruldu. Şimdi bu yeni kurulan servisi çalıştıralım.[/color]
using (System.ServiceProcess.ServiceController kontroller = new System.ServiceProcess.ServiceController("ZwUpdateService")) kontroller.Start();
[Color="blue"] //Servisimiz kuruldu ve çalışıyor.[/color]
}
return;
}
[Color="blue"] //Burada isek, yönetici değiliz demektir. O zaman programcının yapmak istediği "debug" olmalı..[/color]
[Color="blue"] //Bazı programlarımda, bu noktada "ayar penceresi" gibi şeyleri gösteririm, ayarları değiştirtirim. Seçim sizin.[/color]
[Color="blue"] //Siz istediğiniz işlemi bu noktaya koyun.[/color]
}
}
}
İşte bu kadar.. Legal programlar için tatlı bir özelliktir bu ama, tabii ki kendini servis olarak çabucak kuruveren bir trojan da aynı derecede tatlıdır.