20.07.2024

Crowdstrike strikes back

Dün, 19 Temmuz 2024, bir çok firma için bir siber felaket gibiydi. Havayolları, bankalar, hastaneler, büyük basın kuruluşları... Hepsi ya hizmet veremediler, ya da kesintiler yaşadılar. Uçak seferleri durdu, ameliyatlar iptal edildi, TV yayınları durdu. Herkes dünya çapında mavi ekran yaşayan Windows'ları görünce Microsoft kaynaklı olduğunu düşündü. Ama bugünkü suçlumuz Crowdstrike.

Bütün internet bu konu ile ilgili haberlerler dolu [1],[2]. Önemli konu şu ki, bu ne ilk ne de son olacak. Daha önce bu olayın benzerini benim hatırladığım McAfee ve Avira ile de yaşamıştık. Bilişim dünyası bu gibi olaylara hazır değil mi acaba? Bu gibi olaylara hazır olunabilir mi? Güncellemelerin önce küçük bir grup üzerinde denenmesi ve sorun yaşanmaması sonrasında diğer sistemlere uygulanabilmesi yeni kavramlar değil, ama bunların bir uzman tarafından her gün yapılması çok pratik değil. Belki bunları önümüzdeki günlerde otomatik olarak yapan sistemler tasarlanabilir. Aynı şey işletim sistemi güncellemeleri (Windows, Linux, vs) için de gerekli. Ama güncellemeleri kapatmak hiç bir zaman çözüm olarak düşünülmemeli.

---

[1] https://www.bbc.com/news/articles/cp4wnrxqlewo
[2] https://edition.cnn.com/2024/07/19/tech/crowdstrike-update-global-outage-explainer/index.html

19.07.2024

Get-ChildItem ve -Exclude parametresi

Bazen bir dosya aramak için -Path, -Recurse, -Include parametrelerini kullanmak yetmez, bir de -Exclude kullanmak gerekir. Örneğin Windows klasörünün altında sysdm.cpl dosyasını arıyorum, ama WinSxS klasörünün altındaki kopya(lar|s)ını listelemek istemiyorum. O amaçla aşağıdaki komutlardan herhangi birini kullandığımda hiç sonuca ulaşamıyorum.

Get-ChildItem -Path C:\Windows -Include sysdm.cpl -Recurse -Exlude "C:\Windows\WinSxS"
Get-ChildItem -Path C:\Windows -Include sysdm.cpl -Recurse -Exlude "C:\Windows\WinSxS*"
Get-ChildItem -Path C:\Windows -Include sysdm.cpl -Recurse -Exlude "*WinSxS*"

Çünkü, Exclude parametresi bunun için tasarlanmamış. Sadece dosya isimlerinde bir kıyaslama yapabiliyor. Aynı -Include'da olduğu gibi. Alternatif çözümler mevcut. Örneğin

Get-ChildItem -Path C:\Windows -Include sysdm.cpl -Recurse | where-Object {$_.FullName -notlike "*WinSxS*"}

Ama bu şekilde aslında Get-ChildItem bütün WinSxS alt klasörünü tarıyor, ama çıkan sonuçarın içinde WinSxS geçiyorsa bunlar ekrana yazılmıyor. Bu istediğim birşey değil, ama daha iyisini bulamadım.

Windows klasörünün altında bazı yüksek güvenlikli alt klasörler olduğu için ekranın kırmızı hata mesajları ile dolmasını engellemek için -ErrorAction SilentlyContinue (veya kısaca -EA silent) kullanılabilir.

Get-ChildItem -Path C:\Windows -Include sysdm.cpl -Recurse -ErrorAction SilentlyContinue | 
where-Object {$_.FullName -notlike "*WinSxS*"}

Bunu uzun bulanlar için (ki ben de buluyorum) alias'lar ve kısaltmalar var

dir C:\Windows -Inc sysdm.cpl -Rec -EA silent | ? {$_.FullName -notlike "*WinSxS*"}

18.07.2024

WinSxS, sfc ve dism

Uzun dönem Windows kullanıcıları, sistem klasörlerinin (genellikle C:\Windows) altında WinSxS adında bir klasörün olduğunu görmüşlerdir. Bu klasör korunan ve içeriği administrator ile bile değiştirilemeyen bir yapıya sahiptir. Kurulumdaki bütün sistem dosyalarının asılları bu klasörün altındadır. Kullanımdaki kopyası ise aslında bir kopya değil, dosyanın aslına bir harlink'tir. Örnek olarak explorer.exe'ye bakalım.

C:\> dir explorer.exe | fl FullName, LinkType, Target

FullName : C:\Windows\explorer.exe
LinkType : HardLink
Target   : {C:\Windows\WinSxS\amd64_microsoft-windows-explorer...

Bu klasördeki bütün dosyaları görmek için

dir * -file | ft Name, LinkType

kullanabiliriz. Evet, bu güvenlik önlemlerine karşı kötü niyetli yazılımların da karşı yöntemleri var. Herşeye rağmen hardlink bozulabilir, sistem dosyası kötü bir kopya ile değiştirilebilr. Hatta WinSxS klasörü altındaki dosya bile değiştirilebilir. Sistemimizde beklenmeyen bir durum olduğunda her kaynakta ilk yapılması gereken

sfc /scannow

olarak önerilmiş. Arka planda bu komut, sistem dosyalarını tarar, bütünlüğünü, denetler ve güvenilir bir katalogdaki özelliklere (imza, hash vs) sahip olup olmadığını kontrol eder. Ayrıca hardlink'inin doğru olup olmadığına da bakar. Sonra eğer burada bir sorun algılarsa WinSxS altındaki doğru dosyaya olan hardlink'i tekrar oluşturarak onarmaya çalışır. WinSxS altında bir dosyanın birden fazla sürümü olabilir. Bütün sürümler yinw WinSxS klasöründe saklanır. O yüzden en son güncelleme ile gelen sürüme hardlink oluşturulması önemli.

WinSxS klasöründe bir bozulma olmuşsa sfc bu aşamada yetersiz kalır. Bu bozulmayı onarmak için kullanılacak araç dism. Dism'in kafa karıştıran 3 farklı komutu var:

  • CheckHealth
  • ScanHealth
  • RestoreHealth

Bu 3 komutu, bir disk taramasındaki işlemlere benzeterek açıklamaya çalışacağım.

Diskimizde bir sorun olduğunu anlamak için bir tarama yapmak istiyoruz. Ama hızlı olmasını istiyoruz. Bütün diski taramak çok zaman ve enerji alan bir işlem. Aslında diskte işlem yaparken hata ile karşılaşan bir program var mı diye baksak olmaz mı? Bunun için önce evenlog'a bakıyoruz, herhangi bir program buraya disk ile ilgili hata olayı yazmış mı diye. Bu CheckHealth'a karşılık gelen bir işlem. Bütün WinSxS klasöründeki dosyaları taramak da çok zaman ve enerji alan bir işlem olduğu için kaydı düşülen bir sorun var mı, ona bakıyoruz. Bu işlemde bir düzeltme işlemi uygulanmıyor. Örnek kullanım:

DISM /Online /Cleanup-Image /CheckHealth

İkinci komut ScanHealth, daha çok diskte bir düzeltme işlemi yapmadan taramaya benziyor. Sistem açıkken diskte onarım yapmak istemiyorsak, sadece hata var mı yok mu bize onu söylesin diyorsak bu işlem uygulanabilir. Yine bu adımda da diskte bir düzeltme yapılmayacak. Örnek kullanım:

DISM /Online /Cleanup-Image /ScanHealth

Üçüncü komut RestoreHealth ise aktif olarak diskte hataları bul ve düzelt komutu gibi. Bütün WinSxS klasörü taranır, bulunan hatalar (ISO dosyasından veya internetten) bozulmamış kopyalar kullanılarak düzeltilir. Örnek kullanım:

DISM /Online /Cleanup-Image /RestoreHealth

Dism, bunun yanı sıra açık olan Windows kurulumu üzerinde de işlem yapabilir, DVD veya USB'den boot edilerek disklerden birindeki kurulum üzerinde de çalışabilir. /ONLINE anahtarı şu an çalışan sistem üzerinde işlem yapılmasını söylüyor.

Dism'in bunun dışında çok güzel kullanımları var. Örneğin WinSxS klasörümüzün boyutu çok büyüdüyse ve eski  sürümlere ait dosyaları silmek istersek (kesinlikle elle yapılmaması gereken bir işlem) aşağıdaki komut kullanılabilir.

Önce mevcut WinSxS (component store - Windows Side-by-side) boyutunu kontrol edelim:

C:\> dism /online /cleanup-image /analyzecomponentstore

Deployment Image Servicing and Management tool
Version: 10.0.22621.2792

Image Version: 10.0.22631.3880

[===========================99.6%========================= ]

Component Store (WinSxS) information:

Windows Explorer Reported Size of Component Store : 14.29 GB

Actual Size of Component Store : 13.35 GB

    Shared with Windows : 7.30 GB
    Backups and Disabled Features : 6.05 GB
    Cache and Temporary Data :  0 bytes

Date of Last Cleanup : 2024-07-15 12:24:43

Number of Reclaimable Packages : 3
Component Store Cleanup Recommended : Yes

The operation completed successfully.

Eski ve kullanılmayan (superseded) sürümleri silelim

Dism.exe /online /Cleanup-Image /StartComponentCleanup /ResetBase

Bu komut sonrasında da kurulmuş güncellemerin kaldırılması imkansız hale gelir. Görece daha "hafif" bir temizlik için /ResetBase olmadan kullanabiliriz.

Bir service pack ile eski duruma düşmüş dosyaları silmek için (service pack'in kaldırılmasını imkansız hale getirir)

Dism.exe /online /Cleanup-Image /SPSuperseded

dism'in kullanımına benzer olarak Powershell'in de Repair-WindowsImage cmdlet'i var, aşağıdaki gibi kullanımı mümkün:

Repair-WindowsImage -CheckHealth
Repair-WindowsImage -ScanHealth
Repair-WindowsImage -RestoreHealth

Bir sistemde kurulu olan güncellemeleri listelemek için

dism /online /Get-Packages /Format:Table

Şu an açık olan sistemde bunu elde etmenin başka yolları da var. Ama DVD veya USB'den açılmış sistem üzerinde aynısını yapabilmek için

dism /image:D:\ /Get-Packages /Format:Table

Burada /image:D:\ Windows'un bulunduğu sürücüyü göstermeli.

Karşılaşılan sorunlar ile ilgili şu videoya bakılabilir.

17.07.2024

Windows'da istenilen boyutta dosya oluşturmak

Amacım, Windows kullanarak diskte anlık olarak istenilen boyutta bir dosya oluşturmak. Bununla ilgili yöntemleri zaten, konunun bilir kişileri [1] adresinde özetlemiş. Ben de onları bir kez daha burada toparlamak istedim.

Yöntemlerden birisi fsutil kullanmak. 1 MB boyutunda file1.txt dosyasını, mevcut klasörde oluşturmak için, boyutu byte cinsinden vermek gerek:

fsutil file createnew file1.txt 1048576

Powershell (.Net kütüphanesi) kullanarak oluşturmak için yöntem şöyle:

[io.file]::Create("file2.txt").SetLength(1MB).Close

Sysinternals aracı contig'i kullanmak da bir seçenek:

contig64 -n testfile (1MB)

Bu da olmadıysa son yöntem olmasa da bu yazının son önerisi diskpart ile vdisk oluşturmak:

DISKPART
CREATE VDISK FILE="C:\file3.txt" MAXIMUM=1 TYPE=FIXED

Burada dikkat edilecek MAXIMUM parametresi ile verilen dosya boyutunun megabyte cinsinden veri olması gerek. Ayrıca FILE parametresi ile kısmi değil tam dosya yolu verilmeli. Bu yöntem, kullanımı en zor yöntem.

Gördüğüm kadarıyla bu yöntemlerin hepsi içine birşey yazmadan oluşturuyor. Her dosyaya bir veri kaydedilmemiş olsa da dosya boyutu ile diskte yer kapladığı alan bölümleri aynı, 1MB.


 Bir de sparse (seyrek, aralıklı) dosya denen bir kavram var. Önceki yöntemlerin hiçbirinin sparse dosya oluşturmadığı söylenmiş. Bir dosyanın sparse olup olmadığını anlamak için

fsutil sparse queryflag file1.txt

ve bir sparse dosya oluşturmak için

fsutil sparse setflag file2.txt
fsutil Sparse SetRange file2.txt 0 1048576

Bu şekilde oluştup fsutil sparse queryflag ile sorgulayınca dosya tipi sparse olarak görüntülendi.

C:\> fsutil sparse queryflag file2.txt
This file is set as sparse
[1] https://stackoverflow.com/questions/982659/quickly-create-large-file-on-a-windows-system

5.07.2024

Zamanlanmış görevler ile yeniden başlatma ayarlamak

Daha önce şu yazımda tek satırlık powershell komutları ile bir sunucuyu yeniden başlatma fikirleri paylaşmıştım.

Daha iyi bir fikrim var.

Yeniden başlatma zamanlamasını kendi bilgisayarımdan yapmak istiyorum. Tek tek yeniden başlatılacak bilgisayarlara uzak masaüstü veya uzak PSSsession bağlantıları ile değil. Aşağıdaki gibi schedule-restart.ps1 dosyasını oluşturuyorum.

param(    
    [string]$Computername,
    [string]$ScheduledTime="23:00"
)
Invoke-Command -Computername $Computername -ScriptBlock {
    $trig1 = New-JobTrigger -At $ScheduledTime -Once
    $obj1 = Register-ScheduledJob -Trigger $trig1 -Name "YenidenBaslatma" -ScriptBlock {Restart-Computer -Force}
    "Görev oluşturuldu: $($obj1.Name)"
    "Zaman : $($trig1.At)"
}

Bu betikle uzaktaki bir bilgisayar için yeniden başlatma görevi oluşturmak istediğimde şu şekilde bir kullanım yeterli:

.\schedule-restart.ps1 -ComputerName uzakpc

Varsayılan yeniden başlatma zamanı saat 23:00. Eğer değer vermezsek bu saatte, -ScheduledTime ile değer verirsek başka bir saatte yeniden başlatılacak. Zamanlama bir kez, bugün için geçerli olacak. Yani tekrarlama yok.

Peki bu isimde bir görev daha varsa ne olur? Bir önceki seferden oluşturduğum bir zamanlanmış görev varsa bir tane daha oluşturmayacaktır. İsmini farklı seçerek bir görev daha oluşturabilirim, ama bu durumda gereksiz görev kalabalığı olur. Aşağıda kırmızı ile vurguladığım bölüm, önce aynı isimle bir görev olup olmadığını kontrol ediyor, sonra bunu silmeyi öneriyor.

param(    
    [string]$Computername,
    [string]$ScheduledTime="23:00"
)
$bSilme = $false 
Invoke-Command -Computername $Computername -ScriptBlock {
$prev1 = Get-ScheduledJob -Name "YenidenBaslatma" -EA Silent
    if ($prev1) {
$resp1 = Read-Host "Aynı isimde bir görev daha var. Silinsin mi (N/y)?"
if ($resp1 -eq "Y" -or $resp1 -eq "y") {
try {
Unregister-ScheduledJob -InputObject $prev1
}
catch {
$_.ErrorDetails.Message
}
}
else {
"Silmemeyi sectigin icin yeni gorev olusturulmayacak"
$bSilme = $true
}
    }
else {

        $trig1 = New-JobTrigger -At $ScheduledTime -Once
        $job1 = Register-ScheduledJob -Trigger $trig1 -Name "YenidenBaslatma" -ScriptBlock {Restart-Computer -Force}
        "Görev oluşturuldu: $($job1.Name)"
        "Zaman : $($trig1.At)"
    }
}

Takıntılı olanlar, bu görevi daha karmaşık sonuçlar yaratmadan silmek isteyebilir. Bunun için de bir delete-restartjob.ps1 betiği oluşturdum.

param(    
    [string]$Computername
)
Invoke-Command -Computername $Computername -ScriptBlock {
    $job1 = Get-ScheduledJob -Name "YenidenBaslatma" -EA Silent
    if ($job1) {
        $trig1 = $job1 | Get-JobTrigger
        "Bulunan gorev: $($job1.Name)"
        "Zamanlamasi : $($trig1.At)"
        $resp1 = Read-Host "Bu gorev silinsin mi (N/y)?"
        if ($resp1 -eq "Y" -or $resp1 -eq "y") {
            $job1 | Unregister-Scheduled 
        }
        else {
            "Silinme iptal edildi." 
        }
    
    else {
        "Silinecek yeniden baslatma gorevi bulunamadi" 
    
İyileştirmenin sonu yok. Daha neler eklenebilir, neler...

5.06.2024

4 rakamdan oluşan pin seçmek gerektiğinde

Kredi kartları, banka kartları ve SIM kartlarda hala 4 haneli pinler kullanıyoruz. Çoğumuz ilk deneyimimizde belirlediğimiz pinleri hayatımızın sonuna kadar kullanıyor. Ve daha da büyük bir çoğunluk "unutmamak için" bu pinleri kendi doğum tarihleri, eşlerinin veya çocuklarının doğum tarihlerinden seçiyor. Zaman içinde farklı yerlerde ve zamanlarda sızan pinler üzerinden yapılan istatistiksel bir çalışmada olası pinlerin kullanılma oranları görselleştirilmiş. İşte bu çalışmanın sonucu olan görsel:

 
Bazı önemli noktaları vurgulamak lazım:
  • Açık renk en fazla kullanımı, koyu renk en az kullanımı gösteriyor.
  • 19xx veya 20xx gibi (muhtemelen) doğum tarihleri çokça kullanılmış.
  • Belli tarihlerin ay/yıl(son iki hane) veya yıl/ay şeklinde ifade edildiği pinler de çok kullanılmış.
  • Köşegen üzerinde 1111 veya 1212 gibi pinler de çok kullanılmış.
  • Araya sıkışan koyu renk noktalar ilginç bir şekilde en az kullanılan pinler.
  • Aşağıdaki pinler, tüm araştırmaya konu 3,4 milyon içinde %27'lik bir oranlar kabaca 918.000 kişi tarafından tercih edilmiş:
    • 1234, 0000, 7777, 2000, 2222, 5555, 1122, 8888, 2001, 1111, 1212, 1004, 4444, 6969, 3333, 6666, 1313, 4321, 1010
  • Aşağıdaki pinler ise en az kullanılanlar olarak listelenmiş:
    •  8557, 8438, 9539, 7063, 6827, 0859, 6793, 0738, 6835, 8093, 9047, 0439, 8196, 6093, 7394, 9480, 8398, 7637, 9629, 8068 

Benzer bir paylaşım Reddit'te de var.

---

https://www.howtogeek.com/125378/the-most-common-and-least-used-4-digit-pin-numbers-security-analysis-report/

3.06.2024

Lockout policies and cached credentials

İlginç bir olay. Kullanıcı bir bilgisayar oturum açmayı deniyor ama başka yerde kullandığı şifresini giriyor (yeterli sayıda) ve hesabı lockout oluyor. Domain controller üzerinde 4740 olayı oluşuyor, Security eventlog'da. Kullanıcıya da belli sayıda yanlış parola denemesinden sonra hesabının kilitlendiği bilgisi gösteriliyor. Kullanıcı da mevcut bilgisayarını yeniden başlatıyor. Yeniden başladıktan sonra şifresini girmesi gereken doğru şifreyi hatırlıyor ve giriyor. Bu durumda ne olması gerekirdi? Ben domain controller'a danışarak oturum açmayı reddetmesini beklerdim. Ama öyle olmamış. Oturum açma işlemi başarılı olmuş. Söz konusu bilgisayar üzerinde o tarih ve saatteki 4624 eventlog'una baktığımda Logon Type olarak 11 gördüm. 11 neydi? Şifre doğrulaması için domain controller'a sorma, yerel bilgisayardaki cache'lenmiş şifrelere bak. Bu bilgisayarda da en son başarılı şifre girişine ait bir cache'lenmiş şifre olduğu için bilgisayar girişe izin veriyor. Ve kullanıcı oturum açabiliyor.

Kullanıcının hesabının unlock olmasına ait kayıt 4767, domain contorller üzerinde security eventlog'da tutuluyor. Ama bu olayda bu event yok, çünkü bu event sadece bir yetkili kullanıcının hesabı unlock etmesi sırasında oluşuyor. Lockout süresinin geçmesi sonrasında otomatik olarak unlock olması durumunda oluşmuyor.

Bu nahoş bir durum. Ama çözümünü bulamadım. Cached credentials'ı tümden engelleyebiliriz. Ama bu mobil bilgisayarlarını taşıyan ve yerel ağın dışında kullanan kullanıcılar için soruna sebep olur.

Önerilere açığım.

30.05.2024

Powershell ile hava tahminleri

Powershell terminal penceresinden hava tahminlerine ulaşmak kolay, Igor Chubin tarafından sağlanan wttr.in hizmeti sayesinde. Bir web tarayıcıdan girildiğinde HTML olarak sunulan veriler, Powershell veya bash gibi terminallerle yapıldığında ortama uygun şekilde (HTML etiketleri olmadan, ama terminalin desteklediği renklendirme ile-terminal sequences) sunuluyor.

Şehir seçmek için "/" işaretinden sonra şehir adını yazmak yeterli. Sadece şehir değil, hava tahminlerinin yapıldığı Türkçe karakterler de destekleniyor. Örneğin

irm wttr.in/Bursa

Hangi hava durumu hizmetini kullandığı bilgisine ulaşamadım.

wttr.in/:help ile yardıma ulaşılabiliyor. Buradan farkettim ki Türkçeleştirme seçeneğimiz var:

irm wttr.in/Bursa?lang=tr

Help'te daha bir sürü özellikten bahsedilmiş, "~" karakter ile bilen bir yerin yakınında arama yapma, "@" karakteri ile alan adının (IP adresi coğrafi konum) konumuna göre /moon ile ayın evresi hakkında bilgiye ulaşmak gibi. Aşağıdaki ise

irm wttr.in/Bursa?Fnq
F: Follow satırını gösterme
n: Dar görünüş ver
q: sessiz mod

ile gösterir. Bunu bir ps1 dosyasına ya da fonksiyon içine koyup hızlı durum bilgisi almak için kullanabiliriz:

function hava {
irm "wttr.in/Bursa?FTQ0"
}

Bunların tümü linux'ta curl ile de yapılabilir.

28.05.2024

Kapanan sunucudan haberdar olmak

Sunucular önemli. Onlar ne zaman yeniden başlamış, sebebi neymiş gibi sorular hep var. Bu soruların cevaplarını ben daha sormadan bulacak bir yöntemim olmasını istedim. Söz konusu sunucular Windows. Linux için de benzer bir yöntem bulunabilir elbet, önemli olan mantık.

Windows'da bir zamanlanmış göre oluşturayım. D:\bildir.ps1 konumundaki powershell betiğini çalıştırsın.

$act1 = New-ScheduledTaskAction -Execute powershell.exe 
-Argument "-ExecutionPolicy Bypass -NoProfile -File D:\bildir.ps1"
-WorkingDirectory "D:\"

Tetikleyici olarak da Sistem açılışını seçeyim.

$trig1 = New-ScheduledTaskTrigger -AtStartup 

Bu işi bir kullanıcının hesabı ile değil, SYSTEM hesabı ile yapacağım. Yüksek yetkilere ihtiyacım yok, standart yetkiler yeterli.

$prin1 = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Limited 

Önce görev nesnesini oluşturup sonrasında kaydetmek istiyorum. Bu işi iki adımda yapacağım.

$tsk1  = New-ScheduledTask -Action $act1 -Principal $prin1 -Trigger $trig1
$tsk1 | Register-ScheduledTask -TaskName "YenidenbaslatmaBildirimi" -TaskPath "\Firma"

Sıra geldi D:\bildir.ps1 betiğini oluşturmaya. Bu aşamaya kadar bilgisayar çoktan yeniden başladı. Betik bize açılıştan sonra bildirim verecek. Bu amaca yönelik aşağıdaki içerik yeterli:

$tarih = Get-Date -Format "yyyy-MM-ddTHH-mm-ss"
$sunucu_adi=$env:COMPUTERNAME
$smtpParams = @{
   From = "$sunucu_adi@firma.com"
   To = "admin@firma.com"
   Body = "$tarih - $sunucu_adi yeniden basladi"
   Subject = "$sunucu_adi yeniden basladi"
   SmtpServer = "smtp.firma.com"
}
Send-MailMessage @smtpParams
Add-Content -Value "$tarih - $sunucu_adi" -Path D:\bildir.log

Bu kısmı sadece sunucunun ne zaman başladığını bildirdi. Biraz da nedene olaklanalım. Windows'da nedeni bulmak için birkaç farklı yere bakmak gerekebilir. Olası sebepler Windows Update, güç kaybı, operatör isteği veya kritik bir uygulamanın hata vermesi olabilir. Bütün bu olayları içine alan bir olay kaydı günlüğünü inceleyelim:

$olaylar = Get-WinEvent -FilterHashTable @{LogName='System';`
StartTime=(Get-Date).AddHours(-1);`
ID=19,20,21,22,26,27,41,109,1000,1001,1002,1074,1076,6005,6006,6008} -ea silent

Bazen hiçbir olay dönmeyebilir. Bu gibi durumlarda, hata vermemesi sonuna için -ea silent (-ErrorAction SilentlyContinue kısaltması) ekledim. Daha sonra bu olayların içeriklerini gönderilecek mesajın gövdesine ekleyelim. Yukarıdaki betikte mesajın gövdesi $smtpParams içinde kısa bir şekilde oluşturulmuştu. Örneği bozmamak için aşağıdaki kodu bu splat değişkeninin ardından, Send-MailMessage cmdlet'inden önce olmasını düşündüm.

foreach ($olay in $olaylar) {
    $Body += "$($olay.TimeCreated) - $($olay.Id) - $($olay.Message)"
}

Bu şekilde en üstte tarih ve saatin yanında sunucu adı ve "yeniden basladi" görünecek. Ardından son 1 saatte konuyla ilgili olabilecek tüm olay kayıtları listelenecek. Bu elbette iyileştirmeye açık.

15.05.2024

Tarayıcı savaşları

 Bugün Reddit'te aşağıdaki ekran görüntüsünü görünce şaşırdım.


Bu "kullanıcı iradesine müdahale" delilini gördükten sonra önce Linux üzerinden Bing'de yaptığım aramada benzer bir sonuç ile karşılaşmadım. Sonra Windows'da Edge ile yaptığım aramada ben de aynı şeyi gördüm:


Bu bir arama motoru. Arama terimlerine karşılık gelen en yakın sonuçları listelemesi gerek. Ama o sonuçları amacına göre değiştiriyor. İlk sıraya reklam görünümlü mesajı koyuyor: "Yeni bir web tarayıcısı indirmenize gerek yok".

İlk adımı bunun gibi görece masum minik müdahaleler sonraki adımlarda hayatımızda daha büyük etkiye sahip, daha rahatsız edici şekillerde kullanılıyor. Bakınız, Cambridge Analytica olayı. "Kullanıcı davranışında belli belirsiz bir değişikliğe sebep olabiliyorsak başarılı sayılırız" denmişti. Kullanıcı verileri kullanılmıyor gibi gelebilir belki ama Linux'ta aynı sonucu vermediğinden yola çıkarak benim işletim sistemimin Windows olmadığı bilgisini kullandı bile.

---

https://www.reddit.com/r/browsers/comments/1crs3d1/who_else_does_this/
https://www.reddit.com/r/Windows11/comments/17b3bu5/microsoft_stop_making_me_feel_bad_for_downloading/
https://www.reddit.com/r/assholedesign/comments/zz8t4i/microsoft_really_does_not_want_you_to_download/
https://www.reddit.com/r/windows/comments/12skbx9/edge_stopping_me_from_downloading_chrome/
https://www.reddit.com/r/shitposting/comments/199sxv9/edging_to_this_rn/
https://www.reddit.com/r/pcmasterrace/comments/199vks2/chat_is_this_real/
https://www.reddit.com/r/firefox/comments/11cpa98/forgot_how_pushy_edge_is_after_a_clean_install/

8.05.2024

Powershell ve Windows Firewall

Windows Firewall, Windows Advanced Firewall veya Windows Defender Firewall. Microsoft'un ürünlerine verdiği isimler bana hep garip gelmiştir. Bir müzik ve video oynatıcısı yapıp adına media player demek, mesajlaşma uygulamasına messanger, interent tarayıcılarına Internet Explorer demek... İşletim sisteminin adı zaten Windows.

Konuyu dağıtmayalım. Bugün baktığımda kullandığım bilgisayarın güvenlik duvarımda 700'den fazla kural var. En fazla 2 tanesini ben oluşturmuşumdur -belki. Üstüne ek yazılımlar kurduğumuzda daha da fazlası geliyor. Bunlar ile uğraşmak hiç kolay değil. Evet, belli kolaylıklar sağlanmış, gruplanmış. Ağlara göre sınıflandırılmış (etki alanı, özel ağ, genel ağ falan) ama yine de belli bir portu açmak için yeni bir kural mı oluştursam, yoksa var olan kurallardan biri işimi görür mü diye düşündüğüm durumlar hiç az değil. Üstüne üstlük "Gelişmiş Güvenlik Özellikli Windows Defender Güvenlik Duvarı" uygulaması da pek kullanışlı değil. İsme, adrese veya porta göre arama, sıralama, süzme yok. Ama iyi ki Powershell var!

Yeni bir bilgisayara oturduk. Tüm önyargılara rağmen ilk yapılacak iş güvenlik duvarını devreye almak. Güvenlik duvarı, bağlı olunan ağ profiline göre kuralları etkinleştiriyor. Mevcut ağ profilimize bakalım:

Get-NetConnectionProfile

Bakılacak alan NetworkCategory. Private / Public arasında geçiş yapmak için

Set-NetConnectionProfile -NetworkCategory Private

kullanabiliriz. Bu profile uyan güvenlik duvarı profilimizi görmek için

Get-NetFirewallProfile -Name Private | Select-Object Name, Enabled, DefaultInboundAction

kullanabilir, Enabled alanından etkin mi değil mi görebilir, DefaultInboundAction'dan kurala uymayan gelen trafiğe karşı varsayılan eylemini (Block olmalı) tespit edebiliriz. Genel eğilim, güvenlik duvarını etkinleştirirken bütün profilleri etkinleştirmek. Eğer yapmak istediğimiz buysa

Set-NetFirewallProfile -All -Enabled True

Burada garip olan, True değerinin bir boolean değer değil de GpoBoolean olması. Şu sayfa da dahil her yerde $True yerine True yazılmış. Tam olarak aynı şey mi, emin değilim ama şöyle birşey buldum:

$a = [Microsoft.PowerShell.Cmdletization.GeneratedTypes.NetSecurity.GpoBoolean]::True
$a -eq $True
True

yani sanki aynı şey. Bu adımlardan sonra ikinci yaptığım şey genelde bazı temel kuralları etkinleştirmek veya kontrol etmek. Örneğin söz konusu bilgisayara uzak masaüstü yapılmasını istiyorsam ilgili kuralın devrede olup olmadığını kontrol ederim. Bunun için içinde "Uzak Masaüstü*" (İngilizce bilgisayarlarda "Remote Desktop*") geçen kuralları  geçenleri ararım:

Get-NetFirewallRule -DisplayName "Uzak Masaüstü*" | ft Name, DisplayName, Enabled

Bunu dahil oldukları gruba göre arayarak da yapabilirim:

Get-NetFirewallRule -DisplayGroup "Uzak Masaüstü" | ft Name, DisplayName, Enabled

Bu kuralların hangi uygulamaya, hangi porta ve hangi uzak IP grubuna izin/engelleme verdiğini görmek için ise şu sorgu mükemmel:

Get-NetFirewallRule -DisplayGroup "Uzak Masaüstü" | 
Format-Table DisplayName,
@{N="Port";E={($_|Get-NetFirewallPortFilter).LocalPort}},
@{N="Application";E={($_|Get-NetFirewallApplicationFilter).Program}},
@{N="Address";E={($_|Get-NetFirewallAddressFilter).RemoteAddress}} -AutoSize

Benzer şekilde IIS yüklü bir bilgisayarda 80 numaralı port üzerinden HTTP hizmeti vermek için güvenlik duvarındaki kuralları kontrol etmek istersem

Get-NetFirewallRule -DisplayGroup "*World Wide Web Services*" | Ft DisplayGroup, DisplayName, Enabled

Bilgisayarımız ping'lere cevap veriyor mu diye bakmak için (İngilizce bilgisayarlarda):

Get-NetFirewallRule -DisplayName "Core Networking Diagnostics - ICMP Echo Request (ICMPv4-In)" | 
Format-Table DisplayGroup, Displayname, Profile, Enabled

ya da grup bazlı

Get-NetFirewallRule -DisplayGroup "Core Networking Diagnostics" | 
Format-Table DisplayGroup, Displayname, Profile, Enabled

Türkçe bilgisayarlarda aynısını yapmak için ise

Get-NetFirewallRule -DisplayName "Çekirdek Ağ Tanılama - ICMP Yankı İsteği (ICMPv4-Gelen)" | 
Format-Table DisplayGroup, Displayname, Profile, Enabled

grup bazlı olarak da

Get-NetFirewallRule -DisplayGroup "Çekirdek Ağ Tanılama" | 
ft DisplayGroup, DisplayName, Profile, Enabled -AutoSize

gibi birşey olabilir. Bunlardan etkinleştirmek istediklerimizi pipe ile

Get-NetFirewallRule -DisplayName "Çekirdek Ağ Tanılama - ICMP Yankı İsteği (ICMPv4-Gelen)" | 
Enable-NetFirewallRule

ya da devre dışı bırakmak için Disable-NetFirewallRule yapabiliriz. Dosya ve yazıcı paylaşımı kural grubu için sırasıyla Türkçe ve İngilizce bilgisayarlarda komudumuz şöyle olabilir:

Get-NetFirewallRule -DisplayGroup "Dosya ve Yazıcı Paylaşımı" | 
ft Displayname, DisplayGroup, Enabled, Profile -AutoSize

Get-NetFirewallRule -DisplayGroup "File and Printer Sharing" | 
ft Displayname, DisplayGroup, Enabled, Profile -AutoSize

Bütün dosya paylaşımlarını devre dışı bırakmak için

Get-NetFirewallRule -DisplayGroup "Dosya ve Yazıcı Paylaşımı" | Disable-NetFirewallRule

kullanılabilir.

Asıl kilit nokta, herhangi bir porta komuta eden bir kural var mı yok mu aramak istersek

Get-NetFirewallRule | where {($_ | Get-NetFirewallPortFilter).LocalPort -eq 9401} | select DisplayName

Kurallar etki alanı (domain), özel ağlar (private) ve genel ağlar (public) olarak kategorize edildiği için bu profile bilgisine dikkat etmek çok önemli. Özel bir ağda çalışırken genel ağ kuralı üzerinde işlem yaparsak etkili olmayacaktır.

Bunları Get-* komutları ile mevcut durumu görmek üzerine komutlardı. Bir de kurallarda toplu değişiklik yapalım. Örneğin DisplayGroup özelliği "Dosya ve Yazıcı Paylaşımı" olan kural gruplarını sadece belli IP'lerden erişilebilecek şekilde ayarlayalım. Önce "Dosya ve Yazıcı Paylaşımı" DisplayGroup'una dahil kuralların IP adresi kısıtlaması var mı, bakalım:

Get-NetFirewallRule -DisplayGroup "Dosya ve Yazıcı Paylaşımı" | 
Format-Table Enabled, DisplayName, Profile, 
@{N="Scope";E={($_|Get-NetFirewallAddressFilter).RemoteAddress}} -Autosize

Bu şekilde hedef kural grubumun tam olarka istediğim kurallar olduğundan emin olabilirim. Hedef kural grubum basit bir şekilde DisplayName veya DisplayGroup ile ifade edilebilecek grup ise basitçe

Set-NetFirewallRule -DisplayGruop "Dosya ve Yazıcı Paylaşımı" -RemoteAddress 192.168.1.28

gibi bir komutla bu kurala uzaktan erişebilecek bilgisayarların IP adreslerini seçebilirim.

Bu arada LocalSubnet bilgisayarın yerel ağındaki IPv4 ve IPv6 altağını kapsar. Sadece IPv4 için LocalSubnet4, veya sadece IPv6 için LocalSubnet6 kullanılabilir:
Set-NetFirewallRule -DisplayGruop "Dosya ve Yazıcı Paylaşımı" -RemoteAddress LocalSubnet4

Daha karmaşık bir senaryomuz varsa, örneğin domain profilinde ve sadece etkinleştirilmiş kural alt grubununa uzaktan erişebilecek bilgisayarın IP adresini kısıtlamak için

Get-NetFirewallRule -DisplayGroup "Dosya ve Yazıcı Paylaşımı" | 
where {$_.Enabled -eq $true -and $_.Profile -eq "Domain"} | 
Set-NetFirewallRule -RemoteAddress 192.168.1.28

6.05.2024

ProfileNameServer

Etki alanı üyesi taşınabilir bir bilgisayarımız var. Etki alanı dışına çıktığında bir wifi ağına bağlanarak işlem yapamadığına dair bir şikayet geldi. İncelediğimde

ipconfig /all

ile listelenen DNS suncuların hala (yerel ağın dışında bir wifi'a bağlıydı ve bu wifi kendi DNS sunucusunu bildiriyordu) etki alanımızın (aslında halen kullanılmayan eski) sunucularının olduğunu gördüm. Ağ adaptörlerinin hiçbirinde bu IP adresleri girilmemişti. Nereden gelebilir acaba diye registry'de yaptığım bir aramada HKLM\System\CurrentControlSet\Services\Tcpip\Parameters\Interface altında bazı GUID'lerin altında ProfileNameServer adında bazı girişlerin olduğunu ve bu girişlerin içinde eski sunucularımıza ait DNS girişlerinin halen yer aldığını farkettim. Garip bir şekilde bu durumu yaşayan başka [1], [2] insanlar da varmış. Çözüm elle bu değerleri silmek ya da 

Set-DHCPClientServerAddress -InterfaceIndex xx -ResetServerAddress

kullanarak bunu işi Powershell'e yaptırmak.

---

[1] https://learn.microsoft.com/en-us/answers/questions/828532/static-dns-server-address-returning
[2] https://www.qbitnetworking.com/blog-posts/windows-10-wifi-dns-server-address-stuck-wifi-is-connected-but-no-internet

30.04.2024

Uzak masaüstü bağlantıları

Microsoft-Windows-TerminalServices-LocalSessionManager/Operational altındaki olaylar hakkında bulduğum birkaç küçük ipucu:

Genelde gördüğüm 3 olay var:

24: Session disconnected

25: Session reconnected

40: Yukarıdaki 2 olaya eşlik eden, reconnection ve disconnection olaylarına ait reason code'lar verililir.

Reason code yeni yapılan bağlantının ya da kesilen bağlantının sebebi hakkında bazı küçük bilgiler içerir. Şu ve şu sitelere göre:

0: Kullanıcı uzak masaüstü oturumunu pencerenin çarpısına basarak (ya da ağ bağlantı sorunu sebebiyle) kapatmıştır. Bu olay 24 olayı ile birlikte olur. Ayrıntılar 24 olayında belirtilir.

5: Oturumun kapanma sebebi bu oturumun başka bir oturumla değiştirilmesi. Aslında şu demek; disconnect edilmiş bir oturum kalmıştı, yeniden o oturuma bağlanıldı.

Ben sık görmesem de başka reason code'lar:

2: Admin kullanıcı oturumunu sonlandırdı (Admin has signed off/ Terminated the user session.)

3: Boş kalma süresi aşıldı (Idle Session Timer limit reached.)

4: İzin verilen süreden daha fazla bağlanıldığı için sonlandırıldı.

6: Hafıza yetersiz

7: Sunucu bağlantıya izin vermedi

8: Sunucu güvenlik sebebiyle bağlantıyı reddetti.

9: Sunucu güvenlik sebebiyle bağlantıyı reddetti.

10: Kullanıcı adı ve şifrenin tekrar girilmesi gerekli

11: Kullanıcı etkinliği sebebiyle sonlanma.

12: Kullanıcı oturumu kapattı.

26: Ağ kesintisi veya grup ilkesi / admin müdahalesi ile (belki de boş kalma süresi, etkinleştirilmemiş sunucu)

---

https://community.spiceworks.com/t/remote-desktop-services-disconnect-code/375433/7

https://learn.microsoft.com/tr-tr/windows/win32/termserv/extendeddisconnectreasoncode?redirectedfrom=MSDN


25.04.2024

schtasks.exe ile zamanlanmış görevler

Bir RDP oturum açması sırasında çalışacak bir görev oluşturmak

    schtasks /Create 
        /TN "MyEventTask"
        /SC ONEVENT
        /EC "Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational"
        /MO "*[System[Provider[@Name='Microsoft-Windows-TerminalServices-RemoteConnectionManager'] and (EventID=1149)]]"
        /TR "powershell.exe -ExecutionPolicy Bypass -File C:\Scripts\1149.ps1"

Kullanıcı oturum açsa da açmasa da çalışacak bir görev için

    schtasks /Create 
        /TN "MyEventTask"
        /SC ONEVENT
        /EC "Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational"
        /MO "*[System[Provider[@Name='Microsoft-Windows-TerminalServices-RemoteConnectionManager'] and (EventID=1149)]]"
        /TR "powershell.exe -ExecutionPolicy Bypass -File C:\Scripts\1149.ps1"
        /RU SYSTEM
        /NP

Mevcut görevlerin hepsini export etmek için

schtasks /Query /XML /TN "*"

Sonra bu xml dosyalarından tekrar görevler oluşturmak için

dir *.xml | % { schtasks /Create 
/XML $_.FullName
/TN "$_.Name.replace('.xml','')"
/RU "new_username"
/RP "new_password"

Bir görev nesnesini parola kaydetmeden oluşturmak için /NP anahtarı,

Tam tersi, parolayı belirlemek için /RP <parola>

Görevin yetki seviyesini belirlemek için /RL <LIMITED|HIGHEST>

ONSTART, ONLOGON, ONEVENT olayları için rastgele bir gecikme ayarlamak için ise /DELAY mmmm:ss kullanılabilir.

---

https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/schtasks-query

24.04.2024

Powershell ile ileri seviye zamanlanmış görevler

Zamanlanmış görevler sadece belli periyotlarda değil, belli olayların ardından çalışacak şekilde de oluşturulabiliyor. Ama bunu Powershell ile yapmak biraz meşakkatli. Oturum açma sonrasında ve bilgisayarın başlangıcında çalıştırılacak görevler için trigger nesneleri aşağıdaki gibi oluşturulabiliyor:
$trig1 = New-ScheduledTaskTrigger -AtLogon
$trig2 = New-ScheduledTaskTrigger -AtStartup

Ancak örneğin uzakmasaüstü bağlantısı ardından (TerminalServices-RemoteConnectionManager/Operational / Id:1149) çalışacak bir trigger'ın oluşturulması şu şekilde:

$class = Get-CimClass -Class "MSFT_TaskEventTrigger" -ClassRoot "Root/Microsoft/Windows/TaskScheduler"
$trig3 = $class | New-CimInstance -ClientOnly
$trig3.Subscription = "<QueryList>`
<Query>`<Select Path='Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational'>`
    *[System[Provider[@Name='Microsoft-Windows-TerminalServices-RemoteConnectionManager'] `
    and (EventID=1149)]]</Select></Query></QueryList>"

(satırlar ` karakteri ile bölünmüştür) ile, ve görev oluşturulması/değişikliğinde çalışacak görevin trigger'ı

$trigger1 = Get-CimClass -ClassName "MSFT_TaskRegistrationTrigger" -Namespace "Root/Microsoft/Windows/TaskScheduler"

ile yapılabiliyor.

Bir powershell görevinin gizli oluşturulması için

$settings = New-ScheduledTaskSettings -Hidden

kullanılabilir. Belli bir kullanıcının masaüstü ile etkileşime geçebilmesi için

$principal = New-ScheduledTaskPrinciple -UserId "makine\kullanidi" -LogonType Interactive

seçilmeli. LogonType alanı için Group, Interactive, InteractiveOrPassword, None, Password, S4U, ServiceAccount gibi değerler var. Bir görevin kullanıcı oturum açsın ya da açmasın çalışmasını sağlamak için S4U kullanılabilir.

$principal = New-ScheduledTaskPrinciple -UserId "makine\kullanidi" -LogonType S4U

Çalışacak görevin yetkileri RunLevel ile belirtilebilir (Limited/Highest):

$principal = New-ScheduledTaskPrinciple -UserId "makine\kullanidi" -LogonType Interactive -RunLevel Highest

LocalService veya System kullanıcıları ile bir görev çalıştırmak için

$principal = New-ScheduledTaskPrincipal -UserId "LOCALSERVICE" -LogonType ServiceAccount
$principal = New-ScheduledTaskPrincipal -UserId "NT AUTHORITY\SYSTEM" -LogonType ServiceAccount

ve hatta bir grup yetkileriyle görev planlamak için

$principal = New-ScheduledTaskPrincipal -GroupId "BUILTIN\Administrators" -RunLevel Highest

kullanılabilir. Mevcut bir zamanlanmış görevin trigger'larından birini değiştirmek için aşağıdaki yöntem kullanılabilir:

$task = Get-ScheduledTask -TaskName "Gorev1"
$trig = $task.Triggers
$trig.Repetition.Duration = $null
$trig.Repetition.StopAtDurationEnd=$false
Set-ScheduledTask -InputObject $task

Birden fazla trigger eklemek için iki trigger bir dizi olarak atanabilir:

$task.Triggers = ($trigger1, $trigger2)

gibi. Bir dizüstü bilgisayardaki görevin bataryadan çalışıldığı anda bile çalışmasını sağlamak veya uyku kipindeki bilgisayarları uyandırmak için için New-ScheduledTaskSettingsSet'in seçenekleri kullanılabilir.

Tekrarlayacak görevler için RepetitionInterval'a aşağıdaki gibi New-TimeSpan cmdlet'i ile zaman aralığı atanabilir.

$trig1 = New-ScheduledTaskTrigger -At 13:30 -Once -RepetitionInterval (New-TimeSpan -Hours 1)

benzer şekilde -RandomDelay parametresine de bir New-TimeSpan ile zaman aralığı verilmeli

$trig1 = New-ScheduledTaskTrigger -AtLogon -RandomDelay (New-TimeSpan -Minutes 5)

gibi. Sonradan değiştirmek için ise

$task.Triggers[0].Repetition.RepetitionInterval = New-TimeSpan -Hours 1

"No mapping between account names and security IDs was done" gibi bir hata, var olmayan bir kullanıcı belirtildiğini gösterir. Runlevel için Highest seçilecekse Powershell penceresi de yüksek yetkilerle açılmış olmalı. Varsayılan ayarlara sahip bir görev için New-ScheduledTaskSettings ve New-ScheduledTaskPrincipal kullanmak gerekli değil.

Bir olay sonrasında çalıştırılacak görev oluşturduğumuzda çalışmıyorsa yapılabilecek şey göreve ait trigger'ı kontrol etmek olabilir. Genelde provider name yanlış yazdığımda yaşadığım bir hataydı. Kontrol etmek için trigger'ı açtığımda aşağıdaki gibi bir ekranla karşılaştım:


Source (ya da provider) kısmında "Microsoft-Windows-TerminalServices-TaskScheduler" ve "TaskScheduler" seçenekleri var. TaskScheduler gibi Uygulama ve Hizmet Günlüklerinde genelde birden fazla Source olmıyor. Bunlardan üstteki yanlış yazılmış. Ama gerek schtasks.exe gerek powershell ile görev oluştururken sadece TaskSheduler yazmak da yetmiyor. Bunun yerine tam yolu "Microsoft-Windows-TaskScheduler" olarak yazmak gerek, burada o sadece TaskScheduler olarak kısaltılabilir.

Zamanlanmış görevin en son çalıştığı zamanı, döndüğü durum/hata kodu gibi bilgilerini görmek için ise

Get-ScheduledTask -Taskname "gorev-1" | Get-ScheduledTaskInfo 

kullanılabilir. Dönülen hata/durum kodları ile ilgili şu bilgiler var:

0 => "Görev başarıyla çalıştırıldı",
1 => "Yanlış veya bilinmeyen görev",
2 => "Dosya bulunamadı.",
a => "Ortam yanlış.",
00041300 => "Bir sonraki döngüde çalışmaya hazır",
00041301 => "Görev şu anda çalışıyor",
00041302 => "Görev devre dışı",
00041303 => "Görev henüz hiç çalışmadı",
00041304 => "Bu görev için başka çalışma zamanı planlanmamış",
00041305 => "Görevin çalışması için bir veya daha fazla özellik atanmamış",
00041306 => "Görevin çalışması kullanıcı tarafından sonlandırıldı",
00041307 => "Ya trigger yok, ya da trigger'lar devre dışı",
00041308 => "Trigger'larda zaman belirtilmemiş",
80010002 => "Call was canceled by the message filter",
80041309 => "Görevin trigger'ı bulunamadı",
8004130A => "One or more of the properties required to run this task have not been set.",
8004130B => "There is no running instance of the task.",
8004130C => "The Task Scheduler service is not installed on this computer.",
8004130D => "The task object could not be opened.",
8004130E => "The object is either an invalid task object or is not a task object.",
8004130F => "No account information could be found in the Task Scheduler security database for the task indicated.",
80041310 => "Unable to establish existence of the account specified.",
80041311 => "Corruption was detected in the Task Scheduler security database",
80041312 => "Task Scheduler security services are available only on Windows NT.",
80041313 => "The task object version is either unsupported or invalid.",
80041314 => "The task has been configured with an unsupported combination of account settings and run time options.",
80041315 => "The Task Scheduler Service is not running.",
80041316 => "The task XML contains an unexpected node.",
80041317 => "The task XML contains an element or attribute from an unexpected namespace.",
80041318 => "The task XML contains a value which is incorrectly formatted or out of range.",
80041319 => "The task XML is missing a required element or attribute.",
8004131A => "The task XML is malformed.",
0004131B => "The task is registered, but not all specified triggers will start the task.",
0004131C => "The task is registered, but may fail to start.Batch logon privilege needs to be enabled for the task principal.",
8004131D => "The task XML contains too many nodes of the same type.",
8004131E => "The task cannot be started after the trigger end boundary.",
8004131F => "An instance of this task is already running.",
80041320 => "The task will not run because the user is not logged on.",
80041321 => "The task image is corrupt or has been tampered with.",
80041322 => "The Task Scheduler service is not available.",
80041323 => "The Task Scheduler service is too busy to handle your request. Please try again later.",
80041324 => "The Task Scheduler service attempted to run the task, but the task did not run due to one of the constraints in the task definition.",
00041325 => "The Task Scheduler service has asked the task to run.",
80041326 => "The task is disabled.",
80041327 => "The task has properties that are not compatible with earlier versions of Windows.",
80041328 => "The task settings do not allow the task to start on demand.",
80070002 :  The system cannot find the file specified - (https://itexperience.net/0xfffd0000-in-task-scheduler-when-running-powershell-script/, 800070002 yaziyordu)
800710e0 :  Operator refures the request. Check permissions. see https://windowsreport.com/0x800710e0/
8007010b :  Start in folder is incorrect
C000013A => "The application terminated as a result of a CTRL+C.",
C0000142 => "The application failed to initialize properly.",
FFFD0000 :  Path error, possibly for powershell scripts

---

https://xplantefeve.io/posts/SchdTskOnEvent

https://richardspowershellblog.wordpress.com/2014/09/12/multiple-triggers-on-a-scheduled-task/

https://www.pdq.com/powershell/set-scheduledtask/

https://lazyadmin.nl/powershell/how-to-create-a-powershell-scheduled-task/

22.04.2024

Bilgisayar hata mesajları ve kullanıcılar

Genelde yaklaşım "erkanda bişey çıktı ama tamama bastım"dan ibaret olan durumlar. Bu konuda her gördüğümde güldüğüm Jonathan Nightingale imzalı aşağıdaki karikatürize hata mesajını paylaşmaya değer buluyorum.



17.04.2024

PSReadline ve komut geçmişinde aramalar yapmak

Linux'ta (bash veya zsh) Ctrl+R aracılığıyla geçmişte arama yapmak mümkün. Powershell'de de benzer bir işlev var. Ctrl+R'ye bastığımda yazdığım anahtar kelime ile geçmişte geriye doğru bir arama yapabiliyorum. İlk çıkan sonuçtan sonra aramayı devam ettirerek bir öncekine ulaşmak için Ctrl+R'ye basmaya devam edebiliyorum. Ctrl+S ise aramanın yönünü değiştirerek ileriye doğru aramaya geçiyor. Arama geçmiş komutların içinde geçen herhangi bir parçaya göre yapılıyor.

Benzer bir işlev de F8 ile mümkün. Bu ise aramayı sadece yazdığım anahtar kelime ile başlayanlar ile kısıtlıyor. F8'e basmaya devam ederek geçmişte bir adım geriye gidebiliyor veya Shift+F8 ile arama yönünü çevirerek ileriye doğru gidebiliyorum.

Bu işlevleri bana sunan PSReadLine'ın kısayol tuşlarının listesini aşağıdaki komutla almak mümkün [1]:

Get-PSReadlineKeyHandler | ? {$_.function -like '*hist*'}

PSReadLine'ın bütün klavye kısayolları atamalarını görmek için

Get-PSReadLineKeyHandler

ve henüz atanmamış işlevleri görmek için ise

Get-PSReadLineKeyHandler -Unbound

kullanabiliriz. Yeni bir işlev ataması yapmak da mümkün. Örneğin henüz bir klavye ataması yapmadığım CaptureScreen işlevini Ctrl+Alt+p klavye kısayoluna atamak için

Set-PSReadLineKeyHandler -Chord 'Ctrl+Alt+p' -Function CaptureScreen

 yapabiliriz. Herhangi bir klavye kısayolunun bir atamada kullanılıp kullanılmadığını sorgulamak için Shift+Alt+* (İngilizce kaynaklarda Alt+? denmiş ama Türkçe klavyelerde bu sıfır (0)'ın yanındaki asteriks (*)'e Shift ve Alt ile basmak demek) bastıktan sonra istenen klavye kısayoluna basmak yeterli. Klavyelerin sağ bölümünde bulunan keypad'deki tuşlar gibi sıradışı tuşların isimlerini öğrenmek için ise aşağıdaki komutu yazdıktan sonra istenen tuşa basmak yeterli:

[System.Console]::ReadKey()

 ---

[1] https://serverfault.com/questions/891265/how-to-search-powershell-command-history-from-previous-sessions

[2] https://learn.microsoft.com/en-us/powershell/scripting/learn/shell/using-keyhandlers?view=powershell-7.4

15.04.2024

Windows'da betikleri bekletmek

Powershell'in Start-Sleep cmdlet'i betiklerde gerekli süre boyunca beklemek için ideal.

Start-Sleep -Seconds 5

Peki bunu komut satırındaki bat dosyalarında nasıl yaparız? Eskiden sleep gibi bir komut vardı, Windows'da, ama artık yok.

İlk yöntem belki de bat dosyasının içinden powershell komutu çalıştırmak olabilir.

powershell "start-sleep -seconds 5"

Bazı yaratıcı beyinler 5 kere ping atmanın ve çıkışı nul'a göndermenin işe yarayacağını söylemiş:

ping 1.1.1.1 -c 5 > nul

Güzel. Daha güzeli timeout:

timeout 5

Bir tuşa basana kadar beklemesi için pause olabilir:

pause
Press any key to continue...

Bunun eşdeğeri powershell'de Read-Host olabilir

Read-Host "Bir tuşa basın..."

Ayrıca [1] ve [2]'de choice ve waitfor komutlarından da bahsedilmiş:

waitfor /t 5 pause
choice /c AB /N /T 5 /D A /M "5 sn bekliyorum"

---

[1] https://www.wikihow.com/Delay-a-Batch-File

[2] https://stackoverflow.com/questions/1672338/how-to-sleep-for-five-seconds-in-a-batch-file-cmd

14.04.2024

Batch dosyalarında değişkenler

Powershell'de tarih ve saati bir değişkene atmak basit:

$tarih_saat = Get-Date -Format "yyyy-MM-dd HH:mm:ss"

Ancak bunun eşdeğeri batch dosyalarında bu şekilde olmuyor. Ben şöyle bir yol buldum:

echo @off
for /f "tokens=*" %%a in ('date /t') do set tarih=%%a
for /f "tokens=*" %%b in ('time /t') do set saat=%%b
set tarih_saat=%tarih%- %saat%

6.04.2024

openssh ile ilgili xz-utils arka kapısı hakkında (CVE-2024-3094)

Hikaye uzun. Uzun lafın kısası, bir Microsoft yazılımcısı, Andres Freund, OSS-Security'ye bulduğu arka kapı ile ilgili bir eposta gönderiyor ve olay patlıyor.

Benim için önemli olan etkilenen sistemler nasıl tespit edilmeli, hangi sürümlerden korunmalı.

xz-utils paketinin sürümlerine bakmak gerek.

xz --version

ile sürüm kontrolü yapılabilir. 5.6.0 ve 5.6.1 sürümleri kaçınılması gereken sürümler. Ayrıca Debian türevlerinde:

apt list installd xz-utils

Fedora türevlerinde

rpm -q xz

ile sürüm numaraları bulunup yükseltilebilir.

Çevremdeki kurulumlardan birinde 5.6.1'e rastladım:

xz --version ile görüntülenen sürüm numarası 5.6.1 olmasına rağmen asıl sürüm numaram 5.6.1-2, ki bu da arka kapıya karşı yamalanış sürüm. Sonraki satırda pacman -Q xz ile bunu doğruladım. Benim xz güncellemelerim nasıl olmuş diye bakmak istedim:

grep "xz " /var/log/pacman.log

çıktı şöyle oldu:

[2024-03-09T23:57:14+0300] [ALPM] upgraded lib32-xz (5.4.6-1 -> 5.6.0-1)
[2024-03-31T01:08:10+0300] [ALPM] upgraded xz (5.6.0-1 -> 5.6.1-2)
[2024-03-31T01:08:13+0300] [ALPM] upgraded lib32-xz (5.6.0-1 -> 5.6.1-2)

Demek ki Manjaro, 31 Mart'ta yamayı yayınlamış. Debian ve Fedora ana dağıtımlar sorunlu paketleri depolarına dahil etmediği için hala güvenli olarak görüldüler. Ancak "bleeding edge" olarak nitelenen "rolling distro"lar için çok olumlu nitelendirmeler kullanılmadı. Rolling distro deyince de ilk akla gelen Arch Linux. Ama şu sayfada denmiş ki; Arch Linux xz paketini openssh ile ilişkilendirmediği için Arch Linux bu saldırıdan etkilenmemiş.

Eğer sistemdeki sürümü eski sürüme downgrade etmek isteseydim /var/cache/pacman/pkg altındaki paket önbelleğindeki sürümleri kullanabilirdim:

sudo pacman -U /var/cache/pacman/pkg/xz-5.4.4-1-x86_64.pkg.tar.zst

Bundan sonra bu paketin kontrolsüz olarak güncellenmesini engellemek için ise /etc/pacman.conf dosyasına şu satırı eklemem gerekecekti:

IgnorePkg = xz

Alternatif olarak manjaro-downgrade paketi ile bu işin otomatize edilmesi de anlatılmış, ama sisteme bir paket daha kurmak istemedi canım, nedense.

---
https://www.openwall.com/lists/oss-security/2024/03/29/4

https://archlinux.org/news/the-xz-package-has-been-backdoored/

https://wiki.manjaro.org/index.php?title=Downgrading_packages 

https://snyk.io/blog/the-xz-backdoor-cve-2024-3094/

https://www.logpoint.com/en/blog/emerging-threats/xz-utils-backdoor/#
https://www.reddit.com/r/linux/comments/1bqt999/backdoor_in_upstream_xzliblzma_leading_to_ssh/
https://www.youtube.com/watch?v=0pT-dWpmwhA

https://forum.manjaro.org/t/xz-package-contains-a-vulnerability/159028/20

https://boehs.org/node/everything-i-know-about-the-xz-backdoor

https://www.wired.com/story/jia-tan-xz-backdoor/

5.04.2024

Windows Time

Zaman eşitlemesi önemli bir iş. Windows sunucularda da bu amaçla çalışan bir hizmet var; "Windows Time".

PS> gsv w32Time
Status Name DisplayName
------ ---- -----------
Running w32time Windows Time

NTP protokolü de, bilindiği gibi, UDP 123 üzerinden çalışıyor. Bu portun açık olması, arka planda bir zaman sunucusunun etkin olduğunu düşünebilmek için ilk koşul.

ncat -z -v -u 192.168.1.1 123

Yerel ağımızda çalışan bir Windows sunucuyu, linux makinalar tarafından güvenilir bir zaman sunucusu olarak kullanabilir, veya tam tersi linux sunucuyu da Windows makinalar tarafından güvenilir bir zaman sunucusu (NTP) ayarlayabiliriz; teknik olarak mümkün.

Bu hizmetin çalışmasını komut satırından yapabilmek için w32tm komutu var. Örnek olarak bizim bilgisayarımızın saati senkronize edeceği kaynak NTP sunucusu olarak hangi adresi kullandığını bulmak için

w32tm /query /source

kullanabiliriz. Mevcut NTP sunucularını görmek için

w32tm /query /peers

Windows Time hizmeti ve zaman eşitleme durumu ile ilgili ayrıntılı bilgi için ise

w32tm /query /status

kullanılabilir. Yerel bilgisayarda NTP sunucumuzu bulmak için regisry kullanmak istersek okumamız gereken alan HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Parameters altındaki NtpServer değeri:

(gp -Path HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Parameters -Name NtpServer).NtpServer

Bu değeri komut satırından değiştirmek ve uygulamaya geçirmek için aşağıdaki komutlar gerekli:

gsv w32time | spsv
w32tm /config /syncfromflags:manual /manualpeerlist:"192.168.1.1 192.168.1.2"
gsv w32time | sasv
w32tm /config /update
w32tm /resync /rediscover

Bundan sonra herhangi bir anda zaman kaynak sunucumuzdan zaman eşitlemesi yapmak için

w32tm /resync

yeterli.

---

https://learn.microsoft.com/en-us/windows-server/networking/windows-time-service/windows-time-service-tools-and-settings?tabs=config
https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-r2-and-2012/ff799054(v=ws.11)

4.04.2024

Windows API'lerinde ~256 karakterlik tam yol adı sınırlaması

Kullanıcıların dosyalarına neden uzun uzun isimler vermeyi tercih etmeleri üzerine yazılan tezleri araştırmadan önce böyle bir yol izlemiş kişilerin dosyalarını kopyalarken karşılaşılan hataların çözümüne kafa yordum. Microsoft'un niye böyle bir kısıtlamaya ihtiyacı var diye düşündüm. Sonra öğrendim ki Windows 10 1607 sürüm ve üstünde bu kısıtlamayı kaldırmayı seçebiliyormuşuz [1]. Bilmeden bu kısıtlamaya takıldığım için üzüldüm.

İkinin sekizinci kuvveti olması sebebiyle 256 olarak hafızalarda yer etmesine rağmen aslında 260 karakterlik bir sınır söz konusu. Çünkü sürücü adı, iki nokta üst üste, bir ters bölü ve en sondaki NUL karakterini de sayarsak aslında 256 karakterlik klasör  ve dosya isimlerinin toplamına 4 daha ilave ederek 260'a ulaşıyoruz.

Yerel bilgisayarda çalışırken mutlu mesut +260 karakterlik isimler ile klasör oluşturup dosya kaydedebilen, bunları tekrar açabilen kullanıcı, aslında bu dosyaları kopyalayamıyor, yeniden adlandıramıyor hatta silemiyor (ortalama bir kullanıcının robocopy kullanmadığını varsayarak [3]). Ta ki bir gün bu verilerin başka bir diske veya başka bir bilgisayara kopyalanması gerekene kadar. Bunu da yaparken ya eksik kopyalanıyor, ya da bir bilenden yardım isteniyor. Kopyalama sırasında Windows kullanıcıya çok yardımcı olmadığı için o anda ekranda görüntülenen mesajda tam olarak hangi dosya veya klasörün 260 sınırından daha fazla isme sahip olduğunu göremiyor. Bu durumda görüntülenen hata mesajında "Atla" tuşuna basıp işlem bittikten sonra aslında işlemin bitmediğini, eksik kalan dosyaları aramak zorunda olduğumuz ikinci aşamaya geçtiğimizi farkediyoruz. Böyle bir durumda hangi dosyaların kopyalanmadığını bulabilmek için Powershell ile aşağıdaki gibi bir komut ile dosyalar tespit edilerek bir dosyaya yazılabilir [2].

dir -recurse | where {$_.Fullname.Length -gt 260} | select FullName | out-file uzun-dosya-isimleri.txt

Ya da doğrudan cmd.exe ile

dir /s /b | sort /r /+261 /o longpaths.txt

Öngörülü insanların kullanacağı yöntem ise [1]'de belirtildiği gibi:

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem]
"LongPathsEnabled"=dword:00000001

Bu ayarı tek bir makinede Powershell ile yapmak için

New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" 
    -Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force

Bir Active Directory ortamında etki alanına üye bilgisayarların tümünde grup ilkesi ile bunu yapmak istersek yapılacak olan da

Computer Configuration > Administrative Templates > System > Filesystem > Enable Win32 long paths

ile Enable Win32 Long paths'i etkinleştirmek. Bunun öncesinde veya sonrasında LongPathsEnabled değerini kontrol etmek için

Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" 
    -Name "LongPathsEnabled"

---

[1] https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=registry

[2] https://www.mindgems.com/article/find-long-paths-long-file-names/

[3] https://it.cornell.edu/shared-file/windows-file-name-or-destination-path-you-specified-not-valid-or-too-long

3.04.2024

Powershell'de USB cihazları listeleme

Linux'taki lsusb komutuna benzer bir şey arıyorum, uzak makinelere Enter-PSSession ile bağlandığımda kullanabileceğim. Şu adresteki çözüm hoşuma gitti:

Get-PnpDevice -PresentOnly | Where-Object { $_.InstanceId -match '^USB' }

 ---

https://learn.microsoft.com/tr-tr/powershell/module/pnpdevice/get-pnpdevice?view=windowsserver2022-ps&wt.mc_id=ps-gethelp

25.03.2024

Uzak bilgisayarlardaki günlükleri etkinleştirmek

Vista ve sonrası sürümlerde Sistem, Uygulama, Güvenlik ve Kur olay günlüklerine ek olarak bir sürü Uygulama ve Hizmet Günlüğü geldi. Örnek, "Microsoft-Windows-PrintService/Operational". Ancak bu günlüklerin bir kısmı varsayılan olarak devre dışı geliyor. Bu kadar olay günlüğünü açarsak disk kısa sürede dolacaktır. İsteğe göre bir kısmını açmakta bir sakınca yok, geriye doğru çok yüksek sayıda olayı hatırlamak zorunda değilsek.

Örneğimiz "Microsoft-Windows-PrintService/Operational" varsayılan olarak kapalı geliyor. Açmak için şu yazımda anlattığım yöntemi kullanabiliriz. Önce uzak bilgisayardaki bu günlüğe ait bir değişken oluşturalım:

$PrintLog = Get-WinEvent -ListLog "Microsoft-Windows-PrintService/Operational"
$PrintLog.IsEnabled = $true
$PrintLog.SaveChanges()

Birkaç bilgisayarda durumu kontrol etmek için

$bilgisayarlar | 
ForEach-Object {
    Invoke-Command -Computername $_ -ScriptBlock {
        Get-WinEvent -Logname "Microsoft-Windows-PrintService/Operational"} | 
            Format-Table LogName, IsEnabled, RecordCount, MaximumSizeInBytes -AutoSize

gibi birşey kullanılabilir.

Alternatif olarak Windows Events Command Line Utility (wevtutil) kullanılabilir:

wevtutil set-log  Microsoft-Windows-PrintService/Operational /enable:true

20.03.2024

Powershell ile anlık olarak en fazla işlemci kullanan süreci bulmak

Her konuda olduğu gibi bilişimde de kavram karmaşaları oluyor. Bellekte olduğu gibi işlemci kullanımında da bir kavram karışıklığı var. En çok işlemci kullanan süreci belirlememiz gerekseydi anlık olarak mı bir ölçüm yapardık yoksa bilgisayarın açıldığı tarihten itibaren mi?

Poweshell'in Get-Process cmdlet'i bize sadece toplam işlemci kullanım süresini veriyor. Anlık olarak en çok işlemci kullanan süreci bulmak istersek Get-Process işimizi görmez.

Get-Process * | 
    Sort-Object -Property CPU -Descending | 
        Select-Object -First 10

Bu amaçla yaptığım kısa bir araştırmada Windows'un sayaçlarının (counter) kullanılabileceğini gördüm. Performans İzleyicisi (Performance Monitor) ile görüntülenen verilerin kaynağı olan sayaçlar, powershell ile Get-Counter cmdlet'i ile okunabiliyor. Yüzlerce sayaca sahip Windows'da hangi sayacı kullanacağımızı seçmek için şöyle bir arama yapabiliriz:

Get-Counter -ListSet *Process*

Ya da doğrudan performans izleyiciyi açarak buradan istediğimiz sayacı seçebiliriz. Dikkat edilmesi gereken bir konu, Türkçe Windows sürümlerinde sayaç isimlerinin de Türkçe olması. Script'i bir makinede geliştirip sunucuda çalıştırınca hatalar üretebilir. Ben tüm komutları İngilizce sayaç isimleri ile kullanacağım.

Benim bu amaçla kullanacağım sayaç "\İşlemci(*)\% İşlemci Zamanı" (ya da İngilizce işletim sistemlerinde "\Process(*)\% Processor Time". Ama bu şekilde Get-Counter tüm süreçlerin toplam anlık yüzdesini veriyor.

Get-Counter -Counter "\Process(*)\% Processor Time"

Ayrı ayrı süreçlerın anlık işlemci kullanım oranlarını görebilmek için ise bu komutun döndüğü [PerformanceCounterSampleSet] nesnesinin CounterSamples dizisini genişletmek gerek. Bu dizinin içinde her sürecin işlemci kullanımı CookedValue değeri içinde geliyor. Sonra bu değere göre dizmeli (Sort-Object -Property CookedValue -Descending) ve en çok değere sahip 10 (Select-Object -First 10) tanesini listelemeliyim:

Get-Counter -Counter "\Process(*)\% Processor Time" | 
    Select-Object -ExpandProperty CounterSamples |
        Sort-Object -Property CookedValue -Descending |
            Select-Object -First 10 

Bu en çok işlemci kullanımına sahip ilk 10. Ama burada görmek istemeyeceğimiz değerleri süzmek için Where-Objet {$_.InstanceName -notin ("_total","idle","system") kullanabiliriz.

Get-Counter -Counter "\Process(*)\% Processor Time" | 
    Select-Object -ExpandProperty CounterSamples |
        Where-Object {$_.InstanceName -notin ("_total","idle","system")} | 
            Sort-Object -Property CookedValue -Descending |
                Select-Object -First 10

Bu bize anlık olarak en fazla işlemci kullanım oranına sahip 10 süreci verir. İlgilendiğimiz bir süreç varsa ve bu sürecin anlık işlemci kullanım oranıyla ilgileniyorsak, örneğin yazdırma biriktiricisi (print spooler) için şöyle bir script olabilir:

Get-Counter '\Process(*)\% Processor Time' | 
    Select-Object -ExpandProperty CounterSamples | 
        where {$_.InstanceName -eq "spoolsv"}

Bu işler daha kolay olamaz mıydı?

8.03.2024

archlinux kurulumu

Benim kurulum adımlarım şöyle:

Öncelikle BIOS veya UEFI kipte mi kuracağımızı belirlememiz lazım. Sanal makineye kuruyorsak vmx dosyasının içinde firmware="efi" satırının olmasına dikkat etmek lazım.

Zamanla değişmekle birlikte bugünlerlde Archlinux CD'si (veya USB) ile boot edilen bir makinede BIOS kipinde aşağıdaki gibi bir açılış ekranı karşılar bizi.

UEFI kipinde açılan bir makinede ise Archlinux'un açılış ekranı aşağıdaki gibi olur.

Açtıktan sonra UEFI modda olup olmadığını doğrulamak için [1]

cat /sys/firmware/efi/fw_platform_size

ya da şu yazıda belirtilen yöntemler kullanılabilir. Bu satır 64 dönüyorsa sistemimiz şu anda 64 bitlik UEFI ile açılmıştır. 32 dönüyorsa 32 bitlik UEFI ile açılmıştır. Dosya bulunamadı hatası alıyorsak sistemimiz BIOS kipinde açılmıştır.

ISO dosyasınız (veya USB) ile sistemi açtıktan sonra Türkçe klavye alışkanlıkları olan birisi için öncelik klavye düzenini alışık olduğumuz düzene ayarlamaksa çalıştırmamız gereken komut şu:

loadkeys trq

Bu komut sonunda ş ve ğ gibi karakterler doğru görüntülenmeyecektir ama en azından nokta, virgül ve / karakterleri alışık olduğumuz yerlerde olacaktır.

İlk iş disk bölümlendirmesi. Önce diskimiz ne şekilde algılanmış bakmak için 

lsblk

kullanabiliriz. Eski SATA bağlantılı (SSD'ler dahil) diskler sda veya sdb gibi, NVMe diskler de nvme0n1 veya nvme0d2 gibi görüntülenir. Disk bölümlendirmesi için fdisk, gdisk veya cfdisk kullanılabilir. Ben 4 bölümden oluşan şöyle bir yapı kullanacağım (NVMe diskler için bu isimleri uygun karşılıkları ile değiştirerek diğer adımları aynen uygulayabiliriz):

sda1    EFS     512 MB             EF00     fat32
sda2 swap 1xRAM veya 2xRAM 8200 swap
sda3    boot 1 GB 8300 ext4, btrfs, xfs, jfs
sda4 root isteğe göre 8300 ext4, btrfs, xfs, jfs

3. sütundaki değerler EFS ve boot gibi bölümler için önerilen asgari boyutlar. Swap için eski alışkanlık 2xRAM gibi bir değerdi. Ama NVMe disklerin çıkışıyla birlikte bunu 1xRAM gibi bir düzeye indirebileceğimizden bahsedilmiş. 4. sütundaki değerler daha önce bahsettiğim gibi bölüm kodları. Bir EFI bölümünün kodu EF00 olmazsa bazı linux dağıtımları kabul etmiyor.

Bu bölümlendirmeyi oluşturduktan sonra biçimlendirmeye geçelim. EFS (EFI system partition) fat olmalı.

mkfs.vfat -F32 /dev/sda1

Sırayla gidelim, swap bölümü için mkfs değil mkswap kullanmalıyız:

mkswap /dev/sda2

Boot bölümü ext4 olabilir:

mkfs.ext4 /dev/sda3

Root bölümü de ext4 olabilir.

mkfs.ext4 /dev/sda4

Şimdi bu bölümleri bağlayalım. Disk yapısını oluşturmak için arada mkdir ile gereken klasörleri oluşturuyorum:

mount /dev/sda4 /mnt
mkdir /mnt/boot
mount /dev/sda3 /mnt/boot
mkdir /mnt/boot/efi
mount /dev/sda1 /mnt/boot/efi
swapon /dev/sda2

Şimdi bir Archlinux kurulumu yapmak için gereken temel paketleri kuracağız. Bunu yapmak için Archlinux'a özel pacstrap komutunu kullanacağız.

pacstrap -K /mnt base linux linux-firmware

[1]'de temel kurulum için bu 3 paketin yeterli olduğu belirtilmiş. Bu paketlerin toplamı yaklaşık 500 MB civarında olduğu için biraz zaman alacak. (Sanal makine kurulumlarında linux-firmware paketine gerek yok, bu durumda boyut 200+ düşer) Bu komuttaki -K anahtarı linux anahtarlarını ayarlamak için. Bir sebeple bu adım atlarnırsa veya sorun olursa

pacman-key --init
pacman-key --populate

ile anahtarlar yeniden ayarlanabilir. Oluşturulan disk bölümlerimiz ile ilgili bilgiyi fstab dosyasına yazmak için şu komutu kullanabiliriz:

genfstab -U -p /mnt >> /mnt/etc/fstab

Hedef sistemimiz artık /mnt altında oluştuğu için buradaki fstab dosyasına yazıyoruz. Buradaki -U anahtarı fstab dosyasına disk bölümlerini yazarken UUID'ler ile yazmasını, -p ise pseudo bağlantı noktalarını hariç bırakmasını belirtmek için. Ancak -p anahtarının varsayılan olduğu ve açıkca belirtilmesine gerek olmadığı söylenimş [2].

Bu aşamadan sonra canlı linux ortamından hedef Archlinux kurulumuna ait diğer ayarları yapabilmek için arch-chroot ile ortamımızı değiştireceğiz.

arch-chroot /mnt

Burada yapmak isteyeceğimiz işlemlerden biri hedef sistemin zaman dilimini değiştirmek. Varsayılan kurulumlarda ABD zaman dilimleri geliyor. Bunu Türkiye zaman dilimine göre ayarlamak için /usr/share/zoneinfo/ konumundaki zaman dilimlerinden birine sembolik link oluşturmamız gerek.

ln -sf /usr/share/zoneinfo/Turkey /etc/localtime

-s sembolik link (diğer seçenek hard link), -f ise /etc/localtime varsa üzerine yazmak istediğimizi belirtiyor. Hangi koşullarda tam bilmiyorum, ama bazen bu dosya olabiliyor.

Linux'ta genel kural donanım saati UTC (merkezi saat) olarak ayarlanır. Eğer değilse bunu ayarlamak için

hwclock    --systohc --utc

kullanabiliriz. Bu aşamadan sonra bazı yerelleştirme ayarları ile devam edebiliriz. Ama kullanacağımız editörü henüz yüklemedik. Seçeneklerimiz nano, vi, vim veya neovim. Birini yükleyelim. Ben neovim ile devam ediyorum:

pacman -S neovim

Yerel ayarlarımızı Türkiye'ye göre ayarlamak için /etc/locale.gen dosyasının içindeki tr_TR ile başlayan (uygun görülen) satırların başındaki # karekterini silmemiz gerek. Ben sadece

# tr_TR.UTF-8

satırını

tr_TR.UTF-8'e

dönüştürüyorum. Sonrasında 

locale-gen

çalıştırmak gerek. Benzer şekilde /etc/locale.conf dosyasına da bir satır ekleyelim:

echo LANG=tr_TR.UTF-8 > /etc/locale.conf

Bir de yeni bir ortam değişkeni oluşturalım.

export LANG=tr_TR.UTF-8

Bu son 3 adımın her birinin gerekliliğini bilmiyorum, ama [1]'de yazılmış.

Bir sonraki adımımız makine ismimizi belirlemek.

echo "archbox" > /etc/hostname

Eğer istenirse ağ yöneticisi olarak network manager kurulabilir (ayrıca [4])

pacman -S networkmanager nm-connection-manager network-manager-applet

Kurulmadıysa aşağıdaki kurulum sonrası adımlarda bazı yöntemler var.

Hedef sistemimizdeki root kullanıcısının parolasını belirleyelim:

passwd

2 kez aynı parolayı yazmayı isteyecek. Kendimize yeni bir kullanıcı da oluşturalım. Genel eğilim, bu kullanıcının da yetkili bir kullanıcı (wheel grubu üyesi) olması. Bu amaçla aşağıdaki gibi bir komuta ihtiyacımız olacak

useradd -mg users -G wheel,power,storage -s /bin/bash metin

ve bu kullanının şifresini belirleyip (belirlemezek kullanıcı devre dışı gelir) şifre süresini sınırsız yapalım (kötü bir örnek mi?). Linux kullanıcı yönetimi için tıklayın.

passwd metin
chage -d 0 metin

Henüz sudo kurmadık. Bunun yanı sıra ihitiyaç duyulabilecek birkaç paket daha kuralım:

pacman -S sudo grub efibootmgr intel-ucode os-prober zsh

Şimdi wheel grubuna yetkilerini verelim. Önce visudo komutunun ihtiyaç duyduğu varsayılan editörü belirleyelim:

export EDITOR=nvim

sonra

visudo

komutunu çalıştırarak gelen ekranda

#%wheel ALL=(ALL:ALL) ALL

satırının başındaki # işaretini silerek dosyayı kadedip çıkalım.

Son adımlara geldik. Önce grub'ı ayarlayalım:

grub-install --target=x86_64-efi --efi-directory=/boot/efi

Sonra grub.cfg dosyalarını oluşturalım. Gerçek yeri neresi, tam bilemiyorum ama 2 konum için de ayrı ayrı oluşturmayı tercih ediyorum.

grub-mkconfig -o /boot/grub/grub.cfg
grub-mkconfig -o /boot/efi/EFI/arch/grub.cfg

Son adım, initramfs dosyasının oluşturulması. Bunu mkinitcpio ile yapacağız. Ama bunun için bir preset'e ihtiyacımız var. pacstrap ile kurduğumuz linux paketi bize /etc/mkinitcpio.d klasörünün atında linux.preset dosyasını verdi. Bunu -p linux yazarak, veya sadece -P (bütün presetler için oluştur) diyerek yapabiliryoruz. Her rehberde bahsedildiği için bunu yapmaya alışmışım. Ama aslında initramfs dosyasının oluşturulması zaten pacstrap ile kurulan her çekirdek paketinden sonra otomatik yapıldığı için aşağıdaki adım gerekli değil.

mkinicpio -p linux

İşlem tamam. arch-chroot ortamından çıkalım.

exit

Tüm bağladığımız disk bölümlerinin bağlantılarını keselim. 

umount -R /mnt

ve sistemi tekrar başlatalım.

reboot

Tekrar başlayınca oluşturduğum metin kullanıcısıyla oturum açabileceğim. Ama henüz bir masaüstü ortamı yüklemedik. Bunu da başka bir yazıda yazacağım.

Kurulum Sonrası adımlar

Kurulumdan sonra eğer Network Manager gibi bir ağ yöneticisi kurulmadıysa bilgisayar otomatik IP almayabilir. Bu durumda /etc/systemd/network klasörünün altında

20-wired.network
25-wireless.network

dosyaları içine DHCP için

[Match]
Name=wlp2s0

[Network]
DHCP=yes
IgnoreCarrierLoss=3s

sabit IP için

[Match]
Name=enp1s0

[Network]
Address=10.1.10.9/24
Gateway=10.1.10.1
DNS=10.1.10.1

satırları eklenebilir [3].

zsh kurduk, ama ayarlamadık. Aslında zsh kurulumunu useradd adımından önce yaparsak useradd adımında /bin/bash yerine /bin/zsh diyerek bunu doğrudan devreye alabiliriz. Ama yapmadıysak da şu yazımdaki adımlarla yapılabilir.

Tekrar başlattıkan sonra 

grub>

gibi bir ekranda kalıyorsa grub.cfg dosyasını bulamıyor olabilir. CD/USB ile canlı ortamda açarak grub-mkconfig adımını tekrarlamak gerek. UEFI sistemlerde bile /boot/grub/grub.cfg olmasına dikkat etmek gerek.

Kurulan sistemdeki klaveye düzeni Türkçe olmazsa Türkçeleştirmek için

localectl set-keymap trq

kullanılabilir.

Network Manager kurduktan sonra systemd-networkd hizmetini devre dışı bırakmak gerek. dhcpcd ve systemd-resolved hizmetleri de aynı anda çalışmamalıdır [4].

---

[1] https://wiki.archlinux.org/title/installation_guide
[2] https://man.archlinux.org/man/genfstab.8
[3] https://wiki.archlinux.org/title/Systemd-networkd

[4] https://ejmastnak.com/tutorials/arch/network-manager/