Windows işletim sistemi, çalışması sırasında sistem, donanım, uygulama ve güvenlik bileşenlerine ait olayları sürekli olarak kaydeder. Bu kayıtlar, "Event Viewer" (Olay Görüntüleyicisi) aracılığıyla veya komut satırı/PowerShell üzerinden incelenebilir durumdadır.
Neden Windows günlükleri veya neden özel görünümler olduğunu hiç merak ettiniz mi? Aslında ben de etmemiştim. Sadece bununla alakalı bir kodlama yaparken detaylıca araştırmam gerektiği için fark etmiş oldum. Yönetim olayları neden Windows günlüklerine dahil değil? Neden diğerleri gibi basitçe export edilmiyor ki?
Aslında cevabı basit, Yönetim olayları bir sistem günlüğü olmadığı için.
Windows Olay Günlüklerini, sistemdeki her önemli işlemi, hata veya uyarıyı kaydetmek üzere tasarlanmış merkezi bir log sistemi olarak düşünün. Bu sistemde; Sistem (System), Uygulama (Application), Kur (Setup), Güvenlik (Security) gibi temel log kategorileri bulunur. Her olay, bir event ID, zaman damgası, olay seviyesi, kaynak (provider), açıklama ve gerekirse binary veri içerir.
Yönetim Olayları (Administrative Events), bir sistem günlüğü değil. Diğerleri bir günlük ama yönetim olayları değil çünkü bu loglar arasından seviye olarak kritik (Critical), hata (Error) ve uyarı (Warning) olanların öncelikli olarak filtrelenip sistem yöneticisine sunulduğu özel bir kategori olduğu için. Aslında yönetim olayları ayrı bir kategori değil, diğer kategori log'larının filtrelenmiş hali.
Bu filtreleme, genellikle "Custom Views" altında bulunan "Administrative Events" görünümü ile yapılıyor. Bu görüntüleme sayesinde, sistemdeki en önemli olaylara hızlıca erişim sağlanabilir. Bunu sistem üzerinden Yönetim olayları > Özellikler > Filtre düzenle... başlığı altından inceleyebilirsiniz.
Peki bu filtreleme nedir diye düşünürken, Olay düzeyi kısmı dikkatimi çekti. Sonradan bu konuda şunu fark ettim ki Windows bu olayları level sistemine göre kaydedip filtreliyor.
Aslında her Windows olayı, ciddilik derecesini belirtmek için bir "Level" (Seviye) bilgisiyle etiketleniyor. Bu seviye, olayın sistem stabilitesi açısından önemini belirtmekte kullanılıyor...
Bu aslında neden yönetim olaylarının her zaman Level 1, 2 ve 3 (Critical, Error, Warning) olaylarından oluştuğunu kanıtlıyor. Bu olaylar sistem kararlılığı ve sorun tespiti için temel analiz malzemesidir.
Her bir olay, belirli bir bileşen ya da servis tarafından üretilir. Bu servis "Provider" (Kaynak/Sağlayıcı) olarak adlandırılır. Olay kaynağı, olayı hangi sistem bölümü veya uygulamasının oluşturduğunu belirler.
Filtreleme yaparken bu provider adı kullanılarak olaylar daraltılabilir.
Buraya kadar olan kısım bu olayların kısaca ne olduğu ve filtrelendirilmesinin nasıl işlendiği ile ilgiliydi. Bundan sonrası ile bunun nasıl yapılacağı ile ilgili.
=CMD=
İlk ve en temel komutumuz wevtutil. Kendisi olay günlüğü (event log) işlemleri yapmak için kullanılan bir komut satırı aracı. Bu araç sayesinde sistem, güvenlik, uygulama gibi günlükleri yönetebilir, içeriğini görüntüleyebilir, yedekleyebilir veya silebiliyorsun.
Örnek olarak
Wevutil komut aracı ile sistem olaylarını masaüstüne kaydedebilirsiniz.
Örneği ile uygulama olaylarını masaüstüne alabilirsiniz.
Bu noktada Wevtutil ile Administrative Events gibi özel görünümleri expor edemiyoruz direkt olarak çünkü C:\ProgramData\Microsoft\Event Viewer\Views altında sadece .xml olarak tutuluyorlar ve evtx şeklinde export etmek olanaksız oluyor. Bunu 2 şekilde filtreleyebiliyoruz.
Level Filtreleme:
Burada kod açıkça, System olaylarındaki 1,2,3 (Yönetim olayları sorgusu) seviyeleri olayları tespit edip Masaüstündeki Olaylar isimli text belgesine yazıyor.
Dipnot: Bunu kullanmanızı belli bir olay sayısı kullanmıyorsanız (Son 10 olayı al tarzı.) önermem. Muhtemelen o text dosyasını açtığınız an donacaktır. Bunun da UTF8 formatından kaynaklandığını düşünüyorum... XML formatını kullanmaya çalışın daha karışık olsa da.
Consider Filtreleme:
Bu kod da açıkça, System olaylarındaki 41 veya 6008 ID'li olayları tespit edip Masaüstündeki FiltrelenmisOlay isimli text belgesine yazıyor.
Dipnot: Belli bir ID aramasında txt kullanmak hem daha temiz bir sonuç verir hem de donmayan bir not belgesi sunar.
1,2,3 seviyeli son 50 olayı "...." isimli text belgesine yazar. Bu da UTF konusunda sıkıntı çıkarmadan temiz bir sonuç verir.
Aynı şekilde bunları daha düzenli ve esnek olarak PowerShell ile de oluşturabilme şansımız var. Bu noktada karşımıza Get-WinEvent komut aracı çıkıyor. Öncelikle bunun da temelinde wevtutil ile aynı şeyi yaptığını söyleyebilirim. Farkları birden farklı birkaç olayı export etmeye çalışırken ortaya çıkıyor.
Örneğin PowerShell ile basit bir oluşturma kodu yazabiliriz:
Böylelikle son 100 olayı çekebiliriz. CMD komutundan farkı ise, PowerShell ile tarihe göre sıralama yapma şansımız da var:
Böylelikle son 1 günde oluşan 1-2 seviyeli olayları çekebiliriz. Buna bir Out-File komutu da eklemeniz gerekecektir.
Bence asıl farkları birden fazla bir sürü olayı XML olarak çekerken ortaya çıkıyor. Örneğin:
Burada yaptığımız şey $eventIDs dizisinde tanımlı olan her bir Event ID için System günlüklerinden o ID'ye ait olayları çekiyor. Daha sonra her olayı XML formatına dönüştürmek için ToXml() metodunu kullanıyor ve dönüştürülen XML verilerini kullanıcı masaüstünde, ilgili Event ID'yi içeren ayrı bir dosyaya (örneğin event_41.xml, event_161.xml, event_129.xml) kaydediyor. Böylece her Event ID için ayrı bir XML dosyası oluşturulmuş oluyor.
Aynı şeyi CMD üzerinden yapmaya çalışırsak ise:
Her bir ID için tek tek consider filtrelemesi yapmamız gerekecekti.
Bu noktada bu işin en iyi kısmı da özellikle XML kaydında C tabanındaki kütüphane ve kodlarını bulduktan sonra oldu.
C++ ile Windows Event Log’dan belli Event ID’lere göre filtreleyip XML formatında dışa aktarmak da Windows API’leri kullanarak yapılabiliyor. Özellikle Windows Event Log API (EvtOpenLog, EvtQuery, EvtNext, EvtRender) bu iş için varlar.
Bundan önce DumpFinder programımda kullandığım temel XML çekme mantığını anlatacağım.
Kodda ilk önce çok basit bir şekilde System32\\winevt\\Logs\\ dizinini eventLogPath adında bir yola tanımladım. Ardından Application.evtx, System.evtx ve Security.evtx olay günlüklerinin adlarını içeren bir dizi (evtxLogs) tanımladım. Sonra da bir günlük dosyası için döngüye girer ve dosya mevcutsa (fs::exists(src)), fs::copy_file fonksiyonu ile bu dosya, hedef klasöre (logFolder) kopyalanıyor. Kopyalanan klasör DumpFinder içinde DmpGunluk adlı bir klasör. Kopyalama başarılı olunca başarılı olduğuna dair bir uyarı görürsünüz. Genelde filtrelemesi başarılı oluyor lakin evtx formatı için izin gerekebiliyor özellikle.
İkinci döngüde de "Application", "System" ve "Security" günlük isimlerini wevtutil komutu ile sorgulama qe sorgusu başlatmak için kullandım. Her bir günlük için, XML formatında ve sadece belirli öncelik düzeylerindeki olayları içeren bir sorgu (/q) oluşturuyorum ve komut satırında çalıştırılacak komutun tam metni, cmd değişkeninde oluşturuyorum. Bu tabii ki system() fonksiyonu ile çalıştırılması gereken bir fonksiyon. Komutun çıktısı, günlük adına özel olarak oluşturulmuş bir dosyaya yönlendiriliyor (XGünlüğü_Filtred) (> "outFile"). Başarılı olunca da başarılı olduğunu belirtiyor.
Bunlar sonunda da 3 olay XML toplamı bize yönetim olaylarını veriyor neredeyse Yönetim olaylarının bizim yapamadığımız kısmı sadece evtx formatında olması. Biz bunu direkt olarak bir kodla yapamıyoruz ne yazık ki. Export mantığında sadece XML veya UTF sorunu olmazsa txt şansımız var.
Daha komplike bir kod kullanarak da XML yapılarını çekebiliyoruz:
ExportEventsByID fonksiyonu ile parametre olarak aldığımız günlük ismi (logName), Event ID (eventID) ve çıktı dosyasının yolu (outputFile) beraber çalışıyorlar. Fonksiyon içerisinde önce Event ID’ye göre bir XPath sorgusu oluşturuyorr ve bu sorgu EvtQuery() ile yürütülerek sorgu tutacağı elde ediliyor. Eğer sorgu başarısız olursa, sorgu basitçe kapatılıyor.
Daha sonra bir for döngümüz var. EvtNext() fonksiyonu ile sıradaki maksimum 10 olay alınmaya çalışılıyor. Eğer daha fazla olay yoksa döngü bitiyor. Her bir olay için EvtRender() çağrılıyor ki burada olaylar XML biçimine çevriliyor. EvtRender() başlangıçta tahmini bir buffer büyüklüğü ile çağrılıyor ama bu buffer yetersiz kalırsa diye gerekli boyut hesaplanıyor ve buffer yeniden boyutlandırılıyor, sonra tekrar EvtRender() ile XML verisi alınıyor. Bu aşamada oluşabilecek hatalar kontrol ediliyor. Başarılı dönüşümde, elde edilen XML metni çıktı dosyasına yazılır ve kod kapanır.
Ana fonksiyon (wmain) ise bir Event ID listesi (41, 161, 129) ve bu ID’lere karşılık gelen çıktı dosyalarının yollarını tutuyor. Döngü ile her Event ID için ExportEventsByID fonksiyonu çağrılıyor ve işlem gerçekleştiriliyor.
Bunları yapmadan önce mutlaka erişim iznini de kodun içine entegre etmenizi öneriyorum. Zira EvtLog export'ları için Windows yönetici izni arıyor...
Kaynaklar: Orin-Thomas
Neden Windows günlükleri veya neden özel görünümler olduğunu hiç merak ettiniz mi? Aslında ben de etmemiştim. Sadece bununla alakalı bir kodlama yaparken detaylıca araştırmam gerektiği için fark etmiş oldum. Yönetim olayları neden Windows günlüklerine dahil değil? Neden diğerleri gibi basitçe export edilmiyor ki?
Aslında cevabı basit, Yönetim olayları bir sistem günlüğü olmadığı için.
Windows Olay Günlüklerini, sistemdeki her önemli işlemi, hata veya uyarıyı kaydetmek üzere tasarlanmış merkezi bir log sistemi olarak düşünün. Bu sistemde; Sistem (System), Uygulama (Application), Kur (Setup), Güvenlik (Security) gibi temel log kategorileri bulunur. Her olay, bir event ID, zaman damgası, olay seviyesi, kaynak (provider), açıklama ve gerekirse binary veri içerir.
Yönetim Olayları (Administrative Events), bir sistem günlüğü değil. Diğerleri bir günlük ama yönetim olayları değil çünkü bu loglar arasından seviye olarak kritik (Critical), hata (Error) ve uyarı (Warning) olanların öncelikli olarak filtrelenip sistem yöneticisine sunulduğu özel bir kategori olduğu için. Aslında yönetim olayları ayrı bir kategori değil, diğer kategori log'larının filtrelenmiş hali.
Bu filtreleme, genellikle "Custom Views" altında bulunan "Administrative Events" görünümü ile yapılıyor. Bu görüntüleme sayesinde, sistemdeki en önemli olaylara hızlıca erişim sağlanabilir. Bunu sistem üzerinden Yönetim olayları > Özellikler > Filtre düzenle... başlığı altından inceleyebilirsiniz.
Peki bu filtreleme nedir diye düşünürken, Olay düzeyi kısmı dikkatimi çekti. Sonradan bu konuda şunu fark ettim ki Windows bu olayları level sistemine göre kaydedip filtreliyor.
Aslında her Windows olayı, ciddilik derecesini belirtmek için bir "Level" (Seviye) bilgisiyle etiketleniyor. Bu seviye, olayın sistem stabilitesi açısından önemini belirtmekte kullanılıyor...
| Level | Seviye Adı | Anlamı | Kullanım Amacı |
|---|---|---|---|
| 1 | Critical | Önemli | Aniden yeniden başlatma, çökme gibi sistem için yıkıcı etkisi olan olaylar |
| 2 | Error | Hata | Servis durması, disk hatası gibi müdahale gerektiren olaylar |
| 3 | Warning | Uyarı | Geçici sorunlar, potansiyel riskler |
| 4 | Information | Bilgi | Normal işleyiş olayları, servis başlatıldı vb. |
| 5 | Verbose | Ayrıntılı | Debug, ayrıntılı analiz için kullanılır |
Bu aslında neden yönetim olaylarının her zaman Level 1, 2 ve 3 (Critical, Error, Warning) olaylarından oluştuğunu kanıtlıyor. Bu olaylar sistem kararlılığı ve sorun tespiti için temel analiz malzemesidir.
Her bir olay, belirli bir bileşen ya da servis tarafından üretilir. Bu servis "Provider" (Kaynak/Sağlayıcı) olarak adlandırılır. Olay kaynağı, olayı hangi sistem bölümü veya uygulamasının oluşturduğunu belirler.
| Provider | Özellik | Kullanım Amacı |
|---|---|---|
| Kernel-Power | Sistem | Beklenmedik kapanmalar, ACPI sorunları |
| Volmgr | Disk | Volume Manager olayları (disk arızaları) |
| Kernel-PnP | Donanım | Donanım tak- çıkar olayları |
| WHEA-Logger | Donanım | Donanımsal hata raporları |
| Application Error | Uygulama | Uygulama çökmeleri |
Filtreleme yaparken bu provider adı kullanılarak olaylar daraltılabilir.
Buraya kadar olan kısım bu olayların kısaca ne olduğu ve filtrelendirilmesinin nasıl işlendiği ile ilgiliydi. Bundan sonrası ile bunun nasıl yapılacağı ile ilgili.
=CMD=
İlk ve en temel komutumuz wevtutil. Kendisi olay günlüğü (event log) işlemleri yapmak için kullanılan bir komut satırı aracı. Bu araç sayesinde sistem, güvenlik, uygulama gibi günlükleri yönetebilir, içeriğini görüntüleyebilir, yedekleyebilir veya silebiliyorsun.
Örnek olarak
Rich (BB code):
wevtutil epl System "%USERPROFILE%\Desktop\system_log.evtx"
Wevutil komut aracı ile sistem olaylarını masaüstüne kaydedebilirsiniz.
Rich (BB code):
wevtutil epl Application C:\Masaüstü\application.evtx
Örneği ile uygulama olaylarını masaüstüne alabilirsiniz.
epl sorgusu Windows'un wevutil makalesinde de belirttiği gibi [{epl | export-log}} ilgili olayları export etme sorgusudur.Bu noktada Wevtutil ile Administrative Events gibi özel görünümleri expor edemiyoruz direkt olarak çünkü C:\ProgramData\Microsoft\Event Viewer\Views altında sadece .xml olarak tutuluyorlar ve evtx şeklinde export etmek olanaksız oluyor. Bunu 2 şekilde filtreleyebiliyoruz.
Level Filtreleme:
Rich (BB code):
wevtutil qe System /q:"*[System[(Level=1 or Level=2 or Level=3)]]" /f:text > "%USERPROFILE%\Desktop\Olaylar.text"
Dipnot: Bunu kullanmanızı belli bir olay sayısı kullanmıyorsanız (Son 10 olayı al tarzı.) önermem. Muhtemelen o text dosyasını açtığınız an donacaktır. Bunun da UTF8 formatından kaynaklandığını düşünüyorum... XML formatını kullanmaya çalışın daha karışık olsa da.
Consider Filtreleme:
Rich (BB code):
wevtutil qe System /q:"*[System[(EventID=41 or EventID=6008)]" /f:text > %USERPROFILE%\Desktop\FiltrelenmisOlay.txt
Bu kod da açıkça, System olaylarındaki 41 veya 6008 ID'li olayları tespit edip Masaüstündeki FiltrelenmisOlay isimli text belgesine yazıyor.
Dipnot: Belli bir ID aramasında txt kullanmak hem daha temiz bir sonuç verir hem de donmayan bir not belgesi sunar.
qeİlgili olayları sorgulayan yardımcı komut./q:"..."İlgili olay yolunu sorgulayan yardımcı komut./f:textçıktı formatı.
/c: yardımcı komutuyla son ".." olay sayısı kadarının level filtrelemesini yapabilirsiniz. Örneğin:
Rich (BB code):
wevtutil qe System /q:"*[System[(Level=1 or Level=2 or Level=3)]]" /f:text /c:50
Aynı şekilde bunları daha düzenli ve esnek olarak PowerShell ile de oluşturabilme şansımız var. Bu noktada karşımıza Get-WinEvent komut aracı çıkıyor. Öncelikle bunun da temelinde wevtutil ile aynı şeyi yaptığını söyleyebilirim. Farkları birden farklı birkaç olayı export etmeye çalışırken ortaya çıkıyor.
Örneğin PowerShell ile basit bir oluşturma kodu yazabiliriz:
Rich (BB code):
Get-WinEvent -LogName System -MaxEvents 100 | Out-File "C:\Logs\SistemOlaylari.txt"
Böylelikle son 100 olayı çekebiliriz. CMD komutundan farkı ise, PowerShell ile tarihe göre sıralama yapma şansımız da var:
Rich (BB code):
Get-WinEvent -FilterHashtable @{
LogName = 'System'
Level = 1,2
StartTime = (Get-Date).AddDays(-1)
} | Sort-Object TimeCreated -Descending
Böylelikle son 1 günde oluşan 1-2 seviyeli olayları çekebiliriz. Buna bir Out-File komutu da eklemeniz gerekecektir.
Bence asıl farkları birden fazla bir sürü olayı XML olarak çekerken ortaya çıkıyor. Örneğin:
Rich (BB code):
$eventIDs = 41, 161, 129 // Olayları çek
$logName = "System"
foreach ($id in $eventIDs) {
$events = Get-WinEvent -LogName(Örnek olarak sistem olayları) $logName | Where-Object { $_.Id -eq $id }
$outputFile = "$env:USERPROFILE\Desktop\event_$id.xml"
// XML olarak kaydetmek için her olayı ToXml() ile dönüştür ve dosyaya yaz
$events | ForEach-Object { $_.ToXml() } | Out-File -FilePath $outputFile -Encoding UTF8
}
Burada yaptığımız şey $eventIDs dizisinde tanımlı olan her bir Event ID için System günlüklerinden o ID'ye ait olayları çekiyor. Daha sonra her olayı XML formatına dönüştürmek için ToXml() metodunu kullanıyor ve dönüştürülen XML verilerini kullanıcı masaüstünde, ilgili Event ID'yi içeren ayrı bir dosyaya (örneğin event_41.xml, event_161.xml, event_129.xml) kaydediyor. Böylece her Event ID için ayrı bir XML dosyası oluşturulmuş oluyor.
Aynı şeyi CMD üzerinden yapmaya çalışırsak ise:
Rich (BB code):
wevtutil qe System /q:"*[System[EventID=41]]" /f:xml > "%USERPROFILE%\Desktop\event_41.xml"
wevtutil qe System /q:"*[System[EventID=161]]" /f:xml > "%USERPROFILE%\Desktop\event_161.xml"
wevtutil qe System /q:"*[System[EventID=129]]" /f:xml > "%USERPROFILE%\Desktop\event_129.xml"
Her bir ID için tek tek consider filtrelemesi yapmamız gerekecekti.
Bu noktada bu işin en iyi kısmı da özellikle XML kaydında C tabanındaki kütüphane ve kodlarını bulduktan sonra oldu.
C++ ile Windows Event Log’dan belli Event ID’lere göre filtreleyip XML formatında dışa aktarmak da Windows API’leri kullanarak yapılabiliyor. Özellikle Windows Event Log API (EvtOpenLog, EvtQuery, EvtNext, EvtRender) bu iş için varlar.
Bundan önce DumpFinder programımda kullandığım temel XML çekme mantığını anlatacağım.
C++:
std::string eventLogPath = systemRoot + "System32\\winevt\\Logs\\";
std::string evtxLogs[] = { "Application.evtx", "System.evtx", "Security.evtx" };
for (const auto& log : evtxLogs) {
std::string src = eventLogPath + log;
if (fs::exists(src)) {
try {
fs::copy_file(src, logFolder + "\\" + log, fs::copy_options::overwrite_existing);
std::cout << log << msg("log_copy") << std::endl;
reportFile << log << " copied." << std::endl;
}
catch (const std::exception& e) {
std::cerr << msg("log_copy_fail") << e.what() << std::endl;
}
}
else {
std::cerr << log << msg("log_missing") << std::endl;
}
}
std::string logs[] = { "Application", "System", "Security" };
for (const auto& log : logs) {
std::string outFile = logFolder + "\\" + log + "_Filtered.txt";
std::string cmd = "wevtutil qe " + log + " /q:\"*[System[(Level=1 or Level=2 or Level=3)]]\" /f:xml > \"" + outFile + "\"";
int ret = system(cmd.c_str());
if (ret != 0) {
std::cerr << log << msg("log_filter_fail") << std::endl;
}
else {
std::cout << log << msg("log_filter_ok") << outFile << std::endl;
reportFile << log << " filtered -> " << outFile << std::endl;
}
}
}
else {
reportFile << (systemLang == Lang::TR ? "Olay raporlari eklenmedi, sadece dump dosyalarý kopyalandi." : "No event logs included, only dump files copied.") << std::endl;
}
reportFile << "\n" << msg("report_summary");
if (secim == 1)
reportFile << msg("report_with_logs");
reportFile.close();
std::cout << "\n" << msg("report_complete") << reportFilePath << std::endl;
}
catch (const fs::filesystem_error& e) {
std::cerr << "Hata: " << e.what() << std::endl;
if (reportFile.is_open()) reportFile.close();
return 1;
}
Kodda ilk önce çok basit bir şekilde System32\\winevt\\Logs\\ dizinini eventLogPath adında bir yola tanımladım. Ardından Application.evtx, System.evtx ve Security.evtx olay günlüklerinin adlarını içeren bir dizi (evtxLogs) tanımladım. Sonra da bir günlük dosyası için döngüye girer ve dosya mevcutsa (fs::exists(src)), fs::copy_file fonksiyonu ile bu dosya, hedef klasöre (logFolder) kopyalanıyor. Kopyalanan klasör DumpFinder içinde DmpGunluk adlı bir klasör. Kopyalama başarılı olunca başarılı olduğuna dair bir uyarı görürsünüz. Genelde filtrelemesi başarılı oluyor lakin evtx formatı için izin gerekebiliyor özellikle.
İkinci döngüde de "Application", "System" ve "Security" günlük isimlerini wevtutil komutu ile sorgulama qe sorgusu başlatmak için kullandım. Her bir günlük için, XML formatında ve sadece belirli öncelik düzeylerindeki olayları içeren bir sorgu (/q) oluşturuyorum ve komut satırında çalıştırılacak komutun tam metni, cmd değişkeninde oluşturuyorum. Bu tabii ki system() fonksiyonu ile çalıştırılması gereken bir fonksiyon. Komutun çıktısı, günlük adına özel olarak oluşturulmuş bir dosyaya yönlendiriliyor (XGünlüğü_Filtred) (> "outFile"). Başarılı olunca da başarılı olduğunu belirtiyor.
Bunlar sonunda da 3 olay XML toplamı bize yönetim olaylarını veriyor neredeyse
Daha komplike bir kod kullanarak da XML yapılarını çekebiliyoruz:
C++:
#include <windows.h> // Temel Windows API fonksiyonları için
#include <winevt.h> // Windows Event Log API fonksiyonları için
#include <iostream> // Konsola çıktı için
#include <fstream> // Dosya işlemleri için
#include <vector> // Dinamik dizi için
#pragma comment(lib, "wevtapi.lib") // wevtapi.lib kütüphanesi (Event Log API)
void ExportEventsByID(const std::wstring& logName, DWORD eventID, const std::wstring& outputFile)
{
// Event ID filtrelemesi için XPath sorgusu oluşturuyoruz
std::wstring query = L"*[(System/EventID=" + std::to_wstring(eventID) + L")]";
// Belirtilen log ve sorgu ile Event Log sorgusu başlatılıyor
EVT_HANDLE hQuery = EvtQuery(NULL, logName.c_str(), query.c_str(), EvtQueryChannelPath);
// Eğer sorgu başarısızsa hata mesajı yazdırıp fonksiyondan çıkıyoruz
if (!hQuery)
{
std::wcerr << L"EvtQuery failed with error code " << GetLastError() << std::endl;
return;
}
// XML çıktı dosyasını açıyoruz özellikle binary formatı öneriyoruz
std::ofstream outFile(outputFile, std::ios::out | std::ios::binary);
// Dosya açılamazsa hata verip ve sorguyu kapatıyoruz
if (!outFile.is_open())
{
std::wcerr << L"Cannot open output file: " << outputFile << std::endl;
EvtClose(hQuery);
return;
}
const int bufferSize = 4096; // Başlangıç buffer boyutumuz
std::vector<wchar_t> buffer(bufferSize); // XML verisini tutacak buffer
DWORD returnedCount = 0;
EVT_HANDLE events[10]; // Aynı anda 10 olayı tutacak dizi
while (true)
{
// 10 olayı sorgudan alıyoruz, sonsuz bekleme süresi ile (INFINITE)
if (!EvtNext(hQuery, 10, events, INFINITE, 0, &returnedCount))
{
// Eğer artık alınacak olay kalmamışsa döngüyü bitiriyoruz
if (GetLastError() == ERROR_NO_MORE_ITEMS)
break;
std::wcerr << L"EvtNext failed: " << GetLastError() << std::endl;
break;
}
// Alınan her olay için aynı işlemleri yapıyoruz
for (DWORD i = 0; i < returnedCount; i++)
{
DWORD bufferUsed = 0;
DWORD propertyCount = 0;
// Olayı XML formatına dönüştürmek için buffer boyutunu sorguluyoruz
if (!EvtRender(NULL, events[i], EvtRenderEventXml, bufferSize * sizeof(wchar_t), buffer.data(), &bufferUsed, &propertyCount))
{
// Yetersizse büyüt
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
buffer.resize(bufferUsed / sizeof(wchar_t));
if (!EvtRender(NULL, events[i], EvtRenderEventXml, bufferUsed, buffer.data(), &bufferUsed, &propertyCount))
{
std::wcerr << L"EvtRender failed: " << GetLastError() << std::endl;
EvtClose(events[i]);
continue;
}
}
else
{
// Başka hata durumunda mesaj yazıp olayı kapattıyoruz
std::wcerr << L"EvtRender failed: " << GetLastError() << std::endl;
EvtClose(events[i]);
continue;
}
}
// XML verisini dosyaya yaz
outFile << std::wstring(buffer.data()) << std::endl;
// Olayı kapat (kaynakları serbest bırak)
EvtClose(events[i]);
}
}
// Dosyayı kapat
outFile.close();
// Sorguyu kapat
EvtClose(hQuery);
}
int wmain()
{
// İşlem yapılacak Event ID’ler ve karşılık gelen çıktı dosya yolları
std::vector<DWORD> eventIDs = {41, 161, 129};
std::vector<std::wstring> outputFiles = {
L"C:\\Users\\Public\\Desktop\\event_41.xml",
L"C:\\Users\\Public\\Desktop\\event_161.xml",
L"C:\\Users\\Public\\Desktop\\event_129.xml"
};
const std::wstring logName = L"System"; // Okunacak Event Log ismi - Burada sistemi seçtim
// Her Event ID için ExportEventsByID fonksiyonunu çağır
for (size_t i = 0; i < eventIDs.size(); i++)
{
std::wcout << L"Exporting Event ID " << eventIDs[i] << L" to " << outputFiles[i] << std::endl;
ExportEventsByID(logName, eventIDs[i], outputFiles[i]);
}
return 0;
}
ExportEventsByID fonksiyonu ile parametre olarak aldığımız günlük ismi (logName), Event ID (eventID) ve çıktı dosyasının yolu (outputFile) beraber çalışıyorlar. Fonksiyon içerisinde önce Event ID’ye göre bir XPath sorgusu oluşturuyorr ve bu sorgu EvtQuery() ile yürütülerek sorgu tutacağı elde ediliyor. Eğer sorgu başarısız olursa, sorgu basitçe kapatılıyor.
Daha sonra bir for döngümüz var. EvtNext() fonksiyonu ile sıradaki maksimum 10 olay alınmaya çalışılıyor. Eğer daha fazla olay yoksa döngü bitiyor. Her bir olay için EvtRender() çağrılıyor ki burada olaylar XML biçimine çevriliyor. EvtRender() başlangıçta tahmini bir buffer büyüklüğü ile çağrılıyor ama bu buffer yetersiz kalırsa diye gerekli boyut hesaplanıyor ve buffer yeniden boyutlandırılıyor, sonra tekrar EvtRender() ile XML verisi alınıyor. Bu aşamada oluşabilecek hatalar kontrol ediliyor. Başarılı dönüşümde, elde edilen XML metni çıktı dosyasına yazılır ve kod kapanır.
Ana fonksiyon (wmain) ise bir Event ID listesi (41, 161, 129) ve bu ID’lere karşılık gelen çıktı dosyalarının yollarını tutuyor. Döngü ile her Event ID için ExportEventsByID fonksiyonu çağrılıyor ve işlem gerçekleştiriliyor.
Bunları yapmadan önce mutlaka erişim iznini de kodun içine entegre etmenizi öneriyorum. Zira EvtLog export'ları için Windows yönetici izni arıyor...
C++:
#include <AccCtrl.h>
#include <windows.h>
#include <Aclapi.h>
void ErisimIstegi(const std::string& filePath) {
DWORD result = SetNamedSecurityInfoA(
const_cast<char*>(filePath.c_str()),
SE_FILE_OBJECT,
DACL_SECURITY_INFORMATION,
NULL,
NULL,
NULL,
NULL
);
if (result != ERROR_SUCCESS) {
std::cerr << "Izinle Alinamadi: " << result << std::endl;
}
}
Kaynaklar: Orin-Thomas