23.12.2021

Manjaro grub güncelleme hatası

Manjaro'da update-grub kullanırken aşağıdaki gibi bir hata alıyordum:

/usr/share/grub/grub-mkconfig_lib: satır 288: printf: `$': biçim karakteri geçersiz

Bunun sonucunda da grub menüm güncellenmiyordu. Hatadan bir anlam çıkaramadım. Bu, sanıyorum sadece Türkçe olarak yüklenen Manjaro veya linuxlarda oluyor. ŞuraÉda çözüm olarak

$ sudo LC_ALL=C /bin/bash -c "grub-mkconfig -o /boot/grub/grub.cfg"

çalıştırmak önerilmiş. İşe yaradı.

---

[1] https://bbs.archlinux.org/viewtopic.php?id=267763

[2] https://www.technopat.net/sosyal/konu/arch-linux-grub-mkconfig-arizasi.1584960/#post-12110027

29.11.2021

Windows Terminal ve oh-my-posh

Daha önce Powershell komut satırını özelleştirme ile ilgili yazmıştım. Ama bu sefer daha gelişmiş bir terminal arayüzü için özellikle Scott Hanselman'ın videolarında ve blog'unda bahsettiği oh-my-posh (oh-my-bash benzeri) gibi bileşenleri ile ilgili yazacağım.

oh-my-posh'un kurmanın 2 yolu var:

1. PowershellGet:

Yönetici haklarıyla açılmış bir powershell penceresinde

PS> Install-Module oh-my-posh

yazmak. Bunun sonucunda önce PSGallery'ye güvenmek isteyip istemediğimizi soracak. 

2. Windows Paket Yöneticisi (winget). 2022'nin ikinci yarısından itibaren bu önerilen yöntem.

Komut satırından

PS> winget install JanDeDobbeleer.OhMyPosh

yazmak. Burada install'dan sonra gelen tam adı (aslında ID) yazabilmek için  önce içinde 

oh-my-posh yazan paketleri aratmak isteyebiliriz.

PS> winget search ohmyposh

Name       Id                      Version Match           Source
-----------------------------------------------------------------
Oh My Posh JanDeDobbeleer.OhMyPosh 6.18.1  Tag: oh-my-posh winget

Kurulumu tamamladık. Öncelikle kullanabileceğimiz temaları görelim.

PS> Import-Module oh-my-posh

PS> Get-PoshThemes

Burada listelenen temalardan birini seçtikten sonra bunu profil dosyamıza ekleyip kalıcı yapabiliriz. Ben jblab_2021 temasıyla ilerleyeceğim.

PS> notepad $PROFILE

ile profil dosyamızı açıp içine şu satırları ekleyelim (dosya yoksa yeni bir dosya yaratmamız gerekir).

Import-Module oh-my-posh

Set-PoshPrompt jblab_2021

(2023-02-07 Ek: Yeni sürümde $profile dosyasında şu satır ile yapılandırma öneriliyor:

oh-my-posh init pwsh --config "$env:POSH_THEMES_PATH\mojada.omp.json" | Invoke-Expression

Sondaki  mojada, tercih edilen temanın adı.)

Aslında bu aşamadan önce yapılması gerekirdi ama yapılmazsa ne farkediyor diye görmek için sonra bıraktım. Varsayılan olarak kullanılan font aileleri sembol olarak kullanılan bazı karakterleri desteklemiyor. Bu amaçla özel bir font ailesi yüklememiz gerek. Scott Hanselman'ın videosunda gördüğüm nerdfonts.com sitesi güzel. Ben de bu siteden Caskaydia Cove Nerd Font ailesini yükledim. Zip dosyasını indirdikten sonra içindekileri Windows klasörünün içindeki Fonts klasörüne kopyalamak gerekecek. Ardından bu kullandığımız terminalde bu fontları görmek için ayarlardan tercih edilen font ailesi seçimini yapmamız gerek. Terminal derken yeni Windows Terminal'i de kuralım.

PS> winget install Microsoft.WindowsTerminal

Bu aşamadan sonra promptumuz tamamlandı. Eğer git kullanıyorsanız ve komut satırından git işlemleri için görsel destek arıyorsak ek olarak posh-git modülünü kurabiliriz.

PS> Install-Module posh-git

Git dışında başka faydalı özelliklerden biri Spotify'da çalan şarkı bilgisni komut satırı prompt'unda görmek olabilir. Bunu eklemek için cinnamon, jtracey93 veya night-owl gibi temalardan birini seçmek ya da mevcut temamızı Export-PoshTheme ile temanızı bir dosyaya yazmak ve sonra bu dosyı düzenleyerek içine bir Spotify segmenti eklememiz gerek.

Yeni farkettiğim başka bir faydalı özellik de PSReadline için geçmişe dayalı tahmin özelliği. Bunu etkinleştirmek için

PS> Set-PSReadlineOption -PredictionSource History

Yapmak yeterli. Bunu elbette her açılan terminal penceresinde olmasını istiyorsak $Profile içine kaydetmeliyiz. PSReadline sürüm 2.1.0 veya üzeri gerekli. Şu an beta sürümünde bazı yeni özellikler de var, güncellemeleri kaçırmamak gerek.

2022-08-03 Ek: Bugün kontrol ettiğimde depolarda PSReadLine'ın 2.2.6 sürümünün olduğunu görüp

PS> Update-Module PSReadLine -Scope CurrentUser -Force -Verbose

ile güncelledim. (Yukarıda ilk yükleme adımlarında işlem yaptığım bilgisayar ile bu bilgisayar farklı, bunu unutmuşum. Bu sebeple Update-Module kullandım. winget ile yüklediysem winget ile update etmeliyim) Beklediğim özellik tahminlerin görüntülenme yöntemi için var olan Inline kipine ilave olarak ListView kipiydi. Bu özellik gelmiş. Profil dosyamı açarak içinde PSReadline seçeneklerini ayarladığım satırı şu şekilde değiştirdim:

PS> Set-PSReadLineOption -PredictionSource HistoryAndPlugin -PredictionViewStyle ListView

Çalışan powershell konsolunu kapatıp tekrar açtıktan sonra artık öneriler listelenmiş olarak geliyor. 

2022-08-08 Ek: oh-my-posh'u güncelledikten sonra her yeni powershell penceresinde şu mesaj görüntülenmeye başladı:

Hey friend

In an effort to grow oh-my-posh, the decision was made to no
longer support the PowerShell module. Over the past year, the
added benefit of the module disappeared, while the burden of
maintaining it increased.

However, this doesn't mean oh-my-posh disappears from your
terminal, it just means that you'll have to use a different
tool to install it.

All you need to do, is follow the migration guide here:

https://ohmyposh.dev/docs/migrating

Önerilen; artık Install-Module ile değil, winget ile yüklememiz. Evet yükledim, ama yine de bu mesaj çıkmaya devam ediyor. Powershell-core için modül klasörü Belgelerim\Powershell\Modules altında oh-my-posh klasörü altında yeni versiyonun klasörüne girdim ve oh-my-posh.psm1 dosyasını açtığımda dosyanın en üstünde bu mesajın görüntülenme yerinin burası olduğunu gördüm. Satırları # karakteri ile etkisiz hale getirdim.

# Write-Host @'
# Hey friend
#
# In an effort to grow oh-my-posh, the decision was made to no
# longer support the PowerShell module. Over the past year, the
# added benefit of the module disappeared, while the burden of
# maintaining it increased.
#
# However, this doesn't mean oh-my-posh disappears from your
# terminal, it just means that you'll have to use a different
# tool to install it.
#
# All you need to do, is follow the migration guide here:
#
# https://ohmyposh.dev/docs/migrating
# '@

---

Bonus; farklı kullanımlara sahip profil dosyası konumları:

Tüm kullanıcılar & tüm bilgisayarlar -       C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1

Tüm Kullanıcılar & bu bilgisayar -    C:\Windows\System32\WindowsPowerShell\v1.0\Microsoft.PowerShell_profile.ps1

Bu kullanıcı & tüm bilgisayarlar -    C:\Users\UserName\Documents\WindowsPowerShell\profile.ps1

Bu kullanıcı & bu bilgisayar - C:\Users\UserName\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1

---

https://bunchofthoughts.me/powershell-customization/
http://draith.azurewebsites.net/?p=386
https://itecnote.com/tecnote/powershell-unable-to-use-ihtmldocument2/
https://zimmergren.net/making-windows-terminal-look-awesome-with-oh-my-posh/
https://learn.microsoft.com/en-us/windows/terminal/tutorials/custom-prompt-setup
https://www.someoneelsescloud.com/episodes/level-up-your-command-line-with-oh-my-posh
https://scriptingchris.tech/posts/how-i-setup-my-powershell-development-environment/

21.09.2021

linux rm komudu ve parametreleri

Komut satırı, genel olarak grafik arayüze kıyasla daha fazla seçenek sunuyor. Ama tüm komutları ve tüm parametrelerini hatırlamak da ayrıca bir çaba gerektiriyor; ya da en azından bir blog sayfasına yazıp gerektiğinde bakmayı falan.

Silme amacıyla kullanılan rm komutunun bir sürü kullanışlı parametresi var. En basitinden başlayalım.

Bir dosyayı silmek için

# rm dosya

Sorunsuz silinirse hiçbir bilgi vermez. Ama var olmayan bir dosyayı silmeyi denersek

rm: cannot remove 'deneme2.txt': No such file or directory

veya yazma koruması olan bir dosyayı silmeye çalışırsak da 

rm: remove write-protected regular file 'filename'?

gibi bir hata mesajı alabiliriz.

Her türlü bize sildiği dosyanın bilgisini versin istersek -v (verbose) parametresini kullanabiliriz.

# rm -v dosya

Ya da yazma korumasına sahip dosyayı da silmesini istersek -f (force) parametresini kullanabiliriz.

# rm -f dosya

Birden fazla dosyayı silmek için ard arda isimleri yazabilir

# rm dosya1 dosya2 dosya3.txt dosya4.jpg dosya5.zip

ya da wildcard karakterleri kullanabiliriz.

# rm dosya*.txt

Bir klasörü içindekilerle birlikte silmek istersek -r (recursively) parametresini

# rm -r  klasor

ve boş bir klasörü silmek için de -d (directory) parametresini kullanabiliriz.

# rm -d bosklasor

Farkedilebileceği gibi silerken "Emin misini?" gibi bir soru sormaz, rm komudu. Eğer sormasını istersek -i parametresini kullanabiliriz.

# rm -i dosya1 dosya2

Birden fazla dosyayı silerken dosya sayısı kadar değil, sadece 1 kez sorsun yeter diyorsak -I (büyük i) parametresi işimizi görür.

# rm -I dosya*.txt

Hiç başıma gelmemişti ama tire karakteriyle başlayan dosyaları silmek için iki yol önerilmiş[1]. Biri 2 kez tire parametresi:

# rm -- -dosya

İkinci yol da -iv parametresi ile tam yol belirtmek:

# rm -iv /home/metin/-dosya

[1] https://bytexd.com/rm-command-in-linux/

20.08.2021

Fedora ve Ubuntu'da unutulan parolayı sıfırlamak

Parolalar unutulur. Unutulduğunda sıfırlanmaları gerekir. Bu her işletim sistemi için geçerli.

Ubuntu ile başlayalım. Açılıştaki Grub menüsünden recovery seçeneği ile başlamak gerek. Recovery genelde Advanced Options altında kurulu her çekirdek (kernel) sürümü için ayrı ayrı verilmiştir. En yenisi ile başlayabiliriz. Bu adımdan sonra bir nonktada aşağıdaki gibi bir ekran ile karşılaşacağız.

 
Burada kırmızı ile belirtilen root seçimi ile ilerleyeceğiz. Bir sonrak aşamada 

Press Enter for maintenance
(or press Control-D to continue):

mesajı bizi karşılayacak. Enter'a basıp root ile giriş yapacağımız terminali açalım. Control-D sadece normal açılışa devam etmek için. Bizim istediğimiz bu değil.

Artık burada 

# passwd <kullanıcı_adi>

komutu ile parolamızı değiştirebiliriz. Yetki hatası alırsak

# mount -o remount,rw /

komutu ile diskimizi yazma modunda mount ederek passwd komutunu tekrar deneyebiliriz.

Eğer kullanıcı adımızı da unuttuysak

ls /home

komutu ile sisteme oturum açmış kullanıcıların ev klasörleri arasından seçim yapabiliriz. Şifre değiştirme işlemi tamamlandıktan sonra

# reboot

komutu ile sistemi tekrar başlatabiliriz.

Fedora için işler biraz daha farklı. Açılışta grub menüsündeyken Fedora'nın satırında "e"ye basarak düzenleme moduna geçip aşağıdaki ekranda kırmızı ile işaretlediğim satırı

load_video
set gfxpayload=keep
insmod gzio
linux ($root)/vlinux-5.13.9-200.fc34.x86_64 root=/dev/mapper/fedora_localhost\
--live-root ro resume=/dev/mapper/fedora_localhost--live-swap rd.lvm.lv=fedora\
_localhost-live/root rd.lvm.lv=fedora_localhost-live/swap rhgb quiet
initrd ($root)/initramfs-5.13.9-200.fc34.x86_64.img

Şununla değiştirmemiz gerek:

rd.break enforcing=0

Değiştirdikten sonra bu ayalarla sistemi başlatmak için Ctrl+X'e basalım. Sistem yine grafik arayüz olmadan tek kullanıcılı modda açılacak. Burada yine Ubuntu'daki gibi

Press Enter for maintenance
(or press Control-D to continue):

Ekranı gelecek. Enter'la devam edelim. Bu noktada dosya sistemi /sysroot altında sadece okuma kipinde bağlanmış bulunuyor. Fedora'da root partition'ı yazma kipinde tekrar bağlamak ve şifreyi değiştirmek için:

# mount -o remount,rw /sysroot
# chroot /sysroot
# passwd <username>

yapmak gerek. 

Authentication token manipulation error

hatası aldıysak bir önceki adımlarımızı kontrol etmemiz gerek; dosya sistemi rw olarak bağlanmamış demektir.

İşler bittiğinde 2 kez exit yazıp enter'a basarak tekrar başlatacağız.

Açılışta Fedora SELinux koruması bize /etc/shadow dosyasının SELinux etiketlerinin yanlış olduğuyla ilgili uyarılar verebilir. Bir komut satırı açıp şunları yazmamız gerekecek:

# sudo restorecon -v /etc/shadow

Bunun öncesinde

# ls -Z /etc/shadow

system_u:object_r:unlabeled_t:s0 /etc/shadow

olan SELinux etiketinin sonrasında

# ls -Z /etc/shadow

system_u:object_r:shadow_t:s0 /etc/shadow

olarak değiştirildiğini görebiliriz. Son olarak

# sudo set enforcing 1

ile SELinux'u tekrar etkinleştirebiliriz.

Ek 2022-05-24: Etiketleme işini bütün disk için otomatik yapmasını sağlamak için 2 kez exit yazıp çıkmadan hemen önce

touch /.autorelabel

yazabiliriz. Ama bu büyük bir disk için uzun sürecektir.

---

https://docs.fedoraproject.org/en-US/fedora/latest/system-administrators-guide/kernel-module-driver-configuration/Working_with_the_GRUB_2_Boot_Loader/

Server üzerinde bir süreç sonlanınca eposta göndersin

Temel olarak Windows olay günlüğüne belli bir yeni olay düştüğünde eposta göndermek veya başka bir komut çalıştırmak mümkün. Ben de bir sürecin sonlanması durumunda eposta göndereceğim. Sürecin sonlanmasını da Sysinternals arası Sysmon ile denetleyeceğim. İstenmesi durumunda:

  • Bir sürecin başlaması veya sonlanması
  • Bir dosyanın oluşturulması veya silinmesi
  • Kayıt defterinde bir değer oluşturulması veya silinmesi
  • Belirli bir IP adresine/porta ağ bağlantısı yapılması

gibi durumlarda istenen hareket yapılabilir.

Öncelikle sysmon'u istenen sürecin sonlanması durumu için bir yapılandıralım. İlk iş, sistemde var olan bir sysmon yapılandırması bulunuyor mu, bakalım:

> sysmon64 -c

Bu satırdan sonra "Sysmon is not installed on this computer" yazdıysa sistemde var olan bir yapılandırma yoktur ve sıfırdan başlayabiliriz. Eğer bir yapılandırma varsa onu bozmadan üzerine yapılandırma yapmak gerek. -c anahtarıyla gösterilen xml dosyasını bulup yapılandırmamızı bunun üzerine yapmamız gerekecek. Bu konumuz dışında. Eğer varolan bir yapılandırma varsa ve sıfırlamak istiyorsak

> sysmon64 -c --

ile sıfırlayabiliriz.

Tekrar başa dönelim; "Sysmon is not installed on this computer" yazdıysa sistemde hiç sysmon hizmet sürücüsü kurulmamış demektir. Bu durumda sysmon'u -i anahtarıyla çalıştırarak hizmet sürücüsünü kurabiliriz. Ben 64-bitlik sürümünü kullanacağım. Ayrıca ilk çalıştırmada son kullanıcı lisans anlaşmasını (EULA) da kabul etmem gerek (-accepteula).

> sysmon64 -accepteula -i process_end.xml

Terminalimiz Powershell ise dosyaları belirtirken önlerine tam adlarını (aynı klasördeysek de .\process_end.xml şeklinde) yazmamız gerek. Tab tamamlaması bu durumda çok işe yarar.

process_end.xml dosyası oluşturmadan sadece -i anahtarını kullanarak başlarsak varsayılan ayarlar geçerli olur ve takibini yapmak istediğimiz olay sadece bizim ilgilendiğimiz uygulama için değil, sistemdeki tüm uygulamalar için yaratılır, ve çok fazla eposta almaya başlarız. Bunun için böyle bir xml dosyası ile hedefimizi daraltmamız gerekecek.

Gelelim xml dosyasının oluşturulmasına. Şu anda bende sürüm 13.22 yüklü. Bu sürümde şema sürümü de 4.70. Bunu da xml dosyamızda belirtmemiz gerekecek. 13.22 ile birlikte 4.70 veya öncesi şema sürümleri de kullanılabilir ama sürümler arası fark bazen beklenmeyen sonuçlar verdiği için (RuleGrup'ların mantıksal operatörleri varsayılan işleme biçimi gibi) ne yaptığımızı bildiğimizden emin olmamız gerek. XML dosyasındaki ilk satır şema versiyonu:

<Sysmon schemaversion="4.7">

Bu satırdan sonra hash algoritmamızı belirtelim. Olay günlüğüne çalıştırılan her uygulamanın hash'i de hesaplanıp yazılacak.

<HashAlgorithms>sha256</HashAlgorithms>

Sadece 1 ölçütüm (süreç sonlanma) olduğu için daha fazla karıştırmadan bununla ilgili process terminate tag'ını girelim

<EventFiltering>

<ProcessTerminate onmatch="include">

Takip ettiğimiz sürecin hangi özelliği ile ilgileniyoruz? Örnek olarak sistemdeki process explorer'ı takip edelim (procexp64.exe)

<Image condition="end with">procexp64.exe</Image>

Nihai olarak açık taglarımızı kapatlım:

</ProcessTerminate>

</EventFiltering>

</Sysmon>

Her process explorer'ın sonlanmasında "Microsoft-Windows-Sysmon/Operational" EWT olay günlüğüne 5 numaralı olay kayıtları düşer. Görmek için olay günlüğünde Uygulama ve Hizmet Logları (Applications and Services Logs)>Microsoft>Windows>Sysmon>Operational altına bakmak gerek. Gelelim bu olay kayıtları için bir hareket oluşturmaya. Olay günlüğüne bir tane 5 numaralı olay düştükten sonra olay görüntüleyicisi penceresinin sağ kısmında "Bu olaya göreve ekle (Attach Task to this Event)"e tıklayarak işleme devam edebiliriz. Uygulama çalıştırmanın dışındaki diğer seçenekler (eposta göndermek ve bir mesaj görüntülemek) artık terk edilmiş hareketler. Biz eposta gönderebilmek için bir powershell script'i kullanacağız. Şöyle bir script işimizi görür:

$EmailFrom = "gonderen@mailadresi.com"
$EmailTo = "alici@alantaraf.com"
$Subject ="procexp sonlandı"
$Body = "Bekledigimiz Process Explorer'ın sonlanması şu an gerçekleşti."
$SMTPServer = "smtpsunucu.mailadresi.com"
$SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 587)
$SMTPClient.Send($EmailFrom, $EmailTo, $Subject, $Body)

Bu dosyanın ilk 5 satırı sırasıyla gönderenin eposta adresi, alıcının eposta adresi, eposta mesajının konusu, mesajın içeriği ve eposta sunucumuz şeklinde. Bunları uygun şekilde değiştirmek gerek.

Ya da Send-MailMessage cmdlet'ini kullanarak gönderebiliriz:

PS> $smtpParams= @{
From = "gonderen@mailadresi.com"
To = "alici@alantaraf.com"
Subject ="procexp sonlandı"
Body = "Bekledigimiz Process Explorer'ın sonlanması şu an gerçekleşti."
SMTPServer = "smtpsunucu.mailadresi.com"
}

PS> Send-MailMessage @smtpParams

Bu scriptin C:\Users\metin\Belgeler\eposta_gonder.ps1 gibi bir konumuna kaydedildiğini varsayalım.

En son olaya görev ekleme aşamasında kalmıştık. Bu pencerede3 kutu var. Birincisi çalıştırılacak program. Buraya powershell.exe'nin konumunu yazmamız gerek. Bu çoğu durumda 

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe

şeklindedir. Argumanlar için yapmamız gereken birinci iş geçici olarak Execution Policy'yi devre dışı bırakmamız ve scriptimizi bir -File anahtarı ile göstermemiz. Hepsi birlikte şöyle olabilir:

-ExecutionPolicy Bypass -File C:\Users\metin\Belgeler\eposta_gonder.ps1

Üçüncü kutu sadece çalışacak script bulunduğu klasörle ilgili bir dosya işlemi yapacaksa gerekli. Bizimki gerekmeyeceği için boş bırakabiliriz. Boş kalmasın diyenler C:\Users\metin\Belgeler\ gibi birşey yazabilir.

19.08.2021

Regular Expressions

Bu konu bir blog yazısında anlatılacak kadar kısa bir konu değil; farkındayım. Ama konunun ana hatlarını yine de özetlemek istiyorum.

Bir metnin içinde (düzyazı, program, bir klasördeki dosya içerikleri, URL dizileri, dosys sistemi yolları vs) bir "dünya" var mı yok mu aramak istiyoruz. Örneğin bir klasördeki txt uzantılı dosyaların içinde. Bunu powershell ile yapmanın yolu

> Select-String -Pattern "dünya" -Path *.txt

Bu arama bize bazı sonuçlar döner. Ama diyelim ki dosyaların bazılarında "ü" yerine "u" kullanıldığını farkettik. Bu durumda "dunya" anahtar kelimesini de içerecek şekilde aramamızı değiştirelim. Ayrıca varsayılan parametre sıralaması ve alias kullanımıyla komutu biraz kısalatalım.

> sls "dünya|dunya" *.txt

"|" operatörü VEYA işlemi görür. Burada değişen 1 harf olduğu için bunu ü VEYA u şeklinde de yapabilirdik.

> sls "d[u|ü]nya" *.txt

Köşeli parantezler, bir karakter kümesi belirtmek için kullanılır. Örneğin sadece d,u,ü,n,y ve a'dan oluşan 5 veya 6 harfli kelimeleri bulmak istersek

> sls "[adnuüy]{5,6}" *.txt

yazabiliriz. Bu satırla sadece dünya ve dunya anahtar kelimelerini bulmakla kalmayacağız, ayrıca içinde herhangi bir sırada bu harflerin 5 veya 6'sı geçen başka kelimeleri de bulacağız. Ayrıca bunlar tam kelimeler de olmayacak, kelimenin herhangi bir bölümünde bu harflerin 5 veya 6'sının geçmesi yeterli olacak.

Bazı özel operatörleri listeleyelim:

?Önceki karakter 0 veya 1 kez geçecek
*Önceki karakter 0 veya daha fazla geçecek.
+Önceki karakter 1 veya daha fazla geçecek.
{n,}Önceki karakter n veya daha fazla geçecek.
{n,m}Önceki karakter en az n kere, en fazla m kere geçecek. Arasındaki sayılarda da olabilir.
. Yeni satır karakteri hariç her tekil karakteri bulur.
[a-z]Yalnızca "a" ve "z" arazındaki karakterleri bulur.
[^abc]a, b ve c olmayan her karakteri bulur.
^Satırın başını bulur.
$Satırın sonunu bulur.
( )Gruplandırma için. Parantezin içindeki ifade hatırlanır ve daha sonra \n veya $n gibi bir operatör ile kullanılabilir.
(?:)Aynen ( ) gibi gruplandırma için kullanılır, ama parantezin içindeki ifade hatırlanmaz
|veya operatörü
\wtüm harfleri bulur.
\Wharf olmayan karakterleri bulur.
\sBoşluk karakterlerini bulur.
\SBoşluk olmayan karakterleri bulur.
\dSayıları bulur.
\DSayı olmayanları bulur.
\AString'in başını bulur.
\ZString'in sonunu bulur. Bir yeni satır karakteri varsa ondan hemen öncesini bulur.
\zString'in sonunu bulur.Yeni satır karakterini de atlar.
\GEn son aramanın bulduğu noktanın bir sonrasını bulur.
\bKelime sınırlarını bulur.
\BKelime olmayan sınırları bulur\.

Bir string değişkenimiz olsun, aşağıdaki gibi

> $str1 = "Bugün günlerden Pazartesi"

Regular expression ifadelerimizin nasıl çalıştığını bu string üzerinde deneyerek görelim. Powershell'de -match  operatörü regular expression kullanır. Ama -like kullanmaz. Değiştirme operatörü -replace de regular expression kullanır. Değişkenin içinde "gün" geçiyor mu bakalım:

> $str1 -match "gun"

False

Sonuç altta "False" olarak verildi. "u" ve "ü" alternatifli olarak deneyelim:

> $str1 -match "g[u|ü]n"

True

Bu şekilde bir eşleşme bulundu. Pazartesi geçen kelimeleri Salı ile değiştirmek için

> $str1 -replace "Pazartesi","Salı"

Bugün günlerden Salı

Çok karşılaşılabilecek bir şey, ay\gün\yıl şeklinde yazılan tarihlerin gün.ay.yıl şeklinde çevrilmesi olabilir. Bunun için back reference denen gruplandırma operatörü olarak yukarıdaki tabloda listelediğim parantezleri kullanmalıyız. İşlem şu şekilde olacak, 2 haneli ay bilgisi bulunacak, 2 haneli gün bilgisi bulunacak, bunlar yer değiştirilecek ve ayraç olarak kullanılan "\" yerine "." konacak ve sonrasında gelen 4 haneli yıl bilgisi değiştirilmeyecek. Bunun için bulduğu gün, ay ve yıl verilerini hatırlaması gerekecek.

> $str1 = "bugün 11\20\1974"

> $str1 -replace '(\d{2})\\(\d{2})\\(\d{4})','$2.$1.$3'

bugün 20.11.1974

Burada çift tırnak yerine tek tırnak kullanmaya dikkat.

Çok sık yaşadığım bir konu, bir klasörde bir dosya arıyorum. Dosya adında geçen bir anahtar kelimeye göre aramak kolay. Ama 2 anahtar kelimem var ve hangisi önce gelecek, hangisi sonra gelecek, aralarında başka kelime olacak mı, bu ayrıntılar önemli değil. Sadece bu 2 anahtar kelimenin dosya adında geçmesini istiyorum. Bunun için yapılması gereken [3]:

> dir * | where {$_.Name -match "(?=.*format)(?=.*powershell)"}

Benzer şekilde bir klasördeki dosyaların içinde de geçmesini istediğim 2 anahtar kelime için arama yapabilirim [9]:

> sls "(?=.*black)(?=.*star)" *-d.txt

Ekleme  2022-07-13: linux komut satırı aracı grep'in alışkanlıkları biraz farklı. farketebildiğim istisnaları şöyle:

\d    ->  [[:digit:]]

\w   ->  [[:blank:]]

ayrıca [8]'de anlatıldığı gibi şu "karakter sınıfları" da var:

[[:alpha:]]        Alfabetik karakterler

[[:alphanum:]]    Alfanumberik karakterler

[[:lower:]]        küçük harfler

[[:upper:]]        büyük harfler

Bunlara "genişletilmiş düzenli ifadeler" (extended regular expressions) denmiş. Bunları kullanmak için grep'e -E anahtarını sağlamamız istenmiş. Örneğin Manjaro'da linux çekirdeği kurulma tarihlerini /var/log/pacman.log dosyasından süzmek için

> grep -E "installed linux[[:digit:]]{2,3}[[:blank:]]" /var/log/pacman.log

kullandım. Her yeni çekirdek kurulumunda installed linux, güncellemesinde ise upgraded linux, sonrasında ise duruma göre  2 veya 3 karakterlik sürüm numarasını yazıyordu. Yeni çekirden yardımcı paketlerle geldiğinden bir tire karakteriyle bu yardımcı karekterler de listelenmesin diye [[:blank:]] de kullandım.

Ek 2023-06-04: Bazen de grep ile "içine geçmeyen"leri bulmak gerekir. Bu durum için grep için -v anahtarı önerilmiş. Şöyle olabilir:

> grep "icinde_gesin" /var/log/birdosya | grep -v "gecmesin"

Ek 2023-10-17: İçinde IP adresi olan bir string değişkenimiz var. Bu string'in içinde geçen IP adresini görmek istiyoruz. Yöntemlerden biri

PS> ($metin | select-string -Pattern "([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})").Matches[0].Value

diğeri de

PS> $metin -match "([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})"
PS> $matches[0]

---

[1] https://en.wikipedia.org/wiki/Regular_expression

[2] https://regexr.com/

[3] https://www.baeldung.com/string-contains-multiple-words

[4] https://www.regular-expressions.info/

[5] https://www3.ntu.edu.sg/home/ehchua/programming/howto/Regexe.html

[6] https://regex101.com/

[7] https://www.geeksforgeeks.org/write-regular-expressions/

[8] https://linuxize.com/post/regular-expressions-in-grep/

[9] https://www.rexegg.com/regex-lookarounds.html

16.08.2021

Powershell, diziler ve Add-Member

Şöyle bir hedefim var; Bir klasördeki dosyaları tablo olarak listelerken bir sütunda dosya sistemindeki sahip bilgisini de görmek istiyorum. Boş bir dizi oluşturayım.

> $dosyalar = @()

Sonra bu diziyi dosya bilgileri ile doldurayım.

> dir | % { $dosyalar += [PSCustomObject]@{

    Isim = $_.FullName

    Boyut = $_.Length

 }

Bu adımda $dosyalar dizisi şöyle olur:

Isim                          Boyut

-----                         -----

D:\Klasor\Dosya1.txt             22

D:\BaskaKlasor\Dosya2.doc      1366

Bu şekilde Isim ve Boyut sütunları olan bir dizi oluştu. Şimdi NTFS dosya sisteminde dosyayı oluşturan kullanıcı olan "sahip" (owner) bilgisini de eklemek istiyorum.

Oluşturma aşamasında da sahip bilgisini ekleyebilirdik, ama örnek olması açısından bu nesneye oluşturduktan sonra ekleyelim.

> Add-Member -InputObject $dosyalar -MemberType ScriptProperty -Name Sahip -Value {(Get-Acl $this.Isim).Owner}

Bu işlem sonunda şu şekilde bir tablomuz olur:

Isim                          Boyut    Sahip

-----                         -----    -----

D:\Klasor\Dosya1.txt             22    metin

D:\BaskaKlasor\Dosya2.doc      1366    arda

12.08.2021

Powershell ile metin dosyalarını incelemek

Grafik arayüzlü metin editörleri varken ne gerek var Powershell'e. Ama çok büyük metin dosyaları ile çalışırken bütün dosyayı açıp incelemek sorun olabilir. Çok büyük derken örneğin 250 GB. Bu kadar dosyayı açıp belleğe almak bile ciddi bir iş.

Onun yerine Get-Content ve Select-String gibi cmdlet'ler kullanılabilir mi?

Öncelikle alias'ları da kullanarak dosyanın başından 20 satırını bir inceleyelim.

> cat -First 20 .\network.log

First, aslında TotalCount parametresinin alias'ı. Başka bir alias da Head.

Bir de sondan 20 satırı görelim

> cat -Last 20 .\network.log

Last da aslında Tail parametresinin bir alias'ı. TotalCount ve Tail yerine First ve Last kullanmak daha kolay hatırlanabilir gibi geliyor bana. Ya da linux terimleri olarak Head ve Tail.

Başlangıçtan örneğin 5 satır atlayarak geri kalanı oku gibi bir komutu cat ve select-object'in birleşimi ile sağlayabiliriz: 

> cat .\network.log | selet-object -skip 5

Buraya kadar gördüklerimizden dosyanın genel bütün satırlarının System.Net.Sockets ile başladığını gördük diyelim. Peki bununla başlamayanlar var mı? (bkz [1],[2])

> sls "^(?!System.Net.Sockets)" .\network.log

Ve diyelim ki dosyanın içinde bir IIS logu benzeri bir yapı bulduk. Virgülle ayrılmış değerler. İlk alan tarih, daha sonra saat, kaynak ve hedef IP adresleri, kaynak ve hedef portları gibi:

2021-08-10,09:38:22,192.168.7.3,8.8.8.8,1356,443

Bu satırlardan da sadece kaynak IP adresi ve kaynak hedef portu ile ilgileniyoruz. Bu alanları alıp ekrana basmak için kullanabileceğimiz ConvertFrom-String cmdlet'i var. -Delimiter parametresi ile verilen karakterlerle ayrılmış alanları ayrıştırıp P1'den P(N)'e kadar alan isimlerini otomatik yaratıp kullanımımıza sunuyor. Bizim örneğimizde kaynak IP ile hedef portu 4. ve 6. alanalar olduğu için bunları aşağıdaki gibi süzebiliriz:

> sls "^(?!System.Net.Sockets)" .\network.log | ConvertFrom-String | select P4,P6

P4        P6
-------   ---
8.8.8.8   443
1.1.1.1   443

gibi bir sonuç elde edilir.

 Dosyanın içinde 192.168 ile başlayan IP adresleri var mı diye bir bakalım:

> sls "192\.168\.\d{1,3}\.\d{1,3}" .\network.log

Ya da tarih (örneğin 2021-08-10 formatında) yazıyor mu hiç:

> sls "\d{4}-\d{2}-\d{2}" .\network.log

Belki de tarih formatımız 2021.08.10 veya 20210810 da olabilir:

> sls "\d{4}.?\d{2}.?\d{2}" .\network.log

Tüm bunları yaparken bütün dosyayı değil de ilk 1000 veya son 1000 satır üzerinden işlem yapmak da isteyebiliriz. Bunun için "|" operatörü ile cmdlet'leri bağlayabiliriz:

> cat -First 1000 .\network.log | sls "\d{4}.?\d{2}.?\d{2}"

---

[1] https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_regular_expressions?view=powershell-7.1

[2] https://www.tutorialspoint.com/powershell/powershell_regex.htm

6.08.2021

Çok sayıdaki olay kaydını tarihlerine göre gruplamak

Bir bilgisayarda sistem olay günlüğünde çok sayıda diskle ilgili 51 kimlikli kaydın olduğunu gördüm. Genel bir bakışla olayların birkaç günde yoğunlaştığını farkettim.

Get-WinEvent -computer BILGISAYAR -FilterHashTable @{LogName="System";ProviderName="disk";Id=51} | Measure-Object

ile 30.000+ olay kaydı olduğu saydım, ama bunlar her güne düzgün yayılmamıştı. Tarihe göre gruplayarak bir günlük sayıya ulaşmak istedim ama 

Get-WinEvent -computer BILGISAYAR -FilterHashTable @{LogName="System";ProviderName="disk";Id=51} | Group-Object -Property TimeCreated

gibi bir sorgu işe yaramaz, çünkü TimeCreated sadece tarih değil saat, dakika ve saniye bilgilerini de içerir. Dolayısıyla en iyi ihtimalle güne göre değil, saniyeye göre sayı verebilir ki bu da isteğimizin çok daha üstünde bir çözünürlük.

Bunun yerine yeni bir alan oluşturmalı ve o alanı TimeCreated alanının sadece tarih bilgileri ile donatmalıyız. Kısa bir arama ile daha önce hiç kullanmadığım bir yöntem kullanan şu Stackowerflow gönderisine ulaştım:

Get-EventLog -Logname system -Source "Microsoft-Windows-GroupPolicy" -EntryType "Information" |
    Add-Member Day -MemberType ScriptProperty -Value { $this.TimeGenerated.ToString('dd.MM.yyyy') } -PassThru |
    Group-Object 'Day', 'Source'

Aslında bu miktarda olayı kendi bilgisayarıma aktarıp da saymak yerine yerinde yapmak daha mantıklı olurdu. Bu sebeple Invoke-Command ile yukarıdaki bilgiyi de kullanarak

Invoke-Command -ComputerName BILGISAYAR -Command { Get-WinEvent -FilterHashTable @{LogName="System";ProviderName="disk";Id=51} | Add-Member Day -MemberType ScriptProperty -Value { $this.TimeCreated.ToString("dd.MM.yyyy"} -PassThru | Group-Object "Day", "Source" -NoElement

aşağıdaki sonuca ulaştım:

Count Name               PSComputerName
----- ----               --------------
 2542 19.04.2021         BILGISAYAR
 9044 26.01.2021         BILGISAYAR
19841 25.01.2021        
BILGISAYAR

Babadan kalma yöntemlerle yapmak isteseydik

Invoke-Command -computer BILGISAYAR -command { Get-WinEvent -FilterHashtable @{LogName="System";ProviderName="disk";Id=51} | Select Id,@{N="Day";E={$_.TimeCreated.ToString("dd.MM.yyyy")}} | Group-Object 'Day' -NoElement}

gibi bir sorgu oluşturabilirdik.

9.07.2021

IPv6'yı devre dışı bırakmak

Sadece görsel olarak IP adreslerinin arasında uzun uzun sayılar görmeyi istememekten değil, ayrıca yeni bir adres aralığı ve oluşturulmamış kurallar sebebiyle saldırılara açık kalmamak için de eğer kullanılmıyorsa IPv6'yı devre dışı bırakmak isteyebiliriz.

Linux'ta IPv6'yı devrede mi diye bakmak için 

$ ip a

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc fq_codel state UP group default qlen 1000
    link/ether 02:28:a8:b6:01:1c brd ff:ff:ff:ff:ff:ff
    inet 172.31.15.59/20 brd 172.31.15.255 scope global dynamic eth0
       valid_lft 1840sec preferred_lft 1840sec
    inet6 fe80::28:a8ff:feb6:11c/64 scope link
       valid_lft forever preferred_lft forever

Kullanabiliriz. Kırmızı ile işaretlediğim satır IPv6 adresimizin olduğunu ve IPv6'nın devrede olduğunu gösterir. Ayrıca 

$ cat /proc/sys/net/ipv6/conf/all/disable_ipv6

0

İle yukarıdaki gibi 0 sonucu alıyorsak IPv6 devre dışı değildir. Bunu

$ sudo echo 1 > /proc/sys/net/ipv6/conf/all/disable_ipv6

ile 1 yapabilir ve IPv6'yı devre dışı bırakabiliriz. 

Ya da

$ sudo sysctl -w net.ipv6.conf.all.disable_ipv6 = 1

ile aynı etkiye sahip olabiliriz. Bu satırlar tüm ağ arayüzlerindeki (interface) IPv6'yı devre dışı bırakır. Bu komutların etkisini belli bir arayüz ile sınırlandırmak istiyorsak all yerine arayüz adını yazmamız gerekir. Örneğin ens33 için

$ sudo sysctl -w net.ipv6.conf.ens33.disable_ipv6 = 1

ya da

$ cat /proc/sys/net/ipv6/conf/ens33/disable_ipv6

yapabiliriz. Bu işlemlerden sonra 

$ sudo sysctl -p 

uygulamak gerekir. Bu komutlar çalışma anında etkili olurlar, ancak sistem tekrar başlatıldığında etkisini kaybeder. Sistem tekrar başlatıldığında bu ayarların geçerli olması için ise /etc/sysctl.conf dosyasına şu satırı eklememiz gerek:

net.ipv6.conf.ens33.disable_ipv6 = 1



5.07.2021

NTFS ve FAT32 dosya sistemlerinde son değişiklik zamanlarındaki farklılıklar

FAT32 dosya sistemine sahip USB belleğimden NTFS'e kopyaladığım dosyalarımda daha sonra değişiklik karşılaştırması yapmak istediğimde hep son değişiklik zamanlarında 1-2 saniyelik farklılıklar görüyordum, dosyaların üzerinde hiçbir değişiklik olmasa bile. Bunun nedeninin dosya sistemleri arasındaki farklılıktan kaynaklanabileceğini düşünüyordum, ayrıntısını hiç incelememiştim.

Yukarıdaki resimde sol yarıda NTFS biriminde, sağ yarıda ise FAT32 biriminde depolanan ve üzerlerinde hiç değişiklik yapılmamış dosyalara ait son değiştirme zamanları arasında 1-2 saniyelik farklılıklar görülüyor.

Bunun sebebi, [1]'de açıklandığı gibi, FAT32 dosya sistemlerinde dosya yazma (en son değiştirme) alanının çözünürlüğünün 2 saniye olmasından kaynaklanıyor.

Konu ayrıca [2]'de de tartışılmış ve aynı bilgi verilmiş.

--- 

[1] https://docs.microsoft.com/en-us/windows/win32/sysinfo/file-times

[2] https://serverfault.com/questions/11304/why-does-file-copy-from-ntfs-to-fat32-change-a-files-modified-date

29.06.2021

7zip ve Linux

Windows bir bilgisayarda, Fat32 ile biçimlendirilmiş USB belleğe sığmayacak 4 GB'tan büyük içeriği 7zip ile daha küçük parçalara bölmek için tar formatını kullandım. dosya.tar.001, dosya.tar.002,... gibi dosyalar oluştu. Daha sonra bunları linux bir makinede açmayı denedim. tar.001 uzantılı ilk dosyayı Ark ile açarak içeriği çıkar dedim. Beklediğim gibi 4 GB'tan büyük bir dosya oluşturdu. Ancak dosya boyutu beklediğim boyutta olmasına rağmen sadece ilk dosya olan dosya.tar.001'in içeriğine sahipti. Diğer içerikleri otomatik olarak almamıştı.

Bunun üzerine

cat dosya.tar.00* > dosya.tar

ile tüm dosyaları tek tar dosyasında birleştirdim. Sonra da ark ile tekrar çıkarttım. Bu sefer sorun yoktu.

2.06.2021

Windows Server + OpenSSH

Windows Server 2019 varsayılan olarak OpenSSH Client kurulu geliyor. Yönetici ayrıcalıklarıyla açılmış bir Powershell konsolunda şu komut ile kontrol edebiliriz:

PS> Get-WindowsCapability -Online -Name 'OpenSSH*'

Buna ilave olarak OpenSSH Server kurulumunu da bir özellik gibi kurabiliriz:

PS> Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0

Kurulduktan sonra sshd hizmetini hemen başlatmak için

PS> Start-Service sshd

kullanabiliriz. Ama Windows Firewall etkinse önce bir kuralımızın olup olmadığını kontrol etmemiz iyi olabilir:

PS> Get-NetFirewallRule -Name *ssh*

Eğer bir kural yoksa yenisini yaratmak için

PS> New-NetFirewallRule -Name SSHSpec -DisplayName SSHSpec -Profile any
     -Protocol TCP
     -LocalPort 1022
     -Direction Inbound
     -Action Allow

Uzaktan bağlandığımızda varsayılan olarak cmd shell gelir. Bunu powershell ile değiştirmek için (ayrıntılar için [2])

PS>   New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH"
      -Name DefaultShell
      -Value "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"
      -PropertyType String
      -Force

İleri seviye ayarlar sshd_config dosyasında, bu dosya da $env:ProgramData konumunda. Bu dosyadaki birkaç parametreyi hatırlayalım:

DenyUsers: Bağlanması engellenmek istenen kullanıcılar

AllowUsers: SSH sunucunun hedef kitlesi

PermitRootLogin: Windows Server için anlamsız

PermitEmptyPasswords: Boş parolalar geçerli mi değil mi

PasswordAuthentication: Parola ile bağlanmak mümkün mü, yoksa sadece RSA anahtarıyla mı

PubKeyAuthentication: RSA anahtarıyla oturum açmayı etkinleştirmek

Eğer bir anahtar çifti yaratmak istiyorsak ssh-keygen ile yapabiliriz. Bu adımdan sonra id_rsa ve id_rsa.pub adında iki dosya oluşacak. pub uzantılı genel (public) anahtarımız. Bunu sunucuda bırakacağız. id_rsa ise bizim özel anahtarımız. Paylaşmamamız ve yanımızda taşımamız gerekiyor.

Öncelikle sunucu tarafında authorized_keys dosyasını oluşturalım ([1]'de sshd hizmetini başlatınca %programdata%\ssh altında bu anahtarları oluşturacağı söylenmiş).

PS> copy id_rsa.pub authorized_keys

Bu dosyanın yetkileri önemli. Sadece SYSTEM ve bizim kullanıcımızın tam yetkileri olmalı, başka yetki olmamalı. Bu amaçla icacls aracını kullanarak önce yetkilerin devralınmasını engelleyelim, daha sonra da administrators grubunu yetkiler listesinden çıkaralım.

PS> icacls authorized_keys /inheritance:r

PS> icacls authorized_keys /remove:g BUILTIN\Administrators

Yine şu sayfada sshd_config dosyasındaki aşağıdaki satırların başına # işareti konularak etkisizleştirilmesi önerilmiş.

# Match Group administrators
#   AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys

sshd_config dosyasında yapılan değişikliklerden sonra sshd hizmetinin yeniden başlatılması gerek.

PS> Restart-Service sshd

Düzeltme: ssh-keygen bizim bilgisayarımızda çalıştırılmalı, sunucuda değil. Ayrıca sshd_config dosyasında başına # koyarak etkisizleştirilmek yerine yeni düzene uyup, autorized_keys yerine %programdata%\ssh\administrators_authorized_keys dosyasını kullanmaya başlayabiliriz.

Uzaktan bağlanabilmemiz için bir adım kaldı, o da id_rsa anahtarımızı yerel bilgisayara aktarmak. scp ile uzaktan kopyalayabiliriz.

PS> scp administrator@192.168.x.x:/Users/administrator/.ssh/id_rsa C:\Users\username

Ancak bu dosyanın da yetkileri ile ilgili kesin kurallar var. Benzer şekilde sadece SYSTEM ve kendi kullanıcımız yetkili olmalı.

PS> icacls .\id_rsa /inheritance:r

PS> icacls .\id_rsa /grant username:F

PS> icacls .\id_rsa /grant SYSTEM:F

Bundan sonra artık ssh bağlantısı mümkün.

PS> ssh administrator@192.168.x.x -i .ssh/id_rsa

Hala aşağıdaki gibi hata alıyorsanız

Permission denied (publickey,keyboard-interactive)

sondan bir önceki adımda # işaretiyle etkisizleştirmeniz gereken satırı atlamamış olabilirsiniz. Ya da alternatif olarak sshd_config dosyasında geçen

#StrictModes yes

satırını

StrictModes no

olarak değiştirmek önerilmiş.

---

[1] https://github.com/PowerShell/Win32-OpenSSH/wiki/Install-Win32-OpenSSH

[2] https://github.com/PowerShell/Win32-OpenSSH/wiki/DefaultShell

27.04.2021

İnternetten indirilen bir dosyanın engelini kaldırma

Alternate data streams (ADS) konusunu daha önce yazmıştım. İnternetten indirilen bir dosya üzerinde bir "Zone.Identifier" ADS akışı olur. Windows'da çoğu tarayıcı (Firefox, Chrome, Edge, Internet Explorer) internetten indirilen dosyalar için bu ADS akışını oluşturur.


Yukarıdaki diyaloğun altındaki "Engellemeyi Kaldır" kutusu işaretlenmeden zip dosyası açılırsa açılan her dosya için Zone.Identifier akışı oluşur. Bu Windows ayıklama arayüzü kullanıldığında geçerlidir. Diğer dosya sıkıştırma uygulamaları için durum farklı olabilir. Örneğin 7zip, çıkartılan (veya ayıklanan, her neyse) dosyalar için Zone.Identifier akışını korumaz. Ayrıca bu akışa sahip bir dosya çalıştırılırken de kaynağına güvenip güvenmediğimize dair emin misiniz sorusu görüntülenir.

Neyse, çevirideki anlam karmaşasından sıyrılıp konuya dönelim. Zip dosyasına sağ tıklayıp engellemeyi kaldırabiliriz. Ama amacımız bunu komut satırından, powershell ile yapmak.

Önce akışı görelim:

PS> Get-Item -Path Sysinternals* -Stream *

Bu şekilde bulunduğumuz klasörde sysinternals ile başlayan tüm dosyaların ADS verilerini görüntülemiş oluruz. Belli bir dosyanın belli bir akışına (ADS'sine) ulaşmak için daha net olarak

PS> Get-Item SysinternalsSuite.zip -Stream Zone.Identifier

PSPath        : Microsoft.PowerShell.Core\FileSystem::D:\Download\SysinternalsSuite.zip:Zone.Identifier
PSParentPath  : Microsoft.PowerShell.Core\FileSystem::D:\Download
PSChildName   : SysinternalsSuite.zip:Zone.Identifier
PSDrive       : D
PSProvider    : Microsoft.PowerShell.Core\FileSystem
PSIsContainer : False
FileName      : D:\Download\SysinternalsSuite.zip
Stream        : Zone.Identifier
Length        : 97

kullanabiliriz. Zone.Identifier akışını bulduk. Bir de içeriğini görüntüleyelim.

PS> Get-Content SysinternalsSuite.zip:Zone.Identifier

[ZoneTransfer]
ZoneId=3
ReferrerUrl=https://docs.microsoft.com/tr-tr/sysinternals/downloads/sysinternals-suite
HostUrl=https://download.sysinternals.com/files/SysinternalsSuite.zip

Bunu silmek için de

PS> Remove-Item SysinternalsSuite.zip:Zone.Identifier

kullanabiliriz. Hatta bu işi yapan bir cmdlet de var (2023-02-02):

PS> Unblock-File SysinternalsSuite.zip

ADS akışından kurtulduktan sonra powershell ile dosyaları çıkartmak (ayıklamak, genişletmek) için de şöyle bir yol olabilir:

PS> Expand-Archive .\SysinternalsSuite.zip -DestinationPath D:\Tools\ -Force

Burada -Force parametresi o klasörde var olan eski sürümlerin üzerine yazmayı etkinleştirmek için.

Not: Powershell Invoke-WebRequest cmdlet'ini kullanarak indirince ADS oluşturulmaz. Bu şekilde indirmek için mesela

PS> Invoke-WebRequest -Uri "https://download.sysinternals.com/files/SysinternalsSuite.zip" -OutFile "SysinternalsSuite.zip"

yazabiliriz.

14.04.2021

AWS üzerinde WireGuard VPN sunucu kurulumu

Amazon Web Services üzerinde düşük özelliklere sahip bir sunucu ücretsiz olarak kurulabiliyor. Bir hesap açarak aws.amazon.com adresine girerek bu işlemi yapabiliriz. Bunu yaptıktan sonra bugün için 1 GB RAM'e sahip, 1 CPU ve 8 GB depolamaya sahip bir linux sunucumuz olacak.

Yukarıdaki tabloda da görüldüğü gibi aylık 15 GB kotaya sahip olacağız. Ücretsiz kullanımda bizi sınırlayan tek ölçüt bu olacak.

Free Instance'ımızı oluşturduktan sonra bir linux sunucuyu uzaktan yönetmek için bize tahsis edilmiş bir de özel anahtar dosyamız olacak. *.pem uzantılı bu dosyayı işin sonunda download etmeyi unutmamak gerek. Ayrıca security bölümünden inbound rules'a bir SSH bağlantısı (22/TCP) izni oluşturmamız gerek.

Sunucumuza uzak bağlantıyı aşağıdaki gibi yapabiliriz.

> ssh ubuntu@sunucu_ip_adresi_veya_ismi -i ozel_anahtarimiz.pem

Özel anahtarımız aracılığıyla şifresiz ssh bağlantısı yapabiliriz. Oluşabilecek hatalar pem dosyasının yetkilerinin gereğinden fazla olmasından kaynaklanabilir. Bunun için yetkileri kısmak gerek. Sadece kendi kullanıcımıza yetki verelim.

Şu an için benim elimde Ubuntu 20.04.3 LTS bir sunucu var. İlk yapmak gereken paketleri bir güncellemek

> sudo apt update && sudo apt upgrade

Bir sonraki adım otomatik süreçte oluşturulan ubuntu kullanıcısından kurtulup kendi belirlediğimiz bir kullanıcı ve kendi yarattığımız bir özel anahtarla oturum açmak için. Çok gerekli mi? Değil. Ama ben yapmayı tercih ediyorum. Yeni bir grup ve kullanıcı oluşturalım.

> sudo groupadd metin

> sudo adduser metin

Yeni oluşturduğumuz kullanıcıya ayrıcalıklı yetkiler vermek için bazı grupların üyesi yapalım.

> sudo usermod -aG admin,adm,sudo,ssh metin

Bir de varsayılan grup olayımız var

> sudo usermod -g metin metin

Bu kullanıcı için bir RSA anahtar çifti oluşturmamız gerek. Daha sonra SSH bağlantımızı bu kullanıcı üzerinden yapacağız. Bunu yapmanın birkaç yolu var. Yollardan biri ssh-keygen'i ubuntu kullanıcısıyla çalıştırıp yaratılan dosyaları yeni kullanıcımızın profiline aktarmak. ssh-keygen varsayılan olarak 3072 bitlik bir anahtar çifti oluşturacak ve bunları varsayılan olarak .ssh klasörünün altında id_rsa ve id_rsa.pub dosyalarına yazacak. Ubuntu kullanıcısından yaptığımız bu işlem sonucunda oluşan dosyaları metin kullanıcısının authorized_keys dosyasına aktaracağım.

> sudo cp .ssh/id_rsa.pub /home/metin/.ssh/authorized_keys

Sonrasında ssh bağlantısını kesip özel anahtarımızı yerel bilgisayara aktaralım.

> scp -i ozel_anahtarimiz.pem ubuntu@sunucu_ip_adresi_veya_ismi:/home/ubuntu/.ssh/id_rsa D:\ssh\ozel_anahtarim

Özel anahtarımız pem dosyasının yerine geçecek. Bu dosyanın da yetkilerinin sadece kendi kullanıcımızla kısıtlı olması gerek. OpenSSH bu konuda katı. Bu işlemden sonra yeni kullanıcımız ve yeni anahtarımızla bağlantı yapabiliyor olmamız gerek. Deneyelim:

> ssh -i D:\ssh\ozel_anahtarim metin@sunucu_ip_adresi_veya_ismi

Artık ubuntu kullanıcısı ve ev klasörünün altındaki (/home/ubuntu) dosyalara ihtiyacımız kalmadı. Silebiliriz.

Sonrasında benim yapmayı tercih ettiğim ilk şey bir güvenlik duvarı ve fail2ban kurmak.

> sudo apt install ufw fail2ban

Sunucumuza uzaktan ssh ile bağlandığımız için güvenik duvarını kurduktan sonra etkinleştirmeden önce ilk yapmamız gereken SSH bağlantılarına izin veren bir kural oluşturmak.

> sudo ufw allow ssh

> sudo ufw enable

Artık sadece 22 TCP bağlantısına izin verilen bir sunucumuz var. Ben bununla yetinmiyorum. Böyle bir sunucuyu internete bağlı bırakmak aracınızı kilitleyip herkesin kendi anahtarını deneyerek açmaya çalışacağı bir pazar alanına bırakmak gibi. Sürekli birileri gelip bağlanmayı deneyecek. Bunu nispeten engelleyebilmek için fail2ban çok güzel bir çözüm. Fail2ban'in yapılandırma dosyası /etc/fail2ban/jail.conf adresinde. Ama bu dosyayı doğrudan düzenlemek yerine aynı klasöre jail.local kopyası oluşturup onun üzerinden düzenlemek önerilmiş. jail.local kopyası boş olabilir, ya da şu adreste gösterildiği gibi jail.conf dosyasının satırlarının başına # karakteri getirilerek etkisizleştirilmiş bir kopyası olabilir:

> awk '{ printf "# "; print; }' /etc/fail2ban/jail.conf | sudo tee /etc/fail2ban/jail.local

Sonuçta bu dosyayı açıp içinde bazı düzenlemeler yapmamız gerek. Bu dosyaya sadece ben, belirsiz birkaç yerden bağlanacağım. Şifreyi en fazla 2 kere yanlış girme ihtimalim üzerinden yapılandıracağım. Aynı IP adresinden 10 dakika içinde 2'den fazla yanlış şifre denemesi olursa bu IP aderesinin engellenmesini istiyorum. Engelleme de süresiz olacak. Ama güvenli bir IP adresi vereceğim; bu adresten gelen bağlantılar engellenmeyecek. Bunu yapabilmek için şu satırları ekliyorum.

[DEFAULT]
ignoreip = 222.222.222.222
bantime = -1
findtime = 600
maxretry = 3

SSH protokolü varsayılan olarak jail.conf dosyasında izleniyor. Bazı kaynaklarda ssh için aşağıdaki gibi satırların eklenmesi önerilmiş ama ben eklemedim. Buna rağmen SSH protokolü izlemede.

[ssh]
enabled     = true
port        = ssh
filter      = sshd
logpath     = /var/log/auth.log

Fail2ban'in durumunu sorgulamak için

> sudo fail2ban-client status

ya da özellikle sshd durumunu öğrenmek için

> sudo fail2ban-client status sshd

yazıp engellenmiş IP adresi var mı, öğrenebiliriz. Engellenen bir IP adresini karalisteden çıkarmak için ise

> sudo fail2ban-client set sshd unbanip xxx.xxx.xxx.xxx

ya da bir IP adresini engellemek için

> sudo fail2ban-client set sshd banip 1xx.1x.2x.2x

Artık VPN kurulumuna başlayabiliriz. VPN kurulumu işin en kolay kısmı. Wolfgang sağolsun, şu videosunda konuyu oldukça basite indirgemiş.

> wget https://git.io/wireguard -O wireguard-install.sh && sudo bash wireguard-install.sh

Cep telefonlarından bağlanabilmek için Wireguard'ı kurup QR kodu okutmak yeterli. Bilgisayardan yapıyorsanız bu işlem sonucunda oluşan /root klasörünün altındaki *.conf dosyasını bilgisayarınıza aktarmanız yeterli. Linux'ta /etc/wireguard/wg0.conf dosyasına, Windows'da ize grafik arayüzden Wireguard uygulamasının için bu verileri aktararak bağlantı oluşturabilirsiniz. Sınırsız kullanıcı oluşturmak mümkün. Her cihaz/kullanıcı için 

> sudo bash wireguard-install.sh

komutunu tekrar çalıştırmak gerek. Sınır olmadığı için bunu yapmaktan kaçınmamak, bir cihazın/kullanıcının verilerini başka bir cihaz/kullanıcı ile paylaşmamak önerildiği için bu kurala uyuyoruz. Linux'ta VPN'i devreye almak için

> sudo wg-quick up wg0

komutunu çalıştırmak gerekli. Wireguard varsayılan olarak UDP 51820 portunu kullanıyor ama değişitirilebilir. Bu port için de güvenlik duvarının üzerinde izin vermek gerek. Hem Ubuntu'nun güvenlik duvarı, hem de Amazon'un Security Rules içinde. Ubuntu için bu

> sudo ufw allow 51820/UDP

ile mümkün.

Wireguard UDP kullandığı için ve UDP de TCP gibi oturumlu olmadığı için çalışıyor mu diye kontrol etmek biraz daha zor. Kontrol etmek için bir yöntem VPN'i devreye aldıktan sonra sunucuya dahili IP adresi üzerinden ping atmak olabilir. Wireguard kendi içinde başka bir IP aralığı kullanıyor. Örneğin 

> ping 10.7.0.1

ile istemcimiz sunucuya ping atabiliyor mu, ya da sunucu üzerinden

> ping 10.7.0.2

gibi bir şekilde sunucu istemciye ping atabiliyor mu, bakabiliriz. Artan cihaz sayısı ile son oktet değişecektir. Ya da sunucu tarafında 51820 portu üzerinden gerçekleşen trafiğe bakabiliriz.

> sudo tcpdump -i eth0 port 51820

Kriptolanmış paketlerin içini görmeye kalkışmadan bu trafiğin varlığının sistemin çalıştığını gösterdiğine kanaat getirmek genel olarak tercih edilen bir yöntem.

2023-08-13 Ek: Wireguard için kernel debug logu aktifleştirmek için

# echo "module wireguard +p" | sudo tee /sys/kernel/debug/dynamic_debug/control

yapılabilir. Bundan sonra da kernel loglarına

# dmesg -wT

ile bakılabilir. İhtiyacımız kalmadığında wireguard kernel loglarını devre dışı bırakmak için

# echo "module wireguard -p" | sudo tee /sys/kernel/debug/dynamic_debug/control

 Bu güzel bilgi de şu adresten alındı.

16.03.2021

Get-WinEvent ve Logon Type

Uzak masaüstü bağlantılarını eventlog'dan süzmek için garip filtereler kullandım durdum. Ta ki şunu bulana kadar:

PS> Get-WinEvent -FilterHashTable @{LogName="Security";Id=4624} | Where-Object {$_.properties[8].value -eq 10}

Buradaki properties olarak geçen 27 öğelik EventProperty nesnesi, her olay için ayrıntılar sekmesinde çıkan parametrize edilmiş olay özellikleri. Bunu aslında log parser zamanlarından biliyordum, ama nedense bu alanların powershell'den de erişebileceğini hiç düşünmemiştim.

Her durumda Logon Type=10 kaydı düşülmüyor. Sunucunun ekranı kilitlenmişse sadece Logon Type=7 (muhtemelen bir Logon Type=3 sonrasında) kaydı olabiliyor.

2023-04-11 ek: Uygulama ve Hizmet Günlükleri (Application and Service Logs) altında Microsoft>Windows>TerminalServices-RemoteConnectionManager/Operational günlüğü daha net bilgiler sunuyor. Bunun için logon audit policy'si etkinleştirilmiş olmalı.

PS> Get-WinEvent -FilterHashTable @{LogName="Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational";Id=1149} | Select-Object TimeCreated, @{N="User";E={$_.properties[0].Value}},@{N="Domain";E={$_.properties[1].Value}},@{N="IP";E={$_.properties[2].Value}}

2023-11-20 ek: Ayrıca "Microsoft-Windows-TerminalServices-LocalSessionManager/Operational" altında hem uzakmasaüstü oturum açma hem de kapama olayları sırasıyla 21 ve 23 olayları ile kaydedilir.

PS> Get-WinEvent -FilterHashtable @{Logname="Microsoft-Windows-TerminalServices-LocalSessionManager/Operational";Id=21,23} -Max 4 | select TimeCreated, Id, @{N="UserName";E={$_.Properties[0].Value}},@{N="SessionID";E={$_.Properties[1].Value}},@{N="IPAddress";E={$_.Properties[2].Value}} | ft *

15.03.2021

Sysmon

Sysinternals araçlarının fanatiğiyim. Hepsi güzel, ama özellikle Process Monitor, Process Explorer, Autoruns gibi araçların hayranıyım. Bir süredir Sysmon'un varlığından ve işlevlerinden haberdardım ama çok yakınlaşma fırsatım olmamıştı. Aslında yakınlaşınca anladım ki Sysmon hakkında hiçbir şey bilmiyormuşum. Channel9'daki Defrag Tools programındaki konuşmalar falan konuya sadece giriş seviyesinde değinmiş.

İşin özünde Sysmon da Process Monitor benzeri bir araç. Ama bir grafik arayüzü yok. Grafik arayüz olmayınca da onun yerine geçecek filtreleme işlerini başka bir şekilde yapmamız gerekiyor. Bu şekil de burada XML oluyor.

Şu anda web sayfalarında 13.01 sürümü var. Yani ilk çıktığından bu yana çok yol almış. Yolun çoğu XML yapısı ile ilgili alınmış. Sanıyorum ilk çıktığında bugüne kıyasla çok ilkel bir XML yapısı varmış. Zamanla çok değişmiş. Şu anda XML schemaversion 4.5. Bu neleri değiştirmiş, varsayılan olarak VEYA olan filtre 4.22 ile VE olmuş, yeni XML elemanları eklenmiş vs.

Çalıştırıp yakalamaya başlaması için

sysmon -i

komutunu vermek gerekiyor. Bu şekilde varsayılan ayarlarla çalışmaya başlıyor. Yakaladığı olayları Event Viewer'da Application and Service Logs altında Microsoft-Windows-Sysmon/Operational'a yazıyor. -i anahtarı ile çalıştırıldığı anda kendini %SystemRoot% klasörünün altına kopyalayıp bir servis olarak çalışmaya başlıyor. Bu şekilde bilgisayarı tekrar başlattığımızda da çalışmaya devam ediyor. Faaliyetlerini durdurmak için kaldırmak gerekiyor ki bu da

sysmon -u

ile oluyor. Bir kez kurulduktan sonra varsayılan ayarlarla olayları yakalamaya başlıyor demiştik. Yapılandırmayı değiştirmek için bir XML dosyası oluşturarak -c anahtarı ile bu dosyanın yerini göstermek gerekiyor.

sysmon -c ornek.xml

Buraya kadarki konular zaten her yerde yazılan kısım. Bir örnek üzerinden gidelim. Bir sunucumuz var; bu sunucunun üzerinde IIS çalışıyor. C:\inetpub\wwroot\aspnet_client klasörü altında oluşturulan .aspx dosyalarının hangi süreç tarafından oluşturulduğunu bilmek ve bundan haberdar olmak istiyoruz. Aynı zamanda %ProgramData% ve %SystemRoot%\Temp klasörlerinin altında oluşturulabilecek *.7z, *.zip ve *.rar dosyalarına ait olayları takip etmek istioruz. Aslında bunun mantığını kurala dökmek kolay. Bir pseudo code ile:

(Path = C:\inetpub\wwwroot\aspnet_client VE Extension=*.aspx) VEYA ((PATH=%PROGRAMDATA% VEYA PATH=%SystemRoot%\temp) VE (Extension=.7z VEYA Extension=.zip VEYA Extension=.rar))

demek kolay. Ama bunu XML'e dökmek nispeten zor. Tüm süreci nasıl anlatırım bilemiyorum, ama nihayetinde şunları söyleyebilirim. Birden fazla mantık grupları için RuleGroup'lar oluşturmak gerek. Aynı olaya ait ikisi de include veya ikisi de exlude olan RuleGroup'lar geçerli değil. Ayrıca schemaversion 4.22'den sonra EventFiltering elemanı içindeki öğelerin varsayılan mantık operatörü VE. Bunu değiştirmek için groupRelation attribute'larını kullanıyoruz. İkisi de include olan RuleGroup'lardan kaçınmak için birini exclude yapmam gerekti. Bunu da path'ten yana kullandım. Hariç tutulabilecek path'leri toplayıp bir exlude RuleGroup'u oluşturdum. Sonuçta elimde şöyle bir XML dosyası oldu:

<Sysmon schemaversion="4.5">
  <HashAlgorithms>sha256</HashAlgorithms>
  <CheckRevocation/> <!-- Check loaded drivers, log if their code-signing certificate has been revoked, in case malware stole one to sign a kernel driver -->

  <EventFiltering>

    <RuleGroup name="Extension" groupRelation="or">
      <FileCreate onmatch="include">
        <TargetFilename condition="end with">.7z</TargetFilename>
        <TargetFilename condition="end with">.zip</TargetFilename>
        <TargetFilename condition="end with">.rar</TargetFilename>
        <TargetFilename condition="end with">.aspx</TargetFilename>
        <TargetFilename condition="end with">.asp</TargetFilename>
      </FileCreate>
    </RuleGroup>

    <RuleGroup name="Path" groupRelation="or">
      <FileCreate onmatch="exclude">
        <TargetFilename condition="begin with">C:\PerfLogs</TargetFilename>
        <TargetFilename condition="begin with">C:\Program Files (x86)</TargetFilename>
        <TargetFilename condition="begin with">C:\Program Files</TargetFilename>
        <TargetFilename condition="begin with">C:\Scripts</TargetFilename>
        <TargetFilename condition="begin with">C:\inetpub\temp</TargetFilename>
        <TargetFilename condition="begin with">C:\inetpub\logs</TargetFilename>
        <TargetFilename condition="begin with">C:\inetpub\history</TargetFilename>
        <TargetFilename condition="begin with">C:\inetpub\custerr</TargetFilename>
      </FileCreate>
    </RuleGroup>

    <RuleGroup name="ProcStart" groupRelation="or">
      <ProcessCreate onmatch="include">
        <Image condition="end with">cmd.exe</Image>
        <Image condition="end with">powershell.exe</Image>
        <Image condition="end with">iexplore.exe</Image>
      </ProcessCreate>
    </RuleGroup>

    <RuleGroup name="ProcStartExclude" groupRelation="or">
      <ProcessCreate onmatch="exclude">
        <CommandLine condition="contains">...</CommandLine>
      </ProcessCreate>
    </RuleGroup>

    <RuleGroup name="ProcEnd" groupRelation="or">
      <ProcessTerminate onmatch="include"/>
    </RuleGroup>

    <RuleGroup name="NetConnct" groupRelation="or">
      <NetworkConnect onmatch="include">
        <DestinationHostname condition="end with">github.com</DestinationHostname>
        <DestinationHostname condition="end with">githubusercontent.com</DestinationHostname>
      </NetworkConnect>
    </RuleGroup>

  </EventFiltering>

</Sysmon>

Arada başka öğeler de var. Örneğin cmd.exe, powershell.exe ve iexplore.exe süreçlerinin başlaması da takip ediliyor. Süreç başlaması işlemlerinin takibinde bazı hariç tutmalar için "..." ile olası bir durumu da ekledim. Ayrıca github.com ve githubusercontent.com'a girişleri de takip ettim. Bunun dışındaki süreç başlamalarını ve sonlanmalarını hariç tuttum.Şablon olarak şu kaynaktaki xml dosyası üzerinden gittim.

Yardım ve dökümanlarda gözükmeyen, webde de çok rastlanmayan bir komut satırı parametresi de -t ki bu hata ayıklamayı kolaylaştırıyor. Örneğin kalıcı olarak devreye alma öncesinde bir xml dosyasını denemek istiyorsak bunu aşağıdaki gibi deneme modunda başlatabilir, sonra da konsoldan gelen mesajlarla gidişatı izleyebiliriz:

sysmon -t -i testkonfig.xml

2023-12-27 Ek: Şu sayfada bir de sysmon hizmetinin yetkilerinin kısıtlanarak kullanıcılar tarafından durdurulamaması için yetkilerin yapılması anlatılmış:

Gizle:
sc sdset Sysmon D:(D;;DCLCWPDTSD;;;IU)(D;;DCLCWPDTSD;;;SU)(D;;DCLCWPDTSD;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)S:(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)
 

Eski haline döndür:
sc sdset Sysmon D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)S:(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)

Güzel.

---

[1] https://posts.specterops.io/putting-sysmon-v9-0-and-or-grouping-logic-to-the-test-c3ec27263df8

[2] https://www.hexacorn.com/blog/2018/06/29/sysmon-doing-lines-part-3/ 

[3] https://docs.microsoft.com/en-us/sysinternals/downloads/sysmon 

[4] https://techcommunity.microsoft.com/t5/sysinternals-blog/sysmon-the-rules-about-rules/ba-p/733649