[WinAPI ETW] Użycie dostawcy Microsoft-Windows-Security-Auditing w aplikacji konsumenta eventów czasu rzeczywistego

Cześć :slight_smile:

 

Moim zadaniem jest stworzenie aplikacji pod system Windows wykorzystujący ETW (Event Tracing for Windows). Dokładnie muszę napisać konsumenta zdarzeń, przyjmujący i przetwarzający je w czasie rzeczywistym. Jako dostawcę zdarzeń chcę wykorzystać systemowy ‘Microsoft Windows Security Auditing’.

 

Stworzyłem prostą aplikację będącą, w nomenklaturze ETW, kontrolerem i konsumentem jednocześnie. Bazowałem na przykładach stąd http://msdn.microsoft.com/en-us/library/windows/desktop/ee441324%28v=vs.85%29.aspx i stąd http://msdn.microsoft.com/en-us/library/windows/desktop/ee441325%28v=vs.85%29.aspx zmieniając zasadniczo tylko flagi odpowiedzialne za włączenie przetwarzania w trybie czasu rzeczywistego.

 

Z uwagi, że na razie chcę uruchomić cokolwiek, wszystko jest robione w funkcji main, która wygląda tak:

LPTSTR SessionName = L"hahahaaa";
ULONG status = ERROR_SUCCESS;
PEVENT_TRACE_PROPERTIES pSessionProperties = NULL;
EVENT_TRACE_LOGFILE trace;
TRACEHANDLE hTrace = 0;
TRACEHANDLE hSession = 0;
const GUID providerId = { 0x54849625, 0x5478, 0x4994, { 0xA5, 0xBA, 0x3E, 0x3B, 0x03, 0x28, 0xC3, 0x0D } };
//const GUID providerId = { 0xA68CA8B7, 0x004F, 0xD7B6, { 0xA6, 0x98, 0x07, 0xE2, 0xDE, 0x0F, 0x1F, 0x5D } };

HANDLE hToken = NULL;
HANDLE hProcess = NULL;
hProcess = GetCurrentProcess();
if (OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hToken) == FALSE) {
    printf("Error: Couldn't open the process token\n");
    goto cleanup;
}
if(!SetPrivilege(hToken, SE_SECURITY_NAME, TRUE)) goto cleanup;

if (!pSessionProperties) {
    const size_t buffSize = sizeof(EVENT_TRACE_PROPERTIES)+(_tcslen(SessionName) + 1) * sizeof(TCHAR);
    pSessionProperties = reinterpret_cast<EVENT_TRACE_PROPERTIES *>(malloc(buffSize));
    ZeroMemory(pSessionProperties, buffSize);
    pSessionProperties->Wnode.BufferSize = buffSize;
    pSessionProperties->Wnode.ClientContext = 1;
    pSessionProperties->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
    pSessionProperties->LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
    pSessionProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
}

// Create the trace session.
status = StartTrace(&hSession, SessionName, pSessionProperties);
if (ERROR_SUCCESS != status) {
    wprintf(L"StartTrace() failed with %lu\n", status);
    goto cleanup;
}

status = EnableTraceEx2(hSession, &providerId, EVENT_CONTROL_CODE_ENABLE_PROVIDER, TRACE_LEVEL_VERBOSE, 0, 0, 0, NULL);
if (ERROR_SUCCESS != status) {
    wprintf(L"EnableTrace() failed with %lu\n", status);
    goto cleanup;
}

ZeroMemory(&trace, sizeof(EVENT_TRACE_LOGFILE));
trace.LogFileName = NULL;
trace.LoggerName = SessionName;
trace.CurrentTime = 0;
trace.BuffersRead = 0;
trace.BufferSize = 0;
trace.Filled = 0;
trace.EventsLost = 0;
trace.Context = NULL;
trace.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD;
trace.EventRecordCallback = (PEVENT_RECORD_CALLBACK)(ProcessEvent);

hTrace = OpenTrace(&trace);
if (INVALID_PROCESSTRACE_HANDLE == hTrace)
{
    wprintf(L"OpenTrace failed with %lu\n", GetLastError());
    goto cleanup;
}

status = ProcessTrace(&hTrace, 1, 0, 0);
if (status != ERROR_SUCCESS && status != ERROR_CANCELLED)
{
    wprintf(L"ProcessTrace failed with %lu\n", status);
    goto cleanup;
}

W punkcie aplikacji gdzie nadchodzące zdarzenia są przetwarzanie przez funkcję ‘ProcessTrace()’ program powinien wypisać na standardowe wyjście metadane każdego odebranego zdarzenia albo czekać na kolejne zdarzenia. Niestety nic a nic się nie dzieje, jedynie znak zachęty ‘zachęcająco’ sobie miga. Wszystkie zdarzenia wygenerowane przeze mnie (np. włączyłem Szczegółowe śledzenie - Inspekcja tworzenia procesu, i uruchomiłem dowolną aplikację) są zapisywane i wyświetlane w Dzienniku Zdarzeń, ale w moim programie niestety nic się nie dzieje.

 

Pierwszą rzecz na jaką wpadłem to problem z brakiem odpowiednich uprawnień. Używając tego przykładu http://msdn.microsoft.com/en-us/library/windows/desktop/aa446619%28v=vs.85%29.aspx ustawiłem w aplikacji uprawnienie ‘SE_SECURITY_NAME’ no i oczywiście uruchomiłem aplikację z uprawnieniami administratora. Nic to jednak nie dało.

 

Drugą rzeczą jaką spróbowałem była zmiana nazwy sesji. Pomyślałem, że może być podobnie jak z ‘Windows Kernel Trace’ który może logować zdarzenia jedynie do sesji systemowej o nazwie ‘NT Kernel Logger’. Jedynie co znalazłem w sieci to fakt, że ‘Microsoft Windows Security Auditing’ jest powiązany z sesją systemową ‘EventLog-Security’. Gdy ustawiałem nazwę sesji na taką otrzymywałem błąd ‘Access Denied’. Po przejrzeniu wszystkich dostępnych uprawnień nie znalazłem niczego co w tym kontekście miałoby jeszcze sens i nie za bardzo wiem jakie jeszcze uprawnienie powinienem nadać.

 

Ostatnią rzeczą jaką spróbowałem było wykorzystanie windowsowego narzędzia ‘logman’ za pomocą którego zbierałem zdarzenia i zapisywałem do pliku. Jednak sytuacja wyglądała podobnie. Kiedy ustawiałem nazwę sesji na ‘EventLog-Security’ otrzymywałem błąd ‘Access Denied’. Z drugiej strony, gdy podałem inną nazwę sesji, jedynie co zapisywało się do pliku było zdarzenie dostarczone przez ‘MSNT_SystemTrace’, który jest klasą abstrakcyjną dla innych zdarzeń i które jest zawsze zapisywanie w pliku *.etl, na jego początku.

 

Jeżeli zmienię dostawcę na np. ‘Microsoft Windows Kernel General’ (zakomentowany GUID) i wygeneruję dowolne zdarzenie (np. zaktualizuję zegar systemowy) wszystko działa jak należy (zarówno w mojej aplikacji jak i za pomocą ‘logman’).

 

Pracuję na Windows 7 Professional x64 i Visual Studio Ultimate 2013.

 

Moje pytanie brzmi: co mam zrobić by móc odbierać zdarzenia od ‘Microsoft Windows Security Auditing’?

 

Z góry serdecznie dziękuję za wszelką pomoc i sugestie :slight_smile:

Nie odpowiem wprost bo, szczerze, nie chce mi się debugować Twojego kodu. :wink: Ale! Pisałem w życiu kontrolera/konsumenta trzykrotnie, zawsze wychodząc od gotowców z Windows SDK (Samples\winbase\Eventing\Controller i sąsiednie) i nigdy nie miałem zbyt wielu problemów. Kilka rzeczy nie jest udokumentowanych zbyt dobrze i musiałem się na nie raz nadziać. Najdziwniejszym jest to, że często katalog w którym znajduje się aplikacja i/lub manifest nie może zawierać spacji, ani mieć zbyt długiej ścieżki. Brzmi absurdalnie, ale tak bywa. Nie pamiętam też, czy przypadkiem nie trzeba uruchamiać kontrolera z wyniesionej konsoli.

BTW: widzę, że na SO też spytałeś. :smiley:

Dzięki za odpowiedź! :slight_smile:

 

Sprawdziłem czy fakt położenia aplikacji ma znaczenie, ale niestety nic to nie dało. Tak samo wykluczyłem działalność antywirusa/firewalla, który mógłby uznać moją aplikację za szkodliwą.

 

W trybie przetwarzania w czasie rzeczywistym jest to pomocne, bo ProcessTrace() zakończy swoje działanie jeżeli odbierze od kontrolera sygnał zakończenia. Widziałem kilka przykładów gdzie wszystko było w jednej aplikacji i jakoś to ludziom działało.

 

Obstawiam, że coś musi być specyficznego z ‘Microsoft Windows Security Auditing’, bo - tak jak pisałem - jeżeli zmienię dostawcę na np. ‘Microsoft Windows Kernel General’ to wszystko działa jak należy.

Podobnie z narzędziem logman - dla tego pierwszego dostawcy ani myśli działać, podczas gdy dla drugiego wszystko jest w należytym porządku.

 

Niestety w sieci nie znalazłem nic o ‘Microsoft Windows Security Auditing’ w kontekście potrzebnych uprawnień do korzystania z niego, poza faktem, że zarówno administrator jak i użytkownik lokalny może swobodnie czytać z logu Security. Założyłem, że skoro jako administrator mogę czytać ten log to i mogę przetwarzać zdarzenia w czasie rzeczywistym.

 

 

Utknąłem w martwym punkcie i próbuję zwiększyć swoje szanse :stuck_out_tongue:

Nie pamiętam już szczegółów, bo minęło kilka lat, ale coś mi świta, że były dwa zestawy funkcji - jeden do pobierania strumienia danych, drugi do pobierania z dekodowaniem. Miałem raz sytuację, że mogłem czytać blob, ale nie mogłem go zdekodować. Problemem był dostęp do binarki z manifestem. Nie wiem też, czy to może mieć znaczenie, ale klucze w rejestrze opisujące źródła zdarzeń mają ACLe, które można w regedit zmienić. Zerknąłbym i tam w rozpaczliwym przypadku. :wink:

Sęk w tym, że na tym etapie na razie niczego nie dekoduje - po prostu chcę odebrać zdarzenie i wypisać na konsoli, że oto odebrałem zdarzenie. Co prawda funkcja obsługi zdarzenia, wzięta z przykładu, robi nieco więcej bo wypisuje na konsole metadane zdarzenia. O ile się nie mylę nie powinno to mieć znaczenia, gdyż metadane powinny być dostępne zawsze.

 

Co zauważyłem, to problem leży gdzieś wyżej, ponieważ pomimo tego, że generuję zdarzenia, to w mojej aplikacji ani razu nie jest wołana funkcja obsługi zdarzenia. To by oznaczało, że w ogóle nie dostaję żadnych zdarzeń. Sesja jest tworzona, co da się potwierdzić wypisując logman’em aktualnie uruchomione sesje.

 

 

Mógłbyś napisać coś więcej o tym? Ze szczątków informacji jakie są w dokumentacji, wnioskuję że uruchamiając aplikację w trybie administratora, powinienem mieć dostęp co najmniej odczytu.

O to mi chodzi.

6Y56SiK.gif

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Security

Debugowanie takich rzeczy to PITA, ale zerknąłbym na dwie rzeczy po pobraniu procmona.

  1. Jakie pliki/klucze Twoja aplikacja dotyka po uruchomieniu? Czy wszystkie operacje kończą się sukcesem, czy może widać błędy?

  2. Jeśli wszystko jest OK (albo już jest po zmianach w kodzie), ale nie działa, zerknąłbym na aktywność eventvwr.msc kiedy przeglądasz log (w procmonie proces to oczywiście mmc a nie eventvwr). Może zauważysz coś ciekawego.

Co do tych ACLi zdarzeń w rejestrze, to pomimo dodania uprawnień dla użytkownika, który otwiera aplikację, do czytania i pełnej kontroli w całej gałęzi Security, nic to nie zmieniło.

 

Rozwiązanie okazuje się dość proste. Ograniczyłem się do stworzenia typowego konsumenta (OpenTrace() i ProcessTrace()) który korzysta z sesji systemowej ‘Eventlog-Security’. Uruchomiłem za to aplikację z uprawnieniami LocalSystem, tak jak zasugerował Luke w tym wątku http://stackoverflow.com/questions/27734324/using-microsoft-windows-security-auditing-provider-in-real-time-consumer-with i wszystko zaczęło działać :slight_smile:

 

Dzięki za podpowiedź z procmonem - bardzo ciekawe narzędzie :wink:

 

Wielkie dzięki za pomoc i zainteresowanie :slight_smile: